shared_tools 0.3.1 → 0.4.1
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/CHANGELOG.md +46 -16
- data/README.md +257 -262
- data/lib/shared_tools/browser_tool.rb +5 -0
- data/lib/shared_tools/calculator_tool.rb +4 -0
- data/lib/shared_tools/clipboard_tool.rb +4 -0
- data/lib/shared_tools/composite_analysis_tool.rb +4 -0
- data/lib/shared_tools/computer_tool.rb +5 -0
- data/lib/shared_tools/cron_tool.rb +4 -0
- data/lib/shared_tools/current_date_time_tool.rb +4 -0
- data/lib/shared_tools/data_science_kit.rb +4 -0
- data/lib/shared_tools/database.rb +4 -0
- data/lib/shared_tools/database_query_tool.rb +4 -0
- data/lib/shared_tools/database_tool.rb +5 -0
- data/lib/shared_tools/disk_tool.rb +5 -0
- data/lib/shared_tools/dns_tool.rb +4 -0
- data/lib/shared_tools/doc_tool.rb +5 -0
- data/lib/shared_tools/error_handling_tool.rb +4 -0
- data/lib/shared_tools/eval_tool.rb +5 -0
- data/lib/shared_tools/mcp/brave_search_client.rb +37 -0
- data/lib/shared_tools/mcp/chart_client.rb +32 -0
- data/lib/shared_tools/mcp/github_client.rb +38 -0
- data/lib/shared_tools/mcp/hugging_face_client.rb +43 -0
- data/lib/shared_tools/mcp/memory_client.rb +33 -0
- data/lib/shared_tools/mcp/notion_client.rb +40 -0
- data/lib/shared_tools/mcp/sequential_thinking_client.rb +33 -0
- data/lib/shared_tools/mcp/slack_client.rb +54 -0
- data/lib/shared_tools/mcp/streamable_http_patch.rb +42 -0
- data/lib/shared_tools/mcp/tavily_client.rb +41 -0
- data/lib/shared_tools/mcp.rb +45 -16
- data/lib/shared_tools/system_info_tool.rb +4 -0
- data/lib/shared_tools/tools/browser/base_tool.rb +8 -12
- data/lib/shared_tools/tools/browser/click_tool.rb +4 -2
- data/lib/shared_tools/tools/browser/ferrum_driver.rb +119 -0
- data/lib/shared_tools/tools/browser/inspect_tool.rb +4 -2
- data/lib/shared_tools/tools/browser/page_inspect_tool.rb +4 -2
- data/lib/shared_tools/tools/browser/page_screenshot_tool.rb +19 -7
- data/lib/shared_tools/tools/browser/selector_inspect_tool.rb +4 -2
- data/lib/shared_tools/tools/browser/text_field_area_set_tool.rb +4 -2
- data/lib/shared_tools/tools/browser/visit_tool.rb +4 -2
- data/lib/shared_tools/tools/browser.rb +31 -2
- data/lib/shared_tools/tools/browser_tool.rb +6 -0
- data/lib/shared_tools/tools/clipboard_tool.rb +69 -144
- data/lib/shared_tools/tools/composite_analysis_tool.rb +60 -4
- data/lib/shared_tools/tools/computer/mac_driver.rb +37 -4
- data/lib/shared_tools/tools/cron_tool.rb +237 -379
- data/lib/shared_tools/tools/current_date_time_tool.rb +54 -120
- data/lib/shared_tools/tools/data_science_kit.rb +63 -13
- data/lib/shared_tools/tools/dns_tool.rb +335 -269
- data/lib/shared_tools/tools/doc/docx_reader_tool.rb +107 -0
- data/lib/shared_tools/tools/doc/spreadsheet_reader_tool.rb +171 -0
- data/lib/shared_tools/tools/doc/text_reader_tool.rb +57 -0
- data/lib/shared_tools/tools/doc.rb +3 -0
- data/lib/shared_tools/tools/doc_tool.rb +101 -6
- data/lib/shared_tools/tools/docker/compose_run_tool.rb +1 -1
- data/lib/shared_tools/tools/enabler.rb +42 -0
- data/lib/shared_tools/tools/error_handling_tool.rb +3 -1
- data/lib/shared_tools/tools/notification/base_driver.rb +51 -0
- data/lib/shared_tools/tools/notification/linux_driver.rb +115 -0
- data/lib/shared_tools/tools/notification/mac_driver.rb +66 -0
- data/lib/shared_tools/tools/notification/null_driver.rb +29 -0
- data/lib/shared_tools/tools/notification.rb +12 -0
- data/lib/shared_tools/tools/notification_tool.rb +99 -0
- data/lib/shared_tools/tools/system_info_tool.rb +130 -343
- data/lib/shared_tools/tools/workflow_manager_tool.rb +32 -0
- data/lib/shared_tools/utilities.rb +193 -0
- data/lib/shared_tools/version.rb +1 -1
- data/lib/shared_tools/weather_tool.rb +4 -0
- data/lib/shared_tools/workflow_manager_tool.rb +4 -0
- data/lib/shared_tools.rb +28 -38
- metadata +74 -9
- data/lib/shared_tools/mcp/github_mcp_server.rb +0 -58
- data/lib/shared_tools/mcp/imcp.rb +0 -28
- data/lib/shared_tools/mcp/tavily_mcp_server.rb +0 -44
- data/lib/shared_tools/tools/devops_toolkit.rb +0 -420
|
@@ -1,56 +1,34 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
require_relative '../../shared_tools'
|
|
4
4
|
|
|
5
5
|
module SharedTools
|
|
6
6
|
module Tools
|
|
7
|
-
#
|
|
8
|
-
# Provides cross-platform support for macOS, Linux, and Windows.
|
|
7
|
+
# Returns OS, CPU, memory, disk, network, and Ruby runtime information.
|
|
9
8
|
#
|
|
10
9
|
# @example
|
|
11
10
|
# tool = SharedTools::Tools::SystemInfoTool.new
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def self.name = 'system_info'
|
|
11
|
+
# tool.execute # all categories
|
|
12
|
+
# tool.execute(category: 'cpu') # CPU only
|
|
13
|
+
class SystemInfoTool < ::RubyLLM::Tool
|
|
14
|
+
def self.name = 'system_info_tool'
|
|
17
15
|
|
|
18
|
-
description <<~
|
|
19
|
-
Retrieve system information
|
|
20
|
-
|
|
21
|
-
This tool provides cross-platform system information:
|
|
22
|
-
- macOS: Uses system_profiler, sysctl, and df commands
|
|
23
|
-
- Linux: Uses /proc filesystem and df command
|
|
24
|
-
- Windows: Uses wmic and powershell commands
|
|
16
|
+
description <<~DESC
|
|
17
|
+
Retrieve system information from the local machine.
|
|
25
18
|
|
|
26
19
|
Categories:
|
|
27
|
-
- '
|
|
28
|
-
- '
|
|
29
|
-
- '
|
|
30
|
-
- '
|
|
31
|
-
- '
|
|
32
|
-
- '
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
tool = SharedTools::Tools::SystemInfoTool.new
|
|
36
|
-
|
|
37
|
-
# Get all system info
|
|
38
|
-
result = tool.execute(category: 'all')
|
|
39
|
-
|
|
40
|
-
# Get specific category
|
|
41
|
-
result = tool.execute(category: 'memory')
|
|
42
|
-
puts result[:total] # Total RAM
|
|
43
|
-
DESCRIPTION
|
|
20
|
+
- 'os' — Operating system name, version, hostname
|
|
21
|
+
- 'cpu' — CPU model, core count, load averages
|
|
22
|
+
- 'memory' — Total and available RAM in GB
|
|
23
|
+
- 'disk' — Mounted filesystems with used/available space
|
|
24
|
+
- 'network' — Active network interfaces and their IP addresses
|
|
25
|
+
- 'ruby' — Ruby version, platform, engine, RubyGems version
|
|
26
|
+
- 'all' (default) — All of the above combined
|
|
27
|
+
DESC
|
|
44
28
|
|
|
45
29
|
params do
|
|
46
|
-
string :category, description: <<~DESC.strip
|
|
47
|
-
|
|
48
|
-
- 'all' (default): All system information
|
|
49
|
-
- 'os': Operating system details
|
|
50
|
-
- 'cpu': CPU details
|
|
51
|
-
- 'memory': Memory/RAM details
|
|
52
|
-
- 'disk': Disk space details
|
|
53
|
-
- 'network': Network interface details
|
|
30
|
+
string :category, required: false, description: <<~DESC.strip
|
|
31
|
+
Info category. Options: 'os', 'cpu', 'memory', 'disk', 'network', 'ruby', 'all' (default).
|
|
54
32
|
DESC
|
|
55
33
|
end
|
|
56
34
|
|
|
@@ -59,358 +37,167 @@ module SharedTools
|
|
|
59
37
|
@logger = logger || RubyLLM.logger
|
|
60
38
|
end
|
|
61
39
|
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
# @param category [String] category of info to retrieve
|
|
40
|
+
# @param category [String] which subsystem to query
|
|
65
41
|
# @return [Hash] system information
|
|
66
42
|
def execute(category: 'all')
|
|
67
|
-
@logger.info("SystemInfoTool#execute category=#{category
|
|
43
|
+
@logger.info("SystemInfoTool#execute category=#{category}")
|
|
68
44
|
|
|
69
45
|
case category.to_s.downcase
|
|
70
|
-
when '
|
|
71
|
-
|
|
72
|
-
when '
|
|
73
|
-
|
|
74
|
-
when '
|
|
75
|
-
|
|
76
|
-
when 'memory'
|
|
77
|
-
{ success: true, memory: get_memory_info }
|
|
78
|
-
when 'disk'
|
|
79
|
-
{ success: true, disk: get_disk_info }
|
|
80
|
-
when 'network'
|
|
81
|
-
{ success: true, network: get_network_info }
|
|
46
|
+
when 'os' then { success: true }.merge(os_info)
|
|
47
|
+
when 'cpu' then { success: true }.merge(cpu_info)
|
|
48
|
+
when 'memory' then { success: true }.merge(memory_info)
|
|
49
|
+
when 'disk' then { success: true }.merge(disk_info)
|
|
50
|
+
when 'network' then { success: true }.merge(network_info)
|
|
51
|
+
when 'ruby' then { success: true }.merge(ruby_info)
|
|
82
52
|
else
|
|
83
|
-
{
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
53
|
+
{ success: true }
|
|
54
|
+
.merge(os_info)
|
|
55
|
+
.merge(cpu_info)
|
|
56
|
+
.merge(memory_info)
|
|
57
|
+
.merge(disk_info)
|
|
58
|
+
.merge(network_info)
|
|
59
|
+
.merge(ruby_info)
|
|
87
60
|
end
|
|
88
61
|
rescue => e
|
|
89
62
|
@logger.error("SystemInfoTool error: #{e.message}")
|
|
90
|
-
{
|
|
91
|
-
success: false,
|
|
92
|
-
error: e.message
|
|
93
|
-
}
|
|
63
|
+
{ success: false, error: e.message }
|
|
94
64
|
end
|
|
95
65
|
|
|
96
66
|
private
|
|
97
67
|
|
|
98
|
-
def
|
|
68
|
+
def os_info
|
|
99
69
|
{
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
disk: get_disk_info,
|
|
105
|
-
network: get_network_info,
|
|
106
|
-
ruby: get_ruby_info
|
|
70
|
+
os_platform: RUBY_PLATFORM,
|
|
71
|
+
os_name: detect_os_name,
|
|
72
|
+
os_version: detect_os_version,
|
|
73
|
+
hostname: `hostname`.strip
|
|
107
74
|
}
|
|
108
75
|
end
|
|
109
76
|
|
|
110
|
-
def
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
build: `sw_vers -buildVersion 2>/dev/null`.strip,
|
|
117
|
-
hostname: `hostname 2>/dev/null`.strip,
|
|
118
|
-
kernel: `uname -r 2>/dev/null`.strip,
|
|
119
|
-
architecture: `uname -m 2>/dev/null`.strip,
|
|
120
|
-
uptime: parse_uptime(`uptime 2>/dev/null`.strip)
|
|
121
|
-
}
|
|
122
|
-
when :linux
|
|
123
|
-
{
|
|
124
|
-
name: get_linux_distro,
|
|
125
|
-
version: get_linux_version,
|
|
126
|
-
hostname: `hostname 2>/dev/null`.strip,
|
|
127
|
-
kernel: `uname -r 2>/dev/null`.strip,
|
|
128
|
-
architecture: `uname -m 2>/dev/null`.strip,
|
|
129
|
-
uptime: parse_uptime(`uptime 2>/dev/null`.strip)
|
|
130
|
-
}
|
|
131
|
-
when :windows
|
|
132
|
-
{
|
|
133
|
-
name: 'Windows',
|
|
134
|
-
version: `ver 2>nul`.strip,
|
|
135
|
-
hostname: `hostname 2>nul`.strip,
|
|
136
|
-
architecture: ENV['PROCESSOR_ARCHITECTURE'] || 'unknown'
|
|
137
|
-
}
|
|
77
|
+
def cpu_info
|
|
78
|
+
if RUBY_PLATFORM.include?('darwin')
|
|
79
|
+
model = `sysctl -n machdep.cpu.brand_string 2>/dev/null`.strip
|
|
80
|
+
cores = `sysctl -n hw.ncpu 2>/dev/null`.strip.to_i
|
|
81
|
+
load = `sysctl -n vm.loadavg 2>/dev/null`.strip
|
|
82
|
+
.gsub(/[{}]/, '').split.first(3).map(&:to_f)
|
|
138
83
|
else
|
|
139
|
-
|
|
84
|
+
model = File.read('/proc/cpuinfo')
|
|
85
|
+
.match(/model name\s*:\s*(.+)/)&.captures&.first&.strip rescue 'Unknown'
|
|
86
|
+
cores = `nproc 2>/dev/null`.strip.to_i
|
|
87
|
+
load = File.read('/proc/loadavg').split.first(3).map(&:to_f) rescue [0.0, 0.0, 0.0]
|
|
140
88
|
end
|
|
89
|
+
|
|
90
|
+
{
|
|
91
|
+
cpu_model: model.empty? ? 'Unknown' : model,
|
|
92
|
+
cpu_cores: cores,
|
|
93
|
+
load_avg_1m: load[0],
|
|
94
|
+
load_avg_5m: load[1],
|
|
95
|
+
load_avg_15m: load[2]
|
|
96
|
+
}
|
|
141
97
|
end
|
|
142
98
|
|
|
143
|
-
def
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
load_average: get_load_average
|
|
152
|
-
}
|
|
153
|
-
when :linux
|
|
154
|
-
cpu_info = File.read('/proc/cpuinfo') rescue ''
|
|
155
|
-
model = cpu_info[/model name\s*:\s*(.+)/, 1] || 'unknown'
|
|
156
|
-
cores = cpu_info.scan(/^processor/i).count
|
|
157
|
-
{
|
|
158
|
-
model: model,
|
|
159
|
-
cores: cores,
|
|
160
|
-
architecture: `uname -m 2>/dev/null`.strip,
|
|
161
|
-
load_average: get_load_average
|
|
162
|
-
}
|
|
163
|
-
when :windows
|
|
164
|
-
{
|
|
165
|
-
model: `wmic cpu get name 2>nul`.lines[1]&.strip || 'unknown',
|
|
166
|
-
cores: ENV['NUMBER_OF_PROCESSORS']&.to_i || 0,
|
|
167
|
-
architecture: ENV['PROCESSOR_ARCHITECTURE'] || 'unknown'
|
|
168
|
-
}
|
|
99
|
+
def memory_info
|
|
100
|
+
if RUBY_PLATFORM.include?('darwin')
|
|
101
|
+
total = `sysctl -n hw.memsize 2>/dev/null`.strip.to_i
|
|
102
|
+
vm_stat = `vm_stat 2>/dev/null`
|
|
103
|
+
pg_size = vm_stat.match(/page size of (\d+) bytes/)&.captures&.first&.to_i || 4096
|
|
104
|
+
free_pg = vm_stat.match(/Pages free:\s+(\d+)/)&.captures&.first&.to_i || 0
|
|
105
|
+
inact_pg = vm_stat.match(/Pages inactive:\s+(\d+)/)&.captures&.first&.to_i || 0
|
|
106
|
+
available = (free_pg + inact_pg) * pg_size
|
|
169
107
|
else
|
|
170
|
-
|
|
108
|
+
mem = File.read('/proc/meminfo') rescue ''
|
|
109
|
+
total = (mem.match(/MemTotal:\s+(\d+) kB/)&.captures&.first&.to_i || 0) * 1024
|
|
110
|
+
available = (mem.match(/MemAvailable:\s+(\d+) kB/)&.captures&.first&.to_i || 0) * 1024
|
|
171
111
|
end
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
def get_memory_info
|
|
175
|
-
case platform
|
|
176
|
-
when :macos
|
|
177
|
-
total_bytes = `sysctl -n hw.memsize 2>/dev/null`.strip.to_i
|
|
178
|
-
# Get page size and memory statistics
|
|
179
|
-
vm_stat = `vm_stat 2>/dev/null`
|
|
180
|
-
page_size = vm_stat[/page size of (\d+)/, 1]&.to_i || 4096
|
|
181
|
-
pages_free = vm_stat[/Pages free:\s+(\d+)/, 1]&.to_i || 0
|
|
182
|
-
pages_inactive = vm_stat[/Pages inactive:\s+(\d+)/, 1]&.to_i || 0
|
|
183
|
-
|
|
184
|
-
available_bytes = (pages_free + pages_inactive) * page_size
|
|
185
|
-
used_bytes = total_bytes - available_bytes
|
|
186
112
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
percent_used: ((used_bytes.to_f / total_bytes) * 100).round(1)
|
|
195
|
-
}
|
|
196
|
-
when :linux
|
|
197
|
-
meminfo = File.read('/proc/meminfo') rescue ''
|
|
198
|
-
total_kb = meminfo[/MemTotal:\s+(\d+)/, 1]&.to_i || 0
|
|
199
|
-
available_kb = meminfo[/MemAvailable:\s+(\d+)/, 1]&.to_i || 0
|
|
200
|
-
used_kb = total_kb - available_kb
|
|
113
|
+
gb = 1024.0**3
|
|
114
|
+
{
|
|
115
|
+
memory_total_gb: (total / gb).round(2),
|
|
116
|
+
memory_available_gb: (available / gb).round(2),
|
|
117
|
+
memory_used_gb: ((total - available) / gb).round(2)
|
|
118
|
+
}
|
|
119
|
+
end
|
|
201
120
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
used: format_bytes(used_kb * 1024),
|
|
208
|
-
used_bytes: used_kb * 1024,
|
|
209
|
-
percent_used: total_kb > 0 ? ((used_kb.to_f / total_kb) * 100).round(1) : 0
|
|
210
|
-
}
|
|
211
|
-
when :windows
|
|
212
|
-
# Using powershell for more reliable output
|
|
213
|
-
output = `powershell -command "Get-CimInstance Win32_OperatingSystem | Select-Object TotalVisibleMemorySize,FreePhysicalMemory" 2>nul`
|
|
214
|
-
total_kb = output[/TotalVisibleMemorySize\s*:\s*(\d+)/, 1]&.to_i || 0
|
|
215
|
-
free_kb = output[/FreePhysicalMemory\s*:\s*(\d+)/, 1]&.to_i || 0
|
|
216
|
-
used_kb = total_kb - free_kb
|
|
121
|
+
def disk_info
|
|
122
|
+
lines = `df -k 2>/dev/null`.lines.drop(1)
|
|
123
|
+
mounts = lines.filter_map do |line|
|
|
124
|
+
parts = line.split
|
|
125
|
+
next unless parts.size >= 6
|
|
217
126
|
|
|
127
|
+
kb = 1024.0**2
|
|
218
128
|
{
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
percent_used: total_kb > 0 ? ((used_kb.to_f / total_kb) * 100).round(1) : 0
|
|
129
|
+
filesystem: parts[0],
|
|
130
|
+
mount_point: parts[5],
|
|
131
|
+
size_gb: (parts[1].to_i / kb).round(2),
|
|
132
|
+
used_gb: (parts[2].to_i / kb).round(2),
|
|
133
|
+
available_gb: (parts[3].to_i / kb).round(2),
|
|
134
|
+
use_percent: parts[4]
|
|
226
135
|
}
|
|
227
|
-
else
|
|
228
|
-
{ total: 'unknown', available: 'unknown' }
|
|
229
136
|
end
|
|
137
|
+
{ disks: mounts }
|
|
230
138
|
end
|
|
231
139
|
|
|
232
|
-
def
|
|
233
|
-
|
|
140
|
+
def network_info
|
|
141
|
+
interfaces = {}
|
|
234
142
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
disks << {
|
|
245
|
-
filesystem: parts[0],
|
|
246
|
-
size: parts[1],
|
|
247
|
-
used: parts[2],
|
|
248
|
-
available: parts[3],
|
|
249
|
-
percent_used: parts[4],
|
|
250
|
-
mount_point: mount_point
|
|
251
|
-
}
|
|
252
|
-
end
|
|
253
|
-
when :windows
|
|
254
|
-
output = `wmic logicaldisk get size,freespace,caption 2>nul`
|
|
255
|
-
output.lines[1..].each do |line|
|
|
256
|
-
parts = line.split
|
|
257
|
-
next if parts.length < 3
|
|
258
|
-
|
|
259
|
-
caption, free_space, size = parts
|
|
260
|
-
next unless size.to_i > 0
|
|
261
|
-
|
|
262
|
-
disks << {
|
|
263
|
-
filesystem: caption,
|
|
264
|
-
size: format_bytes(size.to_i),
|
|
265
|
-
available: format_bytes(free_space.to_i),
|
|
266
|
-
used: format_bytes(size.to_i - free_space.to_i),
|
|
267
|
-
percent_used: "#{((1 - free_space.to_f / size.to_i) * 100).round}%"
|
|
268
|
-
}
|
|
269
|
-
end
|
|
270
|
-
end
|
|
271
|
-
|
|
272
|
-
disks
|
|
273
|
-
end
|
|
274
|
-
|
|
275
|
-
def get_network_info
|
|
276
|
-
interfaces = []
|
|
277
|
-
|
|
278
|
-
case platform
|
|
279
|
-
when :macos
|
|
280
|
-
ifconfig = `ifconfig 2>/dev/null`
|
|
281
|
-
current_interface = nil
|
|
282
|
-
|
|
283
|
-
ifconfig.each_line do |line|
|
|
284
|
-
if line =~ /^(\w+):/
|
|
285
|
-
current_interface = { name: $1, addresses: [] }
|
|
286
|
-
interfaces << current_interface
|
|
287
|
-
elsif current_interface && line =~ /inet (\d+\.\d+\.\d+\.\d+)/
|
|
288
|
-
current_interface[:addresses] << { type: 'IPv4', address: $1 }
|
|
289
|
-
elsif current_interface && line =~ /inet6 ([a-f0-9:]+)/
|
|
290
|
-
current_interface[:addresses] << { type: 'IPv6', address: $1 }
|
|
291
|
-
end
|
|
292
|
-
end
|
|
293
|
-
when :linux
|
|
294
|
-
# Try ip command first, fall back to ifconfig
|
|
295
|
-
output = `ip addr 2>/dev/null`
|
|
296
|
-
if output.empty?
|
|
297
|
-
output = `ifconfig 2>/dev/null`
|
|
298
|
-
end
|
|
299
|
-
|
|
300
|
-
current_interface = nil
|
|
301
|
-
output.each_line do |line|
|
|
302
|
-
if line =~ /^\d+:\s+(\w+):/
|
|
303
|
-
current_interface = { name: $1, addresses: [] }
|
|
304
|
-
interfaces << current_interface
|
|
305
|
-
elsif line =~ /^(\w+):/
|
|
306
|
-
current_interface = { name: $1, addresses: [] }
|
|
307
|
-
interfaces << current_interface
|
|
308
|
-
elsif current_interface && line =~ /inet (\d+\.\d+\.\d+\.\d+)/
|
|
309
|
-
current_interface[:addresses] << { type: 'IPv4', address: $1 }
|
|
310
|
-
elsif current_interface && line =~ /inet6 ([a-f0-9:]+)/
|
|
311
|
-
current_interface[:addresses] << { type: 'IPv6', address: $1 }
|
|
143
|
+
if RUBY_PLATFORM.include?('darwin')
|
|
144
|
+
current = nil
|
|
145
|
+
`ifconfig 2>/dev/null`.lines.each do |line|
|
|
146
|
+
if (m = line.match(/^(\w[\w:]+\d+):/))
|
|
147
|
+
current = m.captures.first
|
|
148
|
+
interfaces[current] = []
|
|
149
|
+
elsif current && (m = line.match(/\s+inet6?\s+(\S+)/))
|
|
150
|
+
addr = m.captures.first.split('%').first
|
|
151
|
+
interfaces[current] << addr
|
|
312
152
|
end
|
|
313
153
|
end
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
interfaces <<
|
|
322
|
-
elsif current_interface && line =~ /IPv4.*:\s*(\d+\.\d+\.\d+\.\d+)/
|
|
323
|
-
current_interface[:addresses] << { type: 'IPv4', address: $1 }
|
|
324
|
-
elsif current_interface && line =~ /IPv6.*:\s*([a-f0-9:]+)/i
|
|
325
|
-
current_interface[:addresses] << { type: 'IPv6', address: $1 }
|
|
154
|
+
else
|
|
155
|
+
current = nil
|
|
156
|
+
`ip addr 2>/dev/null`.lines.each do |line|
|
|
157
|
+
if (m = line.match(/^\d+: (\w+):/))
|
|
158
|
+
current = m.captures.first
|
|
159
|
+
interfaces[current] = []
|
|
160
|
+
elsif current && (m = line.match(/\s+inet6?\s+(\S+)/))
|
|
161
|
+
interfaces[current] << m.captures.first.split('/').first
|
|
326
162
|
end
|
|
327
163
|
end
|
|
328
164
|
end
|
|
329
165
|
|
|
330
|
-
|
|
331
|
-
interfaces.select { |i| !i[:addresses].empty? }
|
|
166
|
+
{ network_interfaces: interfaces.reject { |_, ips| ips.empty? } }
|
|
332
167
|
end
|
|
333
168
|
|
|
334
|
-
def
|
|
169
|
+
def ruby_info
|
|
335
170
|
{
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
171
|
+
ruby_version: RUBY_VERSION,
|
|
172
|
+
ruby_platform: RUBY_PLATFORM,
|
|
173
|
+
ruby_engine: RUBY_ENGINE,
|
|
174
|
+
ruby_description: RUBY_DESCRIPTION,
|
|
175
|
+
rubygems_version: Gem::VERSION
|
|
341
176
|
}
|
|
342
177
|
end
|
|
343
178
|
|
|
344
|
-
def
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
:linux
|
|
350
|
-
when /mswin|mingw|cygwin/
|
|
351
|
-
:windows
|
|
352
|
-
else
|
|
353
|
-
:unknown
|
|
354
|
-
end
|
|
355
|
-
end
|
|
356
|
-
|
|
357
|
-
def format_bytes(bytes)
|
|
358
|
-
return '0 B' if bytes.nil? || bytes == 0
|
|
359
|
-
|
|
360
|
-
units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
|
|
361
|
-
exp = (Math.log(bytes) / Math.log(1024)).to_i
|
|
362
|
-
exp = units.length - 1 if exp >= units.length
|
|
363
|
-
|
|
364
|
-
"#{(bytes.to_f / (1024**exp)).round(2)} #{units[exp]}"
|
|
365
|
-
end
|
|
366
|
-
|
|
367
|
-
def get_load_average
|
|
368
|
-
case platform
|
|
369
|
-
when :macos, :linux
|
|
370
|
-
uptime_output = `uptime 2>/dev/null`
|
|
371
|
-
if uptime_output =~ /load average[s]?:\s*([\d.]+),?\s*([\d.]+),?\s*([\d.]+)/
|
|
372
|
-
{ '1min' => $1.to_f, '5min' => $2.to_f, '15min' => $3.to_f }
|
|
373
|
-
else
|
|
374
|
-
{}
|
|
375
|
-
end
|
|
376
|
-
else
|
|
377
|
-
{}
|
|
378
|
-
end
|
|
379
|
-
end
|
|
380
|
-
|
|
381
|
-
def parse_uptime(uptime_str)
|
|
382
|
-
# Extract uptime portion from the uptime command output
|
|
383
|
-
if uptime_str =~ /up\s+(.+?),\s+\d+\s+user/
|
|
384
|
-
$1.strip
|
|
385
|
-
elsif uptime_str =~ /up\s+(.+)/
|
|
386
|
-
$1.split(',').first.strip
|
|
387
|
-
else
|
|
388
|
-
uptime_str
|
|
389
|
-
end
|
|
390
|
-
end
|
|
391
|
-
|
|
392
|
-
def get_linux_distro
|
|
393
|
-
if File.exist?('/etc/os-release')
|
|
394
|
-
content = File.read('/etc/os-release')
|
|
395
|
-
content[/^NAME="?([^"\n]+)"?/, 1] || 'Linux'
|
|
396
|
-
elsif File.exist?('/etc/lsb-release')
|
|
397
|
-
content = File.read('/etc/lsb-release')
|
|
398
|
-
content[/DISTRIB_ID=(.+)/, 1] || 'Linux'
|
|
179
|
+
def detect_os_name
|
|
180
|
+
if RUBY_PLATFORM.include?('darwin')
|
|
181
|
+
`sw_vers -productName 2>/dev/null`.strip
|
|
182
|
+
elsif File.exist?('/etc/os-release')
|
|
183
|
+
File.read('/etc/os-release').match(/^NAME="?([^"\n]+)"?/)&.captures&.first || 'Linux'
|
|
399
184
|
else
|
|
400
|
-
'
|
|
185
|
+
'Unknown'
|
|
401
186
|
end
|
|
187
|
+
rescue
|
|
188
|
+
'Unknown'
|
|
402
189
|
end
|
|
403
190
|
|
|
404
|
-
def
|
|
405
|
-
if
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
content = File.read('/etc/lsb-release')
|
|
410
|
-
content[/DISTRIB_RELEASE=(.+)/, 1] || ''
|
|
191
|
+
def detect_os_version
|
|
192
|
+
if RUBY_PLATFORM.include?('darwin')
|
|
193
|
+
`sw_vers -productVersion 2>/dev/null`.strip
|
|
194
|
+
elsif File.exist?('/etc/os-release')
|
|
195
|
+
File.read('/etc/os-release').match(/^VERSION="?([^"\n]+)"?/)&.captures&.first || 'Unknown'
|
|
411
196
|
else
|
|
412
|
-
''
|
|
197
|
+
'Unknown'
|
|
413
198
|
end
|
|
199
|
+
rescue
|
|
200
|
+
'Unknown'
|
|
414
201
|
end
|
|
415
202
|
end
|
|
416
203
|
end
|
|
@@ -34,6 +34,7 @@ module SharedTools
|
|
|
34
34
|
- 'step': Execute the next step in an existing workflow using provided step data
|
|
35
35
|
- 'status': Check the current status and progress of an existing workflow
|
|
36
36
|
- 'complete': Mark a workflow as finished and clean up associated resources
|
|
37
|
+
- 'list': List all existing workflows with their current status and summary information
|
|
37
38
|
Each action requires different combinations of the other parameters.
|
|
38
39
|
DESC
|
|
39
40
|
|
|
@@ -84,6 +85,8 @@ module SharedTools
|
|
|
84
85
|
when "complete"
|
|
85
86
|
return {success: false, error: "workflow_id required for 'complete' action"} unless workflow_id
|
|
86
87
|
complete_workflow(workflow_id)
|
|
88
|
+
when "list"
|
|
89
|
+
list_workflows
|
|
87
90
|
else
|
|
88
91
|
{success: false, error: "Unknown action: #{action}"}
|
|
89
92
|
end
|
|
@@ -279,6 +282,35 @@ module SharedTools
|
|
|
279
282
|
}
|
|
280
283
|
end
|
|
281
284
|
|
|
285
|
+
# List all workflows in the storage directory
|
|
286
|
+
def list_workflows
|
|
287
|
+
pattern = File.join(@storage_dir, "workflow_*.json")
|
|
288
|
+
files = Dir.glob(pattern)
|
|
289
|
+
|
|
290
|
+
workflows = files.filter_map do |file|
|
|
291
|
+
state = JSON.parse(File.read(file), symbolize_names: true)
|
|
292
|
+
{
|
|
293
|
+
workflow_id: state[:id],
|
|
294
|
+
status: state[:status],
|
|
295
|
+
step_count: state[:steps]&.length || 0,
|
|
296
|
+
created_at: state[:created_at],
|
|
297
|
+
updated_at: state[:updated_at],
|
|
298
|
+
completed_at: state[:completed_at]
|
|
299
|
+
}
|
|
300
|
+
rescue => e
|
|
301
|
+
@logger.warn("Skipping unreadable workflow file #{file}: #{e.message}")
|
|
302
|
+
nil
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
workflows.sort_by! { |w| w[:created_at] || "" }
|
|
306
|
+
|
|
307
|
+
{
|
|
308
|
+
success: true,
|
|
309
|
+
total: workflows.size,
|
|
310
|
+
workflows: workflows
|
|
311
|
+
}
|
|
312
|
+
end
|
|
313
|
+
|
|
282
314
|
# Save workflow state to disk
|
|
283
315
|
def save_workflow_state(workflow_id, state)
|
|
284
316
|
file_path = workflow_file_path(workflow_id)
|