kscript 0.1.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 +7 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +169 -0
- data/Rakefile +35 -0
- data/bin/kscript +6 -0
- data/kscript.gemspec +45 -0
- data/lib/kscript/banner.rb +12 -0
- data/lib/kscript/base.rb +50 -0
- data/lib/kscript/cli.rb +184 -0
- data/lib/kscript/logger.rb +94 -0
- data/lib/kscript/plugins/kk_apnic_utils.rb +87 -0
- data/lib/kscript/plugins/kk_cleaner_utils.rb +83 -0
- data/lib/kscript/plugins/kk_es_fingerprint_utils.rb +92 -0
- data/lib/kscript/plugins/kk_ffmpeg_utils.rb +140 -0
- data/lib/kscript/plugins/kk_ip_utils.rb +90 -0
- data/lib/kscript/plugins/kk_jenkins_utils.rb +143 -0
- data/lib/kscript/plugins/kk_kibana_utils.rb +237 -0
- data/lib/kscript/plugins/kk_lvm_utils.rb +200 -0
- data/lib/kscript/plugins/kk_optimize_utils.rb +85 -0
- data/lib/kscript/plugins/kk_portscan_utils.rb +90 -0
- data/lib/kscript/plugins/kk_projscan_utils.rb +90 -0
- data/lib/kscript/plugins/kk_rename_utils.rb +82 -0
- data/lib/kscript/plugins/kk_sh_utils.rb +112 -0
- data/lib/kscript/plugins/kk_syscheck_utils.rb +82 -0
- data/lib/kscript/plugins/kk_top_utils.rb +74 -0
- data/lib/kscript/plugins/kk_usd_utils.rb +71 -0
- data/lib/kscript/plugins/kk_wg_acl_utils.rb +95 -0
- data/lib/kscript/plugins/kk_wg_pass_utils.rb +50 -0
- data/lib/kscript/plugins.rb +32 -0
- data/lib/kscript/utils.rb +64 -0
- data/lib/kscript/version.rb +10 -0
- data/lib/kscript.rb +43 -0
- metadata +130 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) 2025 Kk
|
4
|
+
#
|
5
|
+
# This software is released under the MIT License.
|
6
|
+
# https://opensource.org/licenses/MIT
|
7
|
+
|
8
|
+
require 'kscript'
|
9
|
+
|
10
|
+
module Kscript
|
11
|
+
class KkRenameUtils < Base
|
12
|
+
attr_reader :source_pattern, :target_pattern, :directory
|
13
|
+
|
14
|
+
def initialize(source_pattern = nil, target_pattern = nil, directory = Dir.pwd, *_args, **opts)
|
15
|
+
super(**opts.merge(service: 'kk_rename'))
|
16
|
+
@source_pattern = source_pattern
|
17
|
+
@target_pattern = target_pattern
|
18
|
+
@directory = directory
|
19
|
+
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
with_error_handling do
|
23
|
+
rename
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def rename
|
28
|
+
Dir.entries(@directory).each do |filename|
|
29
|
+
process_file(filename)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.arguments
|
34
|
+
'<pattern> <replacement> [path]'
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.usage
|
38
|
+
"kscript rename foo bar ./src\nkscript rename 'test' 'prod' ~/projects"
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.group
|
42
|
+
'project'
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.author
|
46
|
+
'kk'
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.description
|
50
|
+
'Batch rename files by pattern.'
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def process_file(filename)
|
56
|
+
return unless should_process?(filename)
|
57
|
+
|
58
|
+
new_name = generate_new_name(filename)
|
59
|
+
return unless new_name
|
60
|
+
|
61
|
+
rename_file(filename, new_name)
|
62
|
+
end
|
63
|
+
|
64
|
+
def should_process?(filename)
|
65
|
+
File.file?(File.join(@directory, filename)) && filename =~ /#{@source_pattern}/
|
66
|
+
end
|
67
|
+
|
68
|
+
def generate_new_name(filename)
|
69
|
+
eval("\"#{filename}\"".gsub(/#{@source_pattern}/, @target_pattern))
|
70
|
+
rescue StandardError => e
|
71
|
+
logger.kerror("Error processing #{filename}: #{e.message}")
|
72
|
+
nil
|
73
|
+
end
|
74
|
+
|
75
|
+
def rename_file(old_name, new_name)
|
76
|
+
File.rename(File.join(@directory, old_name), File.join(@directory, new_name))
|
77
|
+
logger.kinfo("Renamed: #{old_name} -> #{new_name}")
|
78
|
+
rescue StandardError => e
|
79
|
+
logger.kerror("Error renaming #{old_name}: #{e.message}")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) 2025 Kk
|
4
|
+
#
|
5
|
+
# This software is released under the MIT License.
|
6
|
+
# https://opensource.org/licenses/MIT
|
7
|
+
|
8
|
+
require 'kscript'
|
9
|
+
require 'nokogiri'
|
10
|
+
|
11
|
+
module Kscript
|
12
|
+
class KkShUtils < Base
|
13
|
+
CHT_SH_URL = 'https://cht.sh'
|
14
|
+
|
15
|
+
attr_reader :command
|
16
|
+
|
17
|
+
# Initialize with shell command to look up
|
18
|
+
# @param command [String] command to get help for
|
19
|
+
def initialize(command = nil, *_args, **opts)
|
20
|
+
super(**opts.merge(service: 'kk_sh'))
|
21
|
+
@command = command
|
22
|
+
end
|
23
|
+
|
24
|
+
def run
|
25
|
+
with_error_handling do
|
26
|
+
help
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def help
|
31
|
+
if command
|
32
|
+
fetch_help
|
33
|
+
else
|
34
|
+
logger.kinfo("Usage: #{$PROGRAM_NAME} <command>")
|
35
|
+
exit 1
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Fetch and display command documentation
|
40
|
+
def fetch_help
|
41
|
+
response = make_request
|
42
|
+
display_result(response)
|
43
|
+
rescue HTTP::Error => e
|
44
|
+
display_error(e)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.arguments
|
48
|
+
'[subcommand] [args...]'
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.usage
|
52
|
+
"kscript sh 'ls'\nkscript sh 'echo hello'"
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.group
|
56
|
+
'system'
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.author
|
60
|
+
'kk'
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.description
|
64
|
+
'Query sh command usage and cheatsheets.'
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
# Make HTTP request to cht.sh
|
70
|
+
# @return [HTTP::Response] response from cht.sh
|
71
|
+
def make_request
|
72
|
+
HTTP.get("#{CHT_SH_URL}/#{command}")
|
73
|
+
end
|
74
|
+
|
75
|
+
# Display command documentation
|
76
|
+
# @param response [HTTP::Response] response from cht.sh
|
77
|
+
def display_result(response)
|
78
|
+
if response.status.success?
|
79
|
+
text = extract_plain_text(response.body)
|
80
|
+
logger.kinfo(text)
|
81
|
+
else
|
82
|
+
logger.kerror("Failed to retrieve data: #{response.status}")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# 提取纯文本内容
|
87
|
+
def extract_plain_text(body)
|
88
|
+
body = body.to_s
|
89
|
+
begin
|
90
|
+
doc = Nokogiri::HTML(body)
|
91
|
+
doc.search('script,style').remove
|
92
|
+
text = doc.text.lines.map(&:strip)
|
93
|
+
# 过滤掉包含广告/社交/Follow等内容的行
|
94
|
+
text = text.reject { |line| line =~ /Follow @|twitter|github|sponsor|donate|chubin|^!function/ }
|
95
|
+
text.reject!(&:empty?)
|
96
|
+
# 去除顶部多余空白行,只保留正文
|
97
|
+
text = text.drop_while(&:empty?)
|
98
|
+
text.join("\n")
|
99
|
+
rescue StandardError
|
100
|
+
body.gsub(/\e\[[\d;]*m/, '').lines.reject do |line|
|
101
|
+
line =~ /Follow @|twitter|github|sponsor|donate|chubin|^!function/
|
102
|
+
end.drop_while { |line| line.strip.empty? }.join
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Display error message
|
107
|
+
# @param error [StandardError] error to display
|
108
|
+
def display_error(error)
|
109
|
+
logger.kerror("An error occurred: #{error.message}")
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) 2025 Kk
|
4
|
+
#
|
5
|
+
# This software is released under the MIT License.
|
6
|
+
# https://opensource.org/licenses/MIT
|
7
|
+
|
8
|
+
require 'kscript'
|
9
|
+
|
10
|
+
module Kscript
|
11
|
+
class KkSyscheckUtils < Base
|
12
|
+
def run
|
13
|
+
with_error_handling do
|
14
|
+
check
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def check
|
19
|
+
logger.kinfo('======= 🍎 macOS System Resource Monitor Report =======')
|
20
|
+
logger.kinfo("📅 Date Time: #{Time.now}")
|
21
|
+
logger.kinfo('')
|
22
|
+
|
23
|
+
# macOS ps command for process monitoring
|
24
|
+
cpu_cmd = 'ps aux | sort -nrk 3 | head -n 10'
|
25
|
+
mem_cmd = 'ps aux | sort -nrk 4 | head -n 10'
|
26
|
+
|
27
|
+
run('CPU Usage (Top 10)', cpu_cmd)
|
28
|
+
run('Memory Usage (Top 10)', mem_cmd)
|
29
|
+
|
30
|
+
if `which powermetrics`.strip.empty?
|
31
|
+
logger.kinfo("\n👉 GPU Usage:")
|
32
|
+
logger.kwarn('⚠️ powermetrics not installed, please run: xcode-select --install')
|
33
|
+
else
|
34
|
+
run('GPU Usage', 'powermetrics --samplers gpu_power -n 1', sudo: true)
|
35
|
+
end
|
36
|
+
|
37
|
+
logger.kinfo("\n👉 Top 10 Processes by Network Connections:")
|
38
|
+
lsof_output = `lsof -i -nP | grep ESTABLISHED`
|
39
|
+
counts = Hash.new(0)
|
40
|
+
lsof_output.each_line do |line|
|
41
|
+
process = line.split.first
|
42
|
+
counts[process] += 1
|
43
|
+
end
|
44
|
+
counts.sort_by { |_, v| -v }.first(10).each do |proc, count|
|
45
|
+
logger.kinfo("#{proc}: #{count} connections")
|
46
|
+
end
|
47
|
+
|
48
|
+
logger.kinfo("\n👉 System Overview:")
|
49
|
+
cpu_core = `sysctl -n hw.ncpu`.strip
|
50
|
+
mem_size = `sysctl -n hw.memsize`.to_i / 1024 / 1024 / 1024
|
51
|
+
logger.kinfo("CPU Cores: #{cpu_core}")
|
52
|
+
logger.kinfo("Physical Memory: #{mem_size} GB")
|
53
|
+
|
54
|
+
vm_stats = `vm_stat`
|
55
|
+
vm_stats.each_line do |line|
|
56
|
+
logger.kinfo(line) if line =~ /Pages (active|wired down|free):/
|
57
|
+
end
|
58
|
+
|
59
|
+
logger.kinfo("\n✅ System resource check completed!")
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.description
|
63
|
+
'Show macOS system resource monitor report.'
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.arguments
|
67
|
+
''
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.usage
|
71
|
+
"kscript syscheck\nkscript syscheck --detail"
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.group
|
75
|
+
'macos'
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.author
|
79
|
+
'kk'
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) 2025 Kk
|
4
|
+
#
|
5
|
+
# This software is released under the MIT License.
|
6
|
+
# https://opensource.org/licenses/MIT
|
7
|
+
|
8
|
+
require 'kscript'
|
9
|
+
|
10
|
+
# 彩色输出定义
|
11
|
+
RED = "\e[1;31m"
|
12
|
+
GREEN = "\e[1;32m"
|
13
|
+
YELLOW = "\e[1;33m"
|
14
|
+
CYAN = "\e[1;36m"
|
15
|
+
NC = "\e[0m" # No Color
|
16
|
+
|
17
|
+
module Kscript
|
18
|
+
class KkTopUtils < Base
|
19
|
+
def run
|
20
|
+
with_error_handling do
|
21
|
+
print_report
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def print_report
|
26
|
+
logger.kinfo("System Resource Top Report - #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}")
|
27
|
+
print_header('Top 10 Processes by CPU Usage')
|
28
|
+
print_process_list(:cpu)
|
29
|
+
print_header('Top 10 Processes by Memory Usage')
|
30
|
+
print_process_list(:mem)
|
31
|
+
end
|
32
|
+
|
33
|
+
def print_header(title)
|
34
|
+
logger.kinfo('')
|
35
|
+
logger.kinfo('===============================')
|
36
|
+
logger.kinfo(" #{title}")
|
37
|
+
logger.kinfo('===============================')
|
38
|
+
end
|
39
|
+
|
40
|
+
def print_process_list(sort_field)
|
41
|
+
lines = `ps aux`.split("\n")
|
42
|
+
lines.shift
|
43
|
+
processes = lines.map { |line| line.split(/\s+/, 11) }
|
44
|
+
index = sort_field == :cpu ? 2 : 3
|
45
|
+
top = processes.sort_by { |p| -p[index].to_f }.first(10)
|
46
|
+
logger.kinfo(format('%-10s %-8s %-5s %-5s %-10s', 'USER', 'PID', '%CPU', '%MEM', 'COMMAND'))
|
47
|
+
top.each do |p|
|
48
|
+
logger.kinfo(format('%-10s %-8s %-5s %-5s %-10s', p[0], p[1], p[2], p[3], p[10][0..30]))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.description
|
53
|
+
'Show top 10 processes by CPU/memory on macOS.'
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.arguments
|
57
|
+
''
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.usage
|
61
|
+
'kscript top'
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.group
|
65
|
+
'macos'
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.author
|
69
|
+
'kk'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
Kscript::KkTopUtils.new.run if __FILE__ == $PROGRAM_NAME
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) 2025 Kk
|
4
|
+
#
|
5
|
+
# This software is released under the MIT License.
|
6
|
+
# https://opensource.org/licenses/MIT
|
7
|
+
|
8
|
+
require 'kscript'
|
9
|
+
|
10
|
+
require 'net/http'
|
11
|
+
require 'json'
|
12
|
+
|
13
|
+
module Kscript
|
14
|
+
class KkUsdUtils < Base
|
15
|
+
API_URL = 'https://api.exchangerate-api.com/v4/latest/USD'
|
16
|
+
|
17
|
+
def initialize(currency_code = nil, *_args, **opts)
|
18
|
+
super(**opts.merge(service: 'kk_usd'))
|
19
|
+
@currency_code = currency_code
|
20
|
+
end
|
21
|
+
|
22
|
+
def run
|
23
|
+
with_error_handling do
|
24
|
+
fetch_rates
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def fetch_rates
|
29
|
+
uri = URI(API_URL)
|
30
|
+
response = Net::HTTP.get(uri)
|
31
|
+
data = JSON.parse(response)
|
32
|
+
if @currency_code && data['rates'][@currency_code.upcase]
|
33
|
+
rate = data['rates'][@currency_code.upcase]
|
34
|
+
if human_output?
|
35
|
+
logger.kinfo("1 USD = #{rate} #{@currency_code.upcase}")
|
36
|
+
else
|
37
|
+
logger.kinfo("USD -> #{@currency_code.upcase}", rate: rate)
|
38
|
+
end
|
39
|
+
elsif @currency_code
|
40
|
+
if human_output?
|
41
|
+
end
|
42
|
+
logger.kerror("Currency code not found: #{@currency_code}")
|
43
|
+
elsif human_output?
|
44
|
+
logger.kinfo('USD Exchange Rates:')
|
45
|
+
data['rates'].each { |k, v| logger.kinfo(" 1 USD = #{v} #{k}") }
|
46
|
+
else
|
47
|
+
logger.kinfo('USD rates', rates: data['rates'])
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.arguments
|
52
|
+
'[currency_code]'
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.usage
|
56
|
+
"kscript usd CNY\nkscript usd EUR"
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.group
|
60
|
+
'finance'
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.author
|
64
|
+
'kk'
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.description
|
68
|
+
'Get latest USD exchange rates.'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) 2025 Kk
|
4
|
+
#
|
5
|
+
# This software is released under the MIT License.
|
6
|
+
# https://opensource.org/licenses/MIT
|
7
|
+
|
8
|
+
require 'kscript'
|
9
|
+
|
10
|
+
module Kscript
|
11
|
+
class KkWgAclUtils < Base
|
12
|
+
WIREGUARD_PORT = 51_821
|
13
|
+
ALLOWED_IPS = %w[127.0.0.1].freeze
|
14
|
+
|
15
|
+
def run
|
16
|
+
with_error_handling do
|
17
|
+
apply_rules
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def apply_rules
|
22
|
+
add_whitelist_rules
|
23
|
+
ensure_firewall_enabled
|
24
|
+
display_current_rules
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.arguments
|
28
|
+
'[subcommand] [options]'
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.usage
|
32
|
+
"kscript wg_acl add --ip=10.0.0.2\nkscript wg_acl list"
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.group
|
36
|
+
'network'
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.author
|
40
|
+
'kk'
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.description
|
44
|
+
'Manage WireGuard firewall ACL rules.'
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# Fetch current UFW rules
|
50
|
+
# @return [Array<String>] list of current firewall rules
|
51
|
+
def fetch_current_rules
|
52
|
+
`sudo ufw status`.lines.map(&:strip)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Check if a specific rule exists
|
56
|
+
# @param ip [String] IP address to check
|
57
|
+
# @param port [Integer] port number to check
|
58
|
+
# @return [Boolean] true if rule exists
|
59
|
+
def rule_exists?(ip, port)
|
60
|
+
@current_rules.any? do |line|
|
61
|
+
line.match?(/#{Regexp.escape(ip)}.*ALLOW.*#{port}/)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Add whitelist rules for allowed IPs
|
66
|
+
def add_whitelist_rules
|
67
|
+
ALLOWED_IPS.each do |ip|
|
68
|
+
if rule_exists?(ip, WIREGUARD_PORT)
|
69
|
+
logger.kinfo("✅ Rule exists: #{ip} → #{WIREGUARD_PORT}, skipping.")
|
70
|
+
else
|
71
|
+
logger.kinfo("👉 Adding rule: allow #{ip} to access port #{WIREGUARD_PORT}")
|
72
|
+
system("sudo ufw allow from #{ip} to any port #{WIREGUARD_PORT}")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Ensure UFW firewall is enabled
|
78
|
+
def ensure_firewall_enabled
|
79
|
+
ufw_status = `sudo ufw status`.strip
|
80
|
+
if ufw_status.start_with?('Status: inactive')
|
81
|
+
logger.kinfo('🔧 UFW is currently disabled, enabling...')
|
82
|
+
system('sudo ufw enable')
|
83
|
+
else
|
84
|
+
logger.kinfo('✅ UFW is enabled.')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Display current firewall rules
|
89
|
+
def display_current_rules
|
90
|
+
logger.kinfo("\n📋 Current firewall rules:")
|
91
|
+
system('sudo ufw status verbose')
|
92
|
+
logger.kinfo("\n✅ Firewall rules update completed!")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (c) 2025 Kk
|
4
|
+
#
|
5
|
+
# This software is released under the MIT License.
|
6
|
+
# https://opensource.org/licenses/MIT
|
7
|
+
|
8
|
+
require 'kscript'
|
9
|
+
require 'bcrypt'
|
10
|
+
|
11
|
+
module Kscript
|
12
|
+
class KkWgPassUtils < Base
|
13
|
+
def initialize(length = 32, *_args, **opts)
|
14
|
+
super(**opts.merge(service: 'kk_wg_pass'))
|
15
|
+
@length = length.to_i
|
16
|
+
end
|
17
|
+
|
18
|
+
def run
|
19
|
+
with_error_handling do
|
20
|
+
generate
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def generate
|
25
|
+
password = Array.new(@length) { rand(33..126).chr }.join
|
26
|
+
logger.kinfo('Generated WireGuard password', password: password)
|
27
|
+
logger.kinfo(password)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.arguments
|
31
|
+
'[length]'
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.usage
|
35
|
+
"kscript wg_pass [length]\nkscript wg_pass 32"
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.group
|
39
|
+
'network'
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.author
|
43
|
+
'kk'
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.description
|
47
|
+
'Generate a random password for WireGuard.'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kscript
|
4
|
+
module PluginLoader
|
5
|
+
PLUGIN_DIR = File.expand_path('plugins', __dir__)
|
6
|
+
|
7
|
+
def self.load_all
|
8
|
+
Dir.glob(File.join(PLUGIN_DIR, 'kk_*.rb')).sort.each do |file|
|
9
|
+
# 解析出类名
|
10
|
+
basename = File.basename(file, '.rb')
|
11
|
+
class_name = basename.split('_').map(&:capitalize).join
|
12
|
+
# 只在未定义时 require
|
13
|
+
require file unless Kscript.const_defined?(class_name, false)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# 返回所有已注册插件的元信息
|
18
|
+
def self.plugin_infos
|
19
|
+
Kscript::Plugin.all.map do |name, klass|
|
20
|
+
{
|
21
|
+
name: name,
|
22
|
+
class: klass,
|
23
|
+
description: klass.respond_to?(:description) ? klass.description : nil,
|
24
|
+
arguments: klass.respond_to?(:arguments) ? klass.arguments : nil,
|
25
|
+
usage: klass.respond_to?(:usage) ? klass.usage : nil,
|
26
|
+
group: klass.respond_to?(:group) ? klass.group : nil,
|
27
|
+
author: klass.respond_to?(:author) ? klass.author : nil
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
module Kscript
|
7
|
+
module Utils
|
8
|
+
class Config
|
9
|
+
DEFAULT_PATH = File.expand_path('~/.kscriptrc')
|
10
|
+
|
11
|
+
def self.load
|
12
|
+
@load ||= new
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@data = File.exist?(DEFAULT_PATH) ? YAML.load_file(DEFAULT_PATH) : {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def [](key)
|
20
|
+
@data[key.to_s] || ENV["KSCRIPT_#{key.to_s.upcase}"]
|
21
|
+
end
|
22
|
+
|
23
|
+
def log_level
|
24
|
+
self['log_level']
|
25
|
+
end
|
26
|
+
|
27
|
+
def trace_id
|
28
|
+
self['trace_id']
|
29
|
+
end
|
30
|
+
|
31
|
+
# 自动检测并安装 shell 补全脚本
|
32
|
+
def self.ensure_completion_installed(shell = nil)
|
33
|
+
shell ||= ENV['SHELL']
|
34
|
+
home = Dir.respond_to?(:home) ? Dir.home : ENV['HOME']
|
35
|
+
return unless shell && home
|
36
|
+
|
37
|
+
if shell.include?('zsh')
|
38
|
+
completion_path = File.join(home, '.zsh/completions/_kscript')
|
39
|
+
shell_type = 'zsh'
|
40
|
+
elsif shell.include?('bash')
|
41
|
+
completion_path = File.join(home, '.bash_completion.d/kscript')
|
42
|
+
shell_type = 'bash'
|
43
|
+
else
|
44
|
+
return
|
45
|
+
end
|
46
|
+
|
47
|
+
# 已存在则跳过
|
48
|
+
return if File.exist?(completion_path)
|
49
|
+
|
50
|
+
# 生成补全脚本内容
|
51
|
+
require_relative 'cli'
|
52
|
+
script = case shell_type
|
53
|
+
when 'zsh' then Kscript::CLI.new.completion('zsh', capture: true)
|
54
|
+
when 'bash' then Kscript::CLI.new.completion('bash', capture: true)
|
55
|
+
end
|
56
|
+
FileUtils.mkdir_p(File.dirname(completion_path))
|
57
|
+
File.write(completion_path, script)
|
58
|
+
puts "\e[32m[kscript] Shell completion installed to #{completion_path}\e[0m"
|
59
|
+
rescue StandardError => e
|
60
|
+
warn "[kscript] Failed to install shell completion: #{e.message}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|