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,519 +0,0 @@
|
|
|
1
|
-
require 'json'
|
|
2
|
-
require 'etc'
|
|
3
|
-
require 'open3'
|
|
4
|
-
require 'fileutils'
|
|
5
|
-
require_relative '../base/system_info'
|
|
6
|
-
|
|
7
|
-
module EasyAI
|
|
8
|
-
module Auth
|
|
9
|
-
class AuthClaude
|
|
10
|
-
# 常量定义
|
|
11
|
-
SERVICE_NAME = "Claude Code-credentials"
|
|
12
|
-
TOKEN_KEY = "CLAUDE_CODE_OAUTH_TOKEN"
|
|
13
|
-
ALT_TOKEN_KEY = "ANTHROPIC_AUTH_TOKEN"
|
|
14
|
-
CLAUDE_JSON_PATH = File.expand_path("~/.claude.json")
|
|
15
|
-
|
|
16
|
-
def self.configure(config, user_name = nil)
|
|
17
|
-
return false unless config
|
|
18
|
-
|
|
19
|
-
# 第一步:清理系统环境,确保干净的运行环境
|
|
20
|
-
clean_system_environment
|
|
21
|
-
|
|
22
|
-
# 检查认证配置优先级
|
|
23
|
-
keychain_exists = macos? && config["key_chain"] && config["key_chain"]["claudeAiOauth"] && config["key_chain"]["service_name"]
|
|
24
|
-
# 检查两种可能的令牌键
|
|
25
|
-
env_token_exists = config["env"] && (config["env"][TOKEN_KEY] || config["env"][ALT_TOKEN_KEY])
|
|
26
|
-
|
|
27
|
-
# 如果使用 Keychain,需要将令牌从 Keychain 读取到配置中
|
|
28
|
-
if keychain_exists
|
|
29
|
-
token_from_keychain = get_token_from_keychain(config["key_chain"]["service_name"])
|
|
30
|
-
if token_from_keychain
|
|
31
|
-
config["env"] ||= {}
|
|
32
|
-
config["env"][TOKEN_KEY] = token_from_keychain
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# 根据优先级确定认证方法
|
|
37
|
-
if keychain_exists
|
|
38
|
-
configure_keychain(config)
|
|
39
|
-
clean_env_variables
|
|
40
|
-
elsif env_token_exists
|
|
41
|
-
verify_token_config(config)
|
|
42
|
-
clean_keychain if macos?
|
|
43
|
-
else
|
|
44
|
-
# 先检查环境变量中是否已有令牌
|
|
45
|
-
token = check_env_tokens
|
|
46
|
-
|
|
47
|
-
if token.nil? || token.empty?
|
|
48
|
-
# 环境变量中没有,才提示用户输入
|
|
49
|
-
token = prompt_for_token
|
|
50
|
-
return false if token.nil? || token.empty?
|
|
51
|
-
else
|
|
52
|
-
log_verbose(" ✓ 使用环境变量中的令牌")
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# 将令牌添加到配置中进行处理
|
|
56
|
-
# 如果配置中已经有 ANTHROPIC_AUTH_TOKEN,使用该键;否则使用 CLAUDE_CODE_OAUTH_TOKEN
|
|
57
|
-
config["env"] ||= {}
|
|
58
|
-
if config["env"][ALT_TOKEN_KEY]
|
|
59
|
-
config["env"][ALT_TOKEN_KEY] = token
|
|
60
|
-
else
|
|
61
|
-
config["env"][TOKEN_KEY] = token
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
verify_token_config(config)
|
|
65
|
-
clean_keychain
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# 更新 ~/.claude.json 文件
|
|
69
|
-
update_claude_json(config) if config["claude_json"]
|
|
70
|
-
|
|
71
|
-
# 配置代理设置
|
|
72
|
-
configure_proxy(config) if config["claude_proxy"]
|
|
73
|
-
|
|
74
|
-
true
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
private
|
|
78
|
-
|
|
79
|
-
# 辅助方法
|
|
80
|
-
def self.macos?
|
|
81
|
-
Base::SystemInfo.macos?
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def self.current_user
|
|
85
|
-
Base::SystemInfo.current_user
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def self.log_verbose(message)
|
|
89
|
-
puts message if ENV['EASYAI_DEBUG'] || ENV['EASYAI_VERBOSE']
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def self.log_debug(message)
|
|
93
|
-
puts message if ENV['EASYAI_DEBUG']
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
# 通用的 shell 文件处理方法
|
|
97
|
-
def self.process_shell_files(operation_name)
|
|
98
|
-
shell_files = get_shell_files
|
|
99
|
-
|
|
100
|
-
shell_files.each do |shell_file|
|
|
101
|
-
next unless File.exist?(shell_file)
|
|
102
|
-
|
|
103
|
-
begin
|
|
104
|
-
content = File.read(shell_file)
|
|
105
|
-
lines = content.split("\n", -1)
|
|
106
|
-
lines.pop if lines.last == ""
|
|
107
|
-
|
|
108
|
-
original_count = lines.length
|
|
109
|
-
lines = yield(lines, shell_file)
|
|
110
|
-
removed_count = original_count - lines.length
|
|
111
|
-
|
|
112
|
-
if removed_count > 0
|
|
113
|
-
File.write(shell_file, lines.join("\n") + "\n")
|
|
114
|
-
log_verbose(" ✓ 已从 #{File.basename(shell_file)} 中清理 #{removed_count} 行#{operation_name}")
|
|
115
|
-
end
|
|
116
|
-
rescue => e
|
|
117
|
-
log_debug(" ⚠ 处理 #{shell_file} 失败: #{e.message}")
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
def self.clean_system_environment
|
|
123
|
-
log_verbose("🧹 清理系统环境...")
|
|
124
|
-
|
|
125
|
-
# 1. 清理 ~/.claude.json 中的 oauthAccount
|
|
126
|
-
clean_claude_json_oauth
|
|
127
|
-
|
|
128
|
-
# 2. 清理 Keychain(仅 macOS)
|
|
129
|
-
clean_keychain if macos?
|
|
130
|
-
|
|
131
|
-
# 3. 清理环境变量中的 token
|
|
132
|
-
clean_env_variables
|
|
133
|
-
|
|
134
|
-
# 4. 清理环境变量中的代理设置
|
|
135
|
-
clean_proxy_variables
|
|
136
|
-
|
|
137
|
-
log_verbose(" ✓ 系统环境清理完成")
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
def self.clean_claude_json_oauth
|
|
141
|
-
return unless File.exist?(CLAUDE_JSON_PATH)
|
|
142
|
-
|
|
143
|
-
begin
|
|
144
|
-
content = File.read(CLAUDE_JSON_PATH)
|
|
145
|
-
config = JSON.parse(content)
|
|
146
|
-
|
|
147
|
-
# 删除 oauthAccount 字段
|
|
148
|
-
if config.delete("oauthAccount")
|
|
149
|
-
# 写回文件
|
|
150
|
-
File.write(CLAUDE_JSON_PATH, JSON.pretty_generate(config))
|
|
151
|
-
log_verbose(" ✓ 已清理 ~/.claude.json 中的 oauthAccount")
|
|
152
|
-
end
|
|
153
|
-
rescue JSON::ParserError => e
|
|
154
|
-
log_debug(" ⚠ 解析 ~/.claude.json 失败: #{e.message}")
|
|
155
|
-
rescue => e
|
|
156
|
-
log_debug(" ⚠ 清理 ~/.claude.json 失败: #{e.message}")
|
|
157
|
-
end
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
def self.clean_proxy_variables
|
|
161
|
-
process_shell_files("代理配置") do |lines, shell_file|
|
|
162
|
-
remove_all_proxy_config(lines, shell_file)
|
|
163
|
-
end
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
def self.get_token_from_keychain(service_name)
|
|
167
|
-
return nil unless macos?
|
|
168
|
-
|
|
169
|
-
cmd = [
|
|
170
|
-
"security",
|
|
171
|
-
"find-generic-password",
|
|
172
|
-
"-a", current_user,
|
|
173
|
-
"-s", service_name,
|
|
174
|
-
"-w"
|
|
175
|
-
]
|
|
176
|
-
|
|
177
|
-
begin
|
|
178
|
-
stdout, stderr, status = Open3.capture3(*cmd)
|
|
179
|
-
|
|
180
|
-
if status.success? && !stdout.strip.empty?
|
|
181
|
-
# 解析 JSON 格式的密码
|
|
182
|
-
credentials = JSON.parse(stdout.strip)
|
|
183
|
-
return credentials["claudeAiOauth"]
|
|
184
|
-
end
|
|
185
|
-
rescue JSON::ParserError, StandardError
|
|
186
|
-
# 如果不是 JSON 格式,尝试直接返回
|
|
187
|
-
return stdout.strip if status.success? && !stdout.strip.empty?
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
nil
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
def self.configure_keychain(config)
|
|
194
|
-
credentials = {
|
|
195
|
-
"claudeAiOauth" => config["key_chain"]["claudeAiOauth"]
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
service_name = config["key_chain"]["service_name"]
|
|
199
|
-
password = credentials.to_json
|
|
200
|
-
|
|
201
|
-
cmd = [
|
|
202
|
-
"security",
|
|
203
|
-
"add-generic-password",
|
|
204
|
-
"-a", current_user,
|
|
205
|
-
"-s", service_name,
|
|
206
|
-
"-l", service_name,
|
|
207
|
-
"-w", password,
|
|
208
|
-
"-U",
|
|
209
|
-
"-T", ""
|
|
210
|
-
]
|
|
211
|
-
|
|
212
|
-
system(*cmd)
|
|
213
|
-
|
|
214
|
-
if $?.success?
|
|
215
|
-
verify_keychain_entry(current_user, service_name)
|
|
216
|
-
else
|
|
217
|
-
puts "✗ 保存凭证到 Keychain 失败"
|
|
218
|
-
return false
|
|
219
|
-
end
|
|
220
|
-
|
|
221
|
-
true
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
def self.verify_keychain_entry(account_name, service_name)
|
|
225
|
-
verify_cmd = [
|
|
226
|
-
"security",
|
|
227
|
-
"find-generic-password",
|
|
228
|
-
"-a", account_name,
|
|
229
|
-
"-s", service_name
|
|
230
|
-
]
|
|
231
|
-
|
|
232
|
-
unless system(*verify_cmd, out: File::NULL, err: File::NULL)
|
|
233
|
-
puts "✗ 无法验证 Keychain 条目"
|
|
234
|
-
end
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
def self.verify_token_config(config)
|
|
238
|
-
# 检查两种可能的令牌键
|
|
239
|
-
token = config["env"][TOKEN_KEY] || config["env"][ALT_TOKEN_KEY]
|
|
240
|
-
return false unless token
|
|
241
|
-
|
|
242
|
-
# 只验证令牌存在且不为空,不写入任何配置文件
|
|
243
|
-
if token && !token.strip.empty?
|
|
244
|
-
# 清理任何现有的环境配置文件中的令牌定义
|
|
245
|
-
clean_env_variables
|
|
246
|
-
return true
|
|
247
|
-
else
|
|
248
|
-
puts "✗ 令牌配置无效"
|
|
249
|
-
return false
|
|
250
|
-
end
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
def self.update_claude_json(config)
|
|
254
|
-
begin
|
|
255
|
-
# 读取现有文件或创建空哈希
|
|
256
|
-
if File.exist?(CLAUDE_JSON_PATH)
|
|
257
|
-
user_config = JSON.parse(File.read(CLAUDE_JSON_PATH))
|
|
258
|
-
else
|
|
259
|
-
user_config = {}
|
|
260
|
-
end
|
|
261
|
-
|
|
262
|
-
# 合并 claude_json 内容,过滤掉代理相关字段
|
|
263
|
-
claude_config = config["claude_json"].dup
|
|
264
|
-
claude_config.delete("claude_proxy")
|
|
265
|
-
claude_config.delete("HTTP_PROXY")
|
|
266
|
-
claude_config.delete("HTTPS_PROXY")
|
|
267
|
-
|
|
268
|
-
user_config.merge!(claude_config)
|
|
269
|
-
|
|
270
|
-
# 写回文件
|
|
271
|
-
File.write(CLAUDE_JSON_PATH, JSON.pretty_generate(user_config))
|
|
272
|
-
|
|
273
|
-
rescue JSON::ParserError => e
|
|
274
|
-
puts "✗ 解析现有 ~/.claude.json 失败: #{e.message}"
|
|
275
|
-
false
|
|
276
|
-
rescue => e
|
|
277
|
-
puts "✗ 更新 ~/.claude.json 失败: #{e.message}"
|
|
278
|
-
false
|
|
279
|
-
end
|
|
280
|
-
end
|
|
281
|
-
|
|
282
|
-
def self.configure_proxy(config)
|
|
283
|
-
return unless config["claude_proxy"] && (config["claude_proxy"]["HTTP_PROXY"] || config["claude_proxy"]["HTTPS_PROXY"])
|
|
284
|
-
|
|
285
|
-
http_proxy = config["claude_proxy"]["HTTP_PROXY"]
|
|
286
|
-
https_proxy = config["claude_proxy"]["HTTPS_PROXY"]
|
|
287
|
-
|
|
288
|
-
shell_files = get_shell_files
|
|
289
|
-
|
|
290
|
-
shell_files.each do |shell_file|
|
|
291
|
-
next unless File.exist?(shell_file)
|
|
292
|
-
|
|
293
|
-
begin
|
|
294
|
-
content = File.read(shell_file)
|
|
295
|
-
lines = content.split("\n", -1)
|
|
296
|
-
lines.pop if lines.last == ""
|
|
297
|
-
|
|
298
|
-
# 移除所有现有的代理相关配置
|
|
299
|
-
lines = remove_all_proxy_config(lines, shell_file)
|
|
300
|
-
|
|
301
|
-
# 只添加代理别名/函数,不直接设置环境变量
|
|
302
|
-
lines = add_proxy_aliases(lines, shell_file, http_proxy, https_proxy)
|
|
303
|
-
|
|
304
|
-
# 写回文件
|
|
305
|
-
File.write(shell_file, lines.join("\n") + "\n")
|
|
306
|
-
|
|
307
|
-
rescue => e
|
|
308
|
-
puts " ✗ 更新 #{shell_file} 失败: #{e.message}"
|
|
309
|
-
end
|
|
310
|
-
end
|
|
311
|
-
|
|
312
|
-
# 显示代理配置完成信息
|
|
313
|
-
puts "✅ 代理别名已配置 | claude_proxy / unclaude_proxy"
|
|
314
|
-
end
|
|
315
|
-
|
|
316
|
-
# 检查环境变量中的令牌
|
|
317
|
-
def self.check_env_tokens
|
|
318
|
-
# 优先使用 CLAUDE_CODE_OAUTH_TOKEN,其次是 ANTHROPIC_AUTH_TOKEN
|
|
319
|
-
token = ENV[TOKEN_KEY] || ENV[ALT_TOKEN_KEY]
|
|
320
|
-
token&.strip&.empty? ? nil : token
|
|
321
|
-
end
|
|
322
|
-
|
|
323
|
-
def self.prompt_for_token
|
|
324
|
-
puts "请输入您的 CLAUDE_CODE_OAUTH_TOKEN:"
|
|
325
|
-
print "> "
|
|
326
|
-
|
|
327
|
-
begin
|
|
328
|
-
# 简化输入处理,直接使用 STDIN
|
|
329
|
-
STDIN.gets&.chomp
|
|
330
|
-
rescue => e
|
|
331
|
-
puts "✗ 无法读取输入: #{e.message}"
|
|
332
|
-
puts " 请使用本地配置文件或 --user 参数"
|
|
333
|
-
nil
|
|
334
|
-
end
|
|
335
|
-
end
|
|
336
|
-
|
|
337
|
-
def self.clean_env_variables
|
|
338
|
-
process_shell_files("令牌配置") do |lines, shell_file|
|
|
339
|
-
patterns = get_token_patterns(shell_file)
|
|
340
|
-
lines.reject { |line| patterns.any? { |pattern| line.match(pattern) } }
|
|
341
|
-
end
|
|
342
|
-
end
|
|
343
|
-
|
|
344
|
-
def self.clean_keychain
|
|
345
|
-
return unless macos?
|
|
346
|
-
|
|
347
|
-
cmd = [
|
|
348
|
-
"security",
|
|
349
|
-
"delete-generic-password",
|
|
350
|
-
"-a", current_user,
|
|
351
|
-
"-s", SERVICE_NAME
|
|
352
|
-
]
|
|
353
|
-
|
|
354
|
-
stdout, stderr, status = Open3.capture3(*cmd)
|
|
355
|
-
|
|
356
|
-
if status.success?
|
|
357
|
-
log_verbose(" ✓ 已清理 Keychain 中的 Claude Code 凭证")
|
|
358
|
-
elsif !stderr.include?("could not be found")
|
|
359
|
-
log_debug(" ⚠ 清理 Keychain 失败: #{stderr}")
|
|
360
|
-
end
|
|
361
|
-
end
|
|
362
|
-
|
|
363
|
-
def self.get_shell_files
|
|
364
|
-
Base::SystemInfo.shell_config_files
|
|
365
|
-
end
|
|
366
|
-
|
|
367
|
-
def self.get_token_patterns(shell_file)
|
|
368
|
-
if shell_file.include?("config.fish")
|
|
369
|
-
[
|
|
370
|
-
/^set\s+-gx\s+#{Regexp.escape(TOKEN_KEY)}\s/,
|
|
371
|
-
/^set\s+-x\s+#{Regexp.escape(TOKEN_KEY)}\s/,
|
|
372
|
-
/^#set\s+-gx\s+#{Regexp.escape(TOKEN_KEY)}\s/
|
|
373
|
-
]
|
|
374
|
-
else
|
|
375
|
-
[
|
|
376
|
-
/^export\s+#{Regexp.escape(TOKEN_KEY)}=/,
|
|
377
|
-
/^#{Regexp.escape(TOKEN_KEY)}=/,
|
|
378
|
-
/^#export\s+#{Regexp.escape(TOKEN_KEY)}=/
|
|
379
|
-
]
|
|
380
|
-
end
|
|
381
|
-
end
|
|
382
|
-
|
|
383
|
-
def self.get_export_line(shell_file, token)
|
|
384
|
-
if shell_file.include?("config.fish")
|
|
385
|
-
"set -gx #{TOKEN_KEY} \"#{token}\""
|
|
386
|
-
else
|
|
387
|
-
"export #{TOKEN_KEY}=\"#{token}\""
|
|
388
|
-
end
|
|
389
|
-
end
|
|
390
|
-
|
|
391
|
-
def self.remove_all_proxy_config(lines, shell_file)
|
|
392
|
-
# 更全面的代理配置清理,包括所有可能的代理设置
|
|
393
|
-
if shell_file.include?("config.fish")
|
|
394
|
-
# Fish shell 代理配置清理
|
|
395
|
-
lines.reject! { |line|
|
|
396
|
-
# 清理环境变量设置
|
|
397
|
-
line.match(/^set\s+-[gx]+\s+(HTTP_PROXY|HTTPS_PROXY|http_proxy|https_proxy|ALL_PROXY|all_proxy|NO_PROXY|no_proxy)\s/) ||
|
|
398
|
-
line.match(/^export\s+(HTTP_PROXY|HTTPS_PROXY|http_proxy|https_proxy|ALL_PROXY|all_proxy|NO_PROXY|no_proxy)=/) ||
|
|
399
|
-
# 清理别名
|
|
400
|
-
line.match(/^alias\s+(claude_proxy|unclaude_proxy|proxy|unproxy)=/) ||
|
|
401
|
-
# 清理函数
|
|
402
|
-
line.match(/^function\s+(claude_proxy|unclaude_proxy|proxy|unproxy)/)
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
# 移除函数块
|
|
406
|
-
in_function = false
|
|
407
|
-
lines = lines.select do |line|
|
|
408
|
-
if line.match(/^function\s+(claude_proxy|unclaude_proxy|proxy|unproxy)/)
|
|
409
|
-
in_function = true
|
|
410
|
-
false
|
|
411
|
-
elsif in_function && line.strip == "end"
|
|
412
|
-
in_function = false
|
|
413
|
-
false
|
|
414
|
-
elsif in_function
|
|
415
|
-
false
|
|
416
|
-
else
|
|
417
|
-
true
|
|
418
|
-
end
|
|
419
|
-
end
|
|
420
|
-
else
|
|
421
|
-
# Bash/zsh 代理配置清理
|
|
422
|
-
lines.reject! { |line|
|
|
423
|
-
# 清理环境变量设置
|
|
424
|
-
line.match(/^export\s+(HTTP_PROXY|HTTPS_PROXY|http_proxy|https_proxy|ALL_PROXY|all_proxy|NO_PROXY|no_proxy)=/) ||
|
|
425
|
-
line.match(/^(HTTP_PROXY|HTTPS_PROXY|http_proxy|https_proxy|ALL_PROXY|all_proxy|NO_PROXY|no_proxy)=/) ||
|
|
426
|
-
# 清理别名
|
|
427
|
-
line.match(/^alias\s+(claude_proxy|unclaude_proxy|proxy|unproxy)=/) ||
|
|
428
|
-
# 清理函数
|
|
429
|
-
line.match(/^(claude_proxy|unclaude_proxy|proxy|unproxy)\(\)/) ||
|
|
430
|
-
# 清理函数定义的另一种格式
|
|
431
|
-
line.match(/^function\s+(claude_proxy|unclaude_proxy|proxy|unproxy)/)
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
# 移除函数块
|
|
435
|
-
in_function = false
|
|
436
|
-
function_depth = 0
|
|
437
|
-
lines = lines.select do |line|
|
|
438
|
-
if line.match(/^(claude_proxy|unclaude_proxy|proxy|unproxy)\(\)/) ||
|
|
439
|
-
line.match(/^function\s+(claude_proxy|unclaude_proxy|proxy|unproxy)/)
|
|
440
|
-
in_function = true
|
|
441
|
-
function_depth = 0
|
|
442
|
-
false
|
|
443
|
-
elsif in_function
|
|
444
|
-
function_depth += 1 if line.include?("{")
|
|
445
|
-
if line.include?("}")
|
|
446
|
-
function_depth -= 1
|
|
447
|
-
if function_depth <= 0
|
|
448
|
-
in_function = false
|
|
449
|
-
false
|
|
450
|
-
else
|
|
451
|
-
false
|
|
452
|
-
end
|
|
453
|
-
else
|
|
454
|
-
false
|
|
455
|
-
end
|
|
456
|
-
else
|
|
457
|
-
true
|
|
458
|
-
end
|
|
459
|
-
end
|
|
460
|
-
end
|
|
461
|
-
|
|
462
|
-
lines
|
|
463
|
-
end
|
|
464
|
-
|
|
465
|
-
# remove_proxy_config 方法已被 remove_all_proxy_config 替代
|
|
466
|
-
# remove_all_proxy_config 提供更全面的清理功能,包括 ALL_PROXY 和 NO_PROXY
|
|
467
|
-
|
|
468
|
-
def self.add_proxy_aliases(lines, shell_file, http_proxy, https_proxy)
|
|
469
|
-
if shell_file.include?("config.fish")
|
|
470
|
-
# Fish shell - 只添加函数,不直接设置环境变量
|
|
471
|
-
proxy_commands = []
|
|
472
|
-
proxy_commands << ""
|
|
473
|
-
proxy_commands << "function claude_proxy"
|
|
474
|
-
if http_proxy
|
|
475
|
-
proxy_commands << " set -gx HTTP_PROXY \"#{http_proxy}\""
|
|
476
|
-
proxy_commands << " set -gx http_proxy \"#{http_proxy}\""
|
|
477
|
-
end
|
|
478
|
-
if https_proxy
|
|
479
|
-
proxy_commands << " set -gx HTTPS_PROXY \"#{https_proxy}\""
|
|
480
|
-
proxy_commands << " set -gx https_proxy \"#{https_proxy}\""
|
|
481
|
-
end
|
|
482
|
-
proxy_commands << " echo '代理已启用: HTTP_PROXY=#{http_proxy || 'not set'}, HTTPS_PROXY=#{https_proxy || 'not set'}'"
|
|
483
|
-
proxy_commands << "end"
|
|
484
|
-
|
|
485
|
-
proxy_commands << ""
|
|
486
|
-
proxy_commands << "function unclaude_proxy"
|
|
487
|
-
proxy_commands << " set -e HTTP_PROXY"
|
|
488
|
-
proxy_commands << " set -e http_proxy"
|
|
489
|
-
proxy_commands << " set -e HTTPS_PROXY"
|
|
490
|
-
proxy_commands << " set -e https_proxy"
|
|
491
|
-
proxy_commands << " echo '代理已禁用'"
|
|
492
|
-
proxy_commands << "end"
|
|
493
|
-
|
|
494
|
-
lines.concat(proxy_commands)
|
|
495
|
-
else
|
|
496
|
-
# Bash/zsh - 只添加别名,不直接设置环境变量
|
|
497
|
-
proxy_exports = []
|
|
498
|
-
if http_proxy
|
|
499
|
-
proxy_exports << "export HTTP_PROXY=\"#{http_proxy}\""
|
|
500
|
-
proxy_exports << "export http_proxy=\"#{http_proxy}\""
|
|
501
|
-
end
|
|
502
|
-
if https_proxy
|
|
503
|
-
proxy_exports << "export HTTPS_PROXY=\"#{https_proxy}\""
|
|
504
|
-
proxy_exports << "export https_proxy=\"#{https_proxy}\""
|
|
505
|
-
end
|
|
506
|
-
|
|
507
|
-
if proxy_exports.any?
|
|
508
|
-
lines << ""
|
|
509
|
-
lines << "alias claude_proxy='#{proxy_exports.join("; ")}; echo \"代理已启用: HTTP_PROXY=#{http_proxy || 'not set'}, HTTPS_PROXY=#{https_proxy || 'not set'}\"'"
|
|
510
|
-
end
|
|
511
|
-
|
|
512
|
-
lines << "alias unclaude_proxy='unset HTTP_PROXY http_proxy HTTPS_PROXY https_proxy; echo \"代理已禁用\"'"
|
|
513
|
-
end
|
|
514
|
-
|
|
515
|
-
lines
|
|
516
|
-
end
|
|
517
|
-
end
|
|
518
|
-
end
|
|
519
|
-
end
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
|
|
3
|
-
require 'jpsclient'
|
|
4
|
-
require 'fileutils'
|
|
5
|
-
require 'json'
|
|
6
|
-
require_relative '../config/easyai_config'
|
|
7
|
-
|
|
8
|
-
module EasyAI
|
|
9
|
-
module Auth
|
|
10
|
-
class JPSLoginHelper
|
|
11
|
-
attr_reader :username
|
|
12
|
-
|
|
13
|
-
def initialize(options = {})
|
|
14
|
-
@verbose = options[:verbose] || false
|
|
15
|
-
|
|
16
|
-
# 如果传入了配置文件路径,直接使用
|
|
17
|
-
if options[:config_file]
|
|
18
|
-
@config_file_path = options[:config_file]
|
|
19
|
-
else
|
|
20
|
-
# 通过 EasyAIConfig 获取 jpsclient 配置文件路径
|
|
21
|
-
# 首先确保配置仓库已初始化
|
|
22
|
-
EasyAIConfig.initialize(verbose: @verbose)
|
|
23
|
-
|
|
24
|
-
# 获取 jpsclient 配置文件路径(会自动解密到临时目录)
|
|
25
|
-
@config_file_path = EasyAIConfig.get_config_path('jps_client_config', verbose: @verbose)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
@client = nil
|
|
29
|
-
@username = nil
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# 主登录入口 - 兼容原 JPSLogin 接口
|
|
33
|
-
def login
|
|
34
|
-
begin
|
|
35
|
-
# 检查配置文件路径
|
|
36
|
-
if @config_file_path.nil?
|
|
37
|
-
puts "✗ 未找到 jpsclient 配置文件"
|
|
38
|
-
puts " 请确保配置仓库已下载并解密"
|
|
39
|
-
return false
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
# 检查配置文件是否存在
|
|
43
|
-
unless File.exist?(@config_file_path)
|
|
44
|
-
puts "✗ 配置文件不存在: #{@config_file_path}"
|
|
45
|
-
puts " 请先从配置仓库下载并解密配置文件"
|
|
46
|
-
return false
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
@client ||= JPSClient::Client.new(config_file: @config_file_path)
|
|
50
|
-
|
|
51
|
-
# 执行登录
|
|
52
|
-
result = @client.do_login(force_login: false)
|
|
53
|
-
|
|
54
|
-
if result == :user_cancelled
|
|
55
|
-
return :user_cancelled
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
if result
|
|
59
|
-
# 登录成功,获取用户名
|
|
60
|
-
@username = get_username_from_client
|
|
61
|
-
return true
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
return false
|
|
65
|
-
rescue => e
|
|
66
|
-
puts "✗ JPS 登录失败: #{e.message}" if @verbose
|
|
67
|
-
return false
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
# 获取用户名 - 兼容原 JPSLogin 接口
|
|
72
|
-
def get_username
|
|
73
|
-
return @username if @username
|
|
74
|
-
|
|
75
|
-
# 如果客户端存在且已登录,尝试获取用户名
|
|
76
|
-
if @client
|
|
77
|
-
@username = get_username_from_client
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
@username
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
private
|
|
84
|
-
|
|
85
|
-
# 从客户端获取用户名
|
|
86
|
-
def get_username_from_client
|
|
87
|
-
if @client && @client.token
|
|
88
|
-
# 从 token 中获取用户名
|
|
89
|
-
token_info = @client.token
|
|
90
|
-
if token_info.is_a?(Hash) && token_info['username']
|
|
91
|
-
return token_info['username']
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
nil
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
end
|