daytona 0.200.1 → 0.201.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.
data/lib/daytona/git.rb CHANGED
@@ -47,8 +47,8 @@ module Daytona
47
47
  toolbox_api.add_files(DaytonaToolboxApiClient::GitAddRequest.new(path:, files:))
48
48
  rescue DaytonaToolboxApiClient::ApiError => e
49
49
  raise map_api_error(e, 'Failed to add files')
50
- rescue StandardError => e
51
- raise Sdk::Error, "Failed to add files: #{e.message}"
50
+ rescue *Sdk::API_ERROR_CLASSES => e
51
+ raise Sdk.wrap_error(e, 'Failed to add files')
52
52
  end
53
53
 
54
54
  # Lists branches in the repository.
@@ -65,8 +65,8 @@ module Daytona
65
65
  toolbox_api.list_branches(path)
66
66
  rescue DaytonaToolboxApiClient::ApiError => e
67
67
  raise map_api_error(e, 'Failed to list branches')
68
- rescue StandardError => e
69
- raise Sdk::Error, "Failed to list branches: #{e.message}"
68
+ rescue *Sdk::API_ERROR_CLASSES => e
69
+ raise Sdk.wrap_error(e, 'Failed to list branches')
70
70
  end
71
71
 
72
72
  # Clones a Git repository into the specified path. It supports
@@ -126,8 +126,8 @@ module Daytona
126
126
  )
127
127
  rescue DaytonaToolboxApiClient::ApiError => e
128
128
  raise map_api_error(e, 'Failed to clone repository')
129
- rescue StandardError => e
130
- raise Sdk::Error, "Failed to clone repository: #{e.message}"
129
+ rescue *Sdk::API_ERROR_CLASSES => e
130
+ raise Sdk.wrap_error(e, 'Failed to clone repository')
131
131
  end
132
132
 
133
133
  # Creates a new commit with the staged changes. Make sure to stage
@@ -160,8 +160,8 @@ module Daytona
160
160
  GitCommitResponse.new(sha: response._hash)
161
161
  rescue DaytonaToolboxApiClient::ApiError => e
162
162
  raise map_api_error(e, 'Failed to commit changes')
163
- rescue StandardError => e
164
- raise Sdk::Error, "Failed to commit changes: #{e.message}"
163
+ rescue *Sdk::API_ERROR_CLASSES => e
164
+ raise Sdk.wrap_error(e, 'Failed to commit changes')
165
165
  end
166
166
 
167
167
  # Pushes all local commits on the current branch to the remote
@@ -197,8 +197,8 @@ module Daytona
197
197
  )
198
198
  rescue DaytonaToolboxApiClient::ApiError => e
199
199
  raise map_api_error(e, 'Failed to push changes')
200
- rescue StandardError => e
201
- raise Sdk::Error, "Failed to push changes: #{e.message}"
200
+ rescue *Sdk::API_ERROR_CLASSES => e
201
+ raise Sdk.wrap_error(e, 'Failed to push changes')
202
202
  end
203
203
 
204
204
  # Pulls changes from the remote repository. If the remote repository requires authentication,
@@ -232,8 +232,8 @@ module Daytona
232
232
  )
233
233
  rescue DaytonaToolboxApiClient::ApiError => e
234
234
  raise map_api_error(e, 'Failed to pull changes')
235
- rescue StandardError => e
236
- raise Sdk::Error, "Failed to pull changes: #{e.message}"
235
+ rescue *Sdk::API_ERROR_CLASSES => e
236
+ raise Sdk.wrap_error(e, 'Failed to pull changes')
237
237
  end
238
238
 
239
239
  # Gets the current Git repository status.
@@ -252,8 +252,8 @@ module Daytona
252
252
  toolbox_api.get_status(path)
253
253
  rescue DaytonaToolboxApiClient::ApiError => e
254
254
  raise map_api_error(e, 'Failed to get status')
255
- rescue StandardError => e
256
- raise Sdk::Error, "Failed to get status: #{e.message}"
255
+ rescue *Sdk::API_ERROR_CLASSES => e
256
+ raise Sdk.wrap_error(e, 'Failed to get status')
257
257
  end
258
258
 
259
259
  # Checkout branch in the repository.
@@ -273,8 +273,8 @@ module Daytona
273
273
  )
274
274
  rescue DaytonaToolboxApiClient::ApiError => e
275
275
  raise map_api_error(e, 'Failed to checkout branch')
276
- rescue StandardError => e
277
- raise Sdk::Error, "Failed to checkout branch: #{e.message}"
276
+ rescue *Sdk::API_ERROR_CLASSES => e
277
+ raise Sdk.wrap_error(e, 'Failed to checkout branch')
278
278
  end
279
279
 
280
280
  # Create branch in the repository.
@@ -295,8 +295,8 @@ module Daytona
295
295
  )
296
296
  rescue DaytonaToolboxApiClient::ApiError => e
297
297
  raise map_api_error(e, 'Failed to create branch')
298
- rescue StandardError => e
299
- raise Sdk::Error, "Failed to create branch: #{e.message}"
298
+ rescue *Sdk::API_ERROR_CLASSES => e
299
+ raise Sdk.wrap_error(e, 'Failed to create branch')
300
300
  end
301
301
 
302
302
  # Delete branch in the repository.
@@ -316,8 +316,8 @@ module Daytona
316
316
  )
317
317
  rescue DaytonaToolboxApiClient::ApiError => e
318
318
  raise map_api_error(e, 'Failed to delete branch')
319
- rescue StandardError => e
320
- raise Sdk::Error, "Failed to delete branch: #{e.message}"
319
+ rescue *Sdk::API_ERROR_CLASSES => e
320
+ raise Sdk.wrap_error(e, 'Failed to delete branch')
321
321
  end
322
322
 
323
323
  # Initializes a new Git repository at the specified path.
@@ -544,17 +544,7 @@ module Daytona
544
544
  attr_reader :otel_state
545
545
 
546
546
  def map_api_error(api_error, prefix)
547
- msg = "#{prefix}: #{api_error.message}"
548
- case api_error.code
549
- when 400 then Sdk::ValidationError.new(msg)
550
- when 401 then Sdk::AuthenticationError.new(msg)
551
- when 403 then Sdk::ForbiddenError.new(msg)
552
- when 404 then Sdk::NotFoundError.new(msg)
553
- when 409 then Sdk::ConflictError.new(msg)
554
- when 429 then Sdk::RateLimitError.new(msg)
555
- when 500..599 then Sdk::ServerError.new(msg)
556
- else Sdk::Error.new(msg)
557
- end
547
+ Sdk.wrap_error(api_error, prefix)
558
548
  end
559
549
  end
560
550
  end
@@ -59,6 +59,8 @@ module Daytona
59
59
  position: DaytonaToolboxApiClient::LspPosition.new(line: position.line, character: position.character)
60
60
  )
61
61
  )
62
+ rescue *Sdk::API_ERROR_CLASSES => e
63
+ raise Sdk.wrap_error(e, 'Failed to get LSP completions')
62
64
  end
63
65
 
64
66
  # Notify the language server that a file has been closed.
@@ -71,6 +73,8 @@ module Daytona
71
73
  toolbox_api.did_close(
72
74
  DaytonaToolboxApiClient::LspDocumentRequest.new(language_id:, path_to_project:, uri: uri(path))
73
75
  )
76
+ rescue *Sdk::API_ERROR_CLASSES => e
77
+ raise Sdk.wrap_error(e, 'Failed to notify LSP server of closed document')
74
78
  end
75
79
 
76
80
  # Notifies the language server that a file has been opened.
@@ -84,20 +88,30 @@ module Daytona
84
88
  toolbox_api.did_open(
85
89
  DaytonaToolboxApiClient::LspDocumentRequest.new(language_id:, path_to_project:, uri: uri(path))
86
90
  )
91
+ rescue *Sdk::API_ERROR_CLASSES => e
92
+ raise Sdk.wrap_error(e, 'Failed to notify LSP server of opened document')
87
93
  end
88
94
 
89
95
  # Gets symbol information (functions, classes, variables, etc.) from a document.
90
96
  #
91
97
  # @param path [String]
92
98
  # @return [Array<DaytonaToolboxApiClient::LspSymbol]
93
- def document_symbols(path) = toolbox_api.document_symbols(language_id, path_to_project, uri(path))
99
+ def document_symbols(path)
100
+ toolbox_api.document_symbols(language_id, path_to_project, uri(path))
101
+ rescue *Sdk::API_ERROR_CLASSES => e
102
+ raise Sdk.wrap_error(e, 'Failed to get LSP document symbols')
103
+ end
94
104
 
95
105
  # Searches for symbols matching the query string across all files
96
106
  # in the Sandbox.
97
107
  #
98
108
  # @param query [String]
99
109
  # @return [Array<DaytonaToolboxApiClient::LspSymbol]
100
- def sandbox_symbols(query) = toolbox_api.workspace_symbols(query, language_id, path_to_project)
110
+ def sandbox_symbols(query)
111
+ toolbox_api.workspace_symbols(query, language_id, path_to_project)
112
+ rescue *Sdk::API_ERROR_CLASSES => e
113
+ raise Sdk.wrap_error(e, 'Failed to get LSP workspace symbols')
114
+ end
101
115
 
102
116
  # Starts the language server.
103
117
  # This method must be called before using any other LSP functionality.
@@ -108,6 +122,8 @@ module Daytona
108
122
  toolbox_api.start(
109
123
  DaytonaToolboxApiClient::LspServerRequest.new(language_id:, path_to_project:)
110
124
  )
125
+ rescue *Sdk::API_ERROR_CLASSES => e
126
+ raise Sdk.wrap_error(e, 'Failed to start LSP server')
111
127
  end
112
128
 
113
129
  # Stops the language server.
@@ -119,6 +135,8 @@ module Daytona
119
135
  toolbox_api.stop(
120
136
  DaytonaToolboxApiClient::LspServerRequest.new(language_id:, path_to_project:)
121
137
  )
138
+ rescue *Sdk::API_ERROR_CLASSES => e
139
+ raise Sdk.wrap_error(e, 'Failed to stop LSP server')
122
140
  end
123
141
 
124
142
  instrument :completions, :did_close, :did_open, :document_symbols, :sandbox_symbols,
@@ -25,9 +25,9 @@ module Daytona
25
25
  # @param aws_secret_access_key [String] The secret access key for the object storage service
26
26
  # @param aws_session_token [String] The session token for the object storage service
27
27
  # @param bucket_name [String] The name of the bucket to use (defaults to "daytona-volume-builds")
28
- # @param region [String] AWS region (defaults to us-east-1)
28
+ # @param region [String] Region of the storage backend
29
29
  def initialize(endpoint_url:, aws_access_key_id:, aws_secret_access_key:, aws_session_token:, # rubocop:disable Metrics/ParameterLists
30
- bucket_name: DEFAULT_BUCKET_NAME, region: DEFAULT_REGION)
30
+ region:, bucket_name: DEFAULT_BUCKET_NAME)
31
31
  @bucket_name = bucket_name
32
32
  @s3_client = Aws::S3::Client.new(
33
33
  region:,
@@ -165,8 +165,5 @@ module Daytona
165
165
 
166
166
  DEFAULT_BUCKET_NAME = 'daytona-volume-builds'
167
167
  private_constant :DEFAULT_BUCKET_NAME
168
-
169
- DEFAULT_REGION = 'us-east-1'
170
- private_constant :DEFAULT_REGION
171
168
  end
172
169
  end
@@ -68,6 +68,8 @@ module Daytona
68
68
  result:,
69
69
  artifacts: ExecutionArtifacts.new(result, [])
70
70
  )
71
+ rescue *Sdk::API_ERROR_CLASSES => e
72
+ raise Sdk.wrap_error(e, 'Failed to execute command')
71
73
  end
72
74
 
73
75
  # Execute code in the Sandbox using the appropriate language runtime
@@ -99,6 +101,8 @@ module Daytona
99
101
  Charts.parse_chart(c)
100
102
  end)
101
103
  )
104
+ rescue *Sdk::API_ERROR_CLASSES => e
105
+ raise Sdk.wrap_error(e, 'Failed to run code')
102
106
  end
103
107
 
104
108
  # Creates a new long-running background session in the Sandbox
@@ -118,6 +122,8 @@ module Daytona
118
122
  # sandbox.process.delete_session(session_id)
119
123
  def create_session(session_id)
120
124
  toolbox_api.create_session(DaytonaToolboxApiClient::CreateSessionRequest.new(session_id:))
125
+ rescue *Sdk::API_ERROR_CLASSES => e
126
+ raise Sdk.wrap_error(e, 'Failed to create session')
121
127
  end
122
128
 
123
129
  # Gets a session in the Sandbox
@@ -130,7 +136,11 @@ module Daytona
130
136
  # session.commands.each do |cmd|
131
137
  # puts "Command: #{cmd.command}"
132
138
  # end
133
- def get_session(session_id) = toolbox_api.get_session(session_id)
139
+ def get_session(session_id)
140
+ toolbox_api.get_session(session_id)
141
+ rescue *Sdk::API_ERROR_CLASSES => e
142
+ raise Sdk.wrap_error(e, 'Failed to get session')
143
+ end
134
144
 
135
145
  # Gets the Sandbox entrypoint session
136
146
  #
@@ -141,7 +151,11 @@ module Daytona
141
151
  # session.commands.each do |cmd|
142
152
  # puts "Command: #{cmd.command}"
143
153
  # end
144
- def get_entrypoint_session = toolbox_api.get_entrypoint_session
154
+ def get_entrypoint_session
155
+ toolbox_api.get_entrypoint_session
156
+ rescue *Sdk::API_ERROR_CLASSES => e
157
+ raise Sdk.wrap_error(e, 'Failed to get entrypoint session')
158
+ end
145
159
 
146
160
  # Gets information about a specific command executed in a session
147
161
  #
@@ -156,6 +170,8 @@ module Daytona
156
170
  # end
157
171
  def get_session_command(session_id:, command_id:)
158
172
  toolbox_api.get_session_command(session_id, command_id)
173
+ rescue *Sdk::API_ERROR_CLASSES => e
174
+ raise Sdk.wrap_error(e, 'Failed to get session command')
159
175
  end
160
176
 
161
177
  # Executes a command in the session
@@ -196,6 +212,8 @@ module Daytona
196
212
  exit_code: response.exit_code,
197
213
  additional_properties: {}
198
214
  )
215
+ rescue *Sdk::API_ERROR_CLASSES => e
216
+ raise Sdk.wrap_error(e, 'Failed to execute session command')
199
217
  end
200
218
 
201
219
  # Get the logs for a command executed in a session
@@ -211,6 +229,8 @@ module Daytona
211
229
  def get_session_command_logs(session_id:, command_id:)
212
230
  response = toolbox_api.get_session_command_logs(session_id, command_id)
213
231
  SessionCommandLogsResponse.new(output: response.output, stdout: response.stdout, stderr: response.stderr)
232
+ rescue *Sdk::API_ERROR_CLASSES => e
233
+ raise Sdk.wrap_error(e, 'Failed to get session command logs')
214
234
  end
215
235
 
216
236
  # Asynchronously retrieves and processes the logs for a command executed in a session as they become available
@@ -282,6 +302,8 @@ module Daytona
282
302
  def get_entrypoint_logs
283
303
  response = toolbox_api.get_entrypoint_logs
284
304
  SessionCommandLogsResponse.new(output: response.output, stdout: response.stdout, stderr: response.stderr)
305
+ rescue *Sdk::API_ERROR_CLASSES => e
306
+ raise Sdk.wrap_error(e, 'Failed to get entrypoint logs')
285
307
  end
286
308
 
287
309
  # Asynchronously retrieves and processes the sandbox entrypoint logs as they become available
@@ -353,6 +375,8 @@ module Daytona
353
375
  command_id,
354
376
  DaytonaToolboxApiClient::SessionSendInputRequest.new(data:)
355
377
  )
378
+ rescue *Sdk::API_ERROR_CLASSES => e
379
+ raise Sdk.wrap_error(e, 'Failed to send session command input')
356
380
  end
357
381
 
358
382
  #
@@ -364,7 +388,11 @@ module Daytona
364
388
  # puts "Session #{session.session_id}:"
365
389
  # puts " Commands: #{session.commands.length}"
366
390
  # end
367
- def list_sessions = toolbox_api.list_sessions
391
+ def list_sessions
392
+ toolbox_api.list_sessions
393
+ rescue *Sdk::API_ERROR_CLASSES => e
394
+ raise Sdk.wrap_error(e, 'Failed to list sessions')
395
+ end
368
396
 
369
397
  # Terminates and removes a session from the Sandbox, cleaning up any resources associated with it
370
398
  #
@@ -377,7 +405,11 @@ module Daytona
377
405
  #
378
406
  # # Clean up when done
379
407
  # sandbox.process.delete_session("temp-session")
380
- def delete_session(session_id) = toolbox_api.delete_session(session_id)
408
+ def delete_session(session_id)
409
+ toolbox_api.delete_session(session_id)
410
+ rescue *Sdk::API_ERROR_CLASSES => e
411
+ raise Sdk.wrap_error(e, 'Failed to delete session')
412
+ end
381
413
 
382
414
  # Creates a new PTY (pseudo-terminal) session in the Sandbox.
383
415
  #
@@ -499,6 +531,8 @@ module Daytona
499
531
  rows: pty_size.rows
500
532
  )
501
533
  )
534
+ rescue *Sdk::API_ERROR_CLASSES => e
535
+ raise Sdk.wrap_error(e, 'Failed to resize PTY session')
502
536
  end
503
537
 
504
538
  # Deletes a PTY session, terminating the associated process
@@ -510,6 +544,8 @@ module Daytona
510
544
  # sandbox.process.delete_pty_session("my-pty")
511
545
  def delete_pty_session(session_id)
512
546
  toolbox_api.delete_pty_session(session_id)
547
+ rescue *Sdk::API_ERROR_CLASSES => e
548
+ raise Sdk.wrap_error(e, 'Failed to delete PTY session')
513
549
  end
514
550
 
515
551
  # Lists all PTY sessions in the Sandbox
@@ -526,6 +562,8 @@ module Daytona
526
562
  return [] if response.nil?
527
563
 
528
564
  response.respond_to?(:sessions) ? (response.sessions || []) : response
565
+ rescue *Sdk::API_ERROR_CLASSES => e
566
+ raise Sdk.wrap_error(e, 'Failed to list PTY sessions')
529
567
  end
530
568
 
531
569
  # Gets detailed information about a specific PTY session
@@ -546,6 +584,8 @@ module Daytona
546
584
  # puts "Terminal Size: #{session_info.cols}x#{session_info.rows}"
547
585
  def get_pty_session_info(session_id)
548
586
  toolbox_api.get_pty_session(session_id)
587
+ rescue *Sdk::API_ERROR_CLASSES => e
588
+ raise Sdk.wrap_error(e, 'Failed to get PTY session info')
549
589
  end
550
590
 
551
591
  instrument :exec, :code_run, :create_session, :get_session, :get_session_command,
@@ -5,6 +5,7 @@
5
5
 
6
6
  require 'time'
7
7
  require 'timeout'
8
+ require_relative 'utils/file_url_signing'
8
9
 
9
10
  module Daytona
10
11
  # A single point-in-time sample of historical Sandbox resource usage.
@@ -196,6 +197,8 @@ module Daytona
196
197
  @analytics_api_url_provider = analytics_api_url_provider
197
198
  @subscription_manager = subscription_manager
198
199
  @sub_id = nil
200
+ @signing_key = nil
201
+ @signing_key_fetched_at = 0
199
202
 
200
203
  # Create toolbox API clients with dynamic configuration
201
204
  @toolbox_api_config = build_toolbox_api_config
@@ -435,8 +438,8 @@ module Daytona
435
438
  # puts "Sandbox user home: #{user_home_dir}"
436
439
  def get_user_home_dir
437
440
  @info_api.get_user_home_dir.dir
438
- rescue StandardError => e
439
- raise Sdk::Error, "Failed to get user home directory: #{e.message}"
441
+ rescue *Sdk::API_ERROR_CLASSES => e
442
+ raise Sdk.wrap_error(e, 'Failed to get user home directory')
440
443
  end
441
444
 
442
445
  # Gets the working directory path inside the Sandbox.
@@ -449,8 +452,8 @@ module Daytona
449
452
  # puts "Sandbox working directory: #{work_dir}"
450
453
  def get_work_dir
451
454
  @info_api.get_work_dir.dir
452
- rescue StandardError => e
453
- raise Sdk::Error, "Failed to get working directory path: #{e.message}"
455
+ rescue *Sdk::API_ERROR_CLASSES => e
456
+ raise Sdk.wrap_error(e, 'Failed to get working directory path')
454
457
  end
455
458
 
456
459
  # Updates the Sandbox daemon's process environment. Newly spawned processes, sessions
@@ -524,6 +527,76 @@ module Daytona
524
527
  sandbox_api.get_port_preview_url(id, port)
525
528
  end
526
529
 
530
+ # Creates a pre-signed URL for downloading a file from the Sandbox.
531
+ #
532
+ # The URL works with any HTTP client without auth headers and stays valid across
533
+ # sandbox restarts (downloads succeed only while the sandbox is running). The signing
534
+ # key is cached locally for up to 15 seconds; if the key was rotated from another
535
+ # client, URLs may be rejected until the cache refreshes.
536
+ #
537
+ # @param path [String] Path to the file in the Sandbox.
538
+ # @param ttl_seconds [Integer, nil] How long the URL stays valid, in seconds.
539
+ # Defaults to 3600. Zero or negative means the URL never expires.
540
+ # @return [String] Pre-signed download URL.
541
+ # @raise [Daytona::Sdk::Error] if the signing key cannot be fetched.
542
+ #
543
+ # @example
544
+ # url = sandbox.download_url('/home/user/report.pdf')
545
+ # # curl "$url" -o report.pdf
546
+ def download_url(path, ttl_seconds: nil)
547
+ Utils::FileUrlSigning.build_signed_file_url(
548
+ @toolbox_proxy_url,
549
+ id,
550
+ '/files/download',
551
+ 'GET',
552
+ path,
553
+ ensure_signing_key,
554
+ ttl_seconds
555
+ )
556
+ end
557
+
558
+ # Creates a pre-signed URL for uploading a file to the Sandbox.
559
+ #
560
+ # Send a POST request with the file as multipart/form-data. The URL works with any
561
+ # HTTP client without auth headers. The signing key is cached locally for up to
562
+ # 15 seconds; if the key was rotated from another client, URLs may be rejected
563
+ # until the cache refreshes.
564
+ #
565
+ # @param path [String] Destination path for the uploaded file in the Sandbox.
566
+ # @param ttl_seconds [Integer, nil] How long the URL stays valid, in seconds.
567
+ # Defaults to 3600. Zero or negative means the URL never expires.
568
+ # @return [String] Pre-signed upload URL.
569
+ # @raise [Daytona::Sdk::Error] if the signing key cannot be fetched.
570
+ #
571
+ # @example
572
+ # url = sandbox.upload_url('/home/user/data.bin')
573
+ # # curl -X POST -F "file=@local.bin" "$url"
574
+ def upload_url(path, ttl_seconds: nil)
575
+ Utils::FileUrlSigning.build_signed_file_url(
576
+ @toolbox_proxy_url,
577
+ id,
578
+ '/files/upload-v2',
579
+ 'POST',
580
+ path,
581
+ ensure_signing_key,
582
+ ttl_seconds
583
+ )
584
+ end
585
+
586
+ # Rotates the sandbox signing key, invalidating all previously signed URLs.
587
+ #
588
+ # @return [void]
589
+ # @raise [DaytonaApiClient::ApiError] if the signing key rotation request fails.
590
+ #
591
+ # @example
592
+ # sandbox.rotate_signing_key
593
+ # puts 'All previously signed URLs are now invalid.'
594
+ def rotate_signing_key
595
+ @signing_key = @sandbox_api.rotate_signing_key(id)
596
+ @signing_key_fetched_at = Time.now.to_f
597
+ nil
598
+ end
599
+
527
600
  # Creates a signed preview URL for the sandbox at the specified port.
528
601
  #
529
602
  # @param port [Integer] The port to open the preview link on
@@ -570,8 +643,8 @@ module Daytona
570
643
  def refresh_activity
571
644
  sandbox_api.update_last_activity(id)
572
645
  nil
573
- rescue StandardError => e
574
- raise Sdk::Error, "Failed to refresh sandbox activity: #{e.message}"
646
+ rescue *Sdk::API_ERROR_CLASSES => e
647
+ raise Sdk.wrap_error(e, 'Failed to refresh sandbox activity')
575
648
  end
576
649
 
577
650
  # Revokes an SSH access token for the sandbox.
@@ -619,8 +692,8 @@ module Daytona
619
692
  error_states: [DaytonaApiClient::SandboxState::ERROR, DaytonaApiClient::SandboxState::BUILD_FAILED]
620
693
  )
621
694
  end
622
- rescue StandardError => e
623
- raise Sdk::Error, "Failed to recover sandbox: #{e.message}"
695
+ rescue *Sdk::API_ERROR_CLASSES => e
696
+ raise Sdk.wrap_error(e, 'Failed to recover sandbox')
624
697
  end
625
698
 
626
699
  # Stops the Sandbox and waits for it to be stopped.
@@ -841,7 +914,8 @@ module Daytona
841
914
  :update_network_settings, :update_secrets, :update_env,
842
915
  :create_ssh_access, :delete, :get_user_home_dir, :get_work_dir, :get_metrics, :get_metrics_latest,
843
916
  :labels=,
844
- :preview_url, :create_signed_preview_url, :expire_signed_preview_url,
917
+ :preview_url, :download_url, :upload_url, :rotate_signing_key,
918
+ :create_signed_preview_url, :expire_signed_preview_url,
845
919
  :refresh, :refresh_activity, :revoke_ssh_access, :start, :recover, :stop,
846
920
  :create_lsp_server, :validate_ssh_access, :wait_for_sandbox_start,
847
921
  :wait_for_sandbox_stop, :resize, :wait_for_resize_complete,
@@ -964,6 +1038,16 @@ module Daytona
964
1038
  @toolbox_api_config.base_path = uri.path.empty? ? '/' : uri.path
965
1039
  end
966
1040
 
1041
+ def ensure_signing_key
1042
+ key = @signing_key
1043
+ if key.nil? || (Time.now.to_f - @signing_key_fetched_at) > 15
1044
+ key = @sandbox_api.get_sandbox_signing_key(id)
1045
+ @signing_key = key
1046
+ @signing_key_fetched_at = Time.now.to_f
1047
+ end
1048
+ key
1049
+ end
1050
+
967
1051
  # @params sandbox_dto [DaytonaApiClient::Sandbox, DaytonaApiClient::SandboxListItem]
968
1052
  # @return [void]
969
1053
  def process_response(sandbox_dto) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
@@ -0,0 +1,18 @@
1
+ # Copyright Daytona Platforms Inc.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ # frozen_string_literal: true
5
+
6
+ # =============================================================================
7
+ # Backward-compatibility aliases. New functionality belongs in errors.rb, not
8
+ # here. Every name below exists solely so pre-typed-error-model user code
9
+ # keeps working, and this file can be removed as a whole in a future major
10
+ # release.
11
+ # =============================================================================
12
+ module Daytona
13
+ module Sdk
14
+ # @deprecated Use {BadRequestError} instead. Kept as an alias so existing
15
+ # `rescue Daytona::Sdk::ValidationError` blocks keep working.
16
+ ValidationError = BadRequestError
17
+ end
18
+ end