daytona 0.173.0 → 0.175.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 83d12903b0910a3d69cd7042e642fe9cc748ee97ec605ae669e1537f433cbe32
4
- data.tar.gz: af534e94adbe126f60c62b8e27b1a7caa1e67230ab10e055e73c4092e13b2684
3
+ metadata.gz: 17bac4d118263d5f248ac719aafb05357330d337618db08f599b59c5bcfbf584
4
+ data.tar.gz: ac0e288d40bc79b1df009fbb4585face3b667b8b01949e13f556a368fbb3bc1b
5
5
  SHA512:
6
- metadata.gz: b45b939dacd1b20b5864a90f0fdc02dcd2c4f34873b81148bd19de59c748d4b0f959b01136b3d1ad580b4f15fff7fef710c967f68cc66d5ce0dc9370fdbafbcd
7
- data.tar.gz: 69cc63a19e83148b9f36206cf6bc4b59de091105bba7ed4709d815743b5689b29ef9e9c85da45ed332b58039e19a89b7730070f5f3d2b44c3c8563e07926570a
6
+ metadata.gz: 575d0f018a49ebddb4e7dcc2e70e96b762f93ea6e4ad219413506809413f4629935102f870e8b67adb0ed1cb2418d5a4a7e78da946e684b18db1d011611c5c18
7
+ data.tar.gz: b3cdd82c715982e6221263e7b2141cfd1562261d5faf49d45d42738ee0fa2a7cbeaf96a7c0e6c577e1208e41f82547242d0c44f12316fdfff76ceb25377a2cad
data/.rubocop.yml CHANGED
@@ -14,3 +14,9 @@ Style/Documentation:
14
14
 
15
15
  Style/AccessorGrouping:
16
16
  EnforcedStyle: separated
17
+
18
+ # RSpec describe / context blocks naturally span the whole file; the
19
+ # stock 25-line cap forces noise instead of catching real complexity.
20
+ Metrics/BlockLength:
21
+ Exclude:
22
+ - 'spec/**/*'
@@ -162,10 +162,17 @@ module Daytona
162
162
  # based on the sandbox working directory.
163
163
  # @param timeout [Integer] Timeout for the download operation in seconds. 0 means no timeout.
164
164
  # Default is 30 minutes.
165
+ # @param on_progress [Proc, nil] Optional callback invoked with a Daytona::DownloadProgress
166
+ # struct containing bytes_received (Integer) and total_bytes (Integer or nil).
167
+ # @param cancel_event [#set?, nil] Optional cancellation token (anything responding to +set?+;
168
+ # the standard library's +Concurrent::Event+ or a small ad-hoc object both work). When set
169
+ # during streaming, the next chunk raises Daytona::Sdk::Error and the underlying HTTP
170
+ # connection is torn down.
165
171
  # @yield [chunk] Yields each chunk of file content as it arrives
166
172
  # @yieldparam chunk [String] A binary string chunk of file content
167
173
  # @return [Enumerator, nil] An Enumerator yielding chunks if no block given, nil otherwise
168
- # @raise [Daytona::Sdk::Error] If the file does not exist or the operation fails
174
+ # @raise [Daytona::Sdk::Error] If the file does not exist, the operation fails, or
175
+ # +cancel_event+ is set during streaming
169
176
  #
170
177
  # @example Stream to a local file without loading into memory
171
178
  # File.open("local_copy.bin", "wb") do |f|
@@ -175,11 +182,12 @@ module Daytona
175
182
  # @example Collect chunks with an Enumerator
176
183
  # content = sandbox.fs.download_file_stream("workspace/data.json").reduce(:+)
177
184
  # puts content
178
- def download_file_stream(remote_path, timeout: 30 * 60, &)
179
- return enum_for(__method__, remote_path, timeout:) unless block_given?
185
+ def download_file_stream(remote_path, timeout: 30 * 60, on_progress: nil, cancel_event: nil, &)
186
+ return enum_for(__method__, remote_path, timeout:, on_progress:, cancel_event:) unless block_given?
180
187
 
181
188
  FileTransfer.stream_download(api_client: toolbox_api.api_client, remote_path: remote_path,
182
- timeout: timeout, &)
189
+ timeout: timeout, on_progress: on_progress,
190
+ cancel_event: cancel_event, &)
183
191
  nil
184
192
  rescue StandardError => e
185
193
  raise Sdk::Error, "Failed to download file: #{e.message}"
@@ -225,6 +233,36 @@ module Daytona
225
233
  raise Sdk::Error, "Failed to upload file: #{e.message}"
226
234
  end
227
235
 
236
+ # Streams +source+ to the Sandbox without buffering its contents in memory, with
237
+ # optional progress reporting.
238
+ #
239
+ # @param source [String, IO] A local file path or any IO-like object responding to
240
+ # +read(n)+. Strings that don't reference an existing file are uploaded as their
241
+ # raw bytes (still streamed, just from memory).
242
+ # @param remote_path [String] Destination path in the Sandbox.
243
+ # @param timeout [Integer] Timeout in seconds. 0 means no timeout. Default 30 minutes.
244
+ # @param on_progress [Proc, nil] Optional callback invoked with a
245
+ # +Daytona::UploadProgress+ struct as libcurl reports bytes actually uploaded.
246
+ # @param cancel_event [#set?, nil] Optional cancellation token. When set while
247
+ # staging a non-file source or during the libcurl upload, the operation raises
248
+ # Daytona::Sdk::Error and the in-progress upload is aborted (no destination file
249
+ # is left on the sandbox thanks to the daemon's atomic-rename behaviour).
250
+ # @return [void]
251
+ # @raise [Daytona::Sdk::Error] If the operation fails or +cancel_event+ is set.
252
+ #
253
+ # @example
254
+ # File.open("large.bin", "rb") do |f|
255
+ # sandbox.fs.upload_file_stream(f, "tmp/large.bin",
256
+ # on_progress: ->(p) { puts "#{p.bytes_sent} bytes sent" })
257
+ # end
258
+ def upload_file_stream(source, remote_path, timeout: 30 * 60, on_progress: nil, cancel_event: nil)
259
+ FileTransfer.stream_upload(api_client: toolbox_api.api_client, remote_path: remote_path,
260
+ source: source, timeout: timeout, on_progress: on_progress,
261
+ cancel_event: cancel_event)
262
+ rescue StandardError => e
263
+ raise Sdk::Error, "Failed to upload file: #{e.message}"
264
+ end
265
+
228
266
  # Uploads multiple files to the Sandbox. If files already exist at the destination paths,
229
267
  # they will be overwritten.
230
268
  #
@@ -4,11 +4,20 @@
4
4
  # frozen_string_literal: true
5
5
 
6
6
  require 'json'
7
+ require 'stringio'
8
+ require 'tempfile'
7
9
  require 'typhoeus'
8
10
 
9
11
  module Daytona
12
+ # Progress information for a streaming download.
13
+ DownloadProgress = Struct.new(:bytes_received, :total_bytes, keyword_init: true)
14
+
15
+ # Progress information for a streaming upload.
16
+ UploadProgress = Struct.new(:bytes_sent, keyword_init: true)
17
+
10
18
  class MultipartDownloadStreamParser
11
19
  attr_reader :error_message
20
+ attr_reader :part_total_bytes
12
21
  attr_writer :boundary_token
13
22
 
14
23
  def initialize(&on_file_chunk)
@@ -17,6 +26,7 @@ module Daytona
17
26
  @buffer = String.new.b
18
27
  @state = :preamble
19
28
  @part_name = nil
29
+ @part_total_bytes = nil
20
30
  @error_buffer = String.new.b
21
31
  end
22
32
 
@@ -62,14 +72,14 @@ module Daytona
62
72
  end
63
73
 
64
74
  def consume_headers?
65
- separator = "\r\n\r\n".b
66
- index = @buffer.index(separator)
75
+ index = @buffer.index("\r\n\r\n".b)
67
76
  return false unless index
68
77
 
69
78
  headers = @buffer.byteslice(0, index)
70
- @buffer = remaining_bytes(index + separator.bytesize)
71
- @part_name = headers[/Content-Disposition:\s*[^\r\n]*\bname="([^"]+)"/i, 1]
72
- raise Sdk::Error, 'Invalid multipart response' if @part_name.nil?
79
+ @buffer = remaining_bytes(index + 4)
80
+ @part_name = headers[/Content-Disposition:\s*[^\r\n]*\bname="([^"]+)"/i, 1] ||
81
+ raise(Sdk::Error, 'Invalid multipart response')
82
+ @part_total_bytes = headers[/Content-Length:\s*(\d+)/i, 1]&.to_i
73
83
 
74
84
  @state = :body
75
85
  true
@@ -124,16 +134,11 @@ module Daytona
124
134
  false
125
135
  end
126
136
 
127
- def remaining_bytes(offset)
128
- @buffer.byteslice(offset, @buffer.bytesize - offset) || String.new.b
129
- end
130
-
131
- def boundary
132
- "--#{@boundary_token}".b
133
- end
137
+ def remaining_bytes(offset) = @buffer.byteslice(offset, @buffer.bytesize - offset) || String.new.b
138
+ def boundary = "--#{@boundary_token}".b
134
139
  end
135
140
 
136
- module FileTransfer
141
+ module FileTransfer # rubocop:disable Metrics/ModuleLength
137
142
  def self.extract_multipart_boundary(content_type)
138
143
  match = content_type&.match(/boundary=(?:"([^"]+)"|([^;]+))/i)
139
144
  return unless match
@@ -141,9 +146,31 @@ module Daytona
141
146
  match.captures.compact.first
142
147
  end
143
148
 
144
- def self.stream_download(api_client:, remote_path:, timeout:, &) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
149
+ def self.assign_download_boundary(parser, content_type)
150
+ boundary = extract_multipart_boundary(content_type)
151
+ raise Sdk::Error, 'Missing multipart boundary in download response' unless boundary
152
+
153
+ parser.boundary_token = boundary
154
+ end
155
+
156
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
157
+ def self.stream_download(api_client:, remote_path:, timeout:, on_progress: nil, cancel_event: nil, &block)
145
158
  config = api_client.config
146
- parser = MultipartDownloadStreamParser.new(&)
159
+ bytes_received = 0
160
+ parser = nil
161
+ wrapped_block = proc do |chunk|
162
+ raise Sdk::Error, "Download cancelled: #{remote_path}" if cancel_event&.set?
163
+
164
+ if on_progress
165
+ bytes_received += chunk.bytesize
166
+ on_progress.call(DownloadProgress.new(
167
+ bytes_received: bytes_received,
168
+ total_bytes: parser&.part_total_bytes
169
+ ))
170
+ end
171
+ block.call(chunk)
172
+ end
173
+ parser = MultipartDownloadStreamParser.new(&wrapped_block)
147
174
  response = nil
148
175
 
149
176
  request = Typhoeus::Request.new(
@@ -160,13 +187,15 @@ module Daytona
160
187
  )
161
188
 
162
189
  request.on_headers do |stream_response|
163
- boundary = extract_multipart_boundary(stream_response.headers['Content-Type'])
164
- raise Sdk::Error, 'Missing multipart boundary in download response' unless boundary
165
-
166
- parser.boundary_token = boundary
190
+ assign_download_boundary(parser, stream_response.headers['Content-Type'])
167
191
  end
168
192
 
193
+ # Returning +:abort+ from the on_body callback tells libcurl to tear down the
194
+ # connection immediately, which is how cancellation actually severs the
195
+ # transfer rather than just stopping our own bookkeeping.
169
196
  request.on_body do |chunk|
197
+ next :abort if cancel_event&.set?
198
+
170
199
  parser << chunk
171
200
  end
172
201
 
@@ -177,8 +206,143 @@ module Daytona
177
206
 
178
207
  request.run
179
208
 
209
+ raise Sdk::Error, "Download cancelled: #{remote_path}" if cancel_event&.set?
180
210
  raise Sdk::Error, parser.error_message if parser.error_message
181
211
  raise Sdk::Error, "HTTP #{response.code}" if response && !response.success?
182
212
  end
213
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
214
+
215
+ # Uploads +source+ to /files/bulk-upload via Typhoeus (libcurl), which streams the
216
+ # request body straight from disk without buffering it in memory. Local file paths
217
+ # are uploaded directly; in-memory IOs/bytes are first drained to a tempfile so we
218
+ # have a stable file handle for libcurl.
219
+ #
220
+ # The daemon owns atomicity (writes to a sibling tempfile then renames), so a
221
+ # client-side abort just leaves no destination file at all.
222
+ #
223
+ # @param api_client The OpenAPI-generated toolbox API client (auth/base-url only).
224
+ # @param remote_path [String] Destination path in the sandbox.
225
+ # @param source [String, IO] Local file path or any IO-like object responding to +read(n)+.
226
+ # @param timeout [Integer] Typhoeus timeout in seconds (0 disables).
227
+ # @param on_progress [Proc, nil] Optional callback invoked with +Daytona::UploadProgress+
228
+ # as libcurl reports real network upload progress.
229
+ # @param cancel_event [#set?, nil] Optional cancellation token. Checked while staging
230
+ # non-file sources and during the libcurl transfer itself.
231
+ # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
232
+ def self.stream_upload(api_client:, remote_path:, source:, timeout:, on_progress: nil, cancel_event: nil)
233
+ with_upload_file(source, cancel_event, remote_path) do |upload_path|
234
+ config = api_client.config
235
+ progress_callback = upload_progress_callback(on_progress, cancel_event)
236
+ response = with_open_upload_file(upload_path) do |file|
237
+ upload_request(
238
+ api_client: api_client,
239
+ config: config,
240
+ remote_path: remote_path,
241
+ file: file,
242
+ timeout: timeout,
243
+ progress_callback: progress_callback
244
+ ).run
245
+ end
246
+ raise_upload_error(response, cancel_event, remote_path)
247
+ end
248
+ end
249
+ # rubocop:enable Metrics/MethodLength, Metrics/ParameterLists
250
+
251
+ # Yields a path on disk that holds the source's bytes, ready for libcurl to stream.
252
+ # Local files are passed through unchanged; everything else is drained into a
253
+ # tempfile that gets unlinked when we return.
254
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
255
+ def self.with_upload_file(source, cancel_event, remote_path)
256
+ raise Sdk::Error, "Upload cancelled: #{remote_path}" if cancel_event&.set?
257
+
258
+ return yield(source) if source.is_a?(String) && File.exist?(source)
259
+
260
+ tmp = Tempfile.new(['daytona-upload-', File.extname(remote_path).to_s])
261
+ tmp.binmode
262
+ begin
263
+ drain_source_to(source, tmp, cancel_event, remote_path)
264
+ tmp.flush
265
+ tmp.close
266
+ yield(tmp.path)
267
+ ensure
268
+ tmp.close unless tmp.closed?
269
+ begin
270
+ tmp.unlink
271
+ rescue StandardError
272
+ # tempfile already gone, nothing to do
273
+ end
274
+ end
275
+ end
276
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
277
+
278
+ def self.drain_source_to(source, sink, cancel_event, remote_path)
279
+ io, owns_io = open_drain_source(source)
280
+ begin
281
+ while (chunk = io.read(64 * 1024))
282
+ break if chunk.empty?
283
+ raise Sdk::Error, "Upload cancelled: #{remote_path}" if cancel_event&.set?
284
+
285
+ sink.write(chunk)
286
+ end
287
+ ensure
288
+ io.close if owns_io && io.respond_to?(:close)
289
+ end
290
+ end
291
+
292
+ def self.with_open_upload_file(upload_path)
293
+ file = File.open(upload_path, 'rb')
294
+ yield(file)
295
+ ensure
296
+ file.close if file && !file.closed?
297
+ end
298
+
299
+ # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
300
+ def self.upload_request(api_client:, config:, remote_path:, file:, timeout:, progress_callback:)
301
+ Typhoeus::Request.new(
302
+ "#{config.base_url}/files/bulk-upload",
303
+ method: :post,
304
+ headers: api_client.default_headers.dup.tap { |h| h.delete('Content-Type') },
305
+ body: {
306
+ 'files[0].path' => remote_path,
307
+ 'files[0].file' => file
308
+ },
309
+ timeout: timeout,
310
+ ssl_verifypeer: config.verify_ssl,
311
+ ssl_verifyhost: config.verify_ssl_host ? 2 : 0,
312
+ noprogress: false,
313
+ progressfunction: progress_callback,
314
+ xferinfofunction: progress_callback
315
+ )
316
+ end
317
+ # rubocop:enable Metrics/MethodLength, Metrics/ParameterLists
318
+
319
+ def self.upload_progress_callback(on_progress, cancel_event)
320
+ last_bytes_sent = -1
321
+
322
+ proc do |_clientp, _dltotal, _dlnow, _ultotal, ulnow|
323
+ next 1 if cancel_event&.set?
324
+
325
+ bytes_sent = ulnow.to_i
326
+ if on_progress && bytes_sent > last_bytes_sent
327
+ last_bytes_sent = bytes_sent
328
+ on_progress.call(UploadProgress.new(bytes_sent: bytes_sent))
329
+ end
330
+
331
+ 0
332
+ end
333
+ end
334
+
335
+ def self.raise_upload_error(response, _cancel_event, remote_path)
336
+ raise Sdk::Error, "Upload timed out: #{remote_path}" if response.timed_out?
337
+ raise Sdk::Error, "Upload cancelled: #{remote_path}" if response.return_code == :aborted_by_callback
338
+ raise Sdk::Error, "HTTP #{response.code}: #{response.body}" unless response.success?
339
+ end
340
+
341
+ def self.open_drain_source(source)
342
+ return [source, false] if source.respond_to?(:read)
343
+ return [StringIO.new(source.b), true] if source.is_a?(String)
344
+
345
+ raise Sdk::Error, "Unsupported upload source: #{source.class}"
346
+ end
183
347
  end
184
348
  end
data/lib/daytona/git.rb CHANGED
@@ -45,6 +45,8 @@ module Daytona
45
45
  # ])
46
46
  def add(path, files)
47
47
  toolbox_api.add_files(DaytonaToolboxApiClient::GitAddRequest.new(path:, files:))
48
+ rescue DaytonaToolboxApiClient::ApiError => e
49
+ raise map_api_error(e, 'Failed to add files')
48
50
  rescue StandardError => e
49
51
  raise Sdk::Error, "Failed to add files: #{e.message}"
50
52
  end
@@ -61,6 +63,8 @@ module Daytona
61
63
  # puts "Branches: #{response.branches}"
62
64
  def branches(path)
63
65
  toolbox_api.list_branches(path)
66
+ rescue DaytonaToolboxApiClient::ApiError => e
67
+ raise map_api_error(e, 'Failed to list branches')
64
68
  rescue StandardError => e
65
69
  raise Sdk::Error, "Failed to list branches: #{e.message}"
66
70
  end
@@ -114,6 +118,8 @@ module Daytona
114
118
  commit_id: commit_id
115
119
  )
116
120
  )
121
+ rescue DaytonaToolboxApiClient::ApiError => e
122
+ raise map_api_error(e, 'Failed to clone repository')
117
123
  rescue StandardError => e
118
124
  raise Sdk::Error, "Failed to clone repository: #{e.message}"
119
125
  end
@@ -146,6 +152,8 @@ module Daytona
146
152
  DaytonaToolboxApiClient::GitCommitRequest.new(path:, message:, author:, email:, allow_empty:)
147
153
  )
148
154
  GitCommitResponse.new(sha: response._hash)
155
+ rescue DaytonaToolboxApiClient::ApiError => e
156
+ raise map_api_error(e, 'Failed to commit changes')
149
157
  rescue StandardError => e
150
158
  raise Sdk::Error, "Failed to commit changes: #{e.message}"
151
159
  end
@@ -175,6 +183,8 @@ module Daytona
175
183
  toolbox_api.push_changes(
176
184
  DaytonaToolboxApiClient::GitRepoRequest.new(path:, username:, password:)
177
185
  )
186
+ rescue DaytonaToolboxApiClient::ApiError => e
187
+ raise map_api_error(e, 'Failed to push changes')
178
188
  rescue StandardError => e
179
189
  raise Sdk::Error, "Failed to push changes: #{e.message}"
180
190
  end
@@ -204,6 +214,8 @@ module Daytona
204
214
  toolbox_api.pull_changes(
205
215
  DaytonaToolboxApiClient::GitRepoRequest.new(path:, username:, password:)
206
216
  )
217
+ rescue DaytonaToolboxApiClient::ApiError => e
218
+ raise map_api_error(e, 'Failed to pull changes')
207
219
  rescue StandardError => e
208
220
  raise Sdk::Error, "Failed to pull changes: #{e.message}"
209
221
  end
@@ -222,6 +234,8 @@ module Daytona
222
234
  # puts "Commits behind: #{status.behind}"
223
235
  def status(path)
224
236
  toolbox_api.get_status(path)
237
+ rescue DaytonaToolboxApiClient::ApiError => e
238
+ raise map_api_error(e, 'Failed to get status')
225
239
  rescue StandardError => e
226
240
  raise Sdk::Error, "Failed to get status: #{e.message}"
227
241
  end
@@ -241,6 +255,8 @@ module Daytona
241
255
  toolbox_api.checkout_branch(
242
256
  DaytonaToolboxApiClient::GitCheckoutRequest.new(path:, branch:)
243
257
  )
258
+ rescue DaytonaToolboxApiClient::ApiError => e
259
+ raise map_api_error(e, 'Failed to checkout branch')
244
260
  rescue StandardError => e
245
261
  raise Sdk::Error, "Failed to checkout branch: #{e.message}"
246
262
  end
@@ -261,6 +277,8 @@ module Daytona
261
277
  toolbox_api.create_branch(
262
278
  DaytonaToolboxApiClient::GitBranchRequest.new(path:, name:)
263
279
  )
280
+ rescue DaytonaToolboxApiClient::ApiError => e
281
+ raise map_api_error(e, 'Failed to create branch')
264
282
  rescue StandardError => e
265
283
  raise Sdk::Error, "Failed to create branch: #{e.message}"
266
284
  end
@@ -280,6 +298,8 @@ module Daytona
280
298
  toolbox_api.delete_branch(
281
299
  DaytonaToolboxApiClient::GitDeleteBranchRequest.new(path:, name:)
282
300
  )
301
+ rescue DaytonaToolboxApiClient::ApiError => e
302
+ raise map_api_error(e, 'Failed to delete branch')
283
303
  rescue StandardError => e
284
304
  raise Sdk::Error, "Failed to delete branch: #{e.message}"
285
305
  end
@@ -292,5 +312,19 @@ module Daytona
292
312
 
293
313
  # @return [Daytona::OtelState, nil]
294
314
  attr_reader :otel_state
315
+
316
+ def map_api_error(api_error, prefix)
317
+ msg = "#{prefix}: #{api_error.message}"
318
+ case api_error.code
319
+ when 400 then Sdk::ValidationError.new(msg)
320
+ when 401 then Sdk::AuthenticationError.new(msg)
321
+ when 403 then Sdk::ForbiddenError.new(msg)
322
+ when 404 then Sdk::NotFoundError.new(msg)
323
+ when 409 then Sdk::ConflictError.new(msg)
324
+ when 429 then Sdk::RateLimitError.new(msg)
325
+ when 500..599 then Sdk::ServerError.new(msg)
326
+ else Sdk::Error.new(msg)
327
+ end
328
+ end
295
329
  end
296
330
  end
@@ -5,6 +5,6 @@
5
5
 
6
6
  module Daytona
7
7
  module Sdk
8
- VERSION = '0.173.0'
8
+ VERSION = '0.175.0'
9
9
  end
10
10
  end
data/lib/daytona/sdk.rb CHANGED
@@ -43,6 +43,13 @@ module Daytona
43
43
  module Sdk
44
44
  class Error < StandardError; end
45
45
  class TimeoutError < Error; end
46
+ class AuthenticationError < Error; end
47
+ class ForbiddenError < Error; end
48
+ class NotFoundError < Error; end
49
+ class ConflictError < Error; end
50
+ class ValidationError < Error; end
51
+ class RateLimitError < Error; end
52
+ class ServerError < Error; end
46
53
 
47
54
  def self.logger = @logger ||= Logger.new($stdout, level: Logger::INFO)
48
55
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: daytona
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.173.0
4
+ version: 0.175.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daytona Platforms Inc.
@@ -85,28 +85,28 @@ dependencies:
85
85
  requirements:
86
86
  - - '='
87
87
  - !ruby/object:Gem::Version
88
- version: 0.173.0
88
+ version: 0.175.0
89
89
  type: :runtime
90
90
  prerelease: false
91
91
  version_requirements: !ruby/object:Gem::Requirement
92
92
  requirements:
93
93
  - - '='
94
94
  - !ruby/object:Gem::Version
95
- version: 0.173.0
95
+ version: 0.175.0
96
96
  - !ruby/object:Gem::Dependency
97
97
  name: daytona_toolbox_api_client
98
98
  requirement: !ruby/object:Gem::Requirement
99
99
  requirements:
100
100
  - - '='
101
101
  - !ruby/object:Gem::Version
102
- version: 0.173.0
102
+ version: 0.175.0
103
103
  type: :runtime
104
104
  prerelease: false
105
105
  version_requirements: !ruby/object:Gem::Requirement
106
106
  requirements:
107
107
  - - '='
108
108
  - !ruby/object:Gem::Version
109
- version: 0.173.0
109
+ version: 0.175.0
110
110
  - !ruby/object:Gem::Dependency
111
111
  name: dotenv
112
112
  requirement: !ruby/object:Gem::Requirement