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.
- checksums.yaml +4 -4
- data/.rubocop.yml +7 -8
- data/CHANGELOG.md +49 -0
- data/CI_STATUS.md +56 -0
- data/Gemfile.lock +2 -2
- data/Makefile +28 -7
- data/Rakefile +29 -0
- data/docs/dsl/SCHEMA_VERSION.md +250 -0
- data/docs/dsl/agent-reference.md +13 -0
- data/lib/language_operator/agent/base.rb +10 -6
- data/lib/language_operator/agent/executor.rb +19 -97
- data/lib/language_operator/agent/safety/ast_validator.rb +62 -43
- data/lib/language_operator/agent/safety/safe_executor.rb +39 -2
- data/lib/language_operator/agent/scheduler.rb +60 -0
- data/lib/language_operator/agent/task_executor.rb +548 -0
- data/lib/language_operator/agent.rb +90 -27
- data/lib/language_operator/cli/base_command.rb +117 -0
- data/lib/language_operator/cli/commands/agent.rb +351 -466
- data/lib/language_operator/cli/commands/cluster.rb +276 -256
- data/lib/language_operator/cli/commands/install.rb +110 -119
- data/lib/language_operator/cli/commands/model.rb +284 -184
- data/lib/language_operator/cli/commands/persona.rb +220 -289
- data/lib/language_operator/cli/commands/quickstart.rb +4 -5
- data/lib/language_operator/cli/commands/status.rb +36 -53
- data/lib/language_operator/cli/commands/system.rb +760 -0
- data/lib/language_operator/cli/commands/tool.rb +356 -422
- data/lib/language_operator/cli/commands/use.rb +19 -22
- data/lib/language_operator/cli/formatters/code_formatter.rb +3 -7
- data/lib/language_operator/cli/formatters/log_formatter.rb +3 -5
- data/lib/language_operator/cli/formatters/progress_formatter.rb +3 -7
- data/lib/language_operator/cli/formatters/status_formatter.rb +37 -0
- data/lib/language_operator/cli/formatters/table_formatter.rb +10 -26
- data/lib/language_operator/cli/helpers/pastel_helper.rb +24 -0
- data/lib/language_operator/cli/helpers/resource_dependency_checker.rb +0 -18
- data/lib/language_operator/cli/main.rb +4 -0
- data/lib/language_operator/cli/wizards/quickstart_wizard.rb +0 -1
- data/lib/language_operator/client/config.rb +20 -21
- data/lib/language_operator/config.rb +115 -3
- data/lib/language_operator/constants.rb +54 -0
- data/lib/language_operator/dsl/agent_context.rb +7 -7
- data/lib/language_operator/dsl/agent_definition.rb +111 -26
- data/lib/language_operator/dsl/config.rb +30 -66
- data/lib/language_operator/dsl/main_definition.rb +114 -0
- data/lib/language_operator/dsl/schema.rb +1143 -0
- data/lib/language_operator/dsl/task_definition.rb +315 -0
- data/lib/language_operator/dsl.rb +1 -1
- data/lib/language_operator/instrumentation/task_tracer.rb +285 -0
- data/lib/language_operator/logger.rb +4 -4
- data/lib/language_operator/synthesis_test_harness.rb +324 -0
- data/lib/language_operator/templates/README.md +23 -0
- data/lib/language_operator/templates/examples/agent_synthesis.tmpl +133 -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 +119 -0
- data/lib/language_operator/templates/schema/agent_dsl_openapi.yaml +306 -0
- data/lib/language_operator/templates/schema/agent_dsl_schema.json +494 -0
- data/lib/language_operator/type_coercion.rb +250 -0
- data/lib/language_operator/ux/base.rb +81 -0
- data/lib/language_operator/ux/concerns/README.md +155 -0
- data/lib/language_operator/ux/concerns/headings.rb +90 -0
- data/lib/language_operator/ux/concerns/input_validation.rb +146 -0
- data/lib/language_operator/ux/concerns/provider_helpers.rb +167 -0
- data/lib/language_operator/ux/create_agent.rb +252 -0
- data/lib/language_operator/ux/create_model.rb +267 -0
- data/lib/language_operator/ux/quickstart.rb +594 -0
- data/lib/language_operator/version.rb +1 -1
- data/lib/language_operator.rb +2 -0
- data/requirements/ARCHITECTURE.md +1 -0
- data/requirements/SCRATCH.md +153 -0
- data/requirements/dsl.md +0 -0
- data/requirements/features +1 -0
- data/requirements/personas +1 -0
- data/requirements/proposals +1 -0
- data/requirements/tasks/iterate.md +14 -15
- data/requirements/tasks/optimize.md +13 -4
- data/synth/001/Makefile +90 -0
- data/synth/001/agent.rb +26 -0
- data/synth/001/agent.yaml +7 -0
- data/synth/001/output.log +44 -0
- data/synth/Makefile +39 -0
- data/synth/README.md +342 -0
- metadata +49 -18
- data/examples/README.md +0 -569
- data/examples/agent_example.rb +0 -86
- data/examples/chat_endpoint_agent.rb +0 -118
- data/examples/github_webhook_agent.rb +0 -171
- data/examples/mcp_agent.rb +0 -158
- data/examples/oauth_callback_agent.rb +0 -296
- data/examples/stripe_webhook_agent.rb +0 -219
- data/examples/webhook_agent.rb +0 -80
- data/lib/language_operator/dsl/workflow_definition.rb +0 -259
- 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 <
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
77
|
+
ctx = Helpers::ClusterContext.from_options(options.merge(cluster: cluster))
|
|
73
78
|
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
88
|
-
|
|
82
|
+
# Generate agent name from description if not provided
|
|
83
|
+
agent_name = options[:name] || generate_agent_name(description)
|
|
89
84
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
101
|
-
|
|
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
|
-
|
|
107
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
115
|
-
|
|
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
|
-
|
|
118
|
-
|
|
115
|
+
# Watch synthesis status
|
|
116
|
+
synthesis_result = watch_synthesis_status(ctx.client, agent_name, ctx.namespace)
|
|
119
117
|
|
|
120
|
-
|
|
121
|
-
|
|
118
|
+
# Exit if synthesis failed
|
|
119
|
+
exit 1 unless synthesis_result[:success]
|
|
122
120
|
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
153
|
-
|
|
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
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
puts "
|
|
196
|
-
|
|
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
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
puts " #{
|
|
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
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
|
|
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
|
-
|
|
240
|
+
handle_command_error('delete agent') do
|
|
241
|
+
ctx = Helpers::ClusterContext.from_options(options)
|
|
250
242
|
|
|
251
|
-
|
|
252
|
-
|
|
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
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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
|
-
|
|
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
|
-
|
|
296
|
-
|
|
276
|
+
handle_command_error('get logs') do
|
|
277
|
+
ctx = Helpers::ClusterContext.from_options(options)
|
|
297
278
|
|
|
298
|
-
|
|
279
|
+
# Get agent to determine the pod name
|
|
280
|
+
agent = get_resource_or_exit('LanguageAgent', name)
|
|
299
281
|
|
|
300
|
-
|
|
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
|
-
|
|
284
|
+
# Build kubectl command for log streaming
|
|
285
|
+
tail_arg = "--tail=#{options[:tail]}"
|
|
286
|
+
follow_arg = options[:follow] ? '-f' : ''
|
|
309
287
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
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
|
-
|
|
327
|
-
|
|
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
|
-
|
|
330
|
-
|
|
300
|
+
Formatters::ProgressFormatter.info("Streaming logs for agent '#{name}'...")
|
|
301
|
+
puts
|
|
331
302
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
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
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
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
|
-
|
|
351
|
-
|
|
352
|
-
|
|
321
|
+
# Wait for both streams to complete
|
|
322
|
+
stdout_thread.join
|
|
323
|
+
stderr_thread.join
|
|
353
324
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
-
|
|
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
|
-
|
|
338
|
+
ctx = Helpers::ClusterContext.from_options(options)
|
|
374
339
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
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
|
-
|
|
399
|
-
|
|
400
|
-
code_content
|
|
401
|
-
|
|
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
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
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
|
-
|
|
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
|
-
|
|
418
|
-
|
|
378
|
+
handle_command_error('edit agent') do
|
|
379
|
+
ctx = Helpers::ClusterContext.from_options(options)
|
|
419
380
|
|
|
420
|
-
|
|
381
|
+
# Get current agent
|
|
382
|
+
agent = get_resource_or_exit('LanguageAgent', name)
|
|
421
383
|
|
|
422
|
-
|
|
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
|
-
|
|
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
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
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
|
-
|
|
440
|
-
|
|
441
|
-
Formatters::ProgressFormatter.info('No changes made')
|
|
442
|
-
return
|
|
443
|
-
end
|
|
399
|
+
# Update agent resource
|
|
400
|
+
agent['spec']['instructions'] = new_instructions
|
|
444
401
|
|
|
445
|
-
|
|
446
|
-
|
|
402
|
+
Formatters::ProgressFormatter.with_spinner('Updating agent instructions') do
|
|
403
|
+
ctx.client.apply_resource(agent)
|
|
404
|
+
end
|
|
447
405
|
|
|
448
|
-
|
|
449
|
-
|
|
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
|
-
|
|
469
|
-
|
|
418
|
+
handle_command_error('pause agent') do
|
|
419
|
+
ctx = Helpers::ClusterContext.from_options(options)
|
|
470
420
|
|
|
471
|
-
|
|
421
|
+
# Get agent
|
|
422
|
+
agent = get_resource_or_exit('LanguageAgent', name)
|
|
472
423
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
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
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
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
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
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
|
-
|
|
501
|
-
|
|
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
|
-
|
|
521
|
-
|
|
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
|
-
|
|
526
|
-
|
|
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
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
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
|
-
|
|
542
|
-
|
|
543
|
-
|
|
470
|
+
# Resume the CronJob by setting spec.suspend = false
|
|
471
|
+
cronjob_name = name
|
|
472
|
+
ctx.namespace
|
|
544
473
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
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
|
-
|
|
551
|
-
|
|
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
|
-
|
|
505
|
+
handle_command_error('access workspace') do
|
|
506
|
+
ctx = Helpers::ClusterContext.from_options(options)
|
|
584
507
|
|
|
585
|
-
|
|
586
|
-
|
|
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
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
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
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
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,
|
|
536
|
+
def handle_agent_not_found(name, ctx)
|
|
623
537
|
# Get available agents for fuzzy matching
|
|
624
|
-
agents =
|
|
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:
|
|
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
|
-
|
|
649
|
-
k8s = Helpers::ClusterValidator.kubernetes_client(cluster)
|
|
559
|
+
ctx = Helpers::ClusterContext.from_options(cluster: cluster)
|
|
650
560
|
configmap_name = "#{agent_name}-code"
|
|
651
|
-
configmap =
|
|
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
|
-
|
|
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
|
-
|
|
839
|
+
ctx = Helpers::ClusterContext.from_options(cluster: cluster)
|
|
944
840
|
|
|
945
841
|
Formatters::ProgressFormatter.info("Agents in cluster '#{cluster}'")
|
|
946
842
|
|
|
947
|
-
|
|
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
|
-
|
|
878
|
+
ctx = Helpers::ClusterContext.from_options(cluster: cluster[:name])
|
|
985
879
|
|
|
986
|
-
agents =
|
|
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
|