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,391 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'json'
|
|
2
|
-
require '
|
|
3
|
-
|
|
4
|
-
require_relative '
|
|
5
|
-
require_relative '../base/update_notifier'
|
|
6
|
-
require_relative '../base/system_info'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
require 'open3'
|
|
6
|
+
require_relative 'ai_tool_base'
|
|
7
7
|
|
|
8
8
|
module EasyAI
|
|
9
9
|
class Command
|
|
10
|
-
class Claude <
|
|
11
|
-
self.summary = '
|
|
10
|
+
class Claude < AIToolBase
|
|
11
|
+
self.summary = '启动 Claude Code 命令行(多平台支持)'
|
|
12
12
|
self.description = <<-DESC
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
主要功能:
|
|
16
|
-
|
|
17
|
-
* 支持多种认证平台 (Claude/Kimi/Deepseek)
|
|
18
|
-
|
|
19
|
-
* 智能选择最优认证方式
|
|
20
|
-
|
|
21
|
-
* 支持远程配置下载
|
|
22
|
-
|
|
23
|
-
* 自动配置环境变量
|
|
24
|
-
|
|
25
|
-
* 支持本地配置文件
|
|
26
|
-
|
|
27
|
-
使用示例:
|
|
28
|
-
|
|
29
|
-
$ easyai claude # 自动选择认证平台
|
|
30
|
-
|
|
31
|
-
$ easyai claude --platform # 交互式选择认证平台
|
|
32
|
-
|
|
33
|
-
$ easyai claude ./config.json # 使用本地配置文件
|
|
34
|
-
|
|
35
|
-
$ easyai claude --verbose # 显示详细信息
|
|
36
|
-
|
|
37
|
-
$ easyai claude --no-keychain # 禁用密码存储
|
|
38
|
-
DESC
|
|
39
|
-
|
|
40
|
-
def self.options
|
|
41
|
-
[
|
|
42
|
-
['--platform', '选择认证平台(交互式选择)'],
|
|
43
|
-
['--no-keychain', '禁用自动密码存储'],
|
|
44
|
-
['--verbose', '显示详细信息']
|
|
45
|
-
].concat(super)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def initialize(argv)
|
|
49
|
-
@platform_flag = argv.flag?('platform')
|
|
50
|
-
@no_keychain = argv.flag?('no-keychain')
|
|
51
|
-
@verbose_mode = argv.flag?('verbose')
|
|
52
|
-
|
|
53
|
-
super
|
|
54
|
-
|
|
55
|
-
# 获取剩余参数
|
|
56
|
-
remaining_args = @argv.remainder!
|
|
57
|
-
|
|
58
|
-
# 检查第一个参数是否是配置文件
|
|
59
|
-
if remaining_args.first && File.exist?(remaining_args.first) && remaining_args.first.end_with?('.json')
|
|
60
|
-
@config_file = remaining_args.shift
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
# 剩余的参数传递给 claude
|
|
64
|
-
@claude_args = remaining_args
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def validate!
|
|
68
|
-
super
|
|
69
|
-
help! '未找到 Claude CLI。请安装:npm install -g @anthropic-ai/claude-code' unless claude_available?
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def run
|
|
73
|
-
begin
|
|
74
|
-
|
|
75
|
-
# 首先尝试从远程下载配置
|
|
76
|
-
remote_config = nil
|
|
77
|
-
|
|
78
|
-
if @config_file
|
|
79
|
-
# 使用指定的本地配置文件
|
|
80
|
-
print_status("📁 使用本地配置", File.basename(@config_file))
|
|
81
|
-
remote_config = load_local_config(@config_file)
|
|
82
|
-
print_success("配置加载成功") if remote_config
|
|
83
|
-
else
|
|
84
|
-
# 从远程下载配置,传递选项(指定工具类型为 claude)
|
|
85
|
-
print_status("🔄 获取远程配置", "默认用户")
|
|
86
|
-
options = {
|
|
87
|
-
no_keychain: @no_keychain,
|
|
88
|
-
verbose: @verbose_mode,
|
|
89
|
-
tool_type: "claude", # 指定使用 claude 组的配置
|
|
90
|
-
platform_flag: @platform_flag # 传递 platform 标志
|
|
91
|
-
}
|
|
92
|
-
remote_config = ConfigManager.download_user_config(nil, options)
|
|
93
|
-
|
|
94
|
-
# 处理用户取消授权的情况
|
|
95
|
-
if remote_config == :user_cancelled
|
|
96
|
-
print_error("用户取消了授权登录")
|
|
97
|
-
exit 0
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
print_success("配置加载成功") if remote_config
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
# 检查是否使用 claude_auth,如果是则进行环境检查
|
|
104
|
-
if remote_config && remote_config['_config_path']&.include?('claude_auth')
|
|
105
|
-
unless check_claude_auth_environment
|
|
106
|
-
exit 1
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
# 如果远程配置获取失败,回退到本地配置
|
|
111
|
-
if remote_config.nil?
|
|
112
|
-
print_warning("使用本地配置")
|
|
113
|
-
remote_config = load_local_yaml_config
|
|
114
|
-
|
|
115
|
-
# 如果本地配置也为空,提示用户先进行设置
|
|
116
|
-
if remote_config.empty?
|
|
117
|
-
print_error("未找到有效配置")
|
|
118
|
-
puts " 请先运行: #{'easyai --setup'.yellow}"
|
|
119
|
-
exit 1
|
|
120
|
-
end
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
# 使用 Auth::AuthClaude 模块配置认证
|
|
124
|
-
print_status("🔐 配置认证", "Claude OAuth")
|
|
125
|
-
unless Auth::AuthClaude.configure(remote_config, nil)
|
|
126
|
-
print_error("配置认证失败")
|
|
127
|
-
exit 1
|
|
128
|
-
end
|
|
129
|
-
print_success("认证配置完成")
|
|
130
|
-
|
|
131
|
-
# 构建环境变量
|
|
132
|
-
env = build_environment(remote_config)
|
|
133
|
-
|
|
134
|
-
# 导出环境变量到当前进程(不包括 CLAUDE_CODE_OAUTH_TOKEN)
|
|
135
|
-
export_environment_variables(env)
|
|
136
|
-
|
|
137
|
-
# 打印分隔线
|
|
138
|
-
puts "─" * 60
|
|
139
|
-
puts "🚀 #{'Claude CLI 已启动'.green}"
|
|
140
|
-
puts "─" * 60
|
|
141
|
-
puts
|
|
142
|
-
|
|
143
|
-
# 启动 Claude
|
|
144
|
-
exec(env, 'claude', *@claude_args)
|
|
145
|
-
|
|
146
|
-
rescue => e
|
|
147
|
-
print_error("Claude 命令执行失败: #{e.message}")
|
|
148
|
-
puts " 请检查配置文件和网络连接"
|
|
149
|
-
puts " 错误详情: #{e.class.name}" if @verbose_mode
|
|
150
|
-
exit 1
|
|
151
|
-
end
|
|
152
|
-
end
|
|
13
|
+
启动 Anthropic Claude Code CLI,从 ~/.easyai/config.json 读取平台配置,并将凭证 / 代理仅注入子进程。
|
|
153
14
|
|
|
154
|
-
|
|
15
|
+
使用示例:
|
|
155
16
|
|
|
17
|
+
$ easyai claude # 多平台时进入交互选择
|
|
18
|
+
$ easyai claude --platform=kimi # 直接使用指定平台
|
|
19
|
+
$ easyai claude ./adhoc.json # 用一次性 JSON 覆盖(单平台扁平 schema)
|
|
20
|
+
$ easyai claude -- --help # 透传参数给 claude CLI
|
|
21
|
+
DESC
|
|
156
22
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
puts icon_text
|
|
162
|
-
end
|
|
163
|
-
end
|
|
23
|
+
self.arguments = [
|
|
24
|
+
CLAide::Argument.new('CONFIG.json', false),
|
|
25
|
+
CLAide::Argument.new('ARGS', false, true)
|
|
26
|
+
]
|
|
164
27
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
28
|
+
# Claude Code 默认禁用遥测与非必要流量;用户可在 config.json 的 env 中显式覆盖
|
|
29
|
+
DEFAULT_ENV = {
|
|
30
|
+
'DISABLE_TELEMETRY' => '1',
|
|
31
|
+
'CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC' => '1'
|
|
32
|
+
}.freeze
|
|
168
33
|
|
|
169
|
-
def
|
|
170
|
-
|
|
34
|
+
def tool_name
|
|
35
|
+
'claude'
|
|
171
36
|
end
|
|
172
37
|
|
|
173
|
-
def
|
|
174
|
-
|
|
38
|
+
def exec_command
|
|
39
|
+
'claude'
|
|
175
40
|
end
|
|
176
41
|
|
|
177
|
-
def
|
|
178
|
-
|
|
179
|
-
Base::SystemInfo.which_command('claude')
|
|
42
|
+
def install_hint
|
|
43
|
+
'未找到 claude CLI。请安装:npm install -g @anthropic-ai/claude-code'
|
|
180
44
|
end
|
|
181
45
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
check_details = Base::SystemInfo.claude_auth_check_details
|
|
185
|
-
|
|
186
|
-
puts "\n🔍 系统环境检查(Claude 官方认证)"
|
|
187
|
-
puts "─" * 60
|
|
188
|
-
|
|
189
|
-
# 1. 操作系统检查
|
|
190
|
-
if check_details[:os_supported]
|
|
191
|
-
puts " ✓ 操作系统: #{check_details[:os_name]}".green
|
|
192
|
-
else
|
|
193
|
-
puts " ❌ 操作系统: #{check_details[:os_name]}(不支持)".red
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
# 2. 显示地区和语言信息
|
|
197
|
-
region_info = Base::SystemInfo.region_info
|
|
198
|
-
if Base::SystemInfo.macos?
|
|
199
|
-
puts " 系统地区: #{region_info[:apple_locale]}"
|
|
200
|
-
puts " 首选语言: #{region_info[:apple_languages]}"
|
|
201
|
-
elsif Base::SystemInfo.windows?
|
|
202
|
-
puts " 系统地区: #{region_info[:system_locale]}"
|
|
203
|
-
puts " 用户地区: #{region_info[:user_locale]}"
|
|
204
|
-
else
|
|
205
|
-
puts " 系统语言: #{region_info[:locale]}"
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
# 3. 地区检查
|
|
209
|
-
if check_details[:is_us_region]
|
|
210
|
-
puts " ✓ 地区检查: 美国地区".green
|
|
211
|
-
else
|
|
212
|
-
puts " ❌ 地区检查: 非美国地区(#{check_details[:region]})".red
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
# 4. 语言检查
|
|
216
|
-
if check_details[:is_english]
|
|
217
|
-
puts " ✓ 语言检查: 英语".green
|
|
218
|
-
else
|
|
219
|
-
puts " ❌ 语言检查: 非英语语言".red
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
puts "─" * 60
|
|
223
|
-
|
|
224
|
-
# 如果检查失败,显示错误信息和解决方案
|
|
225
|
-
unless check_details[:meets_requirements]
|
|
226
|
-
puts
|
|
227
|
-
puts " ⛔ 环境检查未通过".red
|
|
228
|
-
puts
|
|
229
|
-
|
|
230
|
-
failure_reasons = []
|
|
231
|
-
failure_reasons << "Claude 官方认证不支持 Windows 系统" unless check_details[:os_supported]
|
|
232
|
-
failure_reasons << "Claude 官方认证要求美国地区" unless check_details[:is_us_region]
|
|
233
|
-
failure_reasons << "Claude 官方认证要求英语语言环境" unless check_details[:is_english]
|
|
234
|
-
|
|
235
|
-
puts " 失败原因:".yellow
|
|
236
|
-
failure_reasons.each_with_index do |reason, index|
|
|
237
|
-
puts " #{index + 1}. #{reason}".yellow
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
puts
|
|
241
|
-
puts " 解决方案:".cyan
|
|
242
|
-
puts " 1. 使用其他认证平台: easyai claude --platform".cyan
|
|
243
|
-
puts " 2. 切换到 macOS/Linux 系统(如使用 Windows)".cyan unless check_details[:os_supported]
|
|
244
|
-
puts " 3. 切换系统地区设置到美国(英语)".cyan if !check_details[:is_us_region] || !check_details[:is_english]
|
|
245
|
-
puts " 4. 联系管理员获取其他认证方式".cyan
|
|
246
|
-
puts
|
|
247
|
-
|
|
248
|
-
return false
|
|
249
|
-
end
|
|
250
|
-
|
|
251
|
-
puts " ✅ 所有检查通过".green
|
|
252
|
-
puts
|
|
253
|
-
|
|
254
|
-
true
|
|
46
|
+
def default_env
|
|
47
|
+
DEFAULT_ENV
|
|
255
48
|
end
|
|
256
49
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
end
|
|
270
|
-
end
|
|
271
|
-
|
|
272
|
-
def load_local_yaml_config
|
|
273
|
-
# 不再使用本地YAML配置文件,返回空配置
|
|
274
|
-
# claude命令主要依赖远程配置或用户指定的JSON配置文件
|
|
275
|
-
{}
|
|
50
|
+
# 启动 claude 之前把 Claude Code 状态强制对齐到"纯 token 模式",避免 OAuth 残留影响:
|
|
51
|
+
# 1. ~/.claude.json: hasCompletedOnboarding=true(跳过 onboarding)
|
|
52
|
+
# 2. ~/.claude.json: 删除 oauthAccount 字段(OAuth 账户元数据残留)
|
|
53
|
+
# 3. macOS Keychain: 删除 "Claude Code-credentials" 条目(OAuth token 残留)
|
|
54
|
+
# 三步全部幂等:已是目标状态则不操作;任意 IO/JSON/Keychain 异常降级为 warning,不阻塞 exec。
|
|
55
|
+
#
|
|
56
|
+
# 副作用提醒:如果用户直接跑 `claude`(不通过 easyai)走过 OAuth 登录,下一次跑 `easyai claude`
|
|
57
|
+
# 会清掉 OAuth 状态。EasyAI 设计前提是统一走 ANTHROPIC_AUTH_TOKEN 环境变量路径,OAuth 登录
|
|
58
|
+
# 态由 `easyai backup claude` / `easyai restore claude` 显式管理。
|
|
59
|
+
def pre_exec
|
|
60
|
+
reset_claude_state_to_token_mode
|
|
61
|
+
delete_keychain_credential(KEYCHAIN_LABEL)
|
|
276
62
|
end
|
|
277
63
|
|
|
278
|
-
|
|
279
|
-
env = ENV.to_h
|
|
64
|
+
private
|
|
280
65
|
|
|
281
|
-
|
|
282
|
-
|
|
66
|
+
CLAUDE_STATE_FILE = File.expand_path('~/.claude.json').freeze
|
|
67
|
+
KEYCHAIN_LABEL = 'Claude Code-credentials'.freeze
|
|
283
68
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
config['env'].each do |key, value|
|
|
287
|
-
# 将所有环境变量都添加到 env 中
|
|
288
|
-
env[key] = value.to_s
|
|
69
|
+
def reset_claude_state_to_token_mode
|
|
70
|
+
data = File.exist?(CLAUDE_STATE_FILE) ? JSON.parse(File.read(CLAUDE_STATE_FILE)) : {}
|
|
289
71
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
print_status("🔑 #{key}", value_preview)
|
|
295
|
-
puts " 长度: #{value.to_s.length} 字符" if @verbose_mode
|
|
296
|
-
elsif key.include?('URL') || key.include?('ENDPOINT')
|
|
297
|
-
# URL类:显示完整内容
|
|
298
|
-
print_status("🌐 #{key}", value.to_s)
|
|
299
|
-
elsif key.include?('MODEL')
|
|
300
|
-
# 模型配置:显示完整内容
|
|
301
|
-
print_status("🤖 #{key}", value.to_s)
|
|
302
|
-
elsif key.include?('TIMEOUT')
|
|
303
|
-
# 超时配置:显示完整内容
|
|
304
|
-
print_status("⏱️ #{key}", value.to_s)
|
|
305
|
-
elsif key.include?('PROXY')
|
|
306
|
-
# 代理配置:显示部分内容
|
|
307
|
-
print_status("🔄 #{key}", value.to_s)
|
|
308
|
-
else
|
|
309
|
-
# 其他变量:根据 verbose 模式决定是否显示
|
|
310
|
-
print_status("📝 #{key}", value.to_s) if @verbose_mode
|
|
311
|
-
end
|
|
312
|
-
end
|
|
72
|
+
changed = false
|
|
73
|
+
if data['hasCompletedOnboarding'] != true
|
|
74
|
+
data['hasCompletedOnboarding'] = true
|
|
75
|
+
changed = true
|
|
313
76
|
end
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
proxy_urls = []
|
|
318
|
-
if config['claude_proxy']['HTTP_PROXY']
|
|
319
|
-
env['HTTP_PROXY'] = config['claude_proxy']['HTTP_PROXY']
|
|
320
|
-
env['http_proxy'] = config['claude_proxy']['HTTP_PROXY']
|
|
321
|
-
proxy_urls << config['claude_proxy']['HTTP_PROXY']
|
|
322
|
-
end
|
|
323
|
-
if config['claude_proxy']['HTTPS_PROXY'] && config['claude_proxy']['HTTPS_PROXY'] != config['claude_proxy']['HTTP_PROXY']
|
|
324
|
-
env['HTTPS_PROXY'] = config['claude_proxy']['HTTPS_PROXY']
|
|
325
|
-
env['https_proxy'] = config['claude_proxy']['HTTPS_PROXY']
|
|
326
|
-
proxy_urls << config['claude_proxy']['HTTPS_PROXY']
|
|
327
|
-
elsif config['claude_proxy']['HTTPS_PROXY']
|
|
328
|
-
env['HTTPS_PROXY'] = config['claude_proxy']['HTTPS_PROXY']
|
|
329
|
-
env['https_proxy'] = config['claude_proxy']['HTTPS_PROXY']
|
|
330
|
-
end
|
|
331
|
-
|
|
332
|
-
if proxy_urls.any?
|
|
333
|
-
# 统一显示方式:简化代理显示,隐藏密码和部分IP
|
|
334
|
-
simplified_proxies = proxy_urls.uniq.map do |url|
|
|
335
|
-
uri = URI(url) rescue nil
|
|
336
|
-
if uri
|
|
337
|
-
# 隐藏IP地址的前两段,显示后两段
|
|
338
|
-
masked_host = uri.host
|
|
339
|
-
if uri.host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/
|
|
340
|
-
masked_host = "*.*.#{$3}.#{$4}"
|
|
341
|
-
end
|
|
342
|
-
|
|
343
|
-
if uri.password
|
|
344
|
-
"#{uri.scheme}://***@#{masked_host}:#{uri.port}"
|
|
345
|
-
else
|
|
346
|
-
"#{uri.scheme}://#{masked_host}:#{uri.port}"
|
|
347
|
-
end
|
|
348
|
-
else
|
|
349
|
-
url
|
|
350
|
-
end
|
|
351
|
-
end
|
|
352
|
-
print_status("🌐 代理已配置", simplified_proxies.join(', '))
|
|
353
|
-
|
|
354
|
-
end
|
|
77
|
+
if data.key?('oauthAccount')
|
|
78
|
+
data.delete('oauthAccount')
|
|
79
|
+
changed = true
|
|
355
80
|
end
|
|
81
|
+
return unless changed
|
|
356
82
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
83
|
+
FileUtils.mkdir_p(File.dirname(CLAUDE_STATE_FILE))
|
|
84
|
+
File.write(CLAUDE_STATE_FILE, JSON.pretty_generate(data))
|
|
85
|
+
File.chmod(0o600, CLAUDE_STATE_FILE) unless Base::SystemInfo.windows?
|
|
86
|
+
rescue JSON::ParserError, Errno::EACCES, Errno::ENOENT => e
|
|
87
|
+
warn " ⚠ 跳过 ~/.claude.json 状态对齐(#{e.class}: #{e.message}),如首次接入第三方平台请手动设置 hasCompletedOnboarding=true 并删除 oauthAccount"
|
|
361
88
|
end
|
|
362
89
|
|
|
90
|
+
def delete_keychain_credential(label)
|
|
91
|
+
return unless Base::SystemInfo.macos?
|
|
363
92
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
proxy_exported = false
|
|
368
|
-
|
|
369
|
-
env.each do |key, value|
|
|
370
|
-
# 跳过 PATH 等系统关键变量,避免覆盖
|
|
371
|
-
next if ['PATH', 'HOME', 'USER', 'SHELL'].include?(key)
|
|
93
|
+
# 先 find 检查是否存在;不存在 → 幂等直接 return(避免 delete 时的错误日志)
|
|
94
|
+
_, _, find_status = Open3.capture3('security', 'find-generic-password', '-l', label)
|
|
95
|
+
return unless find_status.success?
|
|
372
96
|
|
|
373
|
-
|
|
374
|
-
|
|
97
|
+
_, delete_status = Open3.capture2e('security', 'delete-generic-password', '-l', label)
|
|
98
|
+
return if delete_status.success?
|
|
375
99
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
else
|
|
380
|
-
env_exported = true
|
|
381
|
-
end
|
|
382
|
-
end
|
|
383
|
-
|
|
384
|
-
print_success("环境变量已设置") if env_exported && @verbose_mode
|
|
385
|
-
print_success("代理已设置到环境变量") if proxy_exported && @verbose_mode
|
|
100
|
+
warn " ⚠ 删除 Keychain 条目「#{label}」失败(exit=#{delete_status.exitstatus}),可能影响 token 模式纯净性"
|
|
101
|
+
rescue StandardError => e
|
|
102
|
+
warn " ⚠ 删除 Keychain 条目「#{label}」异常:#{e.message}"
|
|
386
103
|
end
|
|
387
|
-
|
|
388
|
-
|
|
389
104
|
end
|
|
390
105
|
end
|
|
391
|
-
end
|
|
106
|
+
end
|