daytona 0.200.0 → 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.
- checksums.yaml +4 -4
- data/lib/daytona/code_interpreter.rb +6 -6
- data/lib/daytona/common/event_subscription_manager.rb +110 -60
- data/lib/daytona/common/response.rb +2 -0
- data/lib/daytona/computer_use.rb +62 -62
- data/lib/daytona/daytona.rb +9 -0
- data/lib/daytona/file_system.rb +36 -28
- data/lib/daytona/git.rb +21 -31
- data/lib/daytona/lsp_server.rb +20 -2
- data/lib/daytona/object_storage.rb +2 -5
- data/lib/daytona/process.rb +44 -4
- data/lib/daytona/sandbox.rb +93 -9
- data/lib/daytona/sdk/deprecated.rb +18 -0
- data/lib/daytona/sdk/errors.rb +279 -0
- data/lib/daytona/sdk/file_download_patch.rb +131 -0
- data/lib/daytona/sdk/version.rb +1 -1
- data/lib/daytona/sdk.rb +7 -9
- data/lib/daytona/snapshot_service.rb +2 -1
- data/lib/daytona/utils/file_url_signing.rb +56 -0
- metadata +19 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 684e4304ffae6f21d39f795fbcd75754e0148adf43a86963e3c8303b6b9eb87e
|
|
4
|
+
data.tar.gz: 3d4dc419e7b759cce74db9df2cec0bb36900493c39d08b5640b04dd5c4863b11
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 59a7fea3c20c801d9e3d09bad69f6a4318f5b2ae64d397e2848ee893e8f5e1117c973f626ba825fce6bb4604f2875e1f4d393391b0c7dc8b850459a7d7073bd1
|
|
7
|
+
data.tar.gz: ade5a786482e476b4e3baf42eea6828e0d422ec83e1dc1363130165a8ae09b9a5e0d99be687848d5c50f6e4c0ce09342244ddfa1553d9effbff882ad3bd58267
|
|
@@ -232,8 +232,8 @@ module Daytona
|
|
|
232
232
|
def create_context(cwd: nil)
|
|
233
233
|
request = DaytonaToolboxApiClient::CreateContextRequest.new(cwd:)
|
|
234
234
|
@toolbox_api.create_interpreter_context(request)
|
|
235
|
-
rescue
|
|
236
|
-
raise Sdk
|
|
235
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
236
|
+
raise Sdk.wrap_error(e, 'Failed to create interpreter context')
|
|
237
237
|
end
|
|
238
238
|
|
|
239
239
|
# List all user-created interpreter contexts.
|
|
@@ -252,8 +252,8 @@ module Daytona
|
|
|
252
252
|
def list_contexts
|
|
253
253
|
response = @toolbox_api.list_interpreter_contexts
|
|
254
254
|
response.contexts || []
|
|
255
|
-
rescue
|
|
256
|
-
raise Sdk
|
|
255
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
256
|
+
raise Sdk.wrap_error(e, 'Failed to list interpreter contexts')
|
|
257
257
|
end
|
|
258
258
|
|
|
259
259
|
# Delete an interpreter context and shut down all associated processes.
|
|
@@ -272,8 +272,8 @@ module Daytona
|
|
|
272
272
|
def delete_context(context)
|
|
273
273
|
@toolbox_api.delete_interpreter_context(context.id)
|
|
274
274
|
nil
|
|
275
|
-
rescue
|
|
276
|
-
raise Sdk
|
|
275
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
276
|
+
raise Sdk.wrap_error(e, 'Failed to delete interpreter context')
|
|
277
277
|
end
|
|
278
278
|
|
|
279
279
|
instrument :run_code, :create_context, :list_contexts, :delete_context,
|
|
@@ -6,14 +6,29 @@
|
|
|
6
6
|
require 'securerandom'
|
|
7
7
|
|
|
8
8
|
module Daytona
|
|
9
|
+
# Tracks dispatcher subscriptions with TTL auto-expiry.
|
|
10
|
+
#
|
|
11
|
+
# All subscriptions share ONE lazily started expiry worker thread instead of a
|
|
12
|
+
# dedicated sleeping thread per subscription, so listing many sandboxes costs
|
|
13
|
+
# one thread total, not one thread each
|
|
14
|
+
# (https://github.com/daytona/clients/issues/108). #refresh only bumps the
|
|
15
|
+
# subscription deadline — it never creates or kills threads.
|
|
9
16
|
class EventSubscriptionManager
|
|
10
17
|
SUBSCRIPTION_TTL = 300
|
|
11
18
|
private_constant :SUBSCRIPTION_TTL
|
|
12
19
|
|
|
13
|
-
def initialize(dispatcher = nil)
|
|
20
|
+
def initialize(dispatcher = nil, ttl_seconds: SUBSCRIPTION_TTL)
|
|
14
21
|
@dispatcher = dispatcher
|
|
22
|
+
@ttl = ttl_seconds
|
|
15
23
|
@subscriptions = {}
|
|
24
|
+
# Deadline-ordered [expires_at, sub_id] entries. Entries are lazily
|
|
25
|
+
# invalidated: a popped entry is discarded when its subscription is gone,
|
|
26
|
+
# or re-queued at the current deadline when it was refreshed meanwhile.
|
|
27
|
+
# Invariant: every live subscription has at least one queue entry.
|
|
28
|
+
@expiry_queue = []
|
|
16
29
|
@mutex = Mutex.new
|
|
30
|
+
@cond = ConditionVariable.new
|
|
31
|
+
@worker = nil
|
|
17
32
|
@closed = false
|
|
18
33
|
end
|
|
19
34
|
|
|
@@ -24,7 +39,6 @@ module Daytona
|
|
|
24
39
|
|
|
25
40
|
unsubscribe = @dispatcher.subscribe(resource_id, events:, &handler)
|
|
26
41
|
sub_id = SecureRandom.hex(16)
|
|
27
|
-
timer_to_stop = nil
|
|
28
42
|
rollback_unsubscribe = nil
|
|
29
43
|
|
|
30
44
|
@mutex.synchronize do
|
|
@@ -34,8 +48,11 @@ module Daytona
|
|
|
34
48
|
next
|
|
35
49
|
end
|
|
36
50
|
|
|
37
|
-
|
|
38
|
-
|
|
51
|
+
expires_at = monotonic_now + @ttl
|
|
52
|
+
@subscriptions[sub_id] = { unsubscribe:, expires_at: }
|
|
53
|
+
push_entry_locked(expires_at, sub_id)
|
|
54
|
+
ensure_worker_locked
|
|
55
|
+
@cond.signal
|
|
39
56
|
end
|
|
40
57
|
|
|
41
58
|
if rollback_unsubscribe
|
|
@@ -43,44 +60,36 @@ module Daytona
|
|
|
43
60
|
return nil
|
|
44
61
|
end
|
|
45
62
|
|
|
46
|
-
stop_thread(timer_to_stop)
|
|
47
63
|
sub_id
|
|
48
64
|
end
|
|
49
65
|
|
|
50
66
|
def refresh(sub_id)
|
|
51
67
|
@mutex.synchronize do
|
|
52
|
-
# Reject after shutdown to prevent use-after-close
|
|
53
68
|
return false if @closed
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
timer_to_stop = nil
|
|
57
69
|
|
|
58
|
-
|
|
59
|
-
return false unless
|
|
70
|
+
subscription = @subscriptions[sub_id]
|
|
71
|
+
return false unless subscription
|
|
60
72
|
|
|
61
|
-
|
|
73
|
+
# No queue entry or worker wake-up needed: when the old deadline pops,
|
|
74
|
+
# the worker sees the newer expires_at and re-queues the subscription.
|
|
75
|
+
subscription[:expires_at] = monotonic_now + @ttl
|
|
76
|
+
true
|
|
62
77
|
end
|
|
63
|
-
|
|
64
|
-
stop_thread(timer_to_stop)
|
|
65
|
-
true
|
|
66
78
|
end
|
|
67
79
|
|
|
68
80
|
def unsubscribe(sub_id)
|
|
69
|
-
subscription =
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if
|
|
75
|
-
|
|
76
|
-
|
|
81
|
+
subscription = @mutex.synchronize do
|
|
82
|
+
removed = @subscriptions.delete(sub_id)
|
|
83
|
+
# The stale queue entry is discarded when the worker pops it — except
|
|
84
|
+
# when it was the last subscription: drop the leftovers and wake the
|
|
85
|
+
# worker so it exits now instead of idling until the old deadline.
|
|
86
|
+
if removed && @subscriptions.empty?
|
|
87
|
+
@expiry_queue.clear
|
|
88
|
+
@cond.signal
|
|
77
89
|
end
|
|
90
|
+
removed
|
|
78
91
|
end
|
|
79
|
-
|
|
80
|
-
return unless subscription
|
|
81
|
-
|
|
82
|
-
stop_thread(timer)
|
|
83
|
-
subscription[:unsubscribe].call
|
|
92
|
+
subscription&.dig(:unsubscribe)&.call
|
|
84
93
|
end
|
|
85
94
|
|
|
86
95
|
def shutdown
|
|
@@ -90,55 +99,96 @@ module Daytona
|
|
|
90
99
|
@closed = true
|
|
91
100
|
subscriptions = @subscriptions.values
|
|
92
101
|
@subscriptions = {}
|
|
102
|
+
@expiry_queue.clear
|
|
103
|
+
@cond.broadcast
|
|
93
104
|
end
|
|
94
105
|
|
|
95
|
-
subscriptions.each
|
|
96
|
-
stop_thread(subscription[:timer])
|
|
97
|
-
subscription[:unsubscribe].call
|
|
98
|
-
end
|
|
106
|
+
subscriptions.each { |subscription| subscription[:unsubscribe].call }
|
|
99
107
|
end
|
|
100
108
|
|
|
101
109
|
private
|
|
102
110
|
|
|
103
|
-
def
|
|
104
|
-
|
|
105
|
-
|
|
111
|
+
def monotonic_now
|
|
112
|
+
::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
|
113
|
+
end
|
|
106
114
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
115
|
+
# Caller must hold @mutex.
|
|
116
|
+
def push_entry_locked(expires_at, sub_id)
|
|
117
|
+
index = @expiry_queue.bsearch_index { |entry| entry[0] > expires_at } || @expiry_queue.length
|
|
118
|
+
@expiry_queue.insert(index, [expires_at, sub_id])
|
|
119
|
+
end
|
|
110
120
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
121
|
+
# Caller must hold @mutex. The alive? check is a safety net: a worker that
|
|
122
|
+
# crashed for any reason leaves a dead-but-truthy reference, and a plain nil
|
|
123
|
+
# check would then block replacements forever, silently disabling expiry.
|
|
124
|
+
def ensure_worker_locked
|
|
125
|
+
return if @worker&.alive?
|
|
114
126
|
|
|
115
|
-
|
|
116
|
-
|
|
127
|
+
@worker = Thread.new { expiry_loop }
|
|
128
|
+
@worker.name = 'daytona-subscription-expiry'
|
|
129
|
+
@worker.abort_on_exception = false
|
|
130
|
+
end
|
|
117
131
|
|
|
118
|
-
|
|
132
|
+
def expiry_loop
|
|
133
|
+
loop do
|
|
134
|
+
expired = collect_expired
|
|
135
|
+
return if expired.nil?
|
|
136
|
+
|
|
137
|
+
expired.each do |subscription|
|
|
138
|
+
subscription[:unsubscribe].call
|
|
139
|
+
rescue StandardError
|
|
140
|
+
# One failing dispatcher unsubscribe must not kill the shared worker
|
|
141
|
+
# and silently stop expiry for every other subscription.
|
|
142
|
+
nil
|
|
143
|
+
end
|
|
119
144
|
end
|
|
120
|
-
timer.abort_on_exception = false
|
|
121
|
-
subscription[:timer] = timer
|
|
122
|
-
previous_timer
|
|
123
145
|
end
|
|
124
146
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
147
|
+
# Blocks until at least one subscription expires. Returns nil when the
|
|
148
|
+
# worker should exit (shutdown, or no live subscriptions remain).
|
|
149
|
+
def collect_expired
|
|
150
|
+
@mutex.synchronize do
|
|
151
|
+
expired = []
|
|
152
|
+
|
|
153
|
+
while expired.empty?
|
|
154
|
+
return nil if @closed
|
|
155
|
+
|
|
156
|
+
if @expiry_queue.empty?
|
|
157
|
+
# No live subscriptions remain (see queue invariant above):
|
|
158
|
+
# exit and let the next subscribe start a fresh worker.
|
|
159
|
+
@worker = nil
|
|
160
|
+
return nil
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
now = monotonic_now
|
|
164
|
+
deadline = @expiry_queue.first[0]
|
|
165
|
+
if deadline > now
|
|
166
|
+
@cond.wait(@mutex, deadline - now)
|
|
167
|
+
next
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
drain_due_entries_locked(now, expired)
|
|
171
|
+
end
|
|
128
172
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
timer
|
|
173
|
+
expired
|
|
174
|
+
end
|
|
132
175
|
end
|
|
133
176
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
177
|
+
# Caller must hold @mutex.
|
|
178
|
+
def drain_due_entries_locked(now, expired)
|
|
179
|
+
while !@expiry_queue.empty? && @expiry_queue.first[0] <= now
|
|
180
|
+
_, sub_id = @expiry_queue.shift
|
|
181
|
+
subscription = @subscriptions[sub_id]
|
|
182
|
+
next unless subscription
|
|
137
183
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
184
|
+
if subscription[:expires_at] > now
|
|
185
|
+
push_entry_locked(subscription[:expires_at], sub_id)
|
|
186
|
+
next
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
@subscriptions.delete(sub_id)
|
|
190
|
+
expired << subscription
|
|
191
|
+
end
|
|
142
192
|
end
|
|
143
193
|
end
|
|
144
194
|
end
|
data/lib/daytona/computer_use.rb
CHANGED
|
@@ -33,8 +33,8 @@ module Daytona
|
|
|
33
33
|
# puts "Mouse is at: #{position.x}, #{position.y}"
|
|
34
34
|
def position
|
|
35
35
|
toolbox_api.get_mouse_position
|
|
36
|
-
rescue
|
|
37
|
-
raise Sdk
|
|
36
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
37
|
+
raise Sdk.wrap_error(e, 'Failed to get mouse position')
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
# Moves the mouse cursor to the specified coordinates.
|
|
@@ -50,8 +50,8 @@ module Daytona
|
|
|
50
50
|
def move(x:, y:)
|
|
51
51
|
request = DaytonaToolboxApiClient::MouseMoveRequest.new(x:, y:)
|
|
52
52
|
toolbox_api.move_mouse(request)
|
|
53
|
-
rescue
|
|
54
|
-
raise Sdk
|
|
53
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
54
|
+
raise Sdk.wrap_error(e, 'Failed to move mouse')
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
# Clicks the mouse at the specified coordinates.
|
|
@@ -75,8 +75,8 @@ module Daytona
|
|
|
75
75
|
def click(x:, y:, button: 'left', double: false)
|
|
76
76
|
request = DaytonaToolboxApiClient::MouseClickRequest.new(x:, y:, button:, double:)
|
|
77
77
|
toolbox_api.click(request)
|
|
78
|
-
rescue
|
|
79
|
-
raise Sdk
|
|
78
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
79
|
+
raise Sdk.wrap_error(e, 'Failed to click mouse')
|
|
80
80
|
end
|
|
81
81
|
|
|
82
82
|
# Drags the mouse from start coordinates to end coordinates.
|
|
@@ -95,8 +95,8 @@ module Daytona
|
|
|
95
95
|
def drag(start_x:, start_y:, end_x:, end_y:, button: 'left')
|
|
96
96
|
request = DaytonaToolboxApiClient::MouseDragRequest.new(start_x:, start_y:, end_x:, end_y:, button:)
|
|
97
97
|
toolbox_api.drag(request)
|
|
98
|
-
rescue
|
|
99
|
-
raise Sdk
|
|
98
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
99
|
+
raise Sdk.wrap_error(e, 'Failed to drag mouse')
|
|
100
100
|
end
|
|
101
101
|
|
|
102
102
|
# Scrolls the mouse wheel at the specified coordinates.
|
|
@@ -118,8 +118,8 @@ module Daytona
|
|
|
118
118
|
request = DaytonaToolboxApiClient::MouseScrollRequest.new(x:, y:, direction:, amount:)
|
|
119
119
|
toolbox_api.scroll(request)
|
|
120
120
|
true
|
|
121
|
-
rescue
|
|
122
|
-
raise Sdk
|
|
121
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
122
|
+
raise Sdk.wrap_error(e, 'Failed to scroll mouse')
|
|
123
123
|
end
|
|
124
124
|
|
|
125
125
|
instrument :position, :move, :click, :drag, :scroll, component: 'Mouse'
|
|
@@ -164,8 +164,8 @@ module Daytona
|
|
|
164
164
|
def type(text:, delay: nil)
|
|
165
165
|
request = DaytonaToolboxApiClient::KeyboardTypeRequest.new(text:, delay:)
|
|
166
166
|
toolbox_api.type_text(request)
|
|
167
|
-
rescue
|
|
168
|
-
raise Sdk
|
|
167
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
168
|
+
raise Sdk.wrap_error(e, 'Failed to type text')
|
|
169
169
|
end
|
|
170
170
|
|
|
171
171
|
# Presses a key with optional modifiers.
|
|
@@ -187,8 +187,8 @@ module Daytona
|
|
|
187
187
|
def press(key:, modifiers: nil)
|
|
188
188
|
request = DaytonaToolboxApiClient::KeyboardPressRequest.new(key:, modifiers: modifiers || [])
|
|
189
189
|
toolbox_api.press_key(request)
|
|
190
|
-
rescue
|
|
191
|
-
raise Sdk
|
|
190
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
191
|
+
raise Sdk.wrap_error(e, 'Failed to press key')
|
|
192
192
|
end
|
|
193
193
|
|
|
194
194
|
# Presses a hotkey combination.
|
|
@@ -209,8 +209,8 @@ module Daytona
|
|
|
209
209
|
def hotkey(keys:)
|
|
210
210
|
request = DaytonaToolboxApiClient::KeyboardHotkeyRequest.new(keys:)
|
|
211
211
|
toolbox_api.press_hotkey(request)
|
|
212
|
-
rescue
|
|
213
|
-
raise Sdk
|
|
212
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
213
|
+
raise Sdk.wrap_error(e, 'Failed to press hotkey')
|
|
214
214
|
end
|
|
215
215
|
|
|
216
216
|
instrument :type, :press, :hotkey, component: 'Keyboard'
|
|
@@ -254,8 +254,8 @@ module Daytona
|
|
|
254
254
|
# with_cursor = sandbox.computer_use.screenshot.take_full_screen(show_cursor: true)
|
|
255
255
|
def take_full_screen(show_cursor: false)
|
|
256
256
|
toolbox_api.take_screenshot(show_cursor:)
|
|
257
|
-
rescue
|
|
258
|
-
raise Sdk
|
|
257
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
258
|
+
raise Sdk.wrap_error(e, 'Failed to take screenshot')
|
|
259
259
|
end
|
|
260
260
|
|
|
261
261
|
# Takes a screenshot of a specific region.
|
|
@@ -271,8 +271,8 @@ module Daytona
|
|
|
271
271
|
# puts "Captured region: #{screenshot.region.width}x#{screenshot.region.height}"
|
|
272
272
|
def take_region(region:, show_cursor: false)
|
|
273
273
|
toolbox_api.take_region_screenshot(region.height, region.width, region.y, region.x, show_cursor:)
|
|
274
|
-
rescue
|
|
275
|
-
raise Sdk
|
|
274
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
275
|
+
raise Sdk.wrap_error(e, 'Failed to take region screenshot')
|
|
276
276
|
end
|
|
277
277
|
|
|
278
278
|
# Takes a compressed screenshot of the entire screen.
|
|
@@ -303,8 +303,8 @@ module Daytona
|
|
|
303
303
|
format: options.fmt,
|
|
304
304
|
show_cursor: options.show_cursor
|
|
305
305
|
)
|
|
306
|
-
rescue
|
|
307
|
-
raise Sdk
|
|
306
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
307
|
+
raise Sdk.wrap_error(e, 'Failed to take compressed screenshot')
|
|
308
308
|
end
|
|
309
309
|
|
|
310
310
|
# Takes a compressed screenshot of a specific region.
|
|
@@ -334,8 +334,8 @@ module Daytona
|
|
|
334
334
|
format: options.fmt,
|
|
335
335
|
show_cursor: options.show_cursor
|
|
336
336
|
)
|
|
337
|
-
rescue
|
|
338
|
-
raise Sdk
|
|
337
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
338
|
+
raise Sdk.wrap_error(e, 'Failed to take compressed region screenshot')
|
|
339
339
|
end
|
|
340
340
|
|
|
341
341
|
instrument :take_full_screen, :take_region, :take_compressed, :take_compressed_region,
|
|
@@ -380,8 +380,8 @@ module Daytona
|
|
|
380
380
|
# end
|
|
381
381
|
def info
|
|
382
382
|
toolbox_api.get_display_info
|
|
383
|
-
rescue
|
|
384
|
-
raise Sdk
|
|
383
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
384
|
+
raise Sdk.wrap_error(e, 'Failed to get display info')
|
|
385
385
|
end
|
|
386
386
|
|
|
387
387
|
# Gets the list of open windows.
|
|
@@ -397,8 +397,8 @@ module Daytona
|
|
|
397
397
|
# end
|
|
398
398
|
def windows
|
|
399
399
|
toolbox_api.get_windows
|
|
400
|
-
rescue
|
|
401
|
-
raise Sdk
|
|
400
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
401
|
+
raise Sdk.wrap_error(e, 'Failed to get windows')
|
|
402
402
|
end
|
|
403
403
|
|
|
404
404
|
instrument :info, :windows, component: 'Display'
|
|
@@ -446,8 +446,8 @@ module Daytona
|
|
|
446
446
|
opts[:max_depth] = max_depth unless max_depth.nil?
|
|
447
447
|
|
|
448
448
|
toolbox_api.get_accessibility_tree(opts)
|
|
449
|
-
rescue
|
|
450
|
-
raise Sdk
|
|
449
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
450
|
+
raise Sdk.wrap_error(e, 'Failed to get accessibility tree')
|
|
451
451
|
end
|
|
452
452
|
|
|
453
453
|
# Finds AT-SPI accessibility nodes matching the provided filters.
|
|
@@ -482,8 +482,8 @@ module Daytona
|
|
|
482
482
|
|
|
483
483
|
request = DaytonaToolboxApiClient::FindAccessibilityNodesRequest.new(attrs)
|
|
484
484
|
toolbox_api.find_accessibility_nodes(request)
|
|
485
|
-
rescue
|
|
486
|
-
raise Sdk
|
|
485
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
486
|
+
raise Sdk.wrap_error(e, 'Failed to find accessibility nodes')
|
|
487
487
|
end
|
|
488
488
|
|
|
489
489
|
# Focuses an AT-SPI accessibility node.
|
|
@@ -496,8 +496,8 @@ module Daytona
|
|
|
496
496
|
def focus_node(id:)
|
|
497
497
|
request = DaytonaToolboxApiClient::AccessibilityNodeRequest.new(id:)
|
|
498
498
|
toolbox_api.focus_accessibility_node(request)
|
|
499
|
-
rescue
|
|
500
|
-
raise Sdk
|
|
499
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
500
|
+
raise Sdk.wrap_error(e, 'Failed to focus accessibility node')
|
|
501
501
|
end
|
|
502
502
|
|
|
503
503
|
# Invokes an AT-SPI accessibility node action.
|
|
@@ -514,8 +514,8 @@ module Daytona
|
|
|
514
514
|
|
|
515
515
|
request = DaytonaToolboxApiClient::AccessibilityInvokeRequest.new(attrs)
|
|
516
516
|
toolbox_api.invoke_accessibility_node(request)
|
|
517
|
-
rescue
|
|
518
|
-
raise Sdk
|
|
517
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
518
|
+
raise Sdk.wrap_error(e, 'Failed to invoke accessibility node')
|
|
519
519
|
end
|
|
520
520
|
|
|
521
521
|
# Sets an AT-SPI accessibility node value.
|
|
@@ -529,8 +529,8 @@ module Daytona
|
|
|
529
529
|
def set_node_value(id:, value:)
|
|
530
530
|
request = DaytonaToolboxApiClient::AccessibilitySetValueRequest.new(id:, value:)
|
|
531
531
|
toolbox_api.set_accessibility_node_value(request)
|
|
532
|
-
rescue
|
|
533
|
-
raise Sdk
|
|
532
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
533
|
+
raise Sdk.wrap_error(e, 'Failed to set accessibility node value')
|
|
534
534
|
end
|
|
535
535
|
|
|
536
536
|
instrument :get_tree, :find_nodes, :focus_node, :invoke_node, :set_node_value,
|
|
@@ -627,8 +627,8 @@ module Daytona
|
|
|
627
627
|
def start(label: nil)
|
|
628
628
|
request = DaytonaToolboxApiClient::StartRecordingRequest.new(label:)
|
|
629
629
|
toolbox_api.start_recording(request: request)
|
|
630
|
-
rescue
|
|
631
|
-
raise Sdk
|
|
630
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
631
|
+
raise Sdk.wrap_error(e, 'Failed to start recording')
|
|
632
632
|
end
|
|
633
633
|
|
|
634
634
|
# Stops an active screen recording session.
|
|
@@ -644,8 +644,8 @@ module Daytona
|
|
|
644
644
|
def stop(id:)
|
|
645
645
|
request = DaytonaToolboxApiClient::StopRecordingRequest.new(id: id)
|
|
646
646
|
toolbox_api.stop_recording(request)
|
|
647
|
-
rescue
|
|
648
|
-
raise Sdk
|
|
647
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
648
|
+
raise Sdk.wrap_error(e, 'Failed to stop recording')
|
|
649
649
|
end
|
|
650
650
|
|
|
651
651
|
# Lists all recordings (active and completed).
|
|
@@ -661,8 +661,8 @@ module Daytona
|
|
|
661
661
|
# end
|
|
662
662
|
def list
|
|
663
663
|
toolbox_api.list_recordings
|
|
664
|
-
rescue
|
|
665
|
-
raise Sdk
|
|
664
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
665
|
+
raise Sdk.wrap_error(e, 'Failed to list recordings')
|
|
666
666
|
end
|
|
667
667
|
|
|
668
668
|
# Gets details of a specific recording by ID.
|
|
@@ -678,8 +678,8 @@ module Daytona
|
|
|
678
678
|
# puts "Duration: #{recording.duration_seconds} seconds"
|
|
679
679
|
def get(id:)
|
|
680
680
|
toolbox_api.get_recording(id)
|
|
681
|
-
rescue
|
|
682
|
-
raise Sdk
|
|
681
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
682
|
+
raise Sdk.wrap_error(e, 'Failed to get recording')
|
|
683
683
|
end
|
|
684
684
|
|
|
685
685
|
# Deletes a recording by ID.
|
|
@@ -693,8 +693,8 @@ module Daytona
|
|
|
693
693
|
# puts "Recording deleted"
|
|
694
694
|
def delete(id:)
|
|
695
695
|
toolbox_api.delete_recording(id)
|
|
696
|
-
rescue
|
|
697
|
-
raise Sdk
|
|
696
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
697
|
+
raise Sdk.wrap_error(e, 'Failed to delete recording')
|
|
698
698
|
end
|
|
699
699
|
|
|
700
700
|
# Downloads a recording file and saves it to a local path.
|
|
@@ -815,8 +815,8 @@ module Daytona
|
|
|
815
815
|
# puts "Computer use processes started: #{result.message}"
|
|
816
816
|
def start
|
|
817
817
|
toolbox_api.start_computer_use
|
|
818
|
-
rescue
|
|
819
|
-
raise Sdk
|
|
818
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
819
|
+
raise Sdk.wrap_error(e, 'Failed to start computer use')
|
|
820
820
|
end
|
|
821
821
|
|
|
822
822
|
# Stops all computer use processes.
|
|
@@ -829,8 +829,8 @@ module Daytona
|
|
|
829
829
|
# puts "Computer use processes stopped: #{result.message}"
|
|
830
830
|
def stop
|
|
831
831
|
toolbox_api.stop_computer_use
|
|
832
|
-
rescue
|
|
833
|
-
raise Sdk
|
|
832
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
833
|
+
raise Sdk.wrap_error(e, 'Failed to stop computer use')
|
|
834
834
|
end
|
|
835
835
|
|
|
836
836
|
# Gets the status of all computer use processes.
|
|
@@ -843,8 +843,8 @@ module Daytona
|
|
|
843
843
|
# puts "Computer use status: #{response.status}"
|
|
844
844
|
def status
|
|
845
845
|
toolbox_api.get_computer_use_status
|
|
846
|
-
rescue
|
|
847
|
-
raise Sdk
|
|
846
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
847
|
+
raise Sdk.wrap_error(e, 'Failed to get computer use status')
|
|
848
848
|
end
|
|
849
849
|
|
|
850
850
|
# Gets the status of a specific VNC process.
|
|
@@ -858,8 +858,8 @@ module Daytona
|
|
|
858
858
|
# no_vnc_status = sandbox.computer_use.get_process_status("novnc")
|
|
859
859
|
def get_process_status(process_name:)
|
|
860
860
|
toolbox_api.get_process_status(process_name, sandbox_id)
|
|
861
|
-
rescue
|
|
862
|
-
raise Sdk
|
|
861
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
862
|
+
raise Sdk.wrap_error(e, 'Failed to get process status')
|
|
863
863
|
end
|
|
864
864
|
|
|
865
865
|
# Restarts a specific VNC process.
|
|
@@ -873,8 +873,8 @@ module Daytona
|
|
|
873
873
|
# puts "XFCE4 process restarted: #{result.message}"
|
|
874
874
|
def restart_process(process_name:)
|
|
875
875
|
toolbox_api.restart_process(process_name, sandbox_id)
|
|
876
|
-
rescue
|
|
877
|
-
raise Sdk
|
|
876
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
877
|
+
raise Sdk.wrap_error(e, 'Failed to restart process')
|
|
878
878
|
end
|
|
879
879
|
|
|
880
880
|
# Gets logs for a specific VNC process.
|
|
@@ -888,8 +888,8 @@ module Daytona
|
|
|
888
888
|
# puts "NoVNC logs: #{logs}"
|
|
889
889
|
def get_process_logs(process_name:)
|
|
890
890
|
toolbox_api.get_process_logs(process_name, sandbox_id)
|
|
891
|
-
rescue
|
|
892
|
-
raise Sdk
|
|
891
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
892
|
+
raise Sdk.wrap_error(e, 'Failed to get process logs')
|
|
893
893
|
end
|
|
894
894
|
|
|
895
895
|
# Gets error logs for a specific VNC process.
|
|
@@ -903,8 +903,8 @@ module Daytona
|
|
|
903
903
|
# puts "X11VNC errors: #{errors}"
|
|
904
904
|
def get_process_errors(process_name:)
|
|
905
905
|
toolbox_api.get_process_errors(process_name, sandbox_id)
|
|
906
|
-
rescue
|
|
907
|
-
raise Sdk
|
|
906
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
907
|
+
raise Sdk.wrap_error(e, 'Failed to get process errors')
|
|
908
908
|
end
|
|
909
909
|
|
|
910
910
|
instrument :start, :stop, :status, :get_process_status, :restart_process,
|
data/lib/daytona/daytona.rb
CHANGED
|
@@ -127,9 +127,12 @@ module Daytona
|
|
|
127
127
|
#
|
|
128
128
|
# @param id [String]
|
|
129
129
|
# @return [Daytona::Sandbox]
|
|
130
|
+
# @raise [Daytona::Sdk::Error]
|
|
130
131
|
def get(id)
|
|
131
132
|
sandbox_dto = sandbox_api.get_sandbox(id)
|
|
132
133
|
to_sandbox(sandbox_dto:)
|
|
134
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
135
|
+
raise Sdk.wrap_error(e, "Failed to get sandbox #{id}")
|
|
133
136
|
end
|
|
134
137
|
|
|
135
138
|
# Iterates over Sandboxes matching the given query.
|
|
@@ -222,11 +225,15 @@ module Daytona
|
|
|
222
225
|
created_at_before: q.created_at_before,
|
|
223
226
|
last_event_after: q.last_activity_after,
|
|
224
227
|
last_event_before: q.last_activity_before,
|
|
228
|
+
auto_destroy_at_after: q.auto_destroy_at_after,
|
|
229
|
+
auto_destroy_at_before: q.auto_destroy_at_before,
|
|
225
230
|
sort: q.sort,
|
|
226
231
|
order: q.order
|
|
227
232
|
}.compact
|
|
228
233
|
|
|
229
234
|
sandbox_api.list_sandboxes(opts)
|
|
235
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
236
|
+
raise Sdk.wrap_error(e, 'Failed to list sandboxes')
|
|
230
237
|
end
|
|
231
238
|
|
|
232
239
|
instrument :fetch_sandbox_page, component: 'Daytona.list'
|
|
@@ -334,6 +341,8 @@ module Daytona
|
|
|
334
341
|
end
|
|
335
342
|
|
|
336
343
|
sandbox
|
|
344
|
+
rescue *Sdk::API_ERROR_CLASSES => e
|
|
345
|
+
raise Sdk.wrap_error(e, 'Failed to create sandbox')
|
|
337
346
|
end
|
|
338
347
|
|
|
339
348
|
# @return [void]
|