cogbox 0.190.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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +22 -0
  4. data/.ruby-version +1 -0
  5. data/CODE_OF_CONDUCT.md +132 -0
  6. data/LICENSE +190 -0
  7. data/README.md +184 -0
  8. data/Rakefile +12 -0
  9. data/lib/cogbox/code_interpreter.rb +360 -0
  10. data/lib/cogbox/cogbox.rb +337 -0
  11. data/lib/cogbox/common/charts.rb +125 -0
  12. data/lib/cogbox/common/code_interpreter.rb +57 -0
  13. data/lib/cogbox/common/code_language.rb +15 -0
  14. data/lib/cogbox/common/cogbox.rb +231 -0
  15. data/lib/cogbox/common/file_system.rb +27 -0
  16. data/lib/cogbox/common/git.rb +20 -0
  17. data/lib/cogbox/common/image.rb +501 -0
  18. data/lib/cogbox/common/process.rb +150 -0
  19. data/lib/cogbox/common/pty.rb +310 -0
  20. data/lib/cogbox/common/resources.rb +40 -0
  21. data/lib/cogbox/common/response.rb +84 -0
  22. data/lib/cogbox/common/snapshot.rb +125 -0
  23. data/lib/cogbox/computer_use.rb +920 -0
  24. data/lib/cogbox/config.rb +110 -0
  25. data/lib/cogbox/file_system.rb +452 -0
  26. data/lib/cogbox/file_transfer.rb +384 -0
  27. data/lib/cogbox/git.rb +335 -0
  28. data/lib/cogbox/lsp_server.rb +140 -0
  29. data/lib/cogbox/object_storage.rb +173 -0
  30. data/lib/cogbox/otel.rb +184 -0
  31. data/lib/cogbox/process.rb +551 -0
  32. data/lib/cogbox/sandbox.rb +752 -0
  33. data/lib/cogbox/sdk/version.rb +11 -0
  34. data/lib/cogbox/sdk.rb +57 -0
  35. data/lib/cogbox/snapshot_service.rb +239 -0
  36. data/lib/cogbox/util.rb +81 -0
  37. data/lib/cogbox/volume.rb +47 -0
  38. data/lib/cogbox/volume_service.rb +62 -0
  39. data/lib/cogbox.rb +11 -0
  40. data/project.json +100 -0
  41. data/scripts/generate-docs.rb +403 -0
  42. data/sig/cogbox/sdk.rbs +6 -0
  43. metadata +240 -0
@@ -0,0 +1,501 @@
1
+ # Copyright Daytona Platforms Inc.
2
+ # Copyright Cognifyi
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ # frozen_string_literal: true
6
+
7
+ require 'digest'
8
+ require 'fileutils'
9
+ require 'pathname'
10
+ require 'shellwords'
11
+
12
+ module Cogbox
13
+ class Context
14
+ attr_reader :source_path
15
+ attr_reader :archive_path
16
+
17
+ # @param source_path [String] The path to the source file or directory
18
+ # @param archive_path [String, nil] The path inside the archive file in object storage
19
+ def initialize(source_path:, archive_path: nil)
20
+ @source_path = source_path
21
+ @archive_path = archive_path
22
+ end
23
+ end
24
+
25
+ # Represents an image definition for a Cogbox sandbox.
26
+ # Do not construct this class directly. Instead use one of its static factory methods,
27
+ # such as `Image.base()`, `Image.debian_slim()`, or `Image.from_dockerfile()`.
28
+ class Image # rubocop:disable Metrics/ClassLength
29
+ # @return [String, nil] The generated Dockerfile for the image
30
+ attr_reader :dockerfile
31
+
32
+ # @return [Array<Context>] List of context files for the image
33
+ attr_reader :context_list
34
+
35
+ # Supported Python series
36
+ SUPPORTED_PYTHON_SERIES = %w[3.9 3.10 3.11 3.12 3.13].freeze
37
+ LATEST_PYTHON_MICRO_VERSIONS = %w[3.9.22 3.10.17 3.11.12 3.12.10 3.13.3].freeze
38
+
39
+ # @param dockerfile [String, nil] The Dockerfile content
40
+ # @param context_list [Array<Context>] List of context files
41
+ def initialize(dockerfile: nil, context_list: [])
42
+ @dockerfile = dockerfile || ''
43
+ @context_list = context_list
44
+ end
45
+
46
+ # Adds commands to install packages using pip
47
+ #
48
+ # @param packages [Array<String>] The packages to install
49
+ # @param find_links [Array<String>, nil] The find-links to use
50
+ # @param index_url [String, nil] The index URL to use
51
+ # @param extra_index_urls [Array<String>, nil] The extra index URLs to use
52
+ # @param pre [Boolean] Whether to install pre-release packages
53
+ # @param extra_options [String] Additional options to pass to pip
54
+ # @return [Image] The image with the pip install commands added
55
+ #
56
+ # @example
57
+ # image = Image.debian_slim("3.12").pip_install("requests", "pandas")
58
+ def pip_install(*packages, find_links: nil, index_url: nil, extra_index_urls: nil, pre: false, extra_options: '') # rubocop:disable Metrics/ParameterLists
59
+ pkgs = flatten_str_args('pip_install', 'packages', packages)
60
+ return self if pkgs.empty?
61
+
62
+ extra_args = format_pip_install_args(find_links:, index_url:, extra_index_urls:, pre:, extra_options:)
63
+ @dockerfile += "RUN python -m pip install #{Shellwords.join(pkgs.sort)}#{extra_args}\n"
64
+
65
+ self
66
+ end
67
+
68
+ # Installs dependencies from a requirements.txt file
69
+ #
70
+ # @param requirements_txt [String] The path to the requirements.txt file
71
+ # @param find_links [Array<String>, nil] The find-links to use
72
+ # @param index_url [String, nil] The index URL to use
73
+ # @param extra_index_urls [Array<String>, nil] The extra index URLs to use
74
+ # @param pre [Boolean] Whether to install pre-release packages
75
+ # @param extra_options [String] Additional options to pass to pip
76
+ # @return [Image] The image with the pip install commands added
77
+ # @raise [Sdk::Error] If the requirements file does not exist
78
+ #
79
+ # @example
80
+ # image = Image.debian_slim("3.12").pip_install_from_requirements("requirements.txt")
81
+ def pip_install_from_requirements(requirements_txt, find_links: nil, index_url: nil, extra_index_urls: nil, # rubocop:disable Metrics/ParameterLists
82
+ pre: false, extra_options: '')
83
+ requirements_txt = File.expand_path(requirements_txt)
84
+ raise Sdk::Error, "Requirements file #{requirements_txt} does not exist" unless File.exist?(requirements_txt)
85
+
86
+ extra_args = format_pip_install_args(find_links:, index_url:, extra_index_urls:, pre:, extra_options:)
87
+
88
+ archive_path = ObjectStorage.compute_archive_base_path(requirements_txt)
89
+ @context_list << Context.new(source_path: requirements_txt, archive_path:)
90
+ @dockerfile += "COPY #{archive_path} /.requirements.txt\n"
91
+ @dockerfile += "RUN python -m pip install -r /.requirements.txt#{extra_args}\n"
92
+
93
+ self
94
+ end
95
+
96
+ # Installs dependencies from a pyproject.toml file
97
+ #
98
+ # @param pyproject_toml [String] The path to the pyproject.toml file
99
+ # @param optional_dependencies [Array<String>] The optional dependencies to install
100
+ # @param find_links [String, nil] The find-links to use
101
+ # @param index_url [String, nil] The index URL to use
102
+ # @param extra_index_url [String, nil] The extra index URL to use
103
+ # @param pre [Boolean] Whether to install pre-release packages
104
+ # @param extra_options [String] Additional options to pass to pip
105
+ # @return [Image] The image with the pip install commands added
106
+ # @raise [Sdk::Error] If pyproject.toml parsing is not supported
107
+ #
108
+ # @example
109
+ # image = Image.debian_slim("3.12").pip_install_from_pyproject("pyproject.toml", optional_dependencies: ["dev"])
110
+ def pip_install_from_pyproject(pyproject_toml, optional_dependencies: [], find_links: nil, index_url: nil, # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
111
+ extra_index_url: nil, pre: false, extra_options: '')
112
+ data = TOML.load_file(pyproject_toml)
113
+ dependencies = data.dig('project', 'dependencies')
114
+
115
+ unless dependencies
116
+ raise Sdk::Error, 'No [project.dependencies] section in pyproject.toml file. ' \
117
+ 'See https://packaging.python.org/en/latest/guides/writing-pyproject-toml ' \
118
+ 'for further file format guidelines.'
119
+ end
120
+
121
+ return unless optional_dependencies
122
+
123
+ optionals = data.dig('project', 'optional-dependencies')
124
+ optional_dependencies.each do |group|
125
+ dependencies.concat(optionals.fetch(group, []))
126
+ end
127
+
128
+ pip_install(*dependencies, find_links:, index_url:, extra_index_urls: extra_index_url, pre:, extra_options:)
129
+ end
130
+
131
+ # Adds a local file to the image
132
+ #
133
+ # @param local_path [String] The path to the local file
134
+ # @param remote_path [String] The path to the file in the image
135
+ # @return [Image] The image with the local file added
136
+ #
137
+ # @example
138
+ # image = Image.debian_slim("3.12").add_local_file("package.json", "/home/cogbox/package.json")
139
+ def add_local_file(local_path, remote_path)
140
+ remote_path = "#{remote_path}/#{File.basename(local_path)}" if remote_path.end_with?('/')
141
+
142
+ local_path = File.expand_path(local_path)
143
+ archive_path = ObjectStorage.compute_archive_base_path(local_path)
144
+ @context_list << Context.new(source_path: local_path, archive_path: archive_path)
145
+ @dockerfile += "COPY #{archive_path} #{remote_path}\n"
146
+
147
+ self
148
+ end
149
+
150
+ # Adds a local directory to the image
151
+ #
152
+ # @param local_path [String] The path to the local directory
153
+ # @param remote_path [String] The path to the directory in the image
154
+ # @return [Image] The image with the local directory added
155
+ #
156
+ # @example
157
+ # image = Image.debian_slim("3.12").add_local_dir("src", "/home/cogbox/src")
158
+ def add_local_dir(local_path, remote_path)
159
+ local_path = File.expand_path(local_path)
160
+ archive_path = ObjectStorage.compute_archive_base_path(local_path)
161
+ @context_list << Context.new(source_path: local_path, archive_path: archive_path)
162
+ @dockerfile += "COPY #{archive_path} #{remote_path}\n"
163
+
164
+ self
165
+ end
166
+
167
+ # Runs commands in the image
168
+ #
169
+ # @param commands [Array<String>] The commands to run
170
+ # @return [Image] The image with the commands added
171
+ #
172
+ # @example
173
+ # image = Image.debian_slim("3.12").run_commands('echo "Hello, world!"', 'echo "Hello again!"')
174
+ def run_commands(*commands)
175
+ commands.each do |command|
176
+ if command.is_a?(Array)
177
+ escaped = command.map { |c| c.gsub('"', '\\"').gsub("'", "\\'") }
178
+ @dockerfile += "RUN #{escaped.map { |c| "\"#{c}\"" }.join(' ')}\n"
179
+ else
180
+ @dockerfile += "RUN #{command}\n"
181
+ end
182
+ end
183
+
184
+ self
185
+ end
186
+
187
+ # Sets environment variables in the image
188
+ #
189
+ # @param env_vars [Hash<String, String>] The environment variables to set
190
+ # @return [Image] The image with the environment variables added
191
+ #
192
+ # @example
193
+ # image = Image.debian_slim("3.12").env({"PROJECT_ROOT" => "/home/cogbox"})
194
+ def env(env_vars)
195
+ non_str_keys = env_vars.reject { |_key, val| val.is_a?(String) }.keys
196
+ raise Sdk::Error, "Image ENV variables must be strings. Invalid keys: #{non_str_keys}" unless non_str_keys.empty?
197
+
198
+ env_vars.each do |key, val|
199
+ @dockerfile += "ENV #{key}=#{Shellwords.escape(val)}\n"
200
+ end
201
+
202
+ self
203
+ end
204
+
205
+ # Sets the working directory in the image
206
+ #
207
+ # @param path [String] The path to the working directory
208
+ # @return [Image] The image with the working directory added
209
+ #
210
+ # @example
211
+ # image = Image.debian_slim("3.12").workdir("/home/cogbox")
212
+ def workdir(path)
213
+ @dockerfile += "WORKDIR #{Shellwords.escape(path.to_s)}\n"
214
+ self
215
+ end
216
+
217
+ # Sets the entrypoint for the image
218
+ #
219
+ # @param entrypoint_commands [Array<String>] The commands to set as the entrypoint
220
+ # @return [Image] The image with the entrypoint added
221
+ #
222
+ # @example
223
+ # image = Image.debian_slim("3.12").entrypoint(["/bin/bash"])
224
+ def entrypoint(entrypoint_commands)
225
+ unless entrypoint_commands.is_a?(Array) && entrypoint_commands.all? { |x| x.is_a?(String) }
226
+ raise Sdk::Error, 'entrypoint_commands must be a list of strings.'
227
+ end
228
+
229
+ args_str = flatten_str_args('entrypoint', 'entrypoint_commands', entrypoint_commands)
230
+ args_str = args_str.map { |arg| "\"#{arg}\"" }.join(', ') if args_str.any?
231
+ @dockerfile += "ENTRYPOINT [#{args_str}]\n"
232
+
233
+ self
234
+ end
235
+
236
+ # Sets the default command for the image
237
+ #
238
+ # @param cmd [Array<String>] The commands to set as the default command
239
+ # @return [Image] The image with the default command added
240
+ #
241
+ # @example
242
+ # image = Image.debian_slim("3.12").cmd(["/bin/bash"])
243
+ def cmd(cmd)
244
+ unless cmd.is_a?(Array) && cmd.all? { |x| x.is_a?(String) }
245
+ raise Sdk::Error, 'Image CMD must be a list of strings.'
246
+ end
247
+
248
+ cmd_str = flatten_str_args('cmd', 'cmd', cmd)
249
+ cmd_str = cmd_str.map { |arg| "\"#{arg}\"" }.join(', ') if cmd_str.any?
250
+ @dockerfile += "CMD [#{cmd_str}]\n"
251
+ self
252
+ end
253
+
254
+ # Adds arbitrary Dockerfile-like commands to the image
255
+ #
256
+ # @param dockerfile_commands [Array<String>] The commands to add to the Dockerfile
257
+ # @param context_dir [String, nil] The path to the context directory
258
+ # @return [Image] The image with the Dockerfile commands added
259
+ #
260
+ # @example
261
+ # image = Image.debian_slim("3.12").dockerfile_commands(["RUN echo 'Hello, world!'"])
262
+ def dockerfile_commands(dockerfile_commands, context_dir: nil) # rubocop:disable Metrics/MethodLength
263
+ if context_dir
264
+ context_dir = File.expand_path(context_dir)
265
+ raise Sdk::Error, "Context directory #{context_dir} does not exist" unless Dir.exist?(context_dir)
266
+ end
267
+
268
+ # Extract copy sources from dockerfile commands (class-level helper)
269
+ self.class.send(:extract_copy_sources, dockerfile_commands.join("\n"),
270
+ context_dir || '').each do |context_path, original_path|
271
+ archive_base_path = context_path
272
+ if context_dir && !original_path.start_with?(context_dir)
273
+ archive_base_path = context_path.delete_prefix(context_dir)
274
+ end
275
+ @context_list << Context.new(source_path: context_path, archive_path: archive_base_path)
276
+ end
277
+
278
+ @dockerfile += "#{dockerfile_commands.join("\n")}\n"
279
+ self
280
+ end
281
+
282
+ class << self
283
+ # Creates an Image from an existing Dockerfile
284
+ #
285
+ # @param path [String] The path to the Dockerfile
286
+ # @return [Image] The image with the Dockerfile added
287
+ #
288
+ # @example
289
+ # image = Image.from_dockerfile("Dockerfile")
290
+ def from_dockerfile(path) # rubocop:disable Metrics/AbcSize
291
+ path = Pathname.new(File.expand_path(path))
292
+ dockerfile = path.read
293
+ img = new(dockerfile: dockerfile)
294
+
295
+ # Remove dockerfile filename from path
296
+ path_prefix = path.to_s.delete_suffix(path.basename.to_s)
297
+
298
+ extract_copy_sources(dockerfile, path_prefix).each do |context_path, original_path|
299
+ archive_base_path = context_path
300
+ archive_base_path = context_path.delete_prefix(path_prefix) unless original_path.start_with?(path_prefix)
301
+ img.context_list << Context.new(source_path: context_path, archive_path: archive_base_path)
302
+ end
303
+
304
+ img
305
+ end
306
+
307
+ # Creates an Image from an existing base image
308
+ #
309
+ # @param image [String] The base image to use
310
+ # @return [Image] The image with the base image added
311
+ #
312
+ # @example
313
+ # image = Image.base("python:3.12-slim-bookworm")
314
+ def base(image)
315
+ img = new
316
+ img.instance_variable_set(:@dockerfile, "FROM #{image}\n")
317
+ img
318
+ end
319
+
320
+ # Creates a Debian slim image based on the official Python Docker image
321
+ #
322
+ # @param python_version [String, nil] The Python version to use
323
+ # @return [Image] The image with the Debian slim image added
324
+ #
325
+ # @example
326
+ # image = Image.debian_slim("3.12")
327
+ def debian_slim(python_version = nil) # rubocop:disable Metrics/MethodLength
328
+ python_version = process_python_version(python_version)
329
+ img = new
330
+ commands = [
331
+ "FROM python:#{python_version}-slim-bookworm",
332
+ 'RUN apt-get update',
333
+ 'RUN apt-get install -y gcc gfortran build-essential',
334
+ 'RUN pip install --upgrade pip',
335
+ # Set debian front-end to non-interactive to avoid users getting stuck with input prompts.
336
+ "RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections"
337
+ ]
338
+ img.instance_variable_set(:@dockerfile, "#{commands.join("\n")}\n")
339
+ img
340
+ end
341
+
342
+ private
343
+
344
+ # Processes the Python version
345
+ #
346
+ # @param python_version [String, nil] The Python version to process
347
+ # @param allow_micro_granularity [Boolean] Whether to allow micro-level granularity
348
+ # @return [String] The processed Python version
349
+ def process_python_version(python_version = nil)
350
+ python_version ||= SUPPORTED_PYTHON_SERIES.last
351
+
352
+ unless SUPPORTED_PYTHON_SERIES.include?(python_version)
353
+ raise Sdk::Error, "Unsupported Python version: #{python_version}"
354
+ end
355
+
356
+ LATEST_PYTHON_MICRO_VERSIONS.select { |v| v.start_with?(python_version) }.last
357
+ end
358
+
359
+ # Extracts source files from COPY commands in a Dockerfile
360
+ #
361
+ # @param dockerfile_content [String] The content of the Dockerfile
362
+ # @param path_prefix [String] The path prefix to use for the sources
363
+ # @return [Array<Array<String>>] The list of the actual file path and its corresponding COPY-command source path
364
+ def extract_copy_sources(dockerfile_content, path_prefix = '') # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
365
+ sources = []
366
+ lines = dockerfile_content.split("\n")
367
+
368
+ lines.each do |line|
369
+ # Skip empty lines and comments
370
+ next if line.strip.empty? || line.strip.start_with?('#')
371
+
372
+ # Check if the line contains a COPY command (at the beginning of the line)
373
+ next unless line.match?(/^\s*COPY\s+(?!.*--from=)/i)
374
+
375
+ # Skip COPY instructions that use heredoc syntax (inline content, not file references)
376
+ next if line.include?('<<')
377
+
378
+ # Extract the sources from the COPY command
379
+ command_parts = parse_copy_command(line)
380
+ next unless command_parts
381
+
382
+ # Get source paths from the parsed command parts
383
+ command_parts['sources'].each do |source|
384
+ # Handle absolute and relative paths differently
385
+ full_path_pattern = if Pathname.new(source).absolute?
386
+ # Absolute path - use as is
387
+ source
388
+ else
389
+ # Relative path - add prefix
390
+ File.join(path_prefix, source)
391
+ end
392
+
393
+ # Handle glob patterns
394
+ matching_files = Dir.glob(full_path_pattern)
395
+
396
+ if matching_files.any?
397
+ matching_files.each { |matching_file| sources << [matching_file, source] }
398
+ else
399
+ # If no files match, include the pattern anyway
400
+ sources << [full_path_pattern, source]
401
+ end
402
+ end
403
+ end
404
+
405
+ sources
406
+ end
407
+
408
+ # Parses a COPY command to extract sources and destination
409
+ #
410
+ # @param line [String] The line to parse
411
+ # @return [Hash, nil] A hash containing the sources and destination, or nil if parsing fails
412
+ def parse_copy_command(line) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
413
+ # Remove initial "COPY" and strip whitespace
414
+ parts = line.strip[4..].strip
415
+
416
+ # Handle JSON array format: COPY ["src1", "src2", "dest"]
417
+ if parts.start_with?('[')
418
+ begin
419
+ # Parse the JSON-like array format
420
+ elements = Shellwords.split(parts.delete('[]'))
421
+ return nil if elements.length < 2
422
+
423
+ { 'sources' => elements[0..-2], 'dest' => elements[-1] }
424
+ rescue StandardError
425
+ nil
426
+ end
427
+ end
428
+
429
+ # Handle regular format with possible flags
430
+ parts = Shellwords.split(parts)
431
+
432
+ # Extract flags like --chown, --chmod, --from
433
+ sources_start_idx = 0
434
+ parts.each_with_index do |part, i|
435
+ break unless part.start_with?('--')
436
+
437
+ # Skip the flag and its value if it has one
438
+ sources_start_idx = if !part.include?('=') && i + 1 < parts.length && !parts[i + 1].start_with?('--')
439
+ i + 2
440
+ else
441
+ i + 1
442
+ end
443
+ end
444
+
445
+ # After skipping flags, we need at least one source and one destination
446
+ return nil if parts.length - sources_start_idx < 2
447
+
448
+ { 'sources' => parts[sources_start_idx..-2], 'dest' => parts[-1] }
449
+ end
450
+ end
451
+
452
+ private
453
+
454
+ # Flattens a list of strings and arrays of strings into a single array of strings
455
+ #
456
+ # @param function_name [String] The name of the function that is being called
457
+ # @param arg_name [String] The name of the argument that is being passed
458
+ # @param args [Array] The list of arguments to flatten
459
+ # @return [Array<String>] A list of strings
460
+ def flatten_str_args(function_name, arg_name, args) # rubocop:disable Metrics/MethodLength
461
+ ret = []
462
+ args.each do |x|
463
+ case x
464
+ when String
465
+ ret << x
466
+ when Array
467
+ unless x.all? { |y| y.is_a?(String) }
468
+ raise Sdk::Error, "#{function_name}: #{arg_name} must only contain strings"
469
+ end
470
+
471
+ ret.concat(x)
472
+
473
+ else
474
+ raise Sdk::Error, "#{function_name}: #{arg_name} must only contain strings"
475
+ end
476
+ end
477
+ ret
478
+ end
479
+
480
+ # Formats the arguments in a single string
481
+ #
482
+ # @param find_links [Array<String>, nil] The find-links to use
483
+ # @param index_url [String, nil] The index URL to use
484
+ # @param extra_index_urls [Array<String>, nil] The extra index URLs to use
485
+ # @param pre [Boolean] Whether to install pre-release packages
486
+ # @param extra_options [String] Additional options to pass to pip
487
+ # @return [String] The formatted arguments
488
+ def format_pip_install_args(find_links: nil, index_url: nil, extra_index_urls: nil, pre: false, extra_options: '') # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
489
+ extra_args = ''
490
+ find_links&.each { |find_link| extra_args += " --find-links #{Shellwords.escape(find_link)}" }
491
+ extra_args += " --index-url #{Shellwords.escape(index_url)}" if index_url
492
+ extra_index_urls&.each do |extra_index_url|
493
+ extra_args += " --extra-index-url #{Shellwords.escape(extra_index_url)}"
494
+ end
495
+ extra_args += ' --pre' if pre
496
+ extra_args += " #{extra_options.strip}" if extra_options && !extra_options.strip.empty?
497
+
498
+ extra_args
499
+ end
500
+ end
501
+ end
@@ -0,0 +1,150 @@
1
+ # Copyright Daytona Platforms Inc.
2
+ # Copyright Cognifyi
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ # frozen_string_literal: true
6
+
7
+ module Cogbox
8
+ class ExecuteResponse
9
+ # @return [Integer] The exit code from the command execution
10
+ attr_reader :exit_code
11
+
12
+ # @return [String] The output from the command execution
13
+ attr_reader :result
14
+
15
+ # @return [ExecutionArtifacts, nil] Artifacts from the command execution
16
+ attr_reader :artifacts
17
+
18
+ # @return [Hash] Additional properties from the response
19
+ attr_reader :additional_properties
20
+
21
+ # Initialize a new ExecuteResponse
22
+ #
23
+ # @param exit_code [Integer] The exit code from the command execution
24
+ # @param result [String] The output from the command execution
25
+ # @param artifacts [ExecutionArtifacts, nil] Artifacts from the command execution
26
+ # @param additional_properties [Hash] Additional properties from the response
27
+ def initialize(exit_code:, result:, artifacts: nil, additional_properties: {})
28
+ @exit_code = exit_code
29
+ @result = result
30
+ @artifacts = artifacts
31
+ @additional_properties = additional_properties
32
+ end
33
+ end
34
+
35
+ class ExecutionArtifacts
36
+ # @return [String] Standard output from the command, same as `result` in `ExecuteResponse`
37
+ attr_accessor :stdout
38
+
39
+ # @return [Array] List of chart metadata from matplotlib
40
+ attr_accessor :charts
41
+
42
+ # Initialize a new ExecutionArtifacts
43
+ #
44
+ # @param stdout [String] Standard output from the command
45
+ # @param charts [Array] List of chart metadata from matplotlib
46
+ def initialize(stdout = '', charts = [])
47
+ @stdout = stdout
48
+ @charts = charts
49
+ end
50
+ end
51
+
52
+ class CodeRunParams
53
+ # @return [Array<String>, nil] Command line arguments
54
+ attr_accessor :argv
55
+
56
+ # @return [Hash<String, String>, nil] Environment variables
57
+ attr_accessor :env
58
+
59
+ # Initialize a new CodeRunParams
60
+ #
61
+ # @param argv [Array<String>, nil] Command line arguments
62
+ # @param env [Hash<String, String>, nil] Environment variables
63
+ def initialize(argv: nil, env: nil)
64
+ @argv = argv
65
+ @env = env
66
+ end
67
+ end
68
+
69
+ class SessionExecuteRequest
70
+ # @return [String] The command to execute
71
+ attr_accessor :command
72
+
73
+ # @return [Boolean] Whether to execute the command asynchronously
74
+ attr_accessor :run_async
75
+
76
+ # @return [Boolean] Whether to suppress input echo
77
+ attr_accessor :suppress_input_echo
78
+
79
+ # Initialize a new SessionExecuteRequest
80
+ #
81
+ # @param command [String] The command to execute
82
+ # @param run_async [Boolean] Whether to execute the command asynchronously
83
+ # @param suppress_input_echo [Boolean] Whether to suppress input echo (default is false)
84
+ def initialize(command:, run_async: false, suppress_input_echo: false)
85
+ @command = command
86
+ @run_async = run_async
87
+ @suppress_input_echo = suppress_input_echo
88
+ end
89
+ end
90
+
91
+ class SessionExecuteResponse
92
+ # @return [String, nil] Unique identifier for the executed command
93
+ attr_reader :cmd_id
94
+
95
+ # @return [String, nil] The output from the command execution
96
+ attr_reader :output
97
+
98
+ # @return [String, nil] Standard output from the command
99
+ attr_reader :stdout
100
+
101
+ # @return [String, nil] Standard error from the command
102
+ attr_reader :stderr
103
+
104
+ # @return [Integer, nil] The exit code from the command execution
105
+ attr_reader :exit_code
106
+
107
+ # @return [Hash] Additional properties from the response
108
+ attr_reader :additional_properties
109
+
110
+ # Initialize a new SessionExecuteResponse
111
+ #
112
+ # @param opts [Hash] Options for the SessionExecuteResponse
113
+ # @param cmd_id [String, nil] Unique identifier for the executed command
114
+ # @param output [String, nil] The output from the command execution
115
+ # @param stdout [String, nil] Standard output from the command
116
+ # @param stderr [String, nil] Standard error from the command
117
+ # @param exit_code [Integer, nil] The exit code from the command execution
118
+ # @param additional_properties [Hash] Additional properties from the response
119
+ def initialize(opts = {})
120
+ @cmd_id = opts.fetch(:cmd_id, nil)
121
+ @output = opts.fetch(:output, nil)
122
+ @stdout = opts.fetch(:stdout, nil)
123
+ @stderr = opts.fetch(:stderr, nil)
124
+ @exit_code = opts.fetch(:exit_code, nil)
125
+ @additional_properties = opts.fetch(:additional_properties, {})
126
+ end
127
+ end
128
+
129
+ class SessionCommandLogsResponse
130
+ # @return [String, nil] The combined output from the command
131
+ attr_reader :output
132
+
133
+ # @return [String, nil] The stdout from the command
134
+ attr_reader :stdout
135
+
136
+ # @return [String, nil] The stderr from the command
137
+ attr_reader :stderr
138
+
139
+ # Initialize a new SessionCommandLogsResponse
140
+ #
141
+ # @param output [String, nil] The combined output from the command
142
+ # @param stdout [String, nil] The stdout from the command
143
+ # @param stderr [String, nil] The stderr from the command
144
+ def initialize(output: nil, stdout: nil, stderr: nil)
145
+ @output = output
146
+ @stdout = stdout
147
+ @stderr = stderr
148
+ end
149
+ end
150
+ end