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,310 @@
1
+ # Copyright Daytona Platforms Inc.
2
+ # Copyright Cognifyi
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ # frozen_string_literal: true
6
+
7
+ require 'json'
8
+ require 'observer'
9
+
10
+ module Cogbox
11
+ class PtySize
12
+ # @return [Integer] Number of terminal rows (height)
13
+ attr_reader :rows
14
+
15
+ # @return [Integer] Number of terminal columns (width)
16
+ attr_reader :cols
17
+
18
+ # Initialize a new PtySize
19
+ #
20
+ # @param rows [Integer] Number of terminal rows (height)
21
+ # @param cols [Integer] Number of terminal columns (width)
22
+ def initialize(rows:, cols:)
23
+ @rows = rows
24
+ @cols = cols
25
+ end
26
+ end
27
+
28
+ class PtyResult
29
+ # @return [Integer, nil] Exit code of the PTY process (0 for success, non-zero for errors).
30
+ # nil if the process hasn't exited yet or exit code couldn't be determined.
31
+ attr_reader :exit_code
32
+
33
+ # @return [String, nil] Error message if the PTY failed or was terminated abnormally.
34
+ # nil if no error occurred.
35
+ attr_reader :error
36
+
37
+ # Initialize a new PtyResult
38
+ #
39
+ # @param exit_code [Integer, nil] Exit code of the PTY process
40
+ # @param error [String, nil] Error message if the PTY failed
41
+ def initialize(exit_code: nil, error: nil)
42
+ @exit_code = exit_code
43
+ @error = error
44
+ end
45
+ end
46
+
47
+ class PtyHandle # rubocop:disable Metrics/ClassLength
48
+ include Observable
49
+
50
+ # @return [String] Session ID of the PTY session
51
+ attr_reader :session_id
52
+
53
+ # @return [Integer, nil] Exit code of the PTY process (if terminated)
54
+ attr_reader :exit_code
55
+
56
+ # @return [String, nil] Error message if the PTY failed
57
+ attr_reader :error
58
+
59
+ # Initialize the PTY handle.
60
+ #
61
+ # @param websocket [WebSocket::Client::Simple::Client] Connected WebSocket client connection
62
+ # @param session_id [String] Session ID of the PTY session
63
+ # @param handle_resize [Proc, nil] Optional callback for resizing the PTY
64
+ # @param handle_kill [Proc, nil] Optional callback for killing the PTY
65
+ def initialize(websocket, session_id:, handle_resize: nil, handle_kill: nil)
66
+ @websocket = websocket
67
+ @session_id = session_id
68
+ @handle_resize = handle_resize
69
+ @handle_kill = handle_kill
70
+ @exit_code = nil
71
+ @error = nil
72
+ @logger = Sdk.logger
73
+
74
+ @status = Status::INIT
75
+ subscribe
76
+ end
77
+
78
+ # Check if connected to the PTY session
79
+ #
80
+ # @return [Boolean] true if connected, false otherwise
81
+ def connected? = websocket.open?
82
+
83
+ # Wait for the PTY connection to be established
84
+ #
85
+ # @param timeout [Float] Maximum time in seconds to wait for connection. Defaults to 10.0
86
+ # @return [void]
87
+ # @raise [Cogbox::Sdk::Error] If connection timeout is exceeded
88
+ def wait_for_connection(timeout: DEFAULT_TIMEOUT)
89
+ return if status == Status::CONNECTED
90
+
91
+ start_time = Time.now
92
+
93
+ sleep(SLEEP_INTERVAL) until status == Status::CONNECTED || (Time.now - start_time) > timeout
94
+
95
+ raise Sdk::Error, 'PTY connection timeout' unless status == Status::CONNECTED
96
+ end
97
+
98
+ # Send input to the PTY session
99
+ #
100
+ # @param input [String] Input to send to the PTY
101
+ # @return [void]
102
+ def send_input(input)
103
+ raise Sdk::Error, 'PTY session not connected' unless websocket.open?
104
+
105
+ websocket.send(input)
106
+ end
107
+
108
+ # Resize the PTY terminal
109
+ #
110
+ # @param pty_size [PtySize] New terminal size
111
+ # @return [CogboxApiClient::PtySessionInfo] Updated PTY session information
112
+ def resize(pty_size)
113
+ raise Sdk::Error, 'No resize handler available' unless handle_resize
114
+
115
+ handle_resize.call(pty_size)
116
+ end
117
+
118
+ # Delete the PTY session
119
+ #
120
+ # @return [void]
121
+ def kill
122
+ raise Sdk::Error, 'No kill handler available' unless handle_kill
123
+
124
+ handle_kill.call
125
+ end
126
+
127
+ # Wait for the PTY session to complete
128
+ #
129
+ # @param on_data [Proc, nil] Optional callback to handle output data
130
+ # @return [Cogbox::PtyResult] Result containing exit code and error information
131
+ def wait(timeout: nil, &on_data)
132
+ timeout ||= Float::INFINITY
133
+ return unless status == Status::CONNECTED
134
+
135
+ start_time = Time.now
136
+ add_observer(on_data, :call) if on_data
137
+
138
+ sleep(SLEEP_INTERVAL) while status == Status::CONNECTED && (Time.now - start_time) <= timeout
139
+
140
+ PtyResult.new(exit_code:, error:)
141
+ ensure
142
+ delete_observer(on_data) if on_data
143
+ end
144
+
145
+ # @yieldparam [WebSocket::Frame::Data]
146
+ # @return [void]
147
+ def each(&)
148
+ return unless block_given?
149
+
150
+ queue = Queue.new
151
+ add_observer(proc { queue << _1 }, :call)
152
+
153
+ while websocket.open?
154
+ drain(queue, &)
155
+ sleep(SLEEP_INTERVAL)
156
+ end
157
+
158
+ drain(queue, &)
159
+ end
160
+
161
+ # Disconnect from the PTY session
162
+ #
163
+ # @return [void]
164
+ def disconnect = websocket.close
165
+
166
+ private
167
+
168
+ # @return [Symbol]
169
+ attr_reader :status
170
+
171
+ # @return [WebSocket::Client::Simple::Client]
172
+ attr_reader :websocket
173
+
174
+ # @return [Proc, Nil]
175
+ attr_reader :handle_kill
176
+
177
+ # @return [Proc, Nil]
178
+ attr_reader :handle_resize
179
+
180
+ # @return [Logger]
181
+ attr_reader :logger
182
+
183
+ # @return [void]
184
+ def subscribe
185
+ websocket.on(:open, &method(:on_websocket_open))
186
+ websocket.on(:close, &method(:on_websocket_close))
187
+ websocket.on(:message, &method(:on_websocket_message))
188
+ websocket.on(:error, &method(:on_websocket_error))
189
+ end
190
+
191
+ # @return [void]
192
+ def on_websocket_open
193
+ logger.debug('[Websocket] open')
194
+ @status = Status::OPEN
195
+ end
196
+
197
+ # @param error [Object, Nil]
198
+ # @return [void]
199
+ def on_websocket_close(error)
200
+ logger.debug("[Websocket] close: #{error.inspect}")
201
+ @status = Status::CLOSED
202
+ end
203
+
204
+ # @param error [WebSocket::Frame::Incoming::Client]
205
+ # @return [void]
206
+ def on_websocket_message(message)
207
+ logger.debug("[Websocket] message(#{message.type}): #{message.data}")
208
+
209
+ case message.type
210
+ when :binary, :text
211
+ process_websocket_text_message(message)
212
+ when :close
213
+ process_websocket_close_message(message)
214
+ end
215
+ end
216
+
217
+ # @param error [Object]
218
+ # @return [void]
219
+ def on_websocket_error(error)
220
+ logger.debug("[Websocket] error: #{error.inspect}")
221
+ logger.debug("[Websocket] error: #{error.class}")
222
+ @status = Status::ERROR
223
+ end
224
+
225
+ # @param message [WebSocket::Frame::Incoming::Client]
226
+ # @return [void]
227
+ def process_websocket_text_message(message)
228
+ data = JSON.parse(message.data.to_s, symbolize_names: true)
229
+ process_websocket_control_message(data) if data[:type] == WebSocketMessageType::CONTROL
230
+ rescue JSON::ParserError, TypeError
231
+ process_websocket_data_message(message.data.to_s)
232
+ end
233
+
234
+ # @param data [WebSocket::Frame::Data]
235
+ # @return [void]
236
+ def process_websocket_data_message(data)
237
+ changed
238
+ notify_observers(data)
239
+ end
240
+
241
+ # @param data [WebSocket::Frame::Data]
242
+ # @return [void]
243
+ def process_websocket_control_message(data) # rubocop:disable Metrics/MethodLength
244
+ case data[:status]
245
+ when WebSocketControlStatus::CONNECTED
246
+ logger.debug('[control] connected')
247
+ @status = Status::CONNECTED
248
+ when WebSocketControlStatus::ERROR
249
+ logger.debug("[control] error: #{error.inspect}")
250
+ @status = Status::ERROR
251
+ @error = data.fetch(:error, 'Unknown connection error')
252
+ else
253
+ websocket.close
254
+ raise Sdk::Error, "Received invalid control message status: #{data[:status]}"
255
+ end
256
+ end
257
+
258
+ # @param message [WebSocket::Frame::Incoming::Client]
259
+ # @return [void]
260
+ def process_websocket_close_message(message)
261
+ data = JSON.parse(message.data.to_s, symbolize_names: true)
262
+ @exit_code = data.fetch(:exitCode, nil)
263
+ @error = data.fetch(:exitReason, nil)
264
+
265
+ disconnect
266
+ rescue JSON::ParserError, TypeError
267
+ nil
268
+ end
269
+
270
+ # @param queue [Queue]
271
+ # @yieldparam [WebSocket::Frame::Data]
272
+ # @return [void]
273
+ def drain(queue)
274
+ data = nil
275
+
276
+ yield data while (data = queue.pop(true))
277
+ rescue ThreadError => _e
278
+ nil
279
+ end
280
+
281
+ DEFAULT_TIMEOUT = 10.0
282
+ private_constant :DEFAULT_TIMEOUT
283
+
284
+ SLEEP_INTERVAL = 0.1
285
+ private_constant :SLEEP_INTERVAL
286
+
287
+ module Status
288
+ ALL = [
289
+ INIT = 'init',
290
+ OPEN = 'open',
291
+ CONNECTED = 'connected',
292
+ CLOSED = 'closed',
293
+ ERROR = 'error'
294
+ ].freeze
295
+ end
296
+
297
+ module WebSocketMessageType
298
+ ALL = [
299
+ CONTROL = 'control'
300
+ ].freeze
301
+ end
302
+
303
+ module WebSocketControlStatus
304
+ ALL = [
305
+ CONNECTED = 'connected',
306
+ ERROR = 'error'
307
+ ].freeze
308
+ end
309
+ end
310
+ end
@@ -0,0 +1,40 @@
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 Resources
9
+ # @return [Integer, nil] Number of CPU cores to allocate
10
+ attr_reader :cpu
11
+
12
+ # @return [Integer, nil] Amount of memory in GiB to allocate
13
+ attr_reader :memory
14
+
15
+ # @return [Integer, nil] Amount of disk space in GiB to allocate
16
+ attr_reader :disk
17
+
18
+ # @return [Integer, nil] Number of GPUs to allocate
19
+ attr_reader :gpu
20
+
21
+ # @return [String, Array<String>, nil] Preferred GPU type for the Sandbox
22
+ attr_reader :gpu_type
23
+
24
+ # @param cpu [Integer, nil] Number of CPU cores to allocate
25
+ # @param memory [Integer, nil] Amount of memory in GiB to allocate
26
+ # @param disk [Integer, nil] Amount of disk space in GiB to allocate
27
+ # @param gpu [Integer, nil] Number of GPUs to allocate
28
+ # @param gpu_type [String, Array<String>, nil] Preferred GPU type for the Sandbox
29
+ def initialize(cpu: nil, memory: nil, disk: nil, gpu: nil, gpu_type: nil)
30
+ @cpu = cpu
31
+ @memory = memory
32
+ @disk = disk
33
+ @gpu = gpu
34
+ @gpu_type = gpu_type
35
+ end
36
+
37
+ # @return [Hash] Hash representation of the resources
38
+ def to_h = { cpu:, memory:, disk:, gpu:, gpu_type: }.compact
39
+ end
40
+ end
@@ -0,0 +1,84 @@
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
+ # Re-export of api-client enum constants under the Cogbox namespace so
9
+ # SDK consumers never need to import from CogboxApiClient directly.
10
+ SandboxClass = CogboxApiClient::SandboxClass
11
+ SandboxState = CogboxApiClient::SandboxState
12
+ SandboxListSortField = CogboxApiClient::SandboxListSortField
13
+ SandboxListSortDirection = CogboxApiClient::SandboxListSortDirection
14
+ GpuType = CogboxApiClient::GpuType
15
+
16
+ class PaginatedResource
17
+ # @return [Array<Object>]
18
+ attr_reader :items
19
+
20
+ # @return [Float]
21
+ attr_reader :page
22
+
23
+ # @return [Float]
24
+ attr_reader :total
25
+
26
+ # @return [Float]
27
+ attr_reader :total_pages
28
+
29
+ # @param items [Cogbox::Sandbox]
30
+ # @param page [Float]
31
+ # @param total [Float]
32
+ # @param total_pages [Float]
33
+ def initialize(items:, page:, total:, total_pages:)
34
+ @items = items
35
+ @page = page
36
+ @total = total
37
+ @total_pages = total_pages
38
+ end
39
+ end
40
+
41
+ # Query parameters for filtering and sorting when listing Sandboxes.
42
+ #
43
+ # All fields are optional and default to +nil+. Constructed via keyword
44
+ # arguments and immutable (Ruby 3.2+ Data semantics).
45
+ #
46
+ # @example
47
+ # query = Cogbox::ListSandboxesQuery.new(labels: { 'env' => 'prod' }, limit: 10)
48
+ # cogbox.list(query).each { |sandbox| puts sandbox.id }
49
+ ListSandboxesQuery = Data.define(
50
+ :limit,
51
+ :id,
52
+ :name,
53
+ :labels,
54
+ :states,
55
+ :snapshots,
56
+ :targets,
57
+ :min_cpu,
58
+ :max_cpu,
59
+ :min_memory_gib,
60
+ :max_memory_gib,
61
+ :min_disk_gib,
62
+ :max_disk_gib,
63
+ :is_public,
64
+ :is_recoverable,
65
+ :created_at_after,
66
+ :created_at_before,
67
+ :last_activity_after,
68
+ :last_activity_before,
69
+ :sort,
70
+ :order
71
+ ) do
72
+ # All members default to nil so callers pass only the filters they care about.
73
+ DEFAULTS = members.to_h { |m| [m, nil] }.freeze
74
+
75
+ class << self
76
+ alias_method :_data_new, :new
77
+ def new(**attrs) = _data_new(**DEFAULTS, **attrs)
78
+ end
79
+
80
+ # Idiomatic Ruby boolean predicate aliases.
81
+ def public? = is_public
82
+ def recoverable? = is_recoverable
83
+ end
84
+ end
@@ -0,0 +1,125 @@
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 CreateSnapshotParams
9
+ # @return [String] Name of the snapshot
10
+ attr_reader :name
11
+
12
+ # @return [String, Cogbox::Image] Image of the snapshot. If a string is provided,
13
+ # it should be available on some registry. If an Image instance is provided,
14
+ # it will be used to create a new image in Cogbox.
15
+ attr_reader :image
16
+
17
+ # @return [Cogbox::Resources, nil] Resources of the snapshot
18
+ attr_reader :resources
19
+
20
+ # @return [Array<String>, nil] Entrypoint of the snapshot
21
+ attr_reader :entrypoint
22
+
23
+ # @return [String, nil] ID of the region where the snapshot will be available.
24
+ # Defaults to organization default region if not specified.
25
+ attr_reader :region_id
26
+
27
+ # @return [CogboxApiClient::SandboxClass, nil] Target sandbox class.
28
+ attr_reader :sandbox_class
29
+
30
+ # @param name [String] Name of the snapshot
31
+ # @param image [String, Cogbox::Image] Image of the snapshot
32
+ # @param resources [Cogbox::Resources, nil] Resources of the snapshot
33
+ # @param entrypoint [Array<String>, nil] Entrypoint of the snapshot
34
+ # @param region_id [String, nil] ID of the region where the snapshot will be available
35
+ # @param sandbox_class [CogboxApiClient::SandboxClass, nil] Target sandbox class
36
+ def initialize(name:, image:, resources: nil, entrypoint: nil, region_id: nil, sandbox_class: nil)
37
+ @name = name
38
+ @image = image
39
+ @resources = resources
40
+ @entrypoint = entrypoint
41
+ @region_id = region_id
42
+ @sandbox_class = sandbox_class
43
+ end
44
+ end
45
+
46
+ class Snapshot
47
+ # @return [String] Unique identifier for the Snapshot
48
+ attr_reader :id
49
+
50
+ # @return [String, nil] Organization ID of the Snapshot
51
+ attr_reader :organization_id
52
+
53
+ # @return [Boolean, nil] Whether the Snapshot is general
54
+ attr_reader :general
55
+
56
+ # @return [String] Name of the Snapshot
57
+ attr_reader :name
58
+
59
+ # @return [String] Name of the Image of the Snapshot
60
+ attr_reader :image_name
61
+
62
+ # @return [String] State of the Snapshot
63
+ attr_reader :state
64
+
65
+ # @return [Float, nil] Size of the Snapshot
66
+ attr_reader :size
67
+
68
+ # @return [Array<String>, nil] Entrypoint of the Snapshot
69
+ attr_reader :entrypoint
70
+
71
+ # @return [Float] CPU of the Snapshot
72
+ attr_reader :cpu
73
+
74
+ # @return [Float] GPU of the Snapshot
75
+ attr_reader :gpu
76
+
77
+ # @return [Float] Memory of the Snapshot in GiB
78
+ attr_reader :mem
79
+
80
+ # @return [Float] Disk of the Snapshot in GiB
81
+ attr_reader :disk
82
+
83
+ # @return [String, nil] Error reason of the Snapshot
84
+ attr_reader :error_reason
85
+
86
+ # @return [String] Timestamp when the Snapshot was created
87
+ attr_reader :created_at
88
+
89
+ # @return [String] Timestamp when the Snapshot was last updated
90
+ attr_reader :updated_at
91
+
92
+ # @return [String, nil] Timestamp when the Snapshot was last used
93
+ attr_reader :last_used_at
94
+
95
+ # @return [CogboxApiClient::BuildInfo, nil] Build information for the snapshot
96
+ attr_reader :build_info
97
+
98
+ # @param snapshot_dto [CogboxApiClient::SnapshotDto] The snapshot DTO from the API
99
+ def initialize(snapshot_dto) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
100
+ @id = snapshot_dto.id
101
+ @organization_id = snapshot_dto.organization_id
102
+ @general = snapshot_dto.general
103
+ @name = snapshot_dto.name
104
+ @image_name = snapshot_dto.image_name
105
+ @state = snapshot_dto.state
106
+ @size = snapshot_dto.size
107
+ @entrypoint = snapshot_dto.entrypoint
108
+ @cpu = snapshot_dto.cpu
109
+ @gpu = snapshot_dto.gpu
110
+ @mem = snapshot_dto.mem
111
+ @disk = snapshot_dto.disk
112
+ @error_reason = snapshot_dto.error_reason
113
+ @created_at = snapshot_dto.created_at
114
+ @updated_at = snapshot_dto.updated_at
115
+ @last_used_at = snapshot_dto.last_used_at
116
+ @build_info = snapshot_dto.build_info
117
+ end
118
+
119
+ # Creates a Snapshot instance from a SnapshotDto
120
+ #
121
+ # @param dto [CogboxApiClient::SnapshotDto] The snapshot DTO from the API
122
+ # @return [Cogbox::Snapshot] The snapshot instance
123
+ def self.from_dto(dto) = new(dto)
124
+ end
125
+ end