language-operator 0.1.30 → 0.1.35

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.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -8
  3. data/CHANGELOG.md +49 -0
  4. data/CI_STATUS.md +56 -0
  5. data/Gemfile.lock +2 -2
  6. data/Makefile +28 -7
  7. data/Rakefile +29 -0
  8. data/docs/dsl/SCHEMA_VERSION.md +250 -0
  9. data/docs/dsl/agent-reference.md +13 -0
  10. data/lib/language_operator/agent/base.rb +10 -6
  11. data/lib/language_operator/agent/executor.rb +19 -97
  12. data/lib/language_operator/agent/safety/ast_validator.rb +62 -43
  13. data/lib/language_operator/agent/safety/safe_executor.rb +39 -2
  14. data/lib/language_operator/agent/scheduler.rb +60 -0
  15. data/lib/language_operator/agent/task_executor.rb +548 -0
  16. data/lib/language_operator/agent.rb +90 -27
  17. data/lib/language_operator/cli/base_command.rb +117 -0
  18. data/lib/language_operator/cli/commands/agent.rb +351 -466
  19. data/lib/language_operator/cli/commands/cluster.rb +276 -256
  20. data/lib/language_operator/cli/commands/install.rb +110 -119
  21. data/lib/language_operator/cli/commands/model.rb +284 -184
  22. data/lib/language_operator/cli/commands/persona.rb +220 -289
  23. data/lib/language_operator/cli/commands/quickstart.rb +4 -5
  24. data/lib/language_operator/cli/commands/status.rb +36 -53
  25. data/lib/language_operator/cli/commands/system.rb +760 -0
  26. data/lib/language_operator/cli/commands/tool.rb +356 -422
  27. data/lib/language_operator/cli/commands/use.rb +19 -22
  28. data/lib/language_operator/cli/formatters/code_formatter.rb +3 -7
  29. data/lib/language_operator/cli/formatters/log_formatter.rb +3 -5
  30. data/lib/language_operator/cli/formatters/progress_formatter.rb +3 -7
  31. data/lib/language_operator/cli/formatters/status_formatter.rb +37 -0
  32. data/lib/language_operator/cli/formatters/table_formatter.rb +10 -26
  33. data/lib/language_operator/cli/helpers/pastel_helper.rb +24 -0
  34. data/lib/language_operator/cli/helpers/resource_dependency_checker.rb +0 -18
  35. data/lib/language_operator/cli/main.rb +4 -0
  36. data/lib/language_operator/cli/wizards/quickstart_wizard.rb +0 -1
  37. data/lib/language_operator/client/config.rb +20 -21
  38. data/lib/language_operator/config.rb +115 -3
  39. data/lib/language_operator/constants.rb +54 -0
  40. data/lib/language_operator/dsl/agent_context.rb +7 -7
  41. data/lib/language_operator/dsl/agent_definition.rb +111 -26
  42. data/lib/language_operator/dsl/config.rb +30 -66
  43. data/lib/language_operator/dsl/main_definition.rb +114 -0
  44. data/lib/language_operator/dsl/schema.rb +1143 -0
  45. data/lib/language_operator/dsl/task_definition.rb +315 -0
  46. data/lib/language_operator/dsl.rb +1 -1
  47. data/lib/language_operator/instrumentation/task_tracer.rb +285 -0
  48. data/lib/language_operator/logger.rb +4 -4
  49. data/lib/language_operator/synthesis_test_harness.rb +324 -0
  50. data/lib/language_operator/templates/README.md +23 -0
  51. data/lib/language_operator/templates/examples/agent_synthesis.tmpl +133 -0
  52. data/lib/language_operator/templates/examples/persona_distillation.tmpl +19 -0
  53. data/lib/language_operator/templates/schema/.gitkeep +0 -0
  54. data/lib/language_operator/templates/schema/CHANGELOG.md +119 -0
  55. data/lib/language_operator/templates/schema/agent_dsl_openapi.yaml +306 -0
  56. data/lib/language_operator/templates/schema/agent_dsl_schema.json +494 -0
  57. data/lib/language_operator/type_coercion.rb +250 -0
  58. data/lib/language_operator/ux/base.rb +81 -0
  59. data/lib/language_operator/ux/concerns/README.md +155 -0
  60. data/lib/language_operator/ux/concerns/headings.rb +90 -0
  61. data/lib/language_operator/ux/concerns/input_validation.rb +146 -0
  62. data/lib/language_operator/ux/concerns/provider_helpers.rb +167 -0
  63. data/lib/language_operator/ux/create_agent.rb +252 -0
  64. data/lib/language_operator/ux/create_model.rb +267 -0
  65. data/lib/language_operator/ux/quickstart.rb +594 -0
  66. data/lib/language_operator/version.rb +1 -1
  67. data/lib/language_operator.rb +2 -0
  68. data/requirements/ARCHITECTURE.md +1 -0
  69. data/requirements/SCRATCH.md +153 -0
  70. data/requirements/dsl.md +0 -0
  71. data/requirements/features +1 -0
  72. data/requirements/personas +1 -0
  73. data/requirements/proposals +1 -0
  74. data/requirements/tasks/iterate.md +14 -15
  75. data/requirements/tasks/optimize.md +13 -4
  76. data/synth/001/Makefile +90 -0
  77. data/synth/001/agent.rb +26 -0
  78. data/synth/001/agent.yaml +7 -0
  79. data/synth/001/output.log +44 -0
  80. data/synth/Makefile +39 -0
  81. data/synth/README.md +342 -0
  82. metadata +49 -18
  83. data/examples/README.md +0 -569
  84. data/examples/agent_example.rb +0 -86
  85. data/examples/chat_endpoint_agent.rb +0 -118
  86. data/examples/github_webhook_agent.rb +0 -171
  87. data/examples/mcp_agent.rb +0 -158
  88. data/examples/oauth_callback_agent.rb +0 -296
  89. data/examples/stripe_webhook_agent.rb +0 -219
  90. data/examples/webhook_agent.rb +0 -80
  91. data/lib/language_operator/dsl/workflow_definition.rb +0 -259
  92. data/test_agent_dsl.rb +0 -108
@@ -1,24 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'thor'
4
+ require_relative '../base_command'
4
5
  require_relative '../formatters/progress_formatter'
5
6
  require_relative '../formatters/table_formatter'
6
7
  require_relative '../formatters/value_formatter'
8
+ require_relative '../formatters/log_formatter'
9
+ require_relative '../formatters/status_formatter'
7
10
  require_relative '../helpers/cluster_validator'
8
11
  require_relative '../helpers/cluster_context'
9
12
  require_relative '../helpers/user_prompts'
10
13
  require_relative '../helpers/editor_helper'
14
+ require_relative '../helpers/pastel_helper'
11
15
  require_relative '../errors/handler'
12
16
  require_relative '../../config/cluster_config'
13
17
  require_relative '../../kubernetes/client'
14
18
  require_relative '../../kubernetes/resource_builder'
19
+ require_relative '../../ux/create_agent'
15
20
 
16
21
  module LanguageOperator
17
22
  module CLI
18
23
  module Commands
19
24
  # Agent management commands
20
- class Agent < Thor
25
+ class Agent < BaseCommand
21
26
  include Helpers::ClusterValidator
27
+ include Helpers::PastelHelper
22
28
 
23
29
  desc 'create [DESCRIPTION]', 'Create a new agent with natural language description'
24
30
  long_desc <<-DESC
@@ -41,241 +47,216 @@ module LanguageOperator
41
47
  option :dry_run, type: :boolean, default: false, desc: 'Preview what would be created without applying'
42
48
  option :wizard, type: :boolean, default: false, desc: 'Use interactive wizard mode'
43
49
  def create(description = nil)
44
- # Activate wizard mode if --wizard flag or no description provided
45
- if options[:wizard] || description.nil?
46
- require_relative '../wizards/agent_wizard'
47
- wizard = Wizards::AgentWizard.new
48
- description = wizard.run
49
-
50
- # User cancelled wizard
51
- unless description
52
- Formatters::ProgressFormatter.info('Agent creation cancelled')
53
- return
50
+ handle_command_error('create agent') do
51
+ # Activate wizard mode if --wizard flag or no description provided
52
+ if options[:wizard] || description.nil?
53
+ description = Ux::CreateAgent.execute(ctx)
54
+
55
+ # User cancelled wizard
56
+ unless description
57
+ Formatters::ProgressFormatter.info('Agent creation cancelled')
58
+ return
59
+ end
54
60
  end
55
- end
56
61
 
57
- # Handle --create-cluster flag
58
- if options[:create_cluster]
59
- cluster_name = options[:create_cluster]
60
- unless Config::ClusterConfig.cluster_exists?(cluster_name)
61
- Formatters::ProgressFormatter.info("Creating cluster '#{cluster_name}'...")
62
- # Delegate to cluster create command
63
- require_relative 'cluster'
64
- Cluster.new.invoke(:create, [cluster_name], switch: true)
62
+ # Handle --create-cluster flag
63
+ if options[:create_cluster]
64
+ cluster_name = options[:create_cluster]
65
+ unless Config::ClusterConfig.cluster_exists?(cluster_name)
66
+ Formatters::ProgressFormatter.info("Creating cluster '#{cluster_name}'...")
67
+ # Delegate to cluster create command
68
+ require_relative 'cluster'
69
+ Cluster.new.invoke(:create, [cluster_name], switch: true)
70
+ end
71
+ cluster = cluster_name
72
+ else
73
+ # Validate cluster selection (this will exit if none selected)
74
+ cluster = Helpers::ClusterValidator.get_cluster(options[:cluster])
65
75
  end
66
- cluster = cluster_name
67
- else
68
- # Validate cluster selection (this will exit if none selected)
69
- cluster = Helpers::ClusterValidator.get_cluster(options[:cluster])
70
- end
71
76
 
72
- cluster_config = Helpers::ClusterValidator.get_cluster_config(cluster)
77
+ ctx = Helpers::ClusterContext.from_options(options.merge(cluster: cluster))
73
78
 
74
- Formatters::ProgressFormatter.info("Creating agent in cluster '#{cluster}'")
75
- puts
76
-
77
- # Generate agent name from description if not provided
78
- agent_name = options[:name] || generate_agent_name(description)
79
-
80
- # Get models: use specified models, or default to all available models in cluster
81
- models = options[:models]
82
- if models.nil? || models.empty?
83
- k8s = Helpers::ClusterValidator.kubernetes_client(options[:cluster])
84
- available_models = k8s.list_resources('LanguageModel', namespace: cluster_config[:namespace])
85
- models = available_models.map { |m| m.dig('metadata', 'name') }
79
+ Formatters::ProgressFormatter.info("Creating agent in cluster '#{ctx.name}'")
80
+ puts
86
81
 
87
- Errors::Handler.handle_no_models_available(cluster: cluster) if models.empty?
88
- end
82
+ # Generate agent name from description if not provided
83
+ agent_name = options[:name] || generate_agent_name(description)
89
84
 
90
- # Build LanguageAgent resource
91
- agent_resource = Kubernetes::ResourceBuilder.language_agent(
92
- agent_name,
93
- instructions: description,
94
- cluster: cluster_config[:namespace],
95
- persona: options[:persona],
96
- tools: options[:tools] || [],
97
- models: models
98
- )
85
+ # Get models: use specified models, or default to all available models in cluster
86
+ models = options[:models]
87
+ if models.nil? || models.empty?
88
+ available_models = ctx.client.list_resources('LanguageModel', namespace: ctx.namespace)
89
+ models = available_models.map { |m| m.dig('metadata', 'name') }
99
90
 
100
- # Dry-run mode: preview without applying
101
- if options[:dry_run]
102
- display_dry_run_preview(agent_resource, cluster, description)
103
- return
104
- end
91
+ Errors::Handler.handle_no_models_available(cluster: ctx.name) if models.empty?
92
+ end
105
93
 
106
- # Connect to Kubernetes
107
- k8s = Helpers::ClusterValidator.kubernetes_client(options[:cluster])
94
+ # Build LanguageAgent resource
95
+ agent_resource = Kubernetes::ResourceBuilder.language_agent(
96
+ agent_name,
97
+ instructions: description,
98
+ cluster: ctx.namespace,
99
+ persona: options[:persona],
100
+ tools: options[:tools] || [],
101
+ models: models
102
+ )
108
103
 
109
- # Apply resource to cluster
110
- Formatters::ProgressFormatter.with_spinner("Creating agent '#{agent_name}'") do
111
- k8s.apply_resource(agent_resource)
112
- end
104
+ # Dry-run mode: preview without applying
105
+ if options[:dry_run]
106
+ display_dry_run_preview(agent_resource, ctx.name, description)
107
+ return
108
+ end
113
109
 
114
- # Watch synthesis status
115
- synthesis_result = watch_synthesis_status(k8s, agent_name, cluster_config[:namespace])
110
+ # Apply resource to cluster
111
+ Formatters::ProgressFormatter.with_spinner("Creating agent '#{agent_name}'") do
112
+ ctx.client.apply_resource(agent_resource)
113
+ end
116
114
 
117
- # Exit if synthesis failed
118
- exit 1 unless synthesis_result[:success]
115
+ # Watch synthesis status
116
+ synthesis_result = watch_synthesis_status(ctx.client, agent_name, ctx.namespace)
119
117
 
120
- # Fetch the updated agent to get complete details
121
- agent = k8s.get_resource('LanguageAgent', agent_name, cluster_config[:namespace])
118
+ # Exit if synthesis failed
119
+ exit 1 unless synthesis_result[:success]
122
120
 
123
- # Display enhanced success output
124
- display_agent_created(agent, cluster, description, synthesis_result)
125
- rescue StandardError => e
126
- Formatters::ProgressFormatter.error("Failed to create agent: #{e.message}")
127
- raise if ENV['DEBUG']
121
+ # Fetch the updated agent to get complete details
122
+ agent = ctx.client.get_resource('LanguageAgent', agent_name, ctx.namespace)
128
123
 
129
- exit 1
124
+ # Display enhanced success output
125
+ display_agent_created(agent, ctx.name, description, synthesis_result)
126
+ end
130
127
  end
131
128
 
132
129
  desc 'list', 'List all agents in current cluster'
133
130
  option :cluster, type: :string, desc: 'Override current cluster context'
134
131
  option :all_clusters, type: :boolean, default: false, desc: 'Show agents across all clusters'
135
132
  def list
136
- if options[:all_clusters]
137
- list_all_clusters
138
- else
139
- cluster = Helpers::ClusterValidator.get_cluster(options[:cluster])
140
- list_cluster_agents(cluster)
133
+ handle_command_error('list agents') do
134
+ if options[:all_clusters]
135
+ list_all_clusters
136
+ else
137
+ ctx = Helpers::ClusterContext.from_options(options)
138
+ list_cluster_agents(ctx.name)
139
+ end
141
140
  end
142
- rescue StandardError => e
143
- Formatters::ProgressFormatter.error("Failed to list agents: #{e.message}")
144
- raise if ENV['DEBUG']
145
-
146
- exit 1
147
141
  end
148
142
 
149
143
  desc 'inspect NAME', 'Show detailed agent information'
150
144
  option :cluster, type: :string, desc: 'Override current cluster context'
151
145
  def inspect(name)
152
- cluster = Helpers::ClusterValidator.get_cluster(options[:cluster])
153
- cluster_config = Helpers::ClusterValidator.get_cluster_config(cluster)
154
-
155
- k8s = Helpers::ClusterValidator.kubernetes_client(options[:cluster])
156
-
157
- agent = k8s.get_resource('LanguageAgent', name, cluster_config[:namespace])
158
-
159
- puts "Agent: #{name}"
160
- puts " Cluster: #{cluster}"
161
- puts " Namespace: #{cluster_config[:namespace]}"
162
- puts
163
-
164
- # Status
165
- status = agent.dig('status', 'phase') || 'Unknown'
166
- puts "Status: #{format_status(status)}"
167
- puts
146
+ handle_command_error('inspect agent') do
147
+ ctx = Helpers::ClusterContext.from_options(options)
168
148
 
169
- # Spec details
170
- puts 'Configuration:'
171
- puts " Mode: #{agent.dig('spec', 'mode') || 'autonomous'}"
172
- puts " Schedule: #{agent.dig('spec', 'schedule') || 'N/A'}" if agent.dig('spec', 'schedule')
173
- puts " Persona: #{agent.dig('spec', 'persona') || '(auto-selected)'}"
174
- puts
149
+ begin
150
+ agent = ctx.client.get_resource('LanguageAgent', name, ctx.namespace)
151
+ rescue K8s::Error::NotFound
152
+ handle_agent_not_found(name, ctx)
153
+ return
154
+ end
175
155
 
176
- # Instructions
177
- instructions = agent.dig('spec', 'instructions')
178
- if instructions
179
- puts 'Instructions:'
180
- puts " #{instructions}"
156
+ puts "Agent: #{name}"
157
+ puts " Cluster: #{ctx.name}"
158
+ puts " Namespace: #{ctx.namespace}"
181
159
  puts
182
- end
183
160
 
184
- # Tools
185
- tools = agent.dig('spec', 'tools') || []
186
- if tools.any?
187
- puts "Tools (#{tools.length}):"
188
- tools.each { |tool| puts " - #{tool}" }
161
+ # Status
162
+ status = agent.dig('status', 'phase') || 'Unknown'
163
+ puts "Status: #{format_status(status)}"
189
164
  puts
190
- end
191
165
 
192
- # Models
193
- model_refs = agent.dig('spec', 'modelRefs') || []
194
- if model_refs.any?
195
- puts "Models (#{model_refs.length}):"
196
- model_refs.each { |ref| puts " - #{ref['name']}" }
166
+ # Spec details
167
+ puts 'Configuration:'
168
+ puts " Mode: #{agent.dig('spec', 'mode') || 'autonomous'}"
169
+ puts " Schedule: #{agent.dig('spec', 'schedule') || 'N/A'}" if agent.dig('spec', 'schedule')
170
+ puts " Persona: #{agent.dig('spec', 'persona') || '(auto-selected)'}"
197
171
  puts
198
- end
199
172
 
200
- # Synthesis info
201
- synthesis = agent.dig('status', 'synthesis')
202
- if synthesis
203
- puts 'Synthesis:'
204
- puts " Status: #{synthesis['status']}"
205
- puts " Model: #{synthesis['model']}" if synthesis['model']
206
- puts " Completed: #{synthesis['completedAt']}" if synthesis['completedAt']
207
- puts " Duration: #{synthesis['duration']}" if synthesis['duration']
208
- puts " Token Count: #{synthesis['tokenCount']}" if synthesis['tokenCount']
209
- puts
210
- end
173
+ # Instructions
174
+ instructions = agent.dig('spec', 'instructions')
175
+ if instructions
176
+ puts 'Instructions:'
177
+ puts " #{instructions}"
178
+ puts
179
+ end
211
180
 
212
- # Execution stats
213
- execution_count = agent.dig('status', 'executionCount') || 0
214
- last_execution = agent.dig('status', 'lastExecution')
215
- next_run = agent.dig('status', 'nextRun')
181
+ # Tools
182
+ tools = agent.dig('spec', 'tools') || []
183
+ if tools.any?
184
+ puts "Tools (#{tools.length}):"
185
+ tools.each { |tool| puts " - #{tool}" }
186
+ puts
187
+ end
216
188
 
217
- puts 'Execution:'
218
- puts " Total Runs: #{execution_count}"
219
- puts " Last Run: #{last_execution || 'Never'}"
220
- puts " Next Run: #{next_run || 'N/A'}" if agent.dig('spec', 'schedule')
221
- puts
189
+ # Models
190
+ model_refs = agent.dig('spec', 'modelRefs') || []
191
+ if model_refs.any?
192
+ puts "Models (#{model_refs.length}):"
193
+ model_refs.each { |ref| puts " - #{ref['name']}" }
194
+ puts
195
+ end
222
196
 
223
- # Conditions
224
- conditions = agent.dig('status', 'conditions') || []
225
- if conditions.any?
226
- puts "Conditions (#{conditions.length}):"
227
- conditions.each do |condition|
228
- status_icon = condition['status'] == 'True' ? '✓' : '✗'
229
- puts " #{status_icon} #{condition['type']}: #{condition['message'] || condition['reason']}"
197
+ # Synthesis info
198
+ synthesis = agent.dig('status', 'synthesis')
199
+ if synthesis
200
+ puts 'Synthesis:'
201
+ puts " Status: #{synthesis['status']}"
202
+ puts " Model: #{synthesis['model']}" if synthesis['model']
203
+ puts " Completed: #{synthesis['completedAt']}" if synthesis['completedAt']
204
+ puts " Duration: #{synthesis['duration']}" if synthesis['duration']
205
+ puts " Token Count: #{synthesis['tokenCount']}" if synthesis['tokenCount']
206
+ puts
230
207
  end
208
+
209
+ # Execution stats
210
+ execution_count = agent.dig('status', 'executionCount') || 0
211
+ last_execution = agent.dig('status', 'lastExecution')
212
+ next_run = agent.dig('status', 'nextRun')
213
+
214
+ puts 'Execution:'
215
+ puts " Total Runs: #{execution_count}"
216
+ puts " Last Run: #{last_execution || 'Never'}"
217
+ puts " Next Run: #{next_run || 'N/A'}" if agent.dig('spec', 'schedule')
231
218
  puts
232
- end
233
219
 
234
- # Recent events (if available)
235
- # This would require querying events, which we can add later
236
- rescue K8s::Error::NotFound
237
- handle_agent_not_found(name, cluster, k8s, cluster_config)
238
- rescue StandardError => e
239
- Formatters::ProgressFormatter.error("Failed to inspect agent: #{e.message}")
240
- raise if ENV['DEBUG']
220
+ # Conditions
221
+ conditions = agent.dig('status', 'conditions') || []
222
+ if conditions.any?
223
+ puts "Conditions (#{conditions.length}):"
224
+ conditions.each do |condition|
225
+ status_icon = condition['status'] == 'True' ? '✓' : '✗'
226
+ puts " #{status_icon} #{condition['type']}: #{condition['message'] || condition['reason']}"
227
+ end
228
+ puts
229
+ end
241
230
 
242
- exit 1
231
+ # Recent events (if available)
232
+ # This would require querying events, which we can add later
233
+ end
243
234
  end
244
235
 
245
236
  desc 'delete NAME', 'Delete an agent'
246
237
  option :cluster, type: :string, desc: 'Override current cluster context'
247
238
  option :force, type: :boolean, default: false, desc: 'Skip confirmation'
248
239
  def delete(name)
249
- ctx = Helpers::ClusterContext.from_options(options)
240
+ handle_command_error('delete agent') do
241
+ ctx = Helpers::ClusterContext.from_options(options)
250
242
 
251
- # Get agent to show details before deletion
252
- begin
253
- agent = ctx.client.get_resource('LanguageAgent', name, ctx.namespace)
254
- rescue K8s::Error::NotFound
255
- Formatters::ProgressFormatter.error("Agent '#{name}' not found in cluster '#{ctx.name}'")
256
- exit 1
257
- end
258
-
259
- # Confirm deletion using UserPrompts helper
260
- unless options[:force]
261
- puts "This will delete agent '#{name}' from cluster '#{ctx.name}':"
262
- puts " Instructions: #{agent.dig('spec', 'instructions')}"
263
- puts " Mode: #{agent.dig('spec', 'mode') || 'autonomous'}"
264
- puts
265
- return unless Helpers::UserPrompts.confirm('Are you sure?')
266
- end
243
+ # Get agent to show details before deletion
244
+ agent = get_resource_or_exit('LanguageAgent', name)
267
245
 
268
- # Delete the agent
269
- Formatters::ProgressFormatter.with_spinner("Deleting agent '#{name}'") do
270
- ctx.client.delete_resource('LanguageAgent', name, ctx.namespace)
271
- end
246
+ # Confirm deletion
247
+ details = {
248
+ 'Instructions' => agent.dig('spec', 'instructions'),
249
+ 'Mode' => agent.dig('spec', 'mode') || 'autonomous'
250
+ }
251
+ return unless confirm_deletion('agent', name, ctx.name, details: details, force: options[:force])
272
252
 
273
- Formatters::ProgressFormatter.success("Agent '#{name}' deleted successfully")
274
- rescue StandardError => e
275
- Formatters::ProgressFormatter.error("Failed to delete agent: #{e.message}")
276
- raise if ENV['DEBUG']
253
+ # Delete the agent
254
+ Formatters::ProgressFormatter.with_spinner("Deleting agent '#{name}'") do
255
+ ctx.client.delete_resource('LanguageAgent', name, ctx.namespace)
256
+ end
277
257
 
278
- exit 1
258
+ Formatters::ProgressFormatter.success("Agent '#{name}' deleted successfully")
259
+ end
279
260
  end
280
261
 
281
262
  desc 'logs NAME', 'Show agent execution logs'
@@ -292,276 +273,217 @@ module LanguageOperator
292
273
  option :follow, type: :boolean, aliases: '-f', default: false, desc: 'Follow logs'
293
274
  option :tail, type: :numeric, default: 100, desc: 'Number of lines to show from the end'
294
275
  def logs(name)
295
- cluster = Helpers::ClusterValidator.get_cluster(options[:cluster])
296
- cluster_config = Helpers::ClusterValidator.get_cluster_config(cluster)
276
+ handle_command_error('get logs') do
277
+ ctx = Helpers::ClusterContext.from_options(options)
297
278
 
298
- k8s = Helpers::ClusterValidator.kubernetes_client(options[:cluster])
279
+ # Get agent to determine the pod name
280
+ agent = get_resource_or_exit('LanguageAgent', name)
299
281
 
300
- # Get agent to determine the pod name
301
- begin
302
- agent = k8s.get_resource('LanguageAgent', name, cluster_config[:namespace])
303
- rescue K8s::Error::NotFound
304
- Formatters::ProgressFormatter.error("Agent '#{name}' not found in cluster '#{cluster}'")
305
- exit 1
306
- end
282
+ mode = agent.dig('spec', 'mode') || 'autonomous'
307
283
 
308
- mode = agent.dig('spec', 'mode') || 'autonomous'
284
+ # Build kubectl command for log streaming
285
+ tail_arg = "--tail=#{options[:tail]}"
286
+ follow_arg = options[:follow] ? '-f' : ''
309
287
 
310
- # Build kubectl command for log streaming
311
- kubeconfig_arg = cluster_config[:kubeconfig] ? "--kubeconfig=#{cluster_config[:kubeconfig]}" : ''
312
- context_arg = cluster_config[:context] ? "--context=#{cluster_config[:context]}" : ''
313
- namespace_arg = "-n #{cluster_config[:namespace]}"
314
- tail_arg = "--tail=#{options[:tail]}"
315
- follow_arg = options[:follow] ? '-f' : ''
316
-
317
- # For scheduled agents, logs come from CronJob pods
318
- # For autonomous agents, logs come from Deployment pods
319
- if mode == 'scheduled'
320
- # Get most recent job from cronjob
321
- else
322
- # Get pod from deployment
323
- end
324
- label_selector = "app.kubernetes.io/name=#{name}"
288
+ # For scheduled agents, logs come from CronJob pods
289
+ # For autonomous agents, logs come from Deployment pods
290
+ if mode == 'scheduled'
291
+ # Get most recent job from cronjob
292
+ else
293
+ # Get pod from deployment
294
+ end
295
+ label_selector = "app.kubernetes.io/name=#{name}"
325
296
 
326
- # Use kubectl logs with label selector
327
- cmd = "kubectl #{kubeconfig_arg} #{context_arg} #{namespace_arg} logs -l #{label_selector} #{tail_arg} #{follow_arg} --prefix --all-containers"
297
+ # Use kubectl logs with label selector
298
+ cmd = "#{ctx.kubectl_prefix} logs -l #{label_selector} #{tail_arg} #{follow_arg} --prefix --all-containers"
328
299
 
329
- Formatters::ProgressFormatter.info("Streaming logs for agent '#{name}'...")
330
- puts
300
+ Formatters::ProgressFormatter.info("Streaming logs for agent '#{name}'...")
301
+ puts
331
302
 
332
- # Stream and format logs in real-time
333
- require 'open3'
334
- Open3.popen3(cmd) do |_stdin, stdout, stderr, wait_thr|
335
- # Handle stdout (logs)
336
- stdout_thread = Thread.new do
337
- stdout.each_line do |line|
338
- puts Formatters::LogFormatter.format_line(line.chomp)
339
- $stdout.flush
303
+ # Stream and format logs in real-time
304
+ require 'open3'
305
+ Open3.popen3(cmd) do |_stdin, stdout, stderr, wait_thr|
306
+ # Handle stdout (logs)
307
+ stdout_thread = Thread.new do
308
+ stdout.each_line do |line|
309
+ puts Formatters::LogFormatter.format_line(line.chomp)
310
+ $stdout.flush
311
+ end
340
312
  end
341
- end
342
313
 
343
- # Handle stderr (errors)
344
- stderr_thread = Thread.new do
345
- stderr.each_line do |line|
346
- warn line
314
+ # Handle stderr (errors)
315
+ stderr_thread = Thread.new do
316
+ stderr.each_line do |line|
317
+ warn line
318
+ end
347
319
  end
348
- end
349
320
 
350
- # Wait for both streams to complete
351
- stdout_thread.join
352
- stderr_thread.join
321
+ # Wait for both streams to complete
322
+ stdout_thread.join
323
+ stderr_thread.join
353
324
 
354
- # Check exit status
355
- exit_status = wait_thr.value
356
- exit exit_status.exitstatus unless exit_status.success?
325
+ # Check exit status
326
+ exit_status = wait_thr.value
327
+ exit exit_status.exitstatus unless exit_status.success?
328
+ end
357
329
  end
358
- rescue StandardError => e
359
- Formatters::ProgressFormatter.error("Failed to get logs: #{e.message}")
360
- raise if ENV['DEBUG']
361
-
362
- exit 1
363
330
  end
364
331
 
365
332
  desc 'code NAME', 'Display synthesized agent code'
366
333
  option :cluster, type: :string, desc: 'Override current cluster context'
367
334
  def code(name)
368
- require_relative '../formatters/code_formatter'
369
-
370
- cluster = Helpers::ClusterValidator.get_cluster(options[:cluster])
371
- cluster_config = Helpers::ClusterValidator.get_cluster_config(cluster)
335
+ handle_command_error('get code') do
336
+ require_relative '../formatters/code_formatter'
372
337
 
373
- k8s = Helpers::ClusterValidator.kubernetes_client(options[:cluster])
338
+ ctx = Helpers::ClusterContext.from_options(options)
374
339
 
375
- # Get the code ConfigMap for this agent
376
- configmap_name = "#{name}-code"
377
- begin
378
- configmap = k8s.get_resource('ConfigMap', configmap_name, cluster_config[:namespace])
379
- rescue K8s::Error::NotFound
380
- Formatters::ProgressFormatter.error("Synthesized code not found for agent '#{name}'")
381
- puts
382
- puts 'Possible reasons:'
383
- puts ' - Agent synthesis not yet complete'
384
- puts ' - Agent synthesis failed'
385
- puts
386
- puts 'Check synthesis status with:'
387
- puts " aictl agent inspect #{name}"
388
- exit 1
389
- end
390
-
391
- # Get the agent.rb code from the ConfigMap
392
- code_content = configmap.dig('data', 'agent.rb')
393
- unless code_content
394
- Formatters::ProgressFormatter.error('Code content not found in ConfigMap')
395
- exit 1
396
- end
340
+ # Get the code ConfigMap for this agent
341
+ configmap_name = "#{name}-code"
342
+ begin
343
+ configmap = ctx.client.get_resource('ConfigMap', configmap_name, ctx.namespace)
344
+ rescue K8s::Error::NotFound
345
+ Formatters::ProgressFormatter.error("Synthesized code not found for agent '#{name}'")
346
+ puts
347
+ puts 'Possible reasons:'
348
+ puts ' - Agent synthesis not yet complete'
349
+ puts ' - Agent synthesis failed'
350
+ puts
351
+ puts 'Check synthesis status with:'
352
+ puts " aictl agent inspect #{name}"
353
+ exit 1
354
+ end
397
355
 
398
- # Display with syntax highlighting
399
- Formatters::CodeFormatter.display_ruby_code(
400
- code_content,
401
- title: "Synthesized Code for Agent: #{name}"
402
- )
356
+ # Get the agent.rb code from the ConfigMap
357
+ code_content = configmap.dig('data', 'agent.rb')
358
+ unless code_content
359
+ Formatters::ProgressFormatter.error('Code content not found in ConfigMap')
360
+ exit 1
361
+ end
403
362
 
404
- puts
405
- puts 'This code was automatically synthesized from the agent instructions.'
406
- puts "View full agent details with: aictl agent inspect #{name}"
407
- rescue StandardError => e
408
- Formatters::ProgressFormatter.error("Failed to get code: #{e.message}")
409
- raise if ENV['DEBUG']
363
+ # Display with syntax highlighting
364
+ Formatters::CodeFormatter.display_ruby_code(
365
+ code_content,
366
+ title: "Synthesized Code for Agent: #{name}"
367
+ )
410
368
 
411
- exit 1
369
+ puts
370
+ puts 'This code was automatically synthesized from the agent instructions.'
371
+ puts "View full agent details with: aictl agent inspect #{name}"
372
+ end
412
373
  end
413
374
 
414
375
  desc 'edit NAME', 'Edit agent instructions'
415
376
  option :cluster, type: :string, desc: 'Override current cluster context'
416
377
  def edit(name)
417
- cluster = Helpers::ClusterValidator.get_cluster(options[:cluster])
418
- cluster_config = Helpers::ClusterValidator.get_cluster_config(cluster)
378
+ handle_command_error('edit agent') do
379
+ ctx = Helpers::ClusterContext.from_options(options)
419
380
 
420
- k8s = Helpers::ClusterValidator.kubernetes_client(options[:cluster])
381
+ # Get current agent
382
+ agent = get_resource_or_exit('LanguageAgent', name)
421
383
 
422
- # Get current agent
423
- begin
424
- agent = k8s.get_resource('LanguageAgent', name, cluster_config[:namespace])
425
- rescue K8s::Error::NotFound
426
- Formatters::ProgressFormatter.error("Agent '#{name}' not found in cluster '#{cluster}'")
427
- exit 1
428
- end
384
+ current_instructions = agent.dig('spec', 'instructions')
429
385
 
430
- current_instructions = agent.dig('spec', 'instructions')
386
+ # Edit instructions in user's editor
387
+ new_instructions = Helpers::EditorHelper.edit_content(
388
+ current_instructions,
389
+ 'agent-instructions-',
390
+ '.txt'
391
+ ).strip
431
392
 
432
- # Edit instructions in user's editor
433
- new_instructions = Helpers::EditorHelper.edit_content(
434
- current_instructions,
435
- 'agent-instructions-',
436
- '.txt'
437
- ).strip
393
+ # Check if changed
394
+ if new_instructions == current_instructions
395
+ Formatters::ProgressFormatter.info('No changes made')
396
+ return
397
+ end
438
398
 
439
- # Check if changed
440
- if new_instructions == current_instructions
441
- Formatters::ProgressFormatter.info('No changes made')
442
- return
443
- end
399
+ # Update agent resource
400
+ agent['spec']['instructions'] = new_instructions
444
401
 
445
- # Update agent resource
446
- agent['spec']['instructions'] = new_instructions
402
+ Formatters::ProgressFormatter.with_spinner('Updating agent instructions') do
403
+ ctx.client.apply_resource(agent)
404
+ end
447
405
 
448
- Formatters::ProgressFormatter.with_spinner('Updating agent instructions') do
449
- k8s.apply_resource(agent)
406
+ Formatters::ProgressFormatter.success('Agent instructions updated')
407
+ puts
408
+ puts 'The operator will automatically re-synthesize the agent code.'
409
+ puts
410
+ puts 'Watch synthesis progress with:'
411
+ puts " aictl agent inspect #{name}"
450
412
  end
451
-
452
- Formatters::ProgressFormatter.success('Agent instructions updated')
453
- puts
454
- puts 'The operator will automatically re-synthesize the agent code.'
455
- puts
456
- puts 'Watch synthesis progress with:'
457
- puts " aictl agent inspect #{name}"
458
- rescue StandardError => e
459
- Formatters::ProgressFormatter.error("Failed to edit agent: #{e.message}")
460
- raise if ENV['DEBUG']
461
-
462
- exit 1
463
413
  end
464
414
 
465
415
  desc 'pause NAME', 'Pause scheduled agent execution'
466
416
  option :cluster, type: :string, desc: 'Override current cluster context'
467
417
  def pause(name)
468
- cluster = Helpers::ClusterValidator.get_cluster(options[:cluster])
469
- cluster_config = Helpers::ClusterValidator.get_cluster_config(cluster)
418
+ handle_command_error('pause agent') do
419
+ ctx = Helpers::ClusterContext.from_options(options)
470
420
 
471
- k8s = Helpers::ClusterValidator.kubernetes_client(options[:cluster])
421
+ # Get agent
422
+ agent = get_resource_or_exit('LanguageAgent', name)
472
423
 
473
- # Get agent
474
- begin
475
- agent = k8s.get_resource('LanguageAgent', name, cluster_config[:namespace])
476
- rescue K8s::Error::NotFound
477
- Formatters::ProgressFormatter.error("Agent '#{name}' not found in cluster '#{cluster}'")
478
- exit 1
479
- end
480
-
481
- mode = agent.dig('spec', 'mode') || 'autonomous'
482
- unless mode == 'scheduled'
483
- Formatters::ProgressFormatter.warn("Agent '#{name}' is not a scheduled agent (mode: #{mode})")
484
- puts
485
- puts 'Only scheduled agents can be paused.'
486
- puts 'Autonomous agents can be stopped by deleting them.'
487
- exit 1
488
- end
424
+ mode = agent.dig('spec', 'mode') || 'autonomous'
425
+ unless mode == 'scheduled'
426
+ Formatters::ProgressFormatter.warn("Agent '#{name}' is not a scheduled agent (mode: #{mode})")
427
+ puts
428
+ puts 'Only scheduled agents can be paused.'
429
+ puts 'Autonomous agents can be stopped by deleting them.'
430
+ exit 1
431
+ end
489
432
 
490
- # Suspend the CronJob by setting spec.suspend = true
491
- # This is done by patching the underlying CronJob resource
492
- cronjob_name = name
493
- namespace = cluster_config[:namespace]
433
+ # Suspend the CronJob by setting spec.suspend = true
434
+ # This is done by patching the underlying CronJob resource
435
+ cronjob_name = name
436
+ ctx.namespace
494
437
 
495
- Formatters::ProgressFormatter.with_spinner("Pausing agent '#{name}'") do
496
- # Use kubectl to patch the cronjob
497
- kubeconfig_arg = cluster_config[:kubeconfig] ? "--kubeconfig=#{cluster_config[:kubeconfig]}" : ''
498
- context_arg = cluster_config[:context] ? "--context=#{cluster_config[:context]}" : ''
438
+ Formatters::ProgressFormatter.with_spinner("Pausing agent '#{name}'") do
439
+ # Use kubectl to patch the cronjob
440
+ cmd = "#{ctx.kubectl_prefix} patch cronjob #{cronjob_name} -p '{\"spec\":{\"suspend\":true}}'"
441
+ system(cmd)
442
+ end
499
443
 
500
- cmd = "kubectl #{kubeconfig_arg} #{context_arg} -n #{namespace} patch cronjob #{cronjob_name} -p '{\"spec\":{\"suspend\":true}}'"
501
- system(cmd)
444
+ Formatters::ProgressFormatter.success("Agent '#{name}' paused")
445
+ puts
446
+ puts 'The agent will not execute on its schedule until resumed.'
447
+ puts
448
+ puts 'Resume with:'
449
+ puts " aictl agent resume #{name}"
502
450
  end
503
-
504
- Formatters::ProgressFormatter.success("Agent '#{name}' paused")
505
- puts
506
- puts 'The agent will not execute on its schedule until resumed.'
507
- puts
508
- puts 'Resume with:'
509
- puts " aictl agent resume #{name}"
510
- rescue StandardError => e
511
- Formatters::ProgressFormatter.error("Failed to pause agent: #{e.message}")
512
- raise if ENV['DEBUG']
513
-
514
- exit 1
515
451
  end
516
452
 
517
453
  desc 'resume NAME', 'Resume paused agent'
518
454
  option :cluster, type: :string, desc: 'Override current cluster context'
519
455
  def resume(name)
520
- cluster = Helpers::ClusterValidator.get_cluster(options[:cluster])
521
- cluster_config = Helpers::ClusterValidator.get_cluster_config(cluster)
522
-
523
- k8s = Helpers::ClusterValidator.kubernetes_client(options[:cluster])
456
+ handle_command_error('resume agent') do
457
+ ctx = Helpers::ClusterContext.from_options(options)
524
458
 
525
- # Get agent
526
- begin
527
- agent = k8s.get_resource('LanguageAgent', name, cluster_config[:namespace])
528
- rescue K8s::Error::NotFound
529
- Formatters::ProgressFormatter.error("Agent '#{name}' not found in cluster '#{cluster}'")
530
- exit 1
531
- end
459
+ # Get agent
460
+ agent = get_resource_or_exit('LanguageAgent', name)
532
461
 
533
- mode = agent.dig('spec', 'mode') || 'autonomous'
534
- unless mode == 'scheduled'
535
- Formatters::ProgressFormatter.warn("Agent '#{name}' is not a scheduled agent (mode: #{mode})")
536
- puts
537
- puts 'Only scheduled agents can be resumed.'
538
- exit 1
539
- end
462
+ mode = agent.dig('spec', 'mode') || 'autonomous'
463
+ unless mode == 'scheduled'
464
+ Formatters::ProgressFormatter.warn("Agent '#{name}' is not a scheduled agent (mode: #{mode})")
465
+ puts
466
+ puts 'Only scheduled agents can be resumed.'
467
+ exit 1
468
+ end
540
469
 
541
- # Resume the CronJob by setting spec.suspend = false
542
- cronjob_name = name
543
- namespace = cluster_config[:namespace]
470
+ # Resume the CronJob by setting spec.suspend = false
471
+ cronjob_name = name
472
+ ctx.namespace
544
473
 
545
- Formatters::ProgressFormatter.with_spinner("Resuming agent '#{name}'") do
546
- # Use kubectl to patch the cronjob
547
- kubeconfig_arg = cluster_config[:kubeconfig] ? "--kubeconfig=#{cluster_config[:kubeconfig]}" : ''
548
- context_arg = cluster_config[:context] ? "--context=#{cluster_config[:context]}" : ''
474
+ Formatters::ProgressFormatter.with_spinner("Resuming agent '#{name}'") do
475
+ # Use kubectl to patch the cronjob
476
+ cmd = "#{ctx.kubectl_prefix} patch cronjob #{cronjob_name} -p '{\"spec\":{\"suspend\":false}}'"
477
+ system(cmd)
478
+ end
549
479
 
550
- cmd = "kubectl #{kubeconfig_arg} #{context_arg} -n #{namespace} patch cronjob #{cronjob_name} -p '{\"spec\":{\"suspend\":false}}'"
551
- system(cmd)
480
+ Formatters::ProgressFormatter.success("Agent '#{name}' resumed")
481
+ puts
482
+ puts 'The agent will now execute according to its schedule.'
483
+ puts
484
+ puts 'View next execution time with:'
485
+ puts " aictl agent inspect #{name}"
552
486
  end
553
-
554
- Formatters::ProgressFormatter.success("Agent '#{name}' resumed")
555
- puts
556
- puts 'The agent will now execute according to its schedule.'
557
- puts
558
- puts 'View next execution time with:'
559
- puts " aictl agent inspect #{name}"
560
- rescue StandardError => e
561
- Formatters::ProgressFormatter.error("Failed to resume agent: #{e.message}")
562
- raise if ENV['DEBUG']
563
-
564
- exit 1
565
487
  end
566
488
 
567
489
  desc 'workspace NAME', 'Browse agent workspace files'
@@ -580,63 +502,52 @@ module LanguageOperator
580
502
  option :path, type: :string, desc: 'View specific file contents'
581
503
  option :clean, type: :boolean, desc: 'Clear workspace (with confirmation)'
582
504
  def workspace(name)
583
- ctx = Helpers::ClusterContext.from_options(options)
505
+ handle_command_error('access workspace') do
506
+ ctx = Helpers::ClusterContext.from_options(options)
584
507
 
585
- # Get agent to verify it exists
586
- begin
587
- agent = ctx.client.get_resource('LanguageAgent', name, ctx.namespace)
588
- rescue K8s::Error::NotFound
589
- Formatters::ProgressFormatter.error("Agent '#{name}' not found in cluster '#{ctx.name}'")
590
- exit 1
591
- end
508
+ # Get agent to verify it exists
509
+ agent = get_resource_or_exit('LanguageAgent', name)
592
510
 
593
- # Check if workspace is enabled
594
- workspace_enabled = agent.dig('spec', 'workspace', 'enabled')
595
- unless workspace_enabled
596
- Formatters::ProgressFormatter.warn("Workspace is not enabled for agent '#{name}'")
597
- puts
598
- puts 'Enable workspace in agent configuration:'
599
- puts ' spec:'
600
- puts ' workspace:'
601
- puts ' enabled: true'
602
- puts ' size: 10Gi'
603
- exit 1
604
- end
511
+ # Check if workspace is enabled
512
+ workspace_enabled = agent.dig('spec', 'workspace', 'enabled')
513
+ unless workspace_enabled
514
+ Formatters::ProgressFormatter.warn("Workspace is not enabled for agent '#{name}'")
515
+ puts
516
+ puts 'Enable workspace in agent configuration:'
517
+ puts ' spec:'
518
+ puts ' workspace:'
519
+ puts ' enabled: true'
520
+ puts ' size: 10Gi'
521
+ exit 1
522
+ end
605
523
 
606
- if options[:path]
607
- view_workspace_file(ctx, name, options[:path])
608
- elsif options[:clean]
609
- clean_workspace(ctx, name)
610
- else
611
- list_workspace_files(ctx, name)
524
+ if options[:path]
525
+ view_workspace_file(ctx, name, options[:path])
526
+ elsif options[:clean]
527
+ clean_workspace(ctx, name)
528
+ else
529
+ list_workspace_files(ctx, name)
530
+ end
612
531
  end
613
- rescue StandardError => e
614
- Formatters::ProgressFormatter.error("Failed to access workspace: #{e.message}")
615
- raise if ENV['DEBUG']
616
-
617
- exit 1
618
532
  end
619
533
 
620
534
  private
621
535
 
622
- def handle_agent_not_found(name, cluster, k8s, cluster_config)
536
+ def handle_agent_not_found(name, ctx)
623
537
  # Get available agents for fuzzy matching
624
- agents = k8s.list_resources('LanguageAgent', namespace: cluster_config[:namespace])
538
+ agents = ctx.client.list_resources('LanguageAgent', namespace: ctx.namespace)
625
539
  available_names = agents.map { |a| a.dig('metadata', 'name') }
626
540
 
627
541
  error = K8s::Error::NotFound.new(404, 'Not Found', 'LanguageAgent')
628
542
  Errors::Handler.handle_not_found(error,
629
543
  resource_type: 'LanguageAgent',
630
544
  resource_name: name,
631
- cluster: cluster,
545
+ cluster: ctx.name,
632
546
  available_resources: available_names)
633
547
  end
634
548
 
635
549
  def display_agent_created(agent, cluster, _description, synthesis_result)
636
- require 'pastel'
637
550
  require_relative '../formatters/code_formatter'
638
-
639
- pastel = Pastel.new
640
551
  agent_name = agent.dig('metadata', 'name')
641
552
 
642
553
  puts
@@ -645,10 +556,9 @@ module LanguageOperator
645
556
 
646
557
  # Get synthesized code if available
647
558
  begin
648
- cluster_config = Helpers::ClusterValidator.get_cluster_config(cluster)
649
- k8s = Helpers::ClusterValidator.kubernetes_client(cluster)
559
+ ctx = Helpers::ClusterContext.from_options(cluster: cluster)
650
560
  configmap_name = "#{agent_name}-code"
651
- configmap = k8s.get_resource('ConfigMap', configmap_name, cluster_config[:namespace])
561
+ configmap = ctx.client.get_resource('ConfigMap', configmap_name, ctx.namespace)
652
562
  code_content = configmap.dig('data', 'agent.rb')
653
563
 
654
564
  if code_content
@@ -832,21 +742,7 @@ module LanguageOperator
832
742
  end
833
743
 
834
744
  def format_status(status)
835
- require 'pastel'
836
- pastel = Pastel.new
837
-
838
- case status.downcase
839
- when 'ready', 'running', 'active'
840
- "#{pastel.green('●')} #{status}"
841
- when 'pending', 'creating', 'synthesizing'
842
- "#{pastel.yellow('●')} #{status}"
843
- when 'failed', 'error'
844
- "#{pastel.red('●')} #{status}"
845
- when 'paused', 'stopped'
846
- "#{pastel.dim('●')} #{status}"
847
- else
848
- "#{pastel.dim('●')} #{status}"
849
- end
745
+ Formatters::StatusFormatter.format(status)
850
746
  end
851
747
 
852
748
  def generate_agent_name(description)
@@ -940,13 +836,11 @@ module LanguageOperator
940
836
  end
941
837
 
942
838
  def list_cluster_agents(cluster)
943
- cluster_config = Helpers::ClusterValidator.get_cluster_config(cluster)
839
+ ctx = Helpers::ClusterContext.from_options(cluster: cluster)
944
840
 
945
841
  Formatters::ProgressFormatter.info("Agents in cluster '#{cluster}'")
946
842
 
947
- k8s = Helpers::ClusterValidator.kubernetes_client(cluster)
948
-
949
- agents = k8s.list_resources('LanguageAgent', namespace: cluster_config[:namespace])
843
+ agents = ctx.client.list_resources('LanguageAgent', namespace: ctx.namespace)
950
844
 
951
845
  table_data = agents.map do |agent|
952
846
  {
@@ -981,9 +875,9 @@ module LanguageOperator
981
875
  all_agents = []
982
876
 
983
877
  clusters.each do |cluster|
984
- k8s = Helpers::ClusterValidator.kubernetes_client(cluster[:name])
878
+ ctx = Helpers::ClusterContext.from_options(cluster: cluster[:name])
985
879
 
986
- agents = k8s.list_resources('LanguageAgent', namespace: cluster[:namespace])
880
+ agents = ctx.client.list_resources('LanguageAgent', namespace: ctx.namespace)
987
881
 
988
882
  agents.each do |agent|
989
883
  all_agents << {
@@ -1059,9 +953,6 @@ module LanguageOperator
1059
953
  end
1060
954
 
1061
955
  def list_workspace_files(ctx, agent_name)
1062
- require 'pastel'
1063
- pastel = Pastel.new
1064
-
1065
956
  pod_name = get_agent_pod(ctx, agent_name)
1066
957
 
1067
958
  # Check if workspace directory exists
@@ -1148,9 +1039,6 @@ module LanguageOperator
1148
1039
  end
1149
1040
 
1150
1041
  def view_workspace_file(ctx, agent_name, file_path)
1151
- require 'pastel'
1152
- pastel = Pastel.new
1153
-
1154
1042
  pod_name = get_agent_pod(ctx, agent_name)
1155
1043
 
1156
1044
  # Check if file exists
@@ -1191,9 +1079,6 @@ module LanguageOperator
1191
1079
  end
1192
1080
 
1193
1081
  def clean_workspace(ctx, agent_name)
1194
- require 'pastel'
1195
- pastel = Pastel.new
1196
-
1197
1082
  pod_name = get_agent_pod(ctx, agent_name)
1198
1083
 
1199
1084
  # Get current workspace usage