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.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +125 -0
  3. data/CHANGELOG.md +88 -0
  4. data/Gemfile +8 -0
  5. data/Gemfile.lock +284 -0
  6. data/LICENSE +229 -21
  7. data/Makefile +82 -0
  8. data/README.md +3 -11
  9. data/Rakefile +63 -0
  10. data/bin/aictl +7 -0
  11. data/completions/_aictl +232 -0
  12. data/completions/aictl.bash +121 -0
  13. data/completions/aictl.fish +114 -0
  14. data/docs/architecture/agent-runtime.md +585 -0
  15. data/docs/dsl/SCHEMA_VERSION.md +250 -0
  16. data/docs/dsl/agent-reference.md +604 -0
  17. data/docs/dsl/best-practices.md +1078 -0
  18. data/docs/dsl/chat-endpoints.md +895 -0
  19. data/docs/dsl/constraints.md +671 -0
  20. data/docs/dsl/mcp-integration.md +1177 -0
  21. data/docs/dsl/webhooks.md +932 -0
  22. data/docs/dsl/workflows.md +744 -0
  23. data/lib/language_operator/agent/base.rb +110 -0
  24. data/lib/language_operator/agent/executor.rb +440 -0
  25. data/lib/language_operator/agent/instrumentation.rb +54 -0
  26. data/lib/language_operator/agent/metrics_tracker.rb +183 -0
  27. data/lib/language_operator/agent/safety/ast_validator.rb +272 -0
  28. data/lib/language_operator/agent/safety/audit_logger.rb +104 -0
  29. data/lib/language_operator/agent/safety/budget_tracker.rb +175 -0
  30. data/lib/language_operator/agent/safety/content_filter.rb +93 -0
  31. data/lib/language_operator/agent/safety/manager.rb +207 -0
  32. data/lib/language_operator/agent/safety/rate_limiter.rb +150 -0
  33. data/lib/language_operator/agent/safety/safe_executor.rb +127 -0
  34. data/lib/language_operator/agent/scheduler.rb +183 -0
  35. data/lib/language_operator/agent/telemetry.rb +116 -0
  36. data/lib/language_operator/agent/web_server.rb +610 -0
  37. data/lib/language_operator/agent/webhook_authenticator.rb +226 -0
  38. data/lib/language_operator/agent.rb +149 -0
  39. data/lib/language_operator/cli/commands/agent.rb +1205 -0
  40. data/lib/language_operator/cli/commands/cluster.rb +371 -0
  41. data/lib/language_operator/cli/commands/install.rb +404 -0
  42. data/lib/language_operator/cli/commands/model.rb +266 -0
  43. data/lib/language_operator/cli/commands/persona.rb +393 -0
  44. data/lib/language_operator/cli/commands/quickstart.rb +22 -0
  45. data/lib/language_operator/cli/commands/status.rb +143 -0
  46. data/lib/language_operator/cli/commands/system.rb +772 -0
  47. data/lib/language_operator/cli/commands/tool.rb +537 -0
  48. data/lib/language_operator/cli/commands/use.rb +47 -0
  49. data/lib/language_operator/cli/errors/handler.rb +180 -0
  50. data/lib/language_operator/cli/errors/suggestions.rb +176 -0
  51. data/lib/language_operator/cli/formatters/code_formatter.rb +77 -0
  52. data/lib/language_operator/cli/formatters/log_formatter.rb +288 -0
  53. data/lib/language_operator/cli/formatters/progress_formatter.rb +49 -0
  54. data/lib/language_operator/cli/formatters/status_formatter.rb +37 -0
  55. data/lib/language_operator/cli/formatters/table_formatter.rb +163 -0
  56. data/lib/language_operator/cli/formatters/value_formatter.rb +113 -0
  57. data/lib/language_operator/cli/helpers/cluster_context.rb +62 -0
  58. data/lib/language_operator/cli/helpers/cluster_validator.rb +101 -0
  59. data/lib/language_operator/cli/helpers/editor_helper.rb +58 -0
  60. data/lib/language_operator/cli/helpers/kubeconfig_validator.rb +167 -0
  61. data/lib/language_operator/cli/helpers/pastel_helper.rb +24 -0
  62. data/lib/language_operator/cli/helpers/resource_dependency_checker.rb +74 -0
  63. data/lib/language_operator/cli/helpers/schedule_builder.rb +108 -0
  64. data/lib/language_operator/cli/helpers/user_prompts.rb +69 -0
  65. data/lib/language_operator/cli/main.rb +236 -0
  66. data/lib/language_operator/cli/templates/tools/generic.yaml +66 -0
  67. data/lib/language_operator/cli/wizards/agent_wizard.rb +246 -0
  68. data/lib/language_operator/cli/wizards/quickstart_wizard.rb +588 -0
  69. data/lib/language_operator/client/base.rb +214 -0
  70. data/lib/language_operator/client/config.rb +136 -0
  71. data/lib/language_operator/client/cost_calculator.rb +37 -0
  72. data/lib/language_operator/client/mcp_connector.rb +123 -0
  73. data/lib/language_operator/client.rb +19 -0
  74. data/lib/language_operator/config/cluster_config.rb +101 -0
  75. data/lib/language_operator/config/tool_patterns.yaml +57 -0
  76. data/lib/language_operator/config/tool_registry.rb +96 -0
  77. data/lib/language_operator/config.rb +138 -0
  78. data/lib/language_operator/dsl/adapter.rb +124 -0
  79. data/lib/language_operator/dsl/agent_context.rb +90 -0
  80. data/lib/language_operator/dsl/agent_definition.rb +427 -0
  81. data/lib/language_operator/dsl/chat_endpoint_definition.rb +115 -0
  82. data/lib/language_operator/dsl/config.rb +119 -0
  83. data/lib/language_operator/dsl/context.rb +50 -0
  84. data/lib/language_operator/dsl/execution_context.rb +47 -0
  85. data/lib/language_operator/dsl/helpers.rb +109 -0
  86. data/lib/language_operator/dsl/http.rb +184 -0
  87. data/lib/language_operator/dsl/mcp_server_definition.rb +73 -0
  88. data/lib/language_operator/dsl/parameter_definition.rb +124 -0
  89. data/lib/language_operator/dsl/registry.rb +36 -0
  90. data/lib/language_operator/dsl/schema.rb +1102 -0
  91. data/lib/language_operator/dsl/shell.rb +125 -0
  92. data/lib/language_operator/dsl/tool_definition.rb +112 -0
  93. data/lib/language_operator/dsl/webhook_authentication.rb +114 -0
  94. data/lib/language_operator/dsl/webhook_definition.rb +106 -0
  95. data/lib/language_operator/dsl/workflow_definition.rb +259 -0
  96. data/lib/language_operator/dsl.rb +161 -0
  97. data/lib/language_operator/errors.rb +60 -0
  98. data/lib/language_operator/kubernetes/client.rb +279 -0
  99. data/lib/language_operator/kubernetes/resource_builder.rb +194 -0
  100. data/lib/language_operator/loggable.rb +47 -0
  101. data/lib/language_operator/logger.rb +141 -0
  102. data/lib/language_operator/retry.rb +123 -0
  103. data/lib/language_operator/retryable.rb +132 -0
  104. data/lib/language_operator/templates/README.md +23 -0
  105. data/lib/language_operator/templates/examples/agent_synthesis.tmpl +115 -0
  106. data/lib/language_operator/templates/examples/persona_distillation.tmpl +19 -0
  107. data/lib/language_operator/templates/schema/.gitkeep +0 -0
  108. data/lib/language_operator/templates/schema/CHANGELOG.md +93 -0
  109. data/lib/language_operator/templates/schema/agent_dsl_openapi.yaml +306 -0
  110. data/lib/language_operator/templates/schema/agent_dsl_schema.json +452 -0
  111. data/lib/language_operator/tool_loader.rb +242 -0
  112. data/lib/language_operator/validators.rb +170 -0
  113. data/lib/language_operator/version.rb +1 -1
  114. data/lib/language_operator.rb +65 -3
  115. data/requirements/tasks/challenge.md +9 -0
  116. data/requirements/tasks/iterate.md +36 -0
  117. data/requirements/tasks/optimize.md +21 -0
  118. data/requirements/tasks/tag.md +5 -0
  119. data/test_agent_dsl.rb +108 -0
  120. metadata +507 -20
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'shellwords'
4
+ require 'open3'
5
+ require 'timeout'
6
+
7
+ module LanguageOperator
8
+ module Dsl
9
+ # Safe shell command execution for MCP tools
10
+ #
11
+ # Provides methods for executing shell commands safely with automatic
12
+ # argument escaping to prevent injection attacks. All methods are class methods.
13
+ #
14
+ # @example Basic usage
15
+ # result = Shell.run('ls', '-la', '/tmp')
16
+ # if result[:success]
17
+ # puts result[:output]
18
+ # end
19
+ #
20
+ # @example Safe user input
21
+ # # User input is automatically escaped
22
+ # result = Shell.run('grep', user_input, '/etc/hosts')
23
+ module Shell
24
+ # Run a shell command with properly escaped arguments
25
+ #
26
+ # This is safer than using backticks as it prevents shell injection.
27
+ # Arguments are automatically escaped using Shellwords.
28
+ #
29
+ # @param cmd [String] Command to execute
30
+ # @param args [Array<String>] Arguments (will be escaped)
31
+ # @param env [Hash] Environment variables to set
32
+ # @param chdir [String, nil] Working directory
33
+ # @param timeout [Integer] Timeout in seconds (default: 30)
34
+ # @return [Hash] Result with :success, :output, :error, :exitcode, :timeout keys
35
+ def self.run(cmd, *args, env: {}, chdir: nil, timeout: 30)
36
+ # Escape all arguments
37
+ escaped_args = args.map { |arg| Shellwords.escape(arg.to_s) }
38
+ full_cmd = "#{cmd} #{escaped_args.join(' ')}"
39
+
40
+ # Execute with timeout
41
+ stdout = nil
42
+ stderr = nil
43
+ status = nil
44
+
45
+ begin
46
+ Timeout.timeout(timeout) do
47
+ stdout, stderr, status = Open3.capture3(env, full_cmd, chdir: chdir)
48
+ end
49
+ rescue Timeout::Error
50
+ return {
51
+ success: false,
52
+ output: '',
53
+ error: "Command timed out after #{timeout} seconds",
54
+ exitcode: -1,
55
+ timeout: true
56
+ }
57
+ rescue StandardError => e
58
+ return {
59
+ success: false,
60
+ output: '',
61
+ error: e.message,
62
+ exitcode: -1
63
+ }
64
+ end
65
+
66
+ {
67
+ success: status.success?,
68
+ output: stdout,
69
+ error: stderr,
70
+ exitcode: status.exitstatus,
71
+ timeout: false
72
+ }
73
+ end
74
+
75
+ # Run a command and return only stdout (like backticks)
76
+ # Returns nil if the command fails
77
+ def self.capture(cmd, *, **)
78
+ result = run(cmd, *, **)
79
+ result[:success] ? result[:output] : nil
80
+ end
81
+
82
+ # Run a command and return stdout, raising on failure
83
+ def self.capture!(cmd, *, **)
84
+ result = run(cmd, *, **)
85
+ raise "Command failed (exit #{result[:exitcode]}): #{result[:error]}" unless result[:success]
86
+
87
+ result[:output]
88
+ end
89
+
90
+ # Check if a command exists in PATH
91
+ def self.command_exists?(cmd)
92
+ result = run('which', cmd)
93
+ result[:success]
94
+ end
95
+
96
+ # Run a command in the background and return immediately
97
+ # @deprecated This method has been removed for security reasons
98
+ # @raise [SecurityError] Always raises an error
99
+ def self.spawn(_cmd, *_args, env: {}, chdir: nil)
100
+ raise SecurityError, 'Shell.spawn has been removed for security reasons. Background process execution is not allowed in synthesized code.'
101
+ end
102
+
103
+ # Execute raw shell command (REMOVED FOR SECURITY)
104
+ # This method has been removed as it allowed arbitrary shell execution
105
+ # with pipes, redirects, etc. which is a security risk in synthesized code.
106
+ # @deprecated This method has been removed for security reasons
107
+ # @raise [SecurityError] Always raises an error
108
+ def self.raw(_command, env: {}, chdir: nil, timeout: 30)
109
+ raise SecurityError, 'Shell.raw has been removed for security reasons. Use Shell.run with explicit arguments instead.'
110
+ end
111
+
112
+ # Safely build a command string with escaped arguments
113
+ # Useful when you need to construct a command but not execute it yet
114
+ def self.build(cmd, *args)
115
+ escaped_args = args.map { |arg| Shellwords.escape(arg.to_s) }
116
+ "#{cmd} #{escaped_args.join(' ')}"
117
+ end
118
+
119
+ # Escape a single argument for shell usage
120
+ def self.escape(arg)
121
+ Shellwords.escape(arg.to_s)
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'parameter_definition'
4
+ require_relative 'http'
5
+ require_relative 'shell'
6
+ require_relative 'helpers'
7
+
8
+ module LanguageOperator
9
+ module Dsl
10
+ # Tool definition for MCP tools
11
+ #
12
+ # Defines an MCP tool with parameters, description, and execution logic.
13
+ # Used within the DSL to create tools that can be registered and served.
14
+ #
15
+ # @example Define a simple tool
16
+ # tool "greet" do
17
+ # description "Greet a user by name"
18
+ #
19
+ # parameter :name do
20
+ # type :string
21
+ # required true
22
+ # description "Name to greet"
23
+ # end
24
+ #
25
+ # execute do |params|
26
+ # "Hello, #{params['name']}!"
27
+ # end
28
+ # end
29
+ class ToolDefinition
30
+ include LanguageOperator::Dsl::Helpers
31
+
32
+ # Provide access to HTTP and Shell helper classes as constants
33
+ HTTP = LanguageOperator::Dsl::HTTP
34
+ Shell = LanguageOperator::Dsl::Shell
35
+
36
+ attr_reader :name, :parameters, :execute_block
37
+
38
+ def initialize(name)
39
+ @name = name
40
+ @parameters = {}
41
+ @execute_block = nil
42
+ @description = nil
43
+ end
44
+
45
+ def description(val = nil)
46
+ return @description if val.nil?
47
+
48
+ @description = val
49
+ end
50
+
51
+ def parameter(name, &)
52
+ param = ParameterDefinition.new(name)
53
+ param.instance_eval(&) if block_given?
54
+ @parameters[name.to_s] = param
55
+ end
56
+
57
+ def execute(&block)
58
+ @execute_block = block
59
+ end
60
+
61
+ def call(params)
62
+ log_debug "Calling tool '#{@name}' with params: #{params.inspect}"
63
+
64
+ # Apply default values for missing optional parameters
65
+ @parameters.each do |name, param_def|
66
+ default_value = param_def.instance_variable_get(:@default)
67
+ params[name] = default_value if !params.key?(name) && !default_value.nil?
68
+
69
+ # Validate required parameters
70
+ raise ArgumentError, "Missing required parameter: #{name}" if param_def.required? && !params.key?(name)
71
+
72
+ # Validate parameter format if validator is set and value is present
73
+ if params.key?(name)
74
+ error = param_def.validate_value(params[name])
75
+ raise ArgumentError, error if error
76
+ end
77
+ end
78
+
79
+ # Call the execute block with parameters
80
+ result = @execute_block.call(params) if @execute_block
81
+
82
+ log_debug "Tool '#{@name}' completed: #{truncate_for_log(result)}"
83
+ result
84
+ end
85
+
86
+ def to_schema
87
+ {
88
+ 'name' => @name,
89
+ 'description' => @description,
90
+ 'inputSchema' => {
91
+ 'type' => 'object',
92
+ 'properties' => @parameters.transform_values(&:to_schema),
93
+ 'required' => @parameters.select { |_, p| p.required? }.keys
94
+ }
95
+ }
96
+ end
97
+
98
+ private
99
+
100
+ def log_debug(message)
101
+ puts "[DEBUG] #{message}" if ENV['DEBUG'] || ENV['MCP_DEBUG']
102
+ end
103
+
104
+ def truncate_for_log(text)
105
+ return text.inspect if text.nil?
106
+
107
+ str = text.to_s
108
+ str.length > 100 ? "#{str[0..100]}..." : str
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LanguageOperator
4
+ module Dsl
5
+ # Defines authentication configuration for webhooks
6
+ class WebhookAuthentication
7
+ attr_reader :type, :config
8
+
9
+ def initialize
10
+ @type = nil
11
+ @config = {}
12
+ end
13
+
14
+ # Verify HMAC signature (GitHub/Stripe style)
15
+ # @param header [String] Header name containing the signature
16
+ # @param secret [String] Secret key for HMAC
17
+ # @param algorithm [Symbol] Hash algorithm (:sha1, :sha256, :sha512)
18
+ # @param prefix [String, nil] Optional prefix to strip from signature (e.g., "sha256=")
19
+ def verify_signature(header:, secret:, algorithm: :sha256, prefix: nil)
20
+ if @current_methods
21
+ # Inside any_of/all_of - create child auth object
22
+ auth = WebhookAuthentication.new
23
+ auth.verify_signature(header: header, secret: secret, algorithm: algorithm, prefix: prefix)
24
+ @current_methods << auth
25
+ else
26
+ @type = :signature
27
+ @config[:header] = header
28
+ @config[:secret] = secret
29
+ @config[:algorithm] = algorithm
30
+ @config[:prefix] = prefix
31
+ end
32
+ end
33
+
34
+ # Verify API key from header
35
+ # @param header [String] Header name containing the API key
36
+ # @param key [String] Expected API key value
37
+ def verify_api_key(header:, key:)
38
+ if @current_methods
39
+ auth = WebhookAuthentication.new
40
+ auth.verify_api_key(header: header, key: key)
41
+ @current_methods << auth
42
+ else
43
+ @type = :api_key
44
+ @config[:header] = header
45
+ @config[:key] = key
46
+ end
47
+ end
48
+
49
+ # Verify bearer token
50
+ # @param token [String] Expected bearer token value
51
+ def verify_bearer_token(token:)
52
+ if @current_methods
53
+ auth = WebhookAuthentication.new
54
+ auth.verify_bearer_token(token: token)
55
+ @current_methods << auth
56
+ else
57
+ @type = :bearer_token
58
+ @config[:token] = token
59
+ end
60
+ end
61
+
62
+ # Verify basic auth credentials
63
+ # @param username [String] Expected username
64
+ # @param password [String] Expected password
65
+ def verify_basic_auth(username:, password:)
66
+ if @current_methods
67
+ auth = WebhookAuthentication.new
68
+ auth.verify_basic_auth(username: username, password: password)
69
+ @current_methods << auth
70
+ else
71
+ @type = :basic_auth
72
+ @config[:username] = username
73
+ @config[:password] = password
74
+ end
75
+ end
76
+
77
+ # Custom authentication callback
78
+ # @yield [context] Block receives request context hash
79
+ # @yieldreturn [Boolean] true if authenticated, false otherwise
80
+ def verify_custom(&block)
81
+ if @current_methods
82
+ auth = WebhookAuthentication.new
83
+ auth.verify_custom(&block)
84
+ @current_methods << auth
85
+ else
86
+ @type = :custom
87
+ @config[:callback] = block
88
+ end
89
+ end
90
+
91
+ # Allow multiple authentication methods (any can succeed)
92
+ def any_of(&block)
93
+ @type = :any_of
94
+ @config[:methods] = []
95
+ @current_methods = @config[:methods]
96
+ instance_eval(&block) if block
97
+ @current_methods = nil
98
+ # Restore type in case it was overwritten by method calls
99
+ @type = :any_of
100
+ end
101
+
102
+ # Allow multiple authentication methods (all must succeed)
103
+ def all_of(&block)
104
+ @type = :all_of
105
+ @config[:methods] = []
106
+ @current_methods = @config[:methods]
107
+ instance_eval(&block) if block
108
+ @current_methods = nil
109
+ # Restore type in case it was overwritten by method calls
110
+ @type = :all_of
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'webhook_authentication'
4
+
5
+ module LanguageOperator
6
+ module Dsl
7
+ # Webhook Definition
8
+ #
9
+ # Defines webhook endpoints for reactive agents.
10
+ #
11
+ # @example Define a webhook with authentication
12
+ # webhook "/github/pr-opened" do
13
+ # method :post
14
+ # authenticate do
15
+ # verify_signature header: "X-Hub-Signature-256",
16
+ # secret: ENV['GITHUB_WEBHOOK_SECRET'],
17
+ # algorithm: :sha256,
18
+ # prefix: "sha256="
19
+ # end
20
+ # on_request do |context|
21
+ # # Handle the webhook
22
+ # end
23
+ # end
24
+ class WebhookDefinition
25
+ attr_reader :path, :http_method, :handler, :authentication, :validations
26
+
27
+ # Initialize webhook definition
28
+ #
29
+ # @param path [String] URL path for the webhook
30
+ def initialize(path)
31
+ @path = path
32
+ @http_method = :post
33
+ @handler = nil
34
+ @authentication = nil
35
+ @validations = []
36
+ end
37
+
38
+ # Set HTTP method
39
+ #
40
+ # @param method_name [Symbol] HTTP method (:get, :post, :put, :delete, :patch)
41
+ # @return [void]
42
+ def method(method_name)
43
+ @http_method = method_name
44
+ end
45
+
46
+ # Define authentication for this webhook
47
+ #
48
+ # @yield Authentication configuration block
49
+ # @return [void]
50
+ def authenticate(&block)
51
+ @authentication = WebhookAuthentication.new
52
+ @authentication.instance_eval(&block) if block
53
+ end
54
+
55
+ # Require specific headers
56
+ #
57
+ # @param headers [Hash] Required headers and their expected values (nil = just check presence)
58
+ # @return [void]
59
+ def require_headers(headers)
60
+ @validations << { type: :headers, config: headers }
61
+ end
62
+
63
+ # Require specific content type
64
+ #
65
+ # @param content_type [String, Array<String>] Allowed content type(s)
66
+ # @return [void]
67
+ def require_content_type(*content_types)
68
+ @validations << { type: :content_type, config: content_types.flatten }
69
+ end
70
+
71
+ # Add custom validation
72
+ #
73
+ # @yield [context] Validation block
74
+ # @yieldreturn [Boolean, String] true if valid, or error message string if invalid
75
+ # @return [void]
76
+ def validate(&block)
77
+ @validations << { type: :custom, config: block }
78
+ end
79
+
80
+ # Define the request handler
81
+ #
82
+ # @yield [context] Request handler block
83
+ # @yieldparam context [Hash] Request context with :path, :method, :headers, :params, :body
84
+ # @return [void]
85
+ def on_request(&block)
86
+ @handler = block
87
+ end
88
+
89
+ # Register this webhook with a web server
90
+ #
91
+ # @param web_server [LanguageOperator::Agent::WebServer] Web server instance
92
+ # @return [void]
93
+ def register(web_server)
94
+ return unless @handler
95
+
96
+ web_server.register_route(
97
+ @path,
98
+ method: @http_method,
99
+ authentication: @authentication,
100
+ validations: @validations,
101
+ &@handler
102
+ )
103
+ end
104
+ end
105
+ end
106
+ end