easyai 1.0.4 → 1.0.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f7511936104d9115b56bf3ec36425dcdffeedc7cf1c600463901242a281b7505
4
- data.tar.gz: 3f656f1f9da2e0a5ff242707865f960077f5b4b6937b4d5de1fd6db41df03f4f
3
+ metadata.gz: 764f51642d04ee3ba299d468f3d09cc6064899605910fdca6568c536de330e0e
4
+ data.tar.gz: 29d920f541d56356ee766d87f349ee6e2459b9d5a6fdaede676d9457079bf4f7
5
5
  SHA512:
6
- metadata.gz: 05d9c00dfa0732c8379441e29006722e4a9354b255ca6c1209f87b22b586052333fde54edab402406d19883b34f93a36567cf8dd8df4cd08e6b326dd8490323c
7
- data.tar.gz: 9648b22cfbbee85132f78dd034d29ca5340af3051f7465bd95d150de942983148fffe4abf43b691cebe0a5cf5eadc8447e0908235825c2618c80107ff5d21881
6
+ metadata.gz: 68b0aaec2f2996bf44c808d2e1266f47e4fc624a5194e1a29569b942ca35abfc9cd393baf3b29a5c6263798d591dd5b570d1b289e20da0fc30d05cb419f4ad92
7
+ data.tar.gz: 816988a6ec0d2414ee5823621febfb7c28fa7607484f85436b8014a3eb0bab31b2d316e6f342f3939f066f7087bd443dcda9eab1d4302e9a9ca20bb4c2202dd6
@@ -164,13 +164,27 @@ module EasyAI
164
164
  def start_callback_server
165
165
  code = nil
166
166
 
167
+ # 检查并处理端口占用
168
+ unless ensure_port_available(@server_port)
169
+ puts "✗ 无法使用端口 #{@server_port},登录失败"
170
+ return nil
171
+ end
172
+
167
173
  puts "启动本地服务器,监听端口 #{@server_port}..."
168
174
 
169
- server = WEBrick::HTTPServer.new(
170
- Port: @server_port,
171
- Logger: WEBrick::Log.new("/dev/null"),
172
- AccessLog: []
173
- )
175
+ begin
176
+ server = WEBrick::HTTPServer.new(
177
+ Port: @server_port,
178
+ Logger: WEBrick::Log.new("/dev/null"),
179
+ AccessLog: []
180
+ )
181
+ rescue Errno::EADDRINUSE
182
+ puts "✗ 端口 #{@server_port} 仍被占用,无法启动服务器"
183
+ return nil
184
+ rescue => e
185
+ puts "✗ 启动服务器失败: #{e.message}"
186
+ return nil
187
+ end
174
188
 
175
189
  # 处理根路径的请求
176
190
  server.mount_proc '/' do |req, res|
@@ -379,6 +393,176 @@ module EasyAI
379
393
  </html>
380
394
  HTML
381
395
  end
396
+
397
+ # 确保端口可用,处理端口占用问题
398
+ def ensure_port_available(port)
399
+ return true unless port_occupied?(port)
400
+
401
+ puts "⚠️ 端口 #{port} 被占用,正在检查占用进程..."
402
+
403
+ # 获取占用端口的进程信息
404
+ process_info = get_port_process_info(port)
405
+
406
+ if process_info.nil?
407
+ puts "✗ 无法获取端口占用信息"
408
+ return false
409
+ end
410
+
411
+ puts "占用进程信息:"
412
+ puts " PID: #{process_info[:pid]}"
413
+ puts " 进程名: #{process_info[:name]}"
414
+ puts " 命令: #{process_info[:command]}"
415
+
416
+ # 检查是否是自己的进程(可能是之前未正常关闭的实例)
417
+ if process_info[:name].include?('ruby') && process_info[:command].include?('easyai')
418
+ puts "检测到可能是 EasyAI 之前的遗留进程"
419
+ if ask_user_kill_process?
420
+ return kill_process_by_pid(process_info[:pid])
421
+ end
422
+ else
423
+ puts "端口被其他应用程序占用"
424
+ if ask_user_kill_process?
425
+ return kill_process_by_pid(process_info[:pid])
426
+ end
427
+ end
428
+
429
+ return false
430
+ end
431
+
432
+ # 检查端口是否被占用
433
+ def port_occupied?(port)
434
+ require 'socket'
435
+
436
+ begin
437
+ server = TCPServer.new('127.0.0.1', port)
438
+ server.close
439
+ false # 端口未被占用
440
+ rescue Errno::EADDRINUSE
441
+ true # 端口被占用
442
+ rescue => e
443
+ puts "⚠️ 检查端口时出错: #{e.message}"
444
+ true # 出错时假设端口被占用,避免冲突
445
+ end
446
+ end
447
+
448
+ # 获取占用指定端口的进程信息
449
+ def get_port_process_info(port)
450
+ begin
451
+ # macOS/Linux 使用 lsof 命令
452
+ if system('which lsof > /dev/null 2>&1')
453
+ output = `lsof -ti :#{port} 2>/dev/null`.strip
454
+ return nil if output.empty?
455
+
456
+ pid = output.split("\n").first
457
+ return nil if pid.nil? || pid.empty?
458
+
459
+ # 获取进程详细信息
460
+ ps_output = `ps -p #{pid} -o pid,comm,args 2>/dev/null`.lines
461
+ return nil if ps_output.length < 2
462
+
463
+ process_line = ps_output[1].strip
464
+ parts = process_line.split(/\s+/, 3)
465
+
466
+ return {
467
+ pid: parts[0],
468
+ name: parts[1] || 'unknown',
469
+ command: parts[2] || 'unknown'
470
+ }
471
+ end
472
+
473
+ # Windows 使用 netstat 命令
474
+ if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
475
+ output = `netstat -ano | findstr :#{port}`.strip
476
+ return nil if output.empty?
477
+
478
+ lines = output.split("\n")
479
+ listening_line = lines.find { |line| line.include?('LISTENING') }
480
+ return nil unless listening_line
481
+
482
+ pid = listening_line.split.last
483
+ return nil if pid.nil? || pid.empty?
484
+
485
+ # 获取进程名称
486
+ task_output = `tasklist /FI "PID eq #{pid}" /FO CSV /NH 2>nul`.strip
487
+ if !task_output.empty?
488
+ process_name = task_output.split(',').first.gsub('"', '')
489
+ return {
490
+ pid: pid,
491
+ name: process_name,
492
+ command: process_name
493
+ }
494
+ end
495
+ end
496
+
497
+ return nil
498
+ rescue => e
499
+ puts "获取进程信息时出错: #{e.message}"
500
+ return nil
501
+ end
502
+ end
503
+
504
+ # 询问用户是否终止占用端口的进程
505
+ def ask_user_kill_process?
506
+ puts "\n是否终止占用端口的进程?"
507
+ puts " y/yes - 终止进程并继续 (可能影响其他应用)"
508
+ puts " n/no - 取消登录 (默认)"
509
+ print "> "
510
+
511
+ begin
512
+ response = STDIN.gets&.chomp&.downcase
513
+ return ['y', 'yes'].include?(response)
514
+ rescue Interrupt
515
+ puts "\n\n用户中断操作"
516
+ return false
517
+ rescue => e
518
+ puts "无法读取用户输入: #{e.message}"
519
+ return false
520
+ end
521
+ end
522
+
523
+ # 根据 PID 终止进程
524
+ def kill_process_by_pid(pid)
525
+ return false if pid.nil? || pid.empty?
526
+
527
+ begin
528
+ puts "正在终止进程 #{pid}..."
529
+
530
+ # 跨平台的进程终止命令
531
+ if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
532
+ # Windows
533
+ success = system("taskkill /PID #{pid} /F >nul 2>&1")
534
+ else
535
+ # macOS/Linux - 先尝试优雅终止,再强制终止
536
+ success = system("kill -TERM #{pid} 2>/dev/null")
537
+ sleep(2)
538
+ # 检查进程是否还存在
539
+ if system("kill -0 #{pid} 2>/dev/null")
540
+ puts "优雅终止失败,使用强制终止..."
541
+ success = system("kill -KILL #{pid} 2>/dev/null")
542
+ end
543
+ end
544
+
545
+ if success
546
+ puts "✓ 进程 #{pid} 已终止"
547
+ sleep(1) # 等待端口释放
548
+
549
+ # 再次检查端口是否可用
550
+ unless port_occupied?(@server_port)
551
+ puts "✓ 端口 #{@server_port} 现在可用"
552
+ return true
553
+ else
554
+ puts "⚠️ 端口仍被占用,可能需要等待更长时间"
555
+ return false
556
+ end
557
+ else
558
+ puts "✗ 终止进程失败,可能需要管理员权限"
559
+ return false
560
+ end
561
+ rescue => e
562
+ puts "终止进程时出错: #{e.message}"
563
+ return false
564
+ end
565
+ end
382
566
  end
383
567
  end
384
568
  end
@@ -62,9 +62,6 @@ module EasyAI
62
62
  begin
63
63
  # 在非verbose模式下显示简洁的更新提醒
64
64
  Base::UpdateNotifier.maybe_show_notification unless @verbose_mode
65
-
66
- # 美化的启动信息
67
- print_header
68
65
 
69
66
  # 首先尝试从远程下载配置
70
67
  remote_config = nil
@@ -128,11 +125,6 @@ module EasyAI
128
125
 
129
126
  private
130
127
 
131
- def print_header
132
- puts "\n╔#{'═' * 58}╗"
133
- puts "║#{'EasyAI Claude CLI 启动器'.center(58)}║"
134
- puts "╚#{'═' * 58}╝\n"
135
- end
136
128
 
137
129
  def print_status(icon_text, detail = nil)
138
130
  if detail
@@ -1,3 +1,3 @@
1
1
  module EasyAI
2
- VERSION = '1.0.4'
2
+ VERSION = '1.0.5'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: easyai
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wade