ukiryu 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/docs.yml +63 -0
- data/.github/workflows/links.yml +99 -0
- data/.github/workflows/rake.yml +19 -0
- data/.github/workflows/release.yml +27 -0
- data/.gitignore +18 -4
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +213 -0
- data/Gemfile +12 -8
- data/README.adoc +613 -0
- data/Rakefile +2 -2
- data/docs/assets/logo.svg +1 -0
- data/exe/ukiryu +11 -0
- data/lib/ukiryu/action/base.rb +77 -0
- data/lib/ukiryu/cache.rb +199 -0
- data/lib/ukiryu/cli.rb +133 -307
- data/lib/ukiryu/cli_commands/base_command.rb +155 -0
- data/lib/ukiryu/cli_commands/commands_command.rb +120 -0
- data/lib/ukiryu/cli_commands/commands_command.rb.fixed +40 -0
- data/lib/ukiryu/cli_commands/config_command.rb +249 -0
- data/lib/ukiryu/cli_commands/describe_command.rb +326 -0
- data/lib/ukiryu/cli_commands/describe_command.rb.fixed +254 -0
- data/lib/ukiryu/cli_commands/exec_inline_command.rb.fixed +180 -0
- data/lib/ukiryu/cli_commands/extract_command.rb +84 -0
- data/lib/ukiryu/cli_commands/info_command.rb +156 -0
- data/lib/ukiryu/cli_commands/list_command.rb +70 -0
- data/lib/ukiryu/cli_commands/opts_command.rb +106 -0
- data/lib/ukiryu/cli_commands/opts_command.rb.fixed +105 -0
- data/lib/ukiryu/cli_commands/response_formatter.rb +240 -0
- data/lib/ukiryu/cli_commands/run_command.rb +375 -0
- data/lib/ukiryu/cli_commands/run_file_command.rb +215 -0
- data/lib/ukiryu/cli_commands/system_command.rb +90 -0
- data/lib/ukiryu/cli_commands/validate_command.rb +87 -0
- data/lib/ukiryu/cli_commands/version_command.rb +16 -0
- data/lib/ukiryu/cli_commands/which_command.rb +166 -0
- data/lib/ukiryu/command_builder.rb +205 -0
- data/lib/ukiryu/config/env_provider.rb +64 -0
- data/lib/ukiryu/config/env_schema.rb +63 -0
- data/lib/ukiryu/config/override_resolver.rb +68 -0
- data/lib/ukiryu/config/type_converter.rb +59 -0
- data/lib/ukiryu/config.rb +249 -0
- data/lib/ukiryu/errors.rb +3 -0
- data/lib/ukiryu/executable_locator.rb +114 -0
- data/lib/ukiryu/execution/command_info.rb +64 -0
- data/lib/ukiryu/execution/metadata.rb +97 -0
- data/lib/ukiryu/execution/output.rb +144 -0
- data/lib/ukiryu/execution/result.rb +194 -0
- data/lib/ukiryu/execution.rb +15 -0
- data/lib/ukiryu/execution_context.rb +251 -0
- data/lib/ukiryu/executor.rb +76 -493
- data/lib/ukiryu/extractors/base_extractor.rb +63 -0
- data/lib/ukiryu/extractors/extractor.rb +150 -0
- data/lib/ukiryu/extractors/help_parser.rb +188 -0
- data/lib/ukiryu/extractors/native_extractor.rb +47 -0
- data/lib/ukiryu/io.rb +196 -0
- data/lib/ukiryu/logger.rb +544 -0
- data/lib/ukiryu/models/argument.rb +28 -0
- data/lib/ukiryu/models/argument_definition.rb +119 -0
- data/lib/ukiryu/models/arguments.rb +113 -0
- data/lib/ukiryu/models/command_definition.rb +176 -0
- data/lib/ukiryu/models/command_info.rb +37 -0
- data/lib/ukiryu/models/components.rb +107 -0
- data/lib/ukiryu/models/env_var_definition.rb +30 -0
- data/lib/ukiryu/models/error_response.rb +41 -0
- data/lib/ukiryu/models/execution_metadata.rb +31 -0
- data/lib/ukiryu/models/execution_report.rb +236 -0
- data/lib/ukiryu/models/exit_codes.rb +74 -0
- data/lib/ukiryu/models/flag_definition.rb +67 -0
- data/lib/ukiryu/models/option_definition.rb +102 -0
- data/lib/ukiryu/models/output_info.rb +25 -0
- data/lib/ukiryu/models/platform_profile.rb +153 -0
- data/lib/ukiryu/models/routing.rb +211 -0
- data/lib/ukiryu/models/search_paths.rb +39 -0
- data/lib/ukiryu/models/success_response.rb +85 -0
- data/lib/ukiryu/models/tool_definition.rb +145 -0
- data/lib/ukiryu/models/tool_metadata.rb +82 -0
- data/lib/ukiryu/models/validation_result.rb +80 -0
- data/lib/ukiryu/models/version_compatibility.rb +152 -0
- data/lib/ukiryu/models/version_detection.rb +39 -0
- data/lib/ukiryu/models.rb +23 -0
- data/lib/ukiryu/options/base.rb +95 -0
- data/lib/ukiryu/options_builder/formatter.rb +87 -0
- data/lib/ukiryu/options_builder/validator.rb +43 -0
- data/lib/ukiryu/options_builder.rb +311 -0
- data/lib/ukiryu/platform.rb +6 -6
- data/lib/ukiryu/registry.rb +143 -183
- data/lib/ukiryu/response/base.rb +217 -0
- data/lib/ukiryu/runtime.rb +179 -0
- data/lib/ukiryu/schema_validator.rb +8 -10
- data/lib/ukiryu/shell/bash.rb +3 -3
- data/lib/ukiryu/shell/cmd.rb +4 -4
- data/lib/ukiryu/shell/fish.rb +1 -1
- data/lib/ukiryu/shell/powershell.rb +3 -3
- data/lib/ukiryu/shell/sh.rb +1 -1
- data/lib/ukiryu/shell/zsh.rb +1 -1
- data/lib/ukiryu/shell.rb +146 -39
- data/lib/ukiryu/thor_ext.rb +208 -0
- data/lib/ukiryu/tool.rb +649 -258
- data/lib/ukiryu/tool_index.rb +224 -0
- data/lib/ukiryu/tools/base.rb +381 -0
- data/lib/ukiryu/tools/class_generator.rb +132 -0
- data/lib/ukiryu/tools/executable_finder.rb +29 -0
- data/lib/ukiryu/tools/generator.rb +154 -0
- data/lib/ukiryu/tools.rb +109 -0
- data/lib/ukiryu/type.rb +28 -43
- data/lib/ukiryu/validation/constraints.rb +281 -0
- data/lib/ukiryu/validation/validator.rb +188 -0
- data/lib/ukiryu/validation.rb +21 -0
- data/lib/ukiryu/version.rb +1 -1
- data/lib/ukiryu/version_detector.rb +51 -0
- data/lib/ukiryu.rb +31 -15
- data/ukiryu-proposal.md +2952 -0
- data/ukiryu.gemspec +18 -14
- metadata +137 -5
- data/.github/workflows/test.yml +0 -143
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'lutaml/model'
|
|
4
|
+
require_relative 'argument'
|
|
5
|
+
|
|
6
|
+
module Ukiryu
|
|
7
|
+
module Models
|
|
8
|
+
# Structured command arguments
|
|
9
|
+
#
|
|
10
|
+
# Contains the structured arguments passed to a command execution.
|
|
11
|
+
# Each argument has a name, value, and type.
|
|
12
|
+
class Arguments < Lutaml::Model::Serializable
|
|
13
|
+
attribute :options, Argument, collection: true, initialize_empty: true
|
|
14
|
+
attribute :flags, :string, collection: true, default: []
|
|
15
|
+
attribute :positional, Argument, collection: true, initialize_empty: true
|
|
16
|
+
|
|
17
|
+
yaml do
|
|
18
|
+
map_element 'options', to: :options
|
|
19
|
+
map_element 'flags', to: :flags
|
|
20
|
+
map_element 'positional', to: :positional
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
json do
|
|
24
|
+
map 'options', to: :options
|
|
25
|
+
map 'flags', to: :flags
|
|
26
|
+
map 'positional', to: :positional
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Create Arguments from a hash of parameters
|
|
30
|
+
#
|
|
31
|
+
# @param params [Hash] the parameters hash
|
|
32
|
+
# @param command_definition [CommandDefinition] the command definition for context
|
|
33
|
+
# @return [Arguments] the arguments model
|
|
34
|
+
def self.from_params(params, command_definition = nil)
|
|
35
|
+
options_arr = []
|
|
36
|
+
flags_arr = []
|
|
37
|
+
positional_arr = []
|
|
38
|
+
|
|
39
|
+
params.each do |key, value|
|
|
40
|
+
# Determine argument type based on command definition or heuristics
|
|
41
|
+
arg_type = determine_argument_type(key, value, command_definition)
|
|
42
|
+
|
|
43
|
+
case arg_type
|
|
44
|
+
when :flag
|
|
45
|
+
# Boolean flags
|
|
46
|
+
flags_arr << key.to_s if value
|
|
47
|
+
when :option
|
|
48
|
+
# Named options with values
|
|
49
|
+
options_arr << Argument.new(name: key.to_s, value: stringify_value(value), type: 'option')
|
|
50
|
+
when :positional
|
|
51
|
+
# Positional arguments
|
|
52
|
+
positional_arr << Argument.new(name: key.to_s, value: stringify_value(value), type: 'positional')
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Set arrays directly to ensure proper serialization
|
|
57
|
+
args = new
|
|
58
|
+
args.options = options_arr unless options_arr.empty?
|
|
59
|
+
args.flags = flags_arr unless flags_arr.empty?
|
|
60
|
+
args.positional = positional_arr unless positional_arr.empty?
|
|
61
|
+
|
|
62
|
+
args
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Determine the type of an argument
|
|
66
|
+
#
|
|
67
|
+
# @param key [Symbol] the argument key
|
|
68
|
+
# @param value [Object] the argument value
|
|
69
|
+
# @param command_definition [CommandDefinition] the command definition
|
|
70
|
+
# @return [Symbol] :option, :flag, or :positional
|
|
71
|
+
def self.determine_argument_type(key, value, command_definition)
|
|
72
|
+
# Check command definition if available
|
|
73
|
+
if command_definition
|
|
74
|
+
flag_def = command_definition.flags&.find { |f| f.name.to_sym == key }
|
|
75
|
+
return :flag if flag_def
|
|
76
|
+
|
|
77
|
+
option_def = command_definition.options&.find { |o| o.name.to_sym == key }
|
|
78
|
+
return :option if option_def
|
|
79
|
+
|
|
80
|
+
arg_def = command_definition.arguments&.find { |a| a.name.to_sym == key }
|
|
81
|
+
return :positional if arg_def
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Fall back to heuristics
|
|
85
|
+
if value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
|
86
|
+
:flag
|
|
87
|
+
elsif value.is_a?(Array)
|
|
88
|
+
# Arrays are typically options (like inputs)
|
|
89
|
+
:option
|
|
90
|
+
else
|
|
91
|
+
:option
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Convert a value to string for serialization
|
|
96
|
+
#
|
|
97
|
+
# @param value [Object] the value
|
|
98
|
+
# @return [String] string representation
|
|
99
|
+
def self.stringify_value(value)
|
|
100
|
+
case value
|
|
101
|
+
when Array
|
|
102
|
+
value.map(&:to_s).join(', ')
|
|
103
|
+
when Symbol, Integer, Float, TrueClass, FalseClass
|
|
104
|
+
value.to_s
|
|
105
|
+
when NilClass
|
|
106
|
+
''
|
|
107
|
+
else
|
|
108
|
+
value.to_s
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'lutaml/model'
|
|
4
|
+
require_relative 'option_definition'
|
|
5
|
+
require_relative 'flag_definition'
|
|
6
|
+
require_relative 'argument_definition'
|
|
7
|
+
require_relative 'env_var_definition'
|
|
8
|
+
require_relative 'exit_codes'
|
|
9
|
+
|
|
10
|
+
module Ukiryu
|
|
11
|
+
module Models
|
|
12
|
+
# Command definition for a tool
|
|
13
|
+
#
|
|
14
|
+
# @example
|
|
15
|
+
# cmd = CommandDefinition.new(
|
|
16
|
+
# name: 'convert',
|
|
17
|
+
# description: 'Convert image format',
|
|
18
|
+
# options: [OptionDefinition.new(...)],
|
|
19
|
+
# flags: [FlagDefinition.new(...)]
|
|
20
|
+
# )
|
|
21
|
+
class CommandDefinition < Lutaml::Model::Serializable
|
|
22
|
+
attribute :name, :string
|
|
23
|
+
attribute :description, :string
|
|
24
|
+
attribute :usage, :string
|
|
25
|
+
attribute :subcommand, :string
|
|
26
|
+
attribute :execution_mode, :string
|
|
27
|
+
attribute :belongs_to, :string # Parent command this action belongs to
|
|
28
|
+
attribute :cli_flag, :string # CLI flag for this action (e.g., '-d' for delete)
|
|
29
|
+
attribute :aliases, :string, collection: true, default: []
|
|
30
|
+
|
|
31
|
+
# Collections of model objects (lutaml-model handles serialization automatically)
|
|
32
|
+
attribute :options, OptionDefinition, collection: true
|
|
33
|
+
attribute :flags, FlagDefinition, collection: true
|
|
34
|
+
attribute :arguments, ArgumentDefinition, collection: true
|
|
35
|
+
attribute :post_options, OptionDefinition, collection: true
|
|
36
|
+
attribute :env_vars, EnvVarDefinition, collection: true
|
|
37
|
+
attribute :exit_codes, ExitCodes # Exit code definitions for this command
|
|
38
|
+
|
|
39
|
+
yaml do
|
|
40
|
+
map_element 'name', to: :name
|
|
41
|
+
map_element 'description', to: :description
|
|
42
|
+
map_element 'usage', to: :usage
|
|
43
|
+
map_element 'subcommand', to: :subcommand
|
|
44
|
+
map_element 'options', to: :options
|
|
45
|
+
map_element 'flags', to: :flags
|
|
46
|
+
map_element 'arguments', to: :arguments
|
|
47
|
+
map_element 'post_options', to: :post_options
|
|
48
|
+
map_element 'env_vars', to: :env_vars
|
|
49
|
+
map_element 'exit_codes', to: :exit_codes
|
|
50
|
+
map_element 'execution_mode', to: :execution_mode
|
|
51
|
+
map_element 'belongs_to', to: :belongs_to
|
|
52
|
+
map_element 'cli_flag', to: :cli_flag
|
|
53
|
+
map_element 'aliases', to: :aliases
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Check if this command/action belongs to a parent command
|
|
57
|
+
#
|
|
58
|
+
# @return [Boolean] true if belongs_to is set
|
|
59
|
+
def belongs_to_command?
|
|
60
|
+
!belongs_to.nil? && !belongs_to.empty?
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Check if this action is expressed as a flag
|
|
64
|
+
#
|
|
65
|
+
# @return [Boolean] true if cli_flag is set
|
|
66
|
+
def flag_action?
|
|
67
|
+
!cli_flag.nil? && !cli_flag.empty?
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Get an option by name using indexed O(1) lookup
|
|
71
|
+
#
|
|
72
|
+
# @param name [String, Symbol] the option name
|
|
73
|
+
# @return [OptionDefinition, nil] the option
|
|
74
|
+
def option(name)
|
|
75
|
+
return nil unless options
|
|
76
|
+
|
|
77
|
+
build_options_index unless @options_index_built
|
|
78
|
+
@options_index[name.to_s]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Get a flag by name using indexed O(1) lookup
|
|
82
|
+
#
|
|
83
|
+
# @param name [String, Symbol] the flag name
|
|
84
|
+
# @return [FlagDefinition, nil] the flag
|
|
85
|
+
def flag(name)
|
|
86
|
+
return nil unless flags
|
|
87
|
+
|
|
88
|
+
build_flags_index unless @flags_index_built
|
|
89
|
+
@flags_index[name.to_s]
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Get an argument by name using indexed O(1) lookup
|
|
93
|
+
#
|
|
94
|
+
# @param name [String, Symbol] the argument name
|
|
95
|
+
# @return [ArgumentDefinition, nil] the argument
|
|
96
|
+
def argument(name)
|
|
97
|
+
return nil unless arguments
|
|
98
|
+
|
|
99
|
+
build_arguments_index unless @arguments_index_built
|
|
100
|
+
@arguments_index[name.to_s]
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Get the last argument
|
|
104
|
+
#
|
|
105
|
+
# @return [ArgumentDefinition, nil] the last argument
|
|
106
|
+
def last_argument
|
|
107
|
+
return nil unless arguments
|
|
108
|
+
|
|
109
|
+
arguments.find { |a| a.is_a?(ArgumentDefinition) && a.last? }
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Get regular arguments (not last)
|
|
113
|
+
#
|
|
114
|
+
# @return [Array<ArgumentDefinition>] regular arguments sorted by position
|
|
115
|
+
def regular_arguments
|
|
116
|
+
return [] unless arguments
|
|
117
|
+
|
|
118
|
+
args = arguments.select { |a| a.is_a?(ArgumentDefinition) }
|
|
119
|
+
args.reject(&:last?).sort_by(&:numeric_position)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Check if command has a subcommand
|
|
123
|
+
#
|
|
124
|
+
# @return [Boolean] true if has subcommand
|
|
125
|
+
def has_subcommand?
|
|
126
|
+
!subcommand.nil? && !subcommand.empty?
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Clear all indexes
|
|
130
|
+
#
|
|
131
|
+
# Call this if collections are modified after initial loading
|
|
132
|
+
#
|
|
133
|
+
# @api private
|
|
134
|
+
def clear_indexes!
|
|
135
|
+
@options_index = nil
|
|
136
|
+
@options_index_built = false
|
|
137
|
+
@flags_index = nil
|
|
138
|
+
@flags_index_built = false
|
|
139
|
+
@arguments_index = nil
|
|
140
|
+
@arguments_index_built = false
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
private
|
|
144
|
+
|
|
145
|
+
# Build the options index hash for O(1) lookup
|
|
146
|
+
#
|
|
147
|
+
# @api private
|
|
148
|
+
def build_options_index
|
|
149
|
+
return unless options
|
|
150
|
+
|
|
151
|
+
@options_index = options.to_h { |o| [o.name, o] }
|
|
152
|
+
@options_index_built = true
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Build the flags index hash for O(1) lookup
|
|
156
|
+
#
|
|
157
|
+
# @api private
|
|
158
|
+
def build_flags_index
|
|
159
|
+
return unless flags
|
|
160
|
+
|
|
161
|
+
@flags_index = flags.to_h { |f| [f.name, f] }
|
|
162
|
+
@flags_index_built = true
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Build the arguments index hash for O(1) lookup
|
|
166
|
+
#
|
|
167
|
+
# @api private
|
|
168
|
+
def build_arguments_index
|
|
169
|
+
return unless arguments
|
|
170
|
+
|
|
171
|
+
@arguments_index = arguments.to_h { |a| [a.name, a] }
|
|
172
|
+
@arguments_index_built = true
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'lutaml/model'
|
|
4
|
+
require_relative 'arguments'
|
|
5
|
+
|
|
6
|
+
module Ukiryu
|
|
7
|
+
module Models
|
|
8
|
+
# Command execution information
|
|
9
|
+
#
|
|
10
|
+
# Contains details about the executed command including
|
|
11
|
+
# the executable path, structured arguments, full command string, and shell used.
|
|
12
|
+
class CommandInfo < Lutaml::Model::Serializable
|
|
13
|
+
attribute :executable, :string
|
|
14
|
+
attribute :executable_name, :string
|
|
15
|
+
attribute :tool_name, :string # Name of the tool being executed
|
|
16
|
+
attribute :arguments, Arguments
|
|
17
|
+
attribute :full_command, :string
|
|
18
|
+
attribute :shell, :string
|
|
19
|
+
|
|
20
|
+
yaml do
|
|
21
|
+
map_element 'executable', to: :executable
|
|
22
|
+
map_element 'executable_name', to: :executable_name
|
|
23
|
+
map_element 'tool_name', to: :tool_name
|
|
24
|
+
map_element 'arguments', to: :arguments
|
|
25
|
+
map_element 'full_command', to: :full_command
|
|
26
|
+
map_element 'shell', to: :shell
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
json do
|
|
30
|
+
map 'executable', to: :executable
|
|
31
|
+
map 'arguments', to: :arguments
|
|
32
|
+
map 'full_command', to: :full_command
|
|
33
|
+
map 'shell', to: :shell
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'lutaml/model'
|
|
4
|
+
require_relative 'option_definition'
|
|
5
|
+
require_relative 'flag_definition'
|
|
6
|
+
require_relative 'argument_definition'
|
|
7
|
+
require_relative 'exit_codes'
|
|
8
|
+
|
|
9
|
+
module Ukiryu
|
|
10
|
+
module Models
|
|
11
|
+
# Components registry for reusable definitions
|
|
12
|
+
#
|
|
13
|
+
# Enables sharing common option/argument/flag/exit_codes definitions
|
|
14
|
+
# across commands through `$ref` references.
|
|
15
|
+
#
|
|
16
|
+
# @example
|
|
17
|
+
# components = Components.new(
|
|
18
|
+
# options: { 'verbose' => OptionDefinition.new(...) },
|
|
19
|
+
# flags: { 'help' => FlagDefinition.new(...) }
|
|
20
|
+
# )
|
|
21
|
+
class Components < Lutaml::Model::Serializable
|
|
22
|
+
attribute :options, :hash, default: {}
|
|
23
|
+
attribute :flags, :hash, default: {}
|
|
24
|
+
attribute :arguments, :hash, default: {}
|
|
25
|
+
attribute :exit_codes, ExitCodes
|
|
26
|
+
|
|
27
|
+
yaml do
|
|
28
|
+
map_element 'options', to: :options
|
|
29
|
+
map_element 'flags', to: :flags
|
|
30
|
+
map_element 'arguments', to: :arguments
|
|
31
|
+
map_element 'exit_codes', to: :exit_codes
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Get an option by name
|
|
35
|
+
#
|
|
36
|
+
# @param name [String, Symbol] the option name
|
|
37
|
+
# @return [OptionDefinition, nil] the option definition
|
|
38
|
+
def option(name)
|
|
39
|
+
@options&.dig(name.to_s)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Get a flag by name
|
|
43
|
+
#
|
|
44
|
+
# @param name [String, Symbol] the flag name
|
|
45
|
+
# @return [FlagDefinition, nil] the flag definition
|
|
46
|
+
def flag(name)
|
|
47
|
+
@flags&.dig(name.to_s)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Get an argument by name
|
|
51
|
+
#
|
|
52
|
+
# @param name [String, Symbol] the argument name
|
|
53
|
+
# @return [ArgumentDefinition, nil] the argument definition
|
|
54
|
+
def argument(name)
|
|
55
|
+
@arguments&.dig(name.to_s)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Check if a reference can be resolved
|
|
59
|
+
#
|
|
60
|
+
# @param ref [String] the reference path (e.g., '#/components/options/verbose')
|
|
61
|
+
# @return [Boolean] true if the reference can be resolved
|
|
62
|
+
def can_resolve?(ref)
|
|
63
|
+
return false unless ref =~ %r{^#/components/(options|flags|arguments|exit_codes)/(.+)$}
|
|
64
|
+
|
|
65
|
+
type = Regexp.last_match(1)
|
|
66
|
+
name = Regexp.last_match(2)
|
|
67
|
+
|
|
68
|
+
case type
|
|
69
|
+
when 'options'
|
|
70
|
+
@options&.key?(name)
|
|
71
|
+
when 'flags'
|
|
72
|
+
@flags&.key?(name)
|
|
73
|
+
when 'arguments'
|
|
74
|
+
@arguments&.key?(name)
|
|
75
|
+
when 'exit_codes'
|
|
76
|
+
!@exit_codes.nil?
|
|
77
|
+
else
|
|
78
|
+
false
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Resolve a reference path to a component
|
|
83
|
+
#
|
|
84
|
+
# @param ref [String] the reference path (e.g., '#/components/options/verbose')
|
|
85
|
+
# @return [Object, nil] the component or nil if not found
|
|
86
|
+
def resolve(ref)
|
|
87
|
+
return nil unless ref =~ %r{^#/components/(options|flags|arguments|exit_codes)/(.+)$}
|
|
88
|
+
|
|
89
|
+
type = Regexp.last_match(1)
|
|
90
|
+
name = Regexp.last_match(2)
|
|
91
|
+
|
|
92
|
+
case type
|
|
93
|
+
when 'options'
|
|
94
|
+
option(name)
|
|
95
|
+
when 'flags'
|
|
96
|
+
flag(name)
|
|
97
|
+
when 'arguments'
|
|
98
|
+
argument(name)
|
|
99
|
+
when 'exit_codes'
|
|
100
|
+
@exit_codes
|
|
101
|
+
else
|
|
102
|
+
nil
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'lutaml/model'
|
|
4
|
+
|
|
5
|
+
module Ukiryu
|
|
6
|
+
module Models
|
|
7
|
+
# Environment variable definition for a command
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# env_var = EnvVarDefinition.new(
|
|
11
|
+
# name: 'DISPLAY',
|
|
12
|
+
# env_var: 'DISPLAY',
|
|
13
|
+
# value: '',
|
|
14
|
+
# platforms: [:linux, :macos]
|
|
15
|
+
# )
|
|
16
|
+
class EnvVarDefinition < Lutaml::Model::Serializable
|
|
17
|
+
attribute :name, :string
|
|
18
|
+
attribute :env_var, :string
|
|
19
|
+
attribute :value, :string
|
|
20
|
+
attribute :platforms, :string, collection: true, default: []
|
|
21
|
+
|
|
22
|
+
yaml do
|
|
23
|
+
map_element 'name', to: :name
|
|
24
|
+
map_element 'env_var', to: :env_var
|
|
25
|
+
map_element 'value', to: :value
|
|
26
|
+
map_element 'platforms', to: :platforms
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'lutaml/model'
|
|
4
|
+
|
|
5
|
+
module Ukiryu
|
|
6
|
+
module Models
|
|
7
|
+
# Error CLI execution response
|
|
8
|
+
#
|
|
9
|
+
# Contains error information when a command execution fails.
|
|
10
|
+
class ErrorResponse < Lutaml::Model::Serializable
|
|
11
|
+
attribute :status, :string, default: 'error'
|
|
12
|
+
attribute :exit_code, :integer, default: 1
|
|
13
|
+
attribute :error, :string
|
|
14
|
+
|
|
15
|
+
yaml do
|
|
16
|
+
map_element 'status', to: :status
|
|
17
|
+
map_element 'exit_code', to: :exit_code
|
|
18
|
+
map_element 'error', to: :error
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
json do
|
|
22
|
+
map 'status', to: :status
|
|
23
|
+
map 'exit_code', to: :exit_code
|
|
24
|
+
map 'error', to: :error
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Create an ErrorResponse from an error message
|
|
28
|
+
#
|
|
29
|
+
# @param message [String] the error message
|
|
30
|
+
# @param exit_code [Integer] the exit code (default: 1)
|
|
31
|
+
# @return [ErrorResponse] the response model
|
|
32
|
+
def self.from_message(message, exit_code: 1)
|
|
33
|
+
new(
|
|
34
|
+
status: 'error',
|
|
35
|
+
exit_code: exit_code,
|
|
36
|
+
error: message
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'lutaml/model'
|
|
4
|
+
|
|
5
|
+
module Ukiryu
|
|
6
|
+
module Models
|
|
7
|
+
# Execution metadata
|
|
8
|
+
#
|
|
9
|
+
# Contains timing and duration information about command execution.
|
|
10
|
+
class ExecutionMetadata < Lutaml::Model::Serializable
|
|
11
|
+
attribute :started_at, :string
|
|
12
|
+
attribute :finished_at, :string
|
|
13
|
+
attribute :duration_seconds, :float
|
|
14
|
+
attribute :formatted_duration, :string
|
|
15
|
+
|
|
16
|
+
yaml do
|
|
17
|
+
map_element 'started_at', to: :started_at
|
|
18
|
+
map_element 'finished_at', to: :finished_at
|
|
19
|
+
map_element 'duration_seconds', to: :duration_seconds
|
|
20
|
+
map_element 'formatted_duration', to: :formatted_duration
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
json do
|
|
24
|
+
map 'started_at', to: :started_at
|
|
25
|
+
map 'finished_at', to: :finished_at
|
|
26
|
+
map 'duration_seconds', to: :duration_seconds
|
|
27
|
+
map 'formatted_duration', to: :formatted_duration
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|