language-operator 0.0.1 → 0.1.31
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/.rubocop.yml +125 -0
- data/CHANGELOG.md +88 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +284 -0
- data/LICENSE +229 -21
- data/Makefile +82 -0
- data/README.md +3 -11
- data/Rakefile +63 -0
- data/bin/aictl +7 -0
- data/completions/_aictl +232 -0
- data/completions/aictl.bash +121 -0
- data/completions/aictl.fish +114 -0
- data/docs/architecture/agent-runtime.md +585 -0
- data/docs/dsl/SCHEMA_VERSION.md +250 -0
- data/docs/dsl/agent-reference.md +604 -0
- data/docs/dsl/best-practices.md +1078 -0
- data/docs/dsl/chat-endpoints.md +895 -0
- data/docs/dsl/constraints.md +671 -0
- data/docs/dsl/mcp-integration.md +1177 -0
- data/docs/dsl/webhooks.md +932 -0
- data/docs/dsl/workflows.md +744 -0
- data/lib/language_operator/agent/base.rb +110 -0
- data/lib/language_operator/agent/executor.rb +440 -0
- data/lib/language_operator/agent/instrumentation.rb +54 -0
- data/lib/language_operator/agent/metrics_tracker.rb +183 -0
- data/lib/language_operator/agent/safety/ast_validator.rb +272 -0
- data/lib/language_operator/agent/safety/audit_logger.rb +104 -0
- data/lib/language_operator/agent/safety/budget_tracker.rb +175 -0
- data/lib/language_operator/agent/safety/content_filter.rb +93 -0
- data/lib/language_operator/agent/safety/manager.rb +207 -0
- data/lib/language_operator/agent/safety/rate_limiter.rb +150 -0
- data/lib/language_operator/agent/safety/safe_executor.rb +127 -0
- data/lib/language_operator/agent/scheduler.rb +183 -0
- data/lib/language_operator/agent/telemetry.rb +116 -0
- data/lib/language_operator/agent/web_server.rb +610 -0
- data/lib/language_operator/agent/webhook_authenticator.rb +226 -0
- data/lib/language_operator/agent.rb +149 -0
- data/lib/language_operator/cli/commands/agent.rb +1205 -0
- data/lib/language_operator/cli/commands/cluster.rb +371 -0
- data/lib/language_operator/cli/commands/install.rb +404 -0
- data/lib/language_operator/cli/commands/model.rb +266 -0
- data/lib/language_operator/cli/commands/persona.rb +393 -0
- data/lib/language_operator/cli/commands/quickstart.rb +22 -0
- data/lib/language_operator/cli/commands/status.rb +143 -0
- data/lib/language_operator/cli/commands/system.rb +772 -0
- data/lib/language_operator/cli/commands/tool.rb +537 -0
- data/lib/language_operator/cli/commands/use.rb +47 -0
- data/lib/language_operator/cli/errors/handler.rb +180 -0
- data/lib/language_operator/cli/errors/suggestions.rb +176 -0
- data/lib/language_operator/cli/formatters/code_formatter.rb +77 -0
- data/lib/language_operator/cli/formatters/log_formatter.rb +288 -0
- data/lib/language_operator/cli/formatters/progress_formatter.rb +49 -0
- data/lib/language_operator/cli/formatters/status_formatter.rb +37 -0
- data/lib/language_operator/cli/formatters/table_formatter.rb +163 -0
- data/lib/language_operator/cli/formatters/value_formatter.rb +113 -0
- data/lib/language_operator/cli/helpers/cluster_context.rb +62 -0
- data/lib/language_operator/cli/helpers/cluster_validator.rb +101 -0
- data/lib/language_operator/cli/helpers/editor_helper.rb +58 -0
- data/lib/language_operator/cli/helpers/kubeconfig_validator.rb +167 -0
- data/lib/language_operator/cli/helpers/pastel_helper.rb +24 -0
- data/lib/language_operator/cli/helpers/resource_dependency_checker.rb +74 -0
- data/lib/language_operator/cli/helpers/schedule_builder.rb +108 -0
- data/lib/language_operator/cli/helpers/user_prompts.rb +69 -0
- data/lib/language_operator/cli/main.rb +236 -0
- data/lib/language_operator/cli/templates/tools/generic.yaml +66 -0
- data/lib/language_operator/cli/wizards/agent_wizard.rb +246 -0
- data/lib/language_operator/cli/wizards/quickstart_wizard.rb +588 -0
- data/lib/language_operator/client/base.rb +214 -0
- data/lib/language_operator/client/config.rb +136 -0
- data/lib/language_operator/client/cost_calculator.rb +37 -0
- data/lib/language_operator/client/mcp_connector.rb +123 -0
- data/lib/language_operator/client.rb +19 -0
- data/lib/language_operator/config/cluster_config.rb +101 -0
- data/lib/language_operator/config/tool_patterns.yaml +57 -0
- data/lib/language_operator/config/tool_registry.rb +96 -0
- data/lib/language_operator/config.rb +138 -0
- data/lib/language_operator/dsl/adapter.rb +124 -0
- data/lib/language_operator/dsl/agent_context.rb +90 -0
- data/lib/language_operator/dsl/agent_definition.rb +427 -0
- data/lib/language_operator/dsl/chat_endpoint_definition.rb +115 -0
- data/lib/language_operator/dsl/config.rb +119 -0
- data/lib/language_operator/dsl/context.rb +50 -0
- data/lib/language_operator/dsl/execution_context.rb +47 -0
- data/lib/language_operator/dsl/helpers.rb +109 -0
- data/lib/language_operator/dsl/http.rb +184 -0
- data/lib/language_operator/dsl/mcp_server_definition.rb +73 -0
- data/lib/language_operator/dsl/parameter_definition.rb +124 -0
- data/lib/language_operator/dsl/registry.rb +36 -0
- data/lib/language_operator/dsl/schema.rb +1102 -0
- data/lib/language_operator/dsl/shell.rb +125 -0
- data/lib/language_operator/dsl/tool_definition.rb +112 -0
- data/lib/language_operator/dsl/webhook_authentication.rb +114 -0
- data/lib/language_operator/dsl/webhook_definition.rb +106 -0
- data/lib/language_operator/dsl/workflow_definition.rb +259 -0
- data/lib/language_operator/dsl.rb +161 -0
- data/lib/language_operator/errors.rb +60 -0
- data/lib/language_operator/kubernetes/client.rb +279 -0
- data/lib/language_operator/kubernetes/resource_builder.rb +194 -0
- data/lib/language_operator/loggable.rb +47 -0
- data/lib/language_operator/logger.rb +141 -0
- data/lib/language_operator/retry.rb +123 -0
- data/lib/language_operator/retryable.rb +132 -0
- data/lib/language_operator/templates/README.md +23 -0
- data/lib/language_operator/templates/examples/agent_synthesis.tmpl +115 -0
- data/lib/language_operator/templates/examples/persona_distillation.tmpl +19 -0
- data/lib/language_operator/templates/schema/.gitkeep +0 -0
- data/lib/language_operator/templates/schema/CHANGELOG.md +93 -0
- data/lib/language_operator/templates/schema/agent_dsl_openapi.yaml +306 -0
- data/lib/language_operator/templates/schema/agent_dsl_schema.json +452 -0
- data/lib/language_operator/tool_loader.rb +242 -0
- data/lib/language_operator/validators.rb +170 -0
- data/lib/language_operator/version.rb +1 -1
- data/lib/language_operator.rb +65 -3
- data/requirements/tasks/challenge.md +9 -0
- data/requirements/tasks/iterate.md +36 -0
- data/requirements/tasks/optimize.md +21 -0
- data/requirements/tasks/tag.md +5 -0
- data/test_agent_dsl.rb +108 -0
- metadata +507 -20
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'thor'
|
|
4
|
+
require 'yaml'
|
|
5
|
+
require_relative '../formatters/progress_formatter'
|
|
6
|
+
require_relative '../formatters/table_formatter'
|
|
7
|
+
require_relative '../helpers/cluster_validator'
|
|
8
|
+
require_relative '../helpers/user_prompts'
|
|
9
|
+
require_relative '../../config/cluster_config'
|
|
10
|
+
require_relative '../../kubernetes/client'
|
|
11
|
+
require_relative '../../kubernetes/resource_builder'
|
|
12
|
+
|
|
13
|
+
module LanguageOperator
|
|
14
|
+
module CLI
|
|
15
|
+
module Commands
|
|
16
|
+
# Cluster management commands
|
|
17
|
+
class Cluster < Thor
|
|
18
|
+
desc 'create NAME', 'Create a new language cluster'
|
|
19
|
+
option :namespace, type: :string, desc: 'Kubernetes namespace (defaults to current context namespace)'
|
|
20
|
+
option :kubeconfig, type: :string, desc: 'Path to kubeconfig file'
|
|
21
|
+
option :context, type: :string, desc: 'Kubernetes context to use'
|
|
22
|
+
option :switch, type: :boolean, default: true, desc: 'Switch to new cluster context'
|
|
23
|
+
option :dry_run, type: :boolean, default: false, desc: 'Output the manifest without creating'
|
|
24
|
+
def create(name)
|
|
25
|
+
kubeconfig = options[:kubeconfig]
|
|
26
|
+
context = options[:context]
|
|
27
|
+
|
|
28
|
+
# Handle dry-run: output manifest and exit early
|
|
29
|
+
if options[:dry_run]
|
|
30
|
+
namespace = options[:namespace] || 'default'
|
|
31
|
+
resource = Kubernetes::ResourceBuilder.language_cluster(name, namespace: namespace)
|
|
32
|
+
puts resource.to_yaml
|
|
33
|
+
return
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Check if cluster already exists
|
|
37
|
+
if Config::ClusterConfig.cluster_exists?(name)
|
|
38
|
+
Formatters::ProgressFormatter.error("Cluster '#{name}' already exists")
|
|
39
|
+
exit 1
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Create Kubernetes client
|
|
43
|
+
k8s = Formatters::ProgressFormatter.with_spinner('Connecting to Kubernetes cluster') do
|
|
44
|
+
Kubernetes::Client.new(kubeconfig: kubeconfig, context: context)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Determine namespace: use --namespace flag, or current context namespace, or 'default'
|
|
48
|
+
namespace = options[:namespace] || k8s.current_namespace || 'default'
|
|
49
|
+
|
|
50
|
+
# Check if operator is installed
|
|
51
|
+
unless k8s.operator_installed?
|
|
52
|
+
Formatters::ProgressFormatter.error('Language Operator not found in cluster')
|
|
53
|
+
puts "\nInstall the operator first:"
|
|
54
|
+
puts ' aictl install'
|
|
55
|
+
exit 1
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Create namespace if it doesn't exist
|
|
59
|
+
unless k8s.namespace_exists?(namespace)
|
|
60
|
+
Formatters::ProgressFormatter.with_spinner("Creating namespace '#{namespace}'") do
|
|
61
|
+
k8s.create_namespace(namespace, labels: {
|
|
62
|
+
'app.kubernetes.io/managed-by' => 'aictl',
|
|
63
|
+
'langop.io/cluster' => name
|
|
64
|
+
})
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Create LanguageCluster resource
|
|
69
|
+
Formatters::ProgressFormatter.with_spinner('Creating LanguageCluster resource') do
|
|
70
|
+
resource = Kubernetes::ResourceBuilder.language_cluster(name, namespace: namespace)
|
|
71
|
+
k8s.apply_resource(resource)
|
|
72
|
+
resource
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Get the actual Kubernetes context being used
|
|
76
|
+
actual_context = k8s.current_context
|
|
77
|
+
|
|
78
|
+
# Save cluster to config
|
|
79
|
+
Formatters::ProgressFormatter.with_spinner('Saving cluster configuration') do
|
|
80
|
+
Config::ClusterConfig.add_cluster(
|
|
81
|
+
name,
|
|
82
|
+
namespace,
|
|
83
|
+
kubeconfig || ENV.fetch('KUBECONFIG', File.expand_path('~/.kube/config')),
|
|
84
|
+
actual_context
|
|
85
|
+
)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Switch to new cluster if requested
|
|
89
|
+
if options[:switch]
|
|
90
|
+
Config::ClusterConfig.set_current_cluster(name)
|
|
91
|
+
Formatters::ProgressFormatter.success("Created and switched to cluster '#{name}'")
|
|
92
|
+
else
|
|
93
|
+
Formatters::ProgressFormatter.success("Created cluster '#{name}'")
|
|
94
|
+
puts "\nSwitch to this cluster with:"
|
|
95
|
+
puts " aictl use #{name}"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
pastel = Pastel.new
|
|
99
|
+
puts "\nCluster Details"
|
|
100
|
+
puts '----------------'
|
|
101
|
+
puts "Name: #{pastel.bold.white(name)}"
|
|
102
|
+
puts "Namespace: #{pastel.bold.white(namespace)}"
|
|
103
|
+
rescue StandardError => e
|
|
104
|
+
Formatters::ProgressFormatter.error("Failed to create cluster: #{e.message}")
|
|
105
|
+
raise if ENV['DEBUG']
|
|
106
|
+
|
|
107
|
+
exit 1
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
desc 'list', 'List all clusters'
|
|
111
|
+
option :all, type: :boolean, default: false, desc: 'Show all clusters including inactive'
|
|
112
|
+
def list
|
|
113
|
+
clusters = Config::ClusterConfig.list_clusters
|
|
114
|
+
current = Config::ClusterConfig.current_cluster
|
|
115
|
+
|
|
116
|
+
if clusters.empty?
|
|
117
|
+
Formatters::ProgressFormatter.info('No clusters found')
|
|
118
|
+
puts "\nCreate a cluster with:"
|
|
119
|
+
puts ' aictl cluster create <name>'
|
|
120
|
+
return
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Build table data
|
|
124
|
+
# rubocop:disable Metrics/BlockLength
|
|
125
|
+
table_data = clusters.map do |cluster|
|
|
126
|
+
k8s = Helpers::ClusterValidator.kubernetes_client(cluster[:name])
|
|
127
|
+
|
|
128
|
+
# Get cluster stats
|
|
129
|
+
agents = k8s.list_resources('LanguageAgent', namespace: cluster[:namespace])
|
|
130
|
+
tools = k8s.list_resources('LanguageTool', namespace: cluster[:namespace])
|
|
131
|
+
models = k8s.list_resources('LanguageModel', namespace: cluster[:namespace])
|
|
132
|
+
|
|
133
|
+
# Get cluster status
|
|
134
|
+
cluster_resource = k8s.get_resource('LanguageCluster', cluster[:name], cluster[:namespace])
|
|
135
|
+
status = cluster_resource.dig('status', 'phase') || 'Unknown'
|
|
136
|
+
|
|
137
|
+
name_display = cluster[:name]
|
|
138
|
+
name_display += ' *' if cluster[:name] == current
|
|
139
|
+
|
|
140
|
+
{
|
|
141
|
+
name: name_display,
|
|
142
|
+
namespace: cluster[:namespace],
|
|
143
|
+
agents: agents.count,
|
|
144
|
+
tools: tools.count,
|
|
145
|
+
models: models.count,
|
|
146
|
+
status: status
|
|
147
|
+
}
|
|
148
|
+
rescue K8s::Error::NotFound
|
|
149
|
+
# Cluster exists in local config but not in Kubernetes
|
|
150
|
+
name_display = cluster[:name]
|
|
151
|
+
name_display += ' *' if cluster[:name] == current
|
|
152
|
+
|
|
153
|
+
{
|
|
154
|
+
name: name_display,
|
|
155
|
+
namespace: cluster[:namespace],
|
|
156
|
+
agents: '-',
|
|
157
|
+
tools: '-',
|
|
158
|
+
models: '-',
|
|
159
|
+
status: 'Not Found'
|
|
160
|
+
}
|
|
161
|
+
rescue StandardError
|
|
162
|
+
# Other errors (connection issues, auth problems, etc.)
|
|
163
|
+
name_display = cluster[:name]
|
|
164
|
+
name_display += ' *' if cluster[:name] == current
|
|
165
|
+
|
|
166
|
+
{
|
|
167
|
+
name: name_display,
|
|
168
|
+
namespace: cluster[:namespace],
|
|
169
|
+
agents: '?',
|
|
170
|
+
tools: '?',
|
|
171
|
+
models: '?',
|
|
172
|
+
status: 'Error'
|
|
173
|
+
}
|
|
174
|
+
end
|
|
175
|
+
# rubocop:enable Metrics/BlockLength
|
|
176
|
+
|
|
177
|
+
Formatters::TableFormatter.clusters(table_data)
|
|
178
|
+
|
|
179
|
+
if current
|
|
180
|
+
puts "\nCurrent cluster: #{current} (*)"
|
|
181
|
+
else
|
|
182
|
+
puts "\nNo cluster selected. Use 'aictl use <cluster>' to select one."
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Show helpful message if any clusters are not found
|
|
186
|
+
not_found_clusters = table_data.select { |c| c[:status] == 'Not Found' }
|
|
187
|
+
if not_found_clusters.any?
|
|
188
|
+
puts
|
|
189
|
+
Formatters::ProgressFormatter.warn('Some clusters exist in local config but not in Kubernetes')
|
|
190
|
+
puts
|
|
191
|
+
puts 'Clusters with "Not Found" status are defined in ~/.aictl/config.yaml'
|
|
192
|
+
puts 'but the corresponding LanguageCluster resource does not exist in Kubernetes.'
|
|
193
|
+
puts
|
|
194
|
+
puts 'To fix this:'
|
|
195
|
+
not_found_clusters.each do |cluster|
|
|
196
|
+
cluster_name = cluster[:name].gsub(' *', '')
|
|
197
|
+
puts " • Remove from config: aictl cluster delete #{cluster_name}"
|
|
198
|
+
puts " • Or recreate: aictl cluster create #{cluster_name}"
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
rescue StandardError => e
|
|
202
|
+
Formatters::ProgressFormatter.error("Failed to list clusters: #{e.message}")
|
|
203
|
+
raise if ENV['DEBUG']
|
|
204
|
+
|
|
205
|
+
exit 1
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
desc 'current', 'Show current cluster context'
|
|
209
|
+
def current
|
|
210
|
+
cluster_name = Config::ClusterConfig.current_cluster
|
|
211
|
+
|
|
212
|
+
unless cluster_name
|
|
213
|
+
Formatters::ProgressFormatter.info('No cluster selected')
|
|
214
|
+
puts "\nSelect a cluster with:"
|
|
215
|
+
puts ' aictl use <cluster>'
|
|
216
|
+
return
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
cluster = Config::ClusterConfig.get_cluster(cluster_name)
|
|
220
|
+
|
|
221
|
+
unless cluster
|
|
222
|
+
Formatters::ProgressFormatter.error("Cluster '#{cluster_name}' not found in config")
|
|
223
|
+
exit 1
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
puts 'Current Cluster:'
|
|
227
|
+
puts " Name: #{cluster[:name]}"
|
|
228
|
+
puts " Namespace: #{cluster[:namespace]}"
|
|
229
|
+
puts " Context: #{cluster[:context] || 'default'}"
|
|
230
|
+
puts " Created: #{cluster[:created]}"
|
|
231
|
+
|
|
232
|
+
# Check cluster health
|
|
233
|
+
begin
|
|
234
|
+
k8s = Helpers::ClusterValidator.kubernetes_client(cluster_name)
|
|
235
|
+
|
|
236
|
+
if k8s.operator_installed?
|
|
237
|
+
version = k8s.operator_version
|
|
238
|
+
Formatters::ProgressFormatter.success("Operator: #{version || 'installed'}")
|
|
239
|
+
else
|
|
240
|
+
Formatters::ProgressFormatter.warn('Operator: not found')
|
|
241
|
+
end
|
|
242
|
+
rescue StandardError => e
|
|
243
|
+
Formatters::ProgressFormatter.error("Connection: #{e.message}")
|
|
244
|
+
end
|
|
245
|
+
rescue StandardError => e
|
|
246
|
+
Formatters::ProgressFormatter.error("Failed to show current cluster: #{e.message}")
|
|
247
|
+
raise if ENV['DEBUG']
|
|
248
|
+
|
|
249
|
+
exit 1
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
desc 'delete NAME', 'Delete a cluster'
|
|
253
|
+
option :force, type: :boolean, default: false, desc: 'Skip confirmation'
|
|
254
|
+
def delete(name)
|
|
255
|
+
unless Config::ClusterConfig.cluster_exists?(name)
|
|
256
|
+
Formatters::ProgressFormatter.error("Cluster '#{name}' not found")
|
|
257
|
+
exit 1
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
cluster = Config::ClusterConfig.get_cluster(name)
|
|
261
|
+
|
|
262
|
+
# Confirm deletion
|
|
263
|
+
unless options[:force]
|
|
264
|
+
pastel = Pastel.new
|
|
265
|
+
puts "This will delete cluster #{pastel.bold.red(name)} and all its resources (agents, models, tools, personas)."
|
|
266
|
+
puts
|
|
267
|
+
return unless Helpers::UserPrompts.confirm('Are you sure?')
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# Delete LanguageCluster resource
|
|
271
|
+
begin
|
|
272
|
+
k8s = Helpers::ClusterValidator.kubernetes_client(name)
|
|
273
|
+
|
|
274
|
+
Formatters::ProgressFormatter.with_spinner('Deleting LanguageCluster resource') do
|
|
275
|
+
k8s.delete_resource('LanguageCluster', name, cluster[:namespace])
|
|
276
|
+
end
|
|
277
|
+
rescue StandardError => e
|
|
278
|
+
Formatters::ProgressFormatter.warn("Failed to delete cluster resource: #{e.message}")
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Remove from config
|
|
282
|
+
Formatters::ProgressFormatter.with_spinner('Removing cluster from configuration') do
|
|
283
|
+
Config::ClusterConfig.remove_cluster(name)
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# Clear current cluster if this was it
|
|
287
|
+
Config::ClusterConfig.set_current_cluster(nil) if Config::ClusterConfig.current_cluster == name
|
|
288
|
+
|
|
289
|
+
Formatters::ProgressFormatter.success("Deleted cluster '#{name}'")
|
|
290
|
+
rescue StandardError => e
|
|
291
|
+
Formatters::ProgressFormatter.error("Failed to delete cluster: #{e.message}")
|
|
292
|
+
raise if ENV['DEBUG']
|
|
293
|
+
|
|
294
|
+
exit 1
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
desc 'inspect NAME', 'Show detailed cluster information'
|
|
298
|
+
def inspect(name)
|
|
299
|
+
unless Config::ClusterConfig.cluster_exists?(name)
|
|
300
|
+
Formatters::ProgressFormatter.error("Cluster '#{name}' not found")
|
|
301
|
+
exit 1
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
cluster = Config::ClusterConfig.get_cluster(name)
|
|
305
|
+
|
|
306
|
+
puts "Cluster: #{name}"
|
|
307
|
+
puts " Namespace: #{cluster[:namespace]}"
|
|
308
|
+
puts " Context: #{cluster[:context] || 'default'}"
|
|
309
|
+
puts " Created: #{cluster[:created]}"
|
|
310
|
+
puts
|
|
311
|
+
|
|
312
|
+
# Get detailed cluster info
|
|
313
|
+
begin
|
|
314
|
+
k8s = Helpers::ClusterValidator.kubernetes_client(name)
|
|
315
|
+
|
|
316
|
+
# Get cluster resource
|
|
317
|
+
cluster_resource = k8s.get_resource('LanguageCluster', name, cluster[:namespace])
|
|
318
|
+
status = cluster_resource.dig('status', 'phase') || 'Unknown'
|
|
319
|
+
|
|
320
|
+
puts "Status: #{status}"
|
|
321
|
+
puts
|
|
322
|
+
|
|
323
|
+
# Get agents
|
|
324
|
+
agents = k8s.list_resources('LanguageAgent', namespace: cluster[:namespace])
|
|
325
|
+
puts "Agents: #{agents.count}"
|
|
326
|
+
agents.each do |agent|
|
|
327
|
+
agent_status = agent.dig('status', 'phase') || 'Unknown'
|
|
328
|
+
puts " - #{agent.dig('metadata', 'name')} (#{agent_status})"
|
|
329
|
+
end
|
|
330
|
+
puts
|
|
331
|
+
|
|
332
|
+
# Get tools
|
|
333
|
+
tools = k8s.list_resources('LanguageTool', namespace: cluster[:namespace])
|
|
334
|
+
puts "Tools: #{tools.count}"
|
|
335
|
+
tools.each do |tool|
|
|
336
|
+
tool_type = tool.dig('spec', 'type')
|
|
337
|
+
puts " - #{tool.dig('metadata', 'name')} (#{tool_type})"
|
|
338
|
+
end
|
|
339
|
+
puts
|
|
340
|
+
|
|
341
|
+
# Get models
|
|
342
|
+
models = k8s.list_resources('LanguageModel', namespace: cluster[:namespace])
|
|
343
|
+
puts "Models: #{models.count}"
|
|
344
|
+
models.each do |model|
|
|
345
|
+
provider = model.dig('spec', 'provider')
|
|
346
|
+
model_name = model.dig('spec', 'modelName')
|
|
347
|
+
puts " - #{model.dig('metadata', 'name')} (#{provider}/#{model_name})"
|
|
348
|
+
end
|
|
349
|
+
puts
|
|
350
|
+
|
|
351
|
+
# Get personas
|
|
352
|
+
personas = k8s.list_resources('LanguagePersona', namespace: cluster[:namespace])
|
|
353
|
+
puts "Personas: #{personas.count}"
|
|
354
|
+
personas.each do |persona|
|
|
355
|
+
tone = persona.dig('spec', 'tone')
|
|
356
|
+
puts " - #{persona.dig('metadata', 'name')} (#{tone})"
|
|
357
|
+
end
|
|
358
|
+
rescue StandardError => e
|
|
359
|
+
Formatters::ProgressFormatter.error("Failed to get cluster details: #{e.message}")
|
|
360
|
+
raise if ENV['DEBUG']
|
|
361
|
+
end
|
|
362
|
+
rescue StandardError => e
|
|
363
|
+
Formatters::ProgressFormatter.error("Failed to inspect cluster: #{e.message}")
|
|
364
|
+
raise if ENV['DEBUG']
|
|
365
|
+
|
|
366
|
+
exit 1
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
end
|