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,920 @@
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 ComputerUse
9
+ class Mouse
10
+ include Instrumentation
11
+
12
+ # @return [String] The ID of the sandbox
13
+ attr_reader :sandbox_id
14
+
15
+ # @return [CogboxToolboxApiClient::ComputerUseApi] API client for sandbox operations
16
+ attr_reader :toolbox_api
17
+
18
+ # @param sandbox_id [String] The ID of the sandbox
19
+ # @param toolbox_api [CogboxToolboxApiClient::ComputerUseApi] API client for sandbox operations
20
+ # @param otel_state [Cogbox::OtelState, nil]
21
+ def initialize(sandbox_id:, toolbox_api:, otel_state: nil)
22
+ @sandbox_id = sandbox_id
23
+ @toolbox_api = toolbox_api
24
+ @otel_state = otel_state
25
+ end
26
+
27
+ # Gets the current mouse cursor position.
28
+ #
29
+ # @return [CogboxToolboxApiClient::MousePosition] Current mouse position with x and y coordinates
30
+ # @raise [Cogbox::Sdk::Error] If the operation fails
31
+ #
32
+ # @example
33
+ # position = sandbox.computer_use.mouse.get_position
34
+ # puts "Mouse is at: #{position.x}, #{position.y}"
35
+ def position
36
+ toolbox_api.get_mouse_position
37
+ rescue StandardError => e
38
+ raise Sdk::Error, "Failed to get mouse position: #{e.message}"
39
+ end
40
+
41
+ # Moves the mouse cursor to the specified coordinates.
42
+ #
43
+ # @param x [Integer] The x coordinate to move to
44
+ # @param y [Integer] The y coordinate to move to
45
+ # @return [CogboxToolboxApiClient::MouseMoveResponse] Move operation result
46
+ # @raise [Cogbox::Sdk::Error] If the operation fails
47
+ #
48
+ # @example
49
+ # result = sandbox.computer_use.mouse.move(x: 100, y: 200)
50
+ # puts "Mouse moved to: #{result.x}, #{result.y}"
51
+ def move(x:, y:)
52
+ request = CogboxToolboxApiClient::MouseMoveRequest.new(x:, y:)
53
+ toolbox_api.move_mouse(request)
54
+ rescue StandardError => e
55
+ raise Sdk::Error, "Failed to move mouse: #{e.message}"
56
+ end
57
+
58
+ # Clicks the mouse at the specified coordinates.
59
+ #
60
+ # @param x [Integer] The x coordinate to click at
61
+ # @param y [Integer] The y coordinate to click at
62
+ # @param button [String] The mouse button to click ('left', 'right', 'middle'). Defaults to 'left'
63
+ # @param double [Boolean] Whether to perform a double-click. Defaults to false
64
+ # @return [CogboxToolboxApiClient::MouseClickResponse] Click operation result
65
+ # @raise [Cogbox::Sdk::Error] If the operation fails
66
+ #
67
+ # @example
68
+ # # Single left click
69
+ # result = sandbox.computer_use.mouse.click(x: 100, y: 200)
70
+ #
71
+ # # Double click
72
+ # double_click = sandbox.computer_use.mouse.click(x: 100, y: 200, button: 'left', double: true)
73
+ #
74
+ # # Right click
75
+ # right_click = sandbox.computer_use.mouse.click(x: 100, y: 200, button: 'right')
76
+ def click(x:, y:, button: 'left', double: false)
77
+ request = CogboxToolboxApiClient::MouseClickRequest.new(x:, y:, button:, double:)
78
+ toolbox_api.click(request)
79
+ rescue StandardError => e
80
+ raise Sdk::Error, "Failed to click mouse: #{e.message}"
81
+ end
82
+
83
+ # Drags the mouse from start coordinates to end coordinates.
84
+ #
85
+ # @param start_x [Integer] The starting x coordinate
86
+ # @param start_y [Integer] The starting y coordinate
87
+ # @param end_x [Integer] The ending x coordinate
88
+ # @param end_y [Integer] The ending y coordinate
89
+ # @param button [String] The mouse button to use for dragging. Defaults to 'left'
90
+ # @return [CogboxToolboxApiClient::MouseDragResponse] Drag operation result
91
+ # @raise [Cogbox::Sdk::Error] If the operation fails
92
+ #
93
+ # @example
94
+ # result = sandbox.computer_use.mouse.drag(start_x: 50, start_y: 50, end_x: 150, end_y: 150)
95
+ # puts "Drag ended at #{result.x}, #{result.y}"
96
+ def drag(start_x:, start_y:, end_x:, end_y:, button: 'left')
97
+ request = CogboxToolboxApiClient::MouseDragRequest.new(start_x:, start_y:, end_x:, end_y:, button:)
98
+ toolbox_api.drag(request)
99
+ rescue StandardError => e
100
+ raise Sdk::Error, "Failed to drag mouse: #{e.message}"
101
+ end
102
+
103
+ # Scrolls the mouse wheel at the specified coordinates.
104
+ #
105
+ # @param x [Integer] The x coordinate to scroll at
106
+ # @param y [Integer] The y coordinate to scroll at
107
+ # @param direction [String] The direction to scroll ('up' or 'down')
108
+ # @param amount [Integer] The amount to scroll. Defaults to 1
109
+ # @return [Boolean] Whether the scroll operation was successful
110
+ # @raise [Cogbox::Sdk::Error] If the operation fails
111
+ #
112
+ # @example
113
+ # # Scroll up
114
+ # scroll_up = sandbox.computer_use.mouse.scroll(x: 100, y: 200, direction: 'up', amount: 3)
115
+ #
116
+ # # Scroll down
117
+ # scroll_down = sandbox.computer_use.mouse.scroll(x: 100, y: 200, direction: 'down', amount: 5)
118
+ def scroll(x:, y:, direction:, amount: 1)
119
+ request = CogboxToolboxApiClient::MouseScrollRequest.new(x:, y:, direction:, amount:)
120
+ toolbox_api.scroll(request)
121
+ true
122
+ rescue StandardError => e
123
+ raise Sdk::Error, "Failed to scroll mouse: #{e.message}"
124
+ end
125
+
126
+ instrument :position, :move, :click, :drag, :scroll, component: 'Mouse'
127
+
128
+ private
129
+
130
+ # @return [Cogbox::OtelState, nil]
131
+ attr_reader :otel_state
132
+ end
133
+
134
+ # Keyboard operations for computer use functionality.
135
+ class Keyboard
136
+ include Instrumentation
137
+
138
+ # @return [String] The ID of the sandbox
139
+ attr_reader :sandbox_id
140
+
141
+ # @return [CogboxToolboxApiClient::ComputerUseApi] API client for sandbox operations
142
+ attr_reader :toolbox_api
143
+
144
+ # @param sandbox_id [String] The ID of the sandbox
145
+ # @param toolbox_api [CogboxToolboxApiClient::ComputerUseApi] API client for sandbox operations
146
+ # @param otel_state [Cogbox::OtelState, nil]
147
+ def initialize(sandbox_id:, toolbox_api:, otel_state: nil)
148
+ @sandbox_id = sandbox_id
149
+ @toolbox_api = toolbox_api
150
+ @otel_state = otel_state
151
+ end
152
+
153
+ # Types the specified text.
154
+ #
155
+ # @param text [String] The text to type
156
+ # @param delay [Integer, nil] Delay between characters in milliseconds
157
+ # @return [void]
158
+ # @raise [Cogbox::Sdk::Error] If the operation fails
159
+ #
160
+ # @example
161
+ # sandbox.computer_use.keyboard.type("Hello, World!")
162
+ #
163
+ # # With delay between characters
164
+ # sandbox.computer_use.keyboard.type("Slow typing", delay: 100)
165
+ def type(text:, delay: nil)
166
+ request = CogboxToolboxApiClient::KeyboardTypeRequest.new(text:, delay:)
167
+ toolbox_api.type_text(request)
168
+ rescue StandardError => e
169
+ raise Sdk::Error, "Failed to type text: #{e.message}"
170
+ end
171
+
172
+ # Presses a key with optional modifiers.
173
+ #
174
+ # @param key [String] The key to press. Canonical names include 'enter', 'escape', 'tab', letters, digits, unshifted punctuation, function keys, and grammar-safe numpad names such as 'num_plus'. Named keys are case-insensitive, and common aliases such as 'Return' and 'Escape' are normalized.
175
+ # @param modifiers [Array<String>, nil] Canonical modifier names are 'ctrl', 'alt', 'shift', and 'cmd'. Common aliases such as 'control', 'option', 'meta', and 'win' are normalized.
176
+ # @return [void]
177
+ # @raise [Cogbox::Sdk::Error] If the operation fails
178
+ #
179
+ # @example
180
+ # # Press Enter
181
+ # sandbox.computer_use.keyboard.press("enter")
182
+ #
183
+ # # Press Ctrl+C
184
+ # sandbox.computer_use.keyboard.press("c", modifiers: ["ctrl"])
185
+ #
186
+ # # Press Ctrl+Shift+T
187
+ # sandbox.computer_use.keyboard.press("t", modifiers: ["ctrl", "shift"])
188
+ def press(key:, modifiers: nil)
189
+ request = CogboxToolboxApiClient::KeyboardPressRequest.new(key:, modifiers: modifiers || [])
190
+ toolbox_api.press_key(request)
191
+ rescue StandardError => e
192
+ raise Sdk::Error, "Failed to press key: #{e.message}"
193
+ end
194
+
195
+ # Presses a hotkey combination.
196
+ #
197
+ # @param keys [String] A single atomic hotkey chord (e.g., 'ctrl+c', 'alt+tab', 'cmd+shift+t', 'ctrl + c', 'shift'). Uses the same normalized key contract as #press.
198
+ # @return [void]
199
+ # @raise [Cogbox::Sdk::Error] If the operation fails
200
+ #
201
+ # @example
202
+ # # Copy
203
+ # sandbox.computer_use.keyboard.hotkey("ctrl+c")
204
+ #
205
+ # # Paste
206
+ # sandbox.computer_use.keyboard.hotkey("ctrl+v")
207
+ #
208
+ # # Alt+Tab
209
+ # sandbox.computer_use.keyboard.hotkey("alt+tab")
210
+ def hotkey(keys:)
211
+ request = CogboxToolboxApiClient::KeyboardHotkeyRequest.new(keys:)
212
+ toolbox_api.press_hotkey(request)
213
+ rescue StandardError => e
214
+ raise Sdk::Error, "Failed to press hotkey: #{e.message}"
215
+ end
216
+
217
+ instrument :type, :press, :hotkey, component: 'Keyboard'
218
+
219
+ private
220
+
221
+ # @return [Cogbox::OtelState, nil]
222
+ attr_reader :otel_state
223
+ end
224
+
225
+ # Screenshot operations for computer use functionality.
226
+ class Screenshot
227
+ include Instrumentation
228
+
229
+ # @return [String] The ID of the sandbox
230
+ attr_reader :sandbox_id
231
+
232
+ # @return [CogboxToolboxApiClient::ComputerUseApi] API client for sandbox operations
233
+ attr_reader :toolbox_api
234
+
235
+ # @param sandbox_id [String] The ID of the sandbox
236
+ # @param toolbox_api [CogboxToolboxApiClient::ComputerUseApi] API client for sandbox operations
237
+ # @param otel_state [Cogbox::OtelState, nil]
238
+ def initialize(sandbox_id:, toolbox_api:, otel_state: nil)
239
+ @sandbox_id = sandbox_id
240
+ @toolbox_api = toolbox_api
241
+ @otel_state = otel_state
242
+ end
243
+
244
+ # Takes a screenshot of the entire screen.
245
+ #
246
+ # @param show_cursor [Boolean] Whether to show the cursor in the screenshot. Defaults to false
247
+ # @return [CogboxApiClient::ScreenshotResponse] Screenshot data with base64 encoded image
248
+ # @raise [Cogbox::Sdk::Error] If the operation fails
249
+ #
250
+ # @example
251
+ # screenshot = sandbox.computer_use.screenshot.take_full_screen
252
+ # puts "Screenshot size: #{screenshot.width}x#{screenshot.height}"
253
+ #
254
+ # # With cursor visible
255
+ # with_cursor = sandbox.computer_use.screenshot.take_full_screen(show_cursor: true)
256
+ def take_full_screen(show_cursor: false)
257
+ toolbox_api.take_screenshot(show_cursor:)
258
+ rescue StandardError => e
259
+ raise Sdk::Error, "Failed to take screenshot: #{e.message}"
260
+ end
261
+
262
+ # Takes a screenshot of a specific region.
263
+ #
264
+ # @param region [ScreenshotRegion] The region to capture
265
+ # @param show_cursor [Boolean] Whether to show the cursor in the screenshot. Defaults to false
266
+ # @return [CogboxApiClient::RegionScreenshotResponse] Screenshot data with base64 encoded image
267
+ # @raise [Cogbox::Sdk::Error] If the operation fails
268
+ #
269
+ # @example
270
+ # region = ScreenshotRegion.new(x: 100, y: 100, width: 300, height: 200)
271
+ # screenshot = sandbox.computer_use.screenshot.take_region(region)
272
+ # puts "Captured region: #{screenshot.region.width}x#{screenshot.region.height}"
273
+ def take_region(region:, show_cursor: false)
274
+ toolbox_api.take_region_screenshot(region.height, region.width, region.y, region.x, show_cursor:)
275
+ rescue StandardError => e
276
+ raise Sdk::Error, "Failed to take region screenshot: #{e.message}"
277
+ end
278
+
279
+ # Takes a compressed screenshot of the entire screen.
280
+ #
281
+ # @param options [ScreenshotOptions, nil] Compression and display options
282
+ # @return [CogboxApiClient::CompressedScreenshotResponse] Compressed screenshot data
283
+ # @raise [Cogbox::Sdk::Error] If the operation fails
284
+ #
285
+ # @example
286
+ # # Default compression
287
+ # screenshot = sandbox.computer_use.screenshot.take_compressed
288
+ #
289
+ # # High quality JPEG
290
+ # jpeg = sandbox.computer_use.screenshot.take_compressed(
291
+ # options: ScreenshotOptions.new(format: "jpeg", quality: 95, show_cursor: true)
292
+ # )
293
+ #
294
+ # # Scaled down PNG
295
+ # scaled = sandbox.computer_use.screenshot.take_compressed(
296
+ # options: ScreenshotOptions.new(format: "png", scale: 0.5)
297
+ # )
298
+ def take_compressed(options: nil)
299
+ options ||= ScreenshotOptions.new
300
+ toolbox_api.take_compressed_screenshot(
301
+ sandbox_id,
302
+ scale: options.scale,
303
+ quality: options.quality,
304
+ format: options.fmt,
305
+ show_cursor: options.show_cursor
306
+ )
307
+ rescue StandardError => e
308
+ raise Sdk::Error, "Failed to take compressed screenshot: #{e.message}"
309
+ end
310
+
311
+ # Takes a compressed screenshot of a specific region.
312
+ #
313
+ # @param region [ScreenshotRegion] The region to capture
314
+ # @param options [ScreenshotOptions, nil] Compression and display options
315
+ # @return [CogboxApiClient::CompressedScreenshotResponse] Compressed screenshot data
316
+ # @raise [Cogbox::Sdk::Error] If the operation fails
317
+ #
318
+ # @example
319
+ # region = ScreenshotRegion.new(x: 0, y: 0, width: 800, height: 600)
320
+ # screenshot = sandbox.computer_use.screenshot.take_compressed_region(
321
+ # region,
322
+ # options: ScreenshotOptions.new(format: "webp", quality: 80, show_cursor: true)
323
+ # )
324
+ # puts "Compressed size: #{screenshot.size_bytes} bytes"
325
+ def take_compressed_region(region:, options: nil)
326
+ options ||= ScreenshotOptions.new
327
+ toolbox_api.take_compressed_region_screenshot(
328
+ sandbox_id,
329
+ region.height,
330
+ region.width,
331
+ region.y,
332
+ region.x,
333
+ scale: options.scale,
334
+ quality: options.quality,
335
+ format: options.fmt,
336
+ show_cursor: options.show_cursor
337
+ )
338
+ rescue StandardError => e
339
+ raise Sdk::Error, "Failed to take compressed region screenshot: #{e.message}"
340
+ end
341
+
342
+ instrument :take_full_screen, :take_region, :take_compressed, :take_compressed_region,
343
+ component: 'Screenshot'
344
+
345
+ private
346
+
347
+ # @return [Cogbox::OtelState, nil]
348
+ attr_reader :otel_state
349
+ end
350
+
351
+ # Display operations for computer use functionality.
352
+ class Display
353
+ include Instrumentation
354
+
355
+ # @return [String] The ID of the sandbox
356
+ attr_reader :sandbox_id
357
+
358
+ # @return [CogboxToolboxApiClient::ComputerUseApi] API client for sandbox operations
359
+ attr_reader :toolbox_api
360
+
361
+ # @param sandbox_id [String] The ID of the sandbox
362
+ # @param toolbox_api [CogboxToolboxApiClient::ComputerUseApi] API client for sandbox operations
363
+ # @param otel_state [Cogbox::OtelState, nil]
364
+ def initialize(sandbox_id:, toolbox_api:, otel_state: nil)
365
+ @sandbox_id = sandbox_id
366
+ @toolbox_api = toolbox_api
367
+ @otel_state = otel_state
368
+ end
369
+
370
+ # Gets information about the displays.
371
+ #
372
+ # @return [CogboxToolboxApiClient::DisplayInfoResponse] Display information including primary display and all available displays
373
+ # @raise [Cogbox::Sdk::Error] If the operation fails
374
+ #
375
+ # @example
376
+ # info = sandbox.computer_use.display.get_info
377
+ # puts "Primary display: #{info.primary_display.width}x#{info.primary_display.height}"
378
+ # puts "Total displays: #{info.total_displays}"
379
+ # info.displays.each_with_index do |display, i|
380
+ # puts "Display #{i}: #{display.width}x#{display.height} at #{display.x},#{display.y}"
381
+ # end
382
+ def info
383
+ toolbox_api.get_display_info
384
+ rescue StandardError => e
385
+ raise Sdk::Error, "Failed to get display info: #{e.message}"
386
+ end
387
+
388
+ # Gets the list of open windows.
389
+ #
390
+ # @return [CogboxToolboxApiClient::WindowsResponse] List of open windows with their IDs and titles
391
+ # @raise [Cogbox::Sdk::Error] If the operation fails
392
+ #
393
+ # @example
394
+ # windows = sandbox.computer_use.display.get_windows
395
+ # puts "Found #{windows.count} open windows:"
396
+ # windows.windows.each do |window|
397
+ # puts "- #{window.title} (ID: #{window.id})"
398
+ # end
399
+ def windows
400
+ toolbox_api.get_windows
401
+ rescue StandardError => e
402
+ raise Sdk::Error, "Failed to get windows: #{e.message}"
403
+ end
404
+
405
+ instrument :info, :windows, component: 'Display'
406
+
407
+ private
408
+
409
+ # @return [Cogbox::OtelState, nil]
410
+ attr_reader :otel_state
411
+ end
412
+
413
+ # Accessibility operations for computer use functionality.
414
+ class Accessibility
415
+ include Instrumentation
416
+
417
+ # @return [String] The ID of the sandbox
418
+ attr_reader :sandbox_id
419
+
420
+ # @return [CogboxToolboxApiClient::ComputerUseApi] API client for sandbox operations
421
+ attr_reader :toolbox_api
422
+
423
+ # @param sandbox_id [String] The ID of the sandbox
424
+ # @param toolbox_api [CogboxToolboxApiClient::ComputerUseApi] API client for sandbox operations
425
+ # @param otel_state [Cogbox::OtelState, nil]
426
+ def initialize(sandbox_id:, toolbox_api:, otel_state: nil)
427
+ @sandbox_id = sandbox_id
428
+ @toolbox_api = toolbox_api
429
+ @otel_state = otel_state
430
+ end
431
+
432
+ # Fetches the AT-SPI accessibility tree.
433
+ #
434
+ # @param scope [String, nil] Tree scope to inspect: "focused", "pid", or "all"
435
+ # @param pid [Integer, nil] Process ID when scope is "pid"
436
+ # @param max_depth [Integer, nil] Maximum depth to descend; 0 returns only the root
437
+ # @return [CogboxToolboxApiClient::AccessibilityTreeResponse] Accessibility tree response
438
+ # @raise [Cogbox::Sdk::Error] If the operation fails
439
+ #
440
+ # @example
441
+ # tree = sandbox.computer_use.accessibility.get_tree(scope: "all", max_depth: 3)
442
+ # puts tree.root.name
443
+ def get_tree(scope: nil, pid: nil, max_depth: nil)
444
+ opts = {}
445
+ opts[:scope] = scope unless scope.nil?
446
+ opts[:pid] = pid unless pid.nil?
447
+ opts[:max_depth] = max_depth unless max_depth.nil?
448
+
449
+ toolbox_api.get_accessibility_tree(opts)
450
+ rescue StandardError => e
451
+ raise Sdk::Error, "Failed to get accessibility tree: #{e.message}"
452
+ end
453
+
454
+ # Finds AT-SPI accessibility nodes matching the provided filters.
455
+ #
456
+ # @param scope [String, nil] Search scope: "focused", "pid", or "all"
457
+ # @param pid [Integer, nil] Process ID when scope is "pid"
458
+ # @param role [String, nil] Accessibility role to match, such as "button"
459
+ # @param name [String, nil] Accessible name to match
460
+ # @param name_match [String, nil] Name match mode, such as "exact" or "substring"
461
+ # @param states [Array<String>, nil] Required accessibility states
462
+ # @param limit [Integer, nil] Maximum number of matches
463
+ # @return [CogboxToolboxApiClient::AccessibilityNodesResponse] Matching accessibility nodes
464
+ # @raise [Cogbox::Sdk::Error] If the operation fails
465
+ #
466
+ # @example
467
+ # buttons = sandbox.computer_use.accessibility.find_nodes(
468
+ # scope: "all",
469
+ # role: "button",
470
+ # name: "Submit",
471
+ # name_match: "substring"
472
+ # )
473
+ # puts buttons.matches.length
474
+ def find_nodes(scope: nil, pid: nil, role: nil, name: nil, name_match: nil, states: nil, limit: nil)
475
+ attrs = {}
476
+ attrs[:scope] = scope unless scope.nil?
477
+ attrs[:pid] = pid unless pid.nil?
478
+ attrs[:role] = role unless role.nil?
479
+ attrs[:name] = name unless name.nil?
480
+ attrs[:name_match] = name_match unless name_match.nil?
481
+ attrs[:states] = states unless states.nil?
482
+ attrs[:limit] = limit unless limit.nil?
483
+
484
+ request = CogboxToolboxApiClient::FindAccessibilityNodesRequest.new(attrs)
485
+ toolbox_api.find_accessibility_nodes(request)
486
+ rescue StandardError => e
487
+ raise Sdk::Error, "Failed to find accessibility nodes: #{e.message}"
488
+ end
489
+
490
+ # Focuses an AT-SPI accessibility node.
491
+ #
492
+ # @param id [String] Accessibility node ID returned by get_tree or find_nodes
493
+ # @raise [Cogbox::Sdk::Error] If the operation fails
494
+ #
495
+ # @example
496
+ # sandbox.computer_use.accessibility.focus_node(id: node.id)
497
+ def focus_node(id:)
498
+ request = CogboxToolboxApiClient::AccessibilityNodeRequest.new(id:)
499
+ toolbox_api.focus_accessibility_node(request)
500
+ rescue StandardError => e
501
+ raise Sdk::Error, "Failed to focus accessibility node: #{e.message}"
502
+ end
503
+
504
+ # Invokes an AT-SPI accessibility node action.
505
+ #
506
+ # @param id [String] Accessibility node ID returned by get_tree or find_nodes
507
+ # @param action [String, nil] Action name to invoke, or nil for the primary action
508
+ # @raise [Cogbox::Sdk::Error] If the operation fails
509
+ #
510
+ # @example
511
+ # sandbox.computer_use.accessibility.invoke_node(id: node.id, action: "click")
512
+ def invoke_node(id:, action: nil)
513
+ attrs = { id: }
514
+ attrs[:action] = action unless action.nil?
515
+
516
+ request = CogboxToolboxApiClient::AccessibilityInvokeRequest.new(attrs)
517
+ toolbox_api.invoke_accessibility_node(request)
518
+ rescue StandardError => e
519
+ raise Sdk::Error, "Failed to invoke accessibility node: #{e.message}"
520
+ end
521
+
522
+ # Sets an AT-SPI accessibility node value.
523
+ #
524
+ # @param id [String] Accessibility node ID returned by get_tree or find_nodes
525
+ # @param value [String] Value to write to the node
526
+ # @raise [Cogbox::Sdk::Error] If the operation fails
527
+ #
528
+ # @example
529
+ # sandbox.computer_use.accessibility.set_node_value(id: node.id, value: "hello")
530
+ def set_node_value(id:, value:)
531
+ request = CogboxToolboxApiClient::AccessibilitySetValueRequest.new(id:, value:)
532
+ toolbox_api.set_accessibility_node_value(request)
533
+ rescue StandardError => e
534
+ raise Sdk::Error, "Failed to set accessibility node value: #{e.message}"
535
+ end
536
+
537
+ instrument :get_tree, :find_nodes, :focus_node, :invoke_node, :set_node_value,
538
+ component: 'Accessibility'
539
+
540
+ private
541
+
542
+ # @return [Cogbox::OtelState, nil]
543
+ attr_reader :otel_state
544
+ end
545
+
546
+ # Region coordinates for screenshot operations.
547
+ class ScreenshotRegion
548
+ # @return [Integer] X coordinate of the region
549
+ attr_accessor :x
550
+
551
+ # @return [Integer] Y coordinate of the region
552
+ attr_accessor :y
553
+
554
+ # @return [Integer] Width of the region
555
+ attr_accessor :width
556
+
557
+ # @return [Integer] Height of the region
558
+ attr_accessor :height
559
+
560
+ # @param x [Integer] X coordinate of the region
561
+ # @param y [Integer] Y coordinate of the region
562
+ # @param width [Integer] Width of the region
563
+ # @param height [Integer] Height of the region
564
+ def initialize(x:, y:, width:, height:)
565
+ @x = x
566
+ @y = y
567
+ @width = width
568
+ @height = height
569
+ end
570
+ end
571
+
572
+ # Options for screenshot compression and display.
573
+ class ScreenshotOptions
574
+ # @return [Boolean, nil] Whether to show the cursor in the screenshot
575
+ attr_accessor :show_cursor
576
+
577
+ # @return [String, nil] Image format (e.g., 'png', 'jpeg', 'webp')
578
+ attr_accessor :fmt
579
+
580
+ # @return [Integer, nil] Compression quality (0-100)
581
+ attr_accessor :quality
582
+
583
+ # @return [Float, nil] Scale factor for the screenshot
584
+ attr_accessor :scale
585
+
586
+ # @param show_cursor [Boolean, nil] Whether to show the cursor in the screenshot
587
+ # @param format [String, nil] Image format (e.g., 'png', 'jpeg', 'webp')
588
+ # @param quality [Integer, nil] Compression quality (0-100)
589
+ # @param scale [Float, nil] Scale factor for the screenshot
590
+ def initialize(show_cursor: nil, format: nil, quality: nil, scale: nil)
591
+ @show_cursor = show_cursor
592
+ @fmt = format
593
+ @quality = quality
594
+ @scale = scale
595
+ end
596
+ end
597
+
598
+ # Recording operations for computer use functionality.
599
+ class Recording
600
+ include Instrumentation
601
+
602
+ # @return [String] The ID of the sandbox
603
+ attr_reader :sandbox_id
604
+
605
+ # @return [CogboxToolboxApiClient::ComputerUseApi] API client for sandbox operations
606
+ attr_reader :toolbox_api
607
+
608
+ # @param sandbox_id [String] The ID of the sandbox
609
+ # @param toolbox_api [CogboxToolboxApiClient::ComputerUseApi] API client for sandbox operations
610
+ # @param otel_state [Cogbox::OtelState, nil]
611
+ def initialize(sandbox_id:, toolbox_api:, otel_state: nil)
612
+ @sandbox_id = sandbox_id
613
+ @toolbox_api = toolbox_api
614
+ @otel_state = otel_state
615
+ end
616
+
617
+ # Starts a new screen recording session.
618
+ #
619
+ # @param label [String, nil] Optional custom label for the recording
620
+ # @return [CogboxToolboxApiClient::Recording] Started recording details
621
+ # @raise [Cogbox::Sdk::Error] If the operation fails
622
+ #
623
+ # @example
624
+ # # Start a recording with a label
625
+ # recording = sandbox.computer_use.recording.start(label: "my-test-recording")
626
+ # puts "Recording started: #{recording.id}"
627
+ # puts "File: #{recording.file_path}"
628
+ def start(label: nil)
629
+ request = CogboxToolboxApiClient::StartRecordingRequest.new(label:)
630
+ toolbox_api.start_recording(request: request)
631
+ rescue StandardError => e
632
+ raise Sdk::Error, "Failed to start recording: #{e.message}"
633
+ end
634
+
635
+ # Stops an active screen recording session.
636
+ #
637
+ # @param id [String] The ID of the recording to stop
638
+ # @return [CogboxToolboxApiClient::Recording] Stopped recording details
639
+ # @raise [Cogbox::Sdk::Error] If the operation fails
640
+ #
641
+ # @example
642
+ # result = sandbox.computer_use.recording.stop(id: recording.id)
643
+ # puts "Recording stopped: #{result.duration_seconds} seconds"
644
+ # puts "Saved to: #{result.file_path}"
645
+ def stop(id:)
646
+ request = CogboxToolboxApiClient::StopRecordingRequest.new(id: id)
647
+ toolbox_api.stop_recording(request)
648
+ rescue StandardError => e
649
+ raise Sdk::Error, "Failed to stop recording: #{e.message}"
650
+ end
651
+
652
+ # Lists all recordings (active and completed).
653
+ #
654
+ # @return [CogboxToolboxApiClient::ListRecordingsResponse] List of all recordings
655
+ # @raise [Cogbox::Sdk::Error] If the operation fails
656
+ #
657
+ # @example
658
+ # recordings = sandbox.computer_use.recording.list
659
+ # puts "Found #{recordings.recordings.length} recordings"
660
+ # recordings.recordings.each do |rec|
661
+ # puts "- #{rec.file_name}: #{rec.status}"
662
+ # end
663
+ def list
664
+ toolbox_api.list_recordings
665
+ rescue StandardError => e
666
+ raise Sdk::Error, "Failed to list recordings: #{e.message}"
667
+ end
668
+
669
+ # Gets details of a specific recording by ID.
670
+ #
671
+ # @param id [String] The ID of the recording to retrieve
672
+ # @return [CogboxToolboxApiClient::Recording] Recording details
673
+ # @raise [Cogbox::Sdk::Error] If the operation fails
674
+ #
675
+ # @example
676
+ # recording = sandbox.computer_use.recording.get(id: recording_id)
677
+ # puts "Recording: #{recording.file_name}"
678
+ # puts "Status: #{recording.status}"
679
+ # puts "Duration: #{recording.duration_seconds} seconds"
680
+ def get(id:)
681
+ toolbox_api.get_recording(id)
682
+ rescue StandardError => e
683
+ raise Sdk::Error, "Failed to get recording: #{e.message}"
684
+ end
685
+
686
+ # Deletes a recording by ID.
687
+ #
688
+ # @param id [String] The ID of the recording to delete
689
+ # @return [void]
690
+ # @raise [Cogbox::Sdk::Error] If the operation fails
691
+ #
692
+ # @example
693
+ # sandbox.computer_use.recording.delete(id: recording_id)
694
+ # puts "Recording deleted"
695
+ def delete(id:)
696
+ toolbox_api.delete_recording(id)
697
+ rescue StandardError => e
698
+ raise Sdk::Error, "Failed to delete recording: #{e.message}"
699
+ end
700
+
701
+ # Downloads a recording file and saves it to a local path.
702
+ #
703
+ # The file is streamed directly to disk without loading the entire content into memory.
704
+ #
705
+ # @param id [String] The ID of the recording to download
706
+ # @param local_path [String] Path to save the recording file locally
707
+ # @return [void]
708
+ # @raise [Cogbox::Sdk::Error] If the operation fails
709
+ #
710
+ # @example
711
+ # sandbox.computer_use.recording.download(id: recording_id, local_path: "local_recording.mp4")
712
+ # puts "Recording downloaded"
713
+ def download(id:, local_path:)
714
+ require 'fileutils'
715
+ require 'typhoeus'
716
+
717
+ # Get the API configuration and build the download URL
718
+ api_client = toolbox_api.api_client
719
+ config = api_client.config
720
+ base_url = config.base_url
721
+ download_url = "#{base_url}/computeruse/recordings/#{id}/download"
722
+
723
+ # Create parent directory if it doesn't exist
724
+ parent_dir = File.dirname(local_path)
725
+ FileUtils.mkdir_p(parent_dir) unless parent_dir.empty?
726
+
727
+ # Stream the download directly to file
728
+ file = File.open(local_path, 'wb')
729
+ request = Typhoeus::Request.new(
730
+ download_url,
731
+ method: :get,
732
+ headers: api_client.default_headers,
733
+ timeout: config.timeout,
734
+ ssl_verifypeer: config.verify_ssl,
735
+ ssl_verifyhost: config.verify_ssl_host ? 2 : 0
736
+ )
737
+
738
+ # Stream chunks directly to file
739
+ request.on_body do |chunk|
740
+ file.write(chunk)
741
+ end
742
+
743
+ request.on_complete do |response|
744
+ file.close
745
+ unless response.success?
746
+ File.delete(local_path) if File.exist?(local_path)
747
+ raise Sdk::Error, "Failed to download recording: HTTP #{response.code}"
748
+ end
749
+ end
750
+
751
+ request.run
752
+ rescue StandardError => e
753
+ file&.close
754
+ File.delete(local_path) if File.exist?(local_path)
755
+ raise Sdk::Error, "Failed to download recording: #{e.message}"
756
+ end
757
+
758
+ instrument :start, :stop, :list, :get, :delete, :download, component: 'Recording'
759
+
760
+ private
761
+
762
+ # @return [Cogbox::OtelState, nil]
763
+ attr_reader :otel_state
764
+ end
765
+
766
+ include Instrumentation
767
+
768
+ # @return [String] The ID of the sandbox
769
+ attr_reader :sandbox_id
770
+
771
+ # @return [CogboxApiClient::ToolboxApi] API client for sandbox operations
772
+ attr_reader :toolbox_api
773
+
774
+ # @return [Mouse] Mouse operations interface
775
+ attr_reader :mouse
776
+
777
+ # @return [Keyboard] Keyboard operations interface
778
+ attr_reader :keyboard
779
+
780
+ # @return [Screenshot] Screenshot operations interface
781
+ attr_reader :screenshot
782
+
783
+ # @return [Display] Display operations interface
784
+ attr_reader :display
785
+
786
+ # @return [Recording] Screen recording operations interface
787
+ attr_reader :recording
788
+
789
+ # @return [Accessibility] Accessibility operations interface
790
+ attr_reader :accessibility
791
+
792
+ # Initialize a new ComputerUse instance.
793
+ #
794
+ # @param sandbox_id [String] The ID of the sandbox
795
+ # @param toolbox_api [CogboxApiClient::ToolboxApi] API client for sandbox operations
796
+ # @param otel_state [Cogbox::OtelState, nil]
797
+ def initialize(sandbox_id:, toolbox_api:, otel_state: nil)
798
+ @sandbox_id = sandbox_id
799
+ @toolbox_api = toolbox_api
800
+ @otel_state = otel_state
801
+ @mouse = Mouse.new(sandbox_id:, toolbox_api:, otel_state:)
802
+ @keyboard = Keyboard.new(sandbox_id:, toolbox_api:, otel_state:)
803
+ @screenshot = Screenshot.new(sandbox_id:, toolbox_api:, otel_state:)
804
+ @display = Display.new(sandbox_id:, toolbox_api:, otel_state:)
805
+ @recording = Recording.new(sandbox_id:, toolbox_api:, otel_state:)
806
+ @accessibility = Accessibility.new(sandbox_id:, toolbox_api:, otel_state:)
807
+ end
808
+
809
+ # Starts all computer use processes (Xvfb, xfce4, x11vnc, novnc).
810
+ #
811
+ # @return [CogboxApiClient::ComputerUseStartResponse] Computer use start response
812
+ # @raise [Cogbox::Sdk::Error] If the operation fails
813
+ #
814
+ # @example
815
+ # result = sandbox.computer_use.start
816
+ # puts "Computer use processes started: #{result.message}"
817
+ def start
818
+ toolbox_api.start_computer_use
819
+ rescue StandardError => e
820
+ raise Sdk::Error, "Failed to start computer use: #{e.message}"
821
+ end
822
+
823
+ # Stops all computer use processes.
824
+ #
825
+ # @return [CogboxApiClient::ComputerUseStopResponse] Computer use stop response
826
+ # @raise [Cogbox::Sdk::Error] If the operation fails
827
+ #
828
+ # @example
829
+ # result = sandbox.computer_use.stop
830
+ # puts "Computer use processes stopped: #{result.message}"
831
+ def stop
832
+ toolbox_api.stop_computer_use
833
+ rescue StandardError => e
834
+ raise Sdk::Error, "Failed to stop computer use: #{e.message}"
835
+ end
836
+
837
+ # Gets the status of all computer use processes.
838
+ #
839
+ # @return [CogboxApiClient::ComputerUseStatusResponse] Status information about all VNC desktop processes
840
+ # @raise [Cogbox::Sdk::Error] If the operation fails
841
+ #
842
+ # @example
843
+ # response = sandbox.computer_use.get_status
844
+ # puts "Computer use status: #{response.status}"
845
+ def status
846
+ toolbox_api.get_computer_use_status
847
+ rescue StandardError => e
848
+ raise Sdk::Error, "Failed to get computer use status: #{e.message}"
849
+ end
850
+
851
+ # Gets the status of a specific VNC process.
852
+ #
853
+ # @param process_name [String] Name of the process to check
854
+ # @return [CogboxApiClient::ProcessStatusResponse] Status information about the specific process
855
+ # @raise [Cogbox::Sdk::Error] If the operation fails
856
+ #
857
+ # @example
858
+ # xvfb_status = sandbox.computer_use.get_process_status("xvfb")
859
+ # no_vnc_status = sandbox.computer_use.get_process_status("novnc")
860
+ def get_process_status(process_name:)
861
+ toolbox_api.get_process_status(process_name, sandbox_id)
862
+ rescue StandardError => e
863
+ raise Sdk::Error, "Failed to get process status: #{e.message}"
864
+ end
865
+
866
+ # Restarts a specific VNC process.
867
+ #
868
+ # @param process_name [String] Name of the process to restart
869
+ # @return [CogboxApiClient::ProcessRestartResponse] Process restart response
870
+ # @raise [Cogbox::Sdk::Error] If the operation fails
871
+ #
872
+ # @example
873
+ # result = sandbox.computer_use.restart_process("xfce4")
874
+ # puts "XFCE4 process restarted: #{result.message}"
875
+ def restart_process(process_name:)
876
+ toolbox_api.restart_process(process_name, sandbox_id)
877
+ rescue StandardError => e
878
+ raise Sdk::Error, "Failed to restart process: #{e.message}"
879
+ end
880
+
881
+ # Gets logs for a specific VNC process.
882
+ #
883
+ # @param process_name [String] Name of the process to get logs for
884
+ # @return [CogboxApiClient::ProcessLogsResponse] Process logs
885
+ # @raise [Cogbox::Sdk::Error] If the operation fails
886
+ #
887
+ # @example
888
+ # logs = sandbox.computer_use.get_process_logs("novnc")
889
+ # puts "NoVNC logs: #{logs}"
890
+ def get_process_logs(process_name:)
891
+ toolbox_api.get_process_logs(process_name, sandbox_id)
892
+ rescue StandardError => e
893
+ raise Sdk::Error, "Failed to get process logs: #{e.message}"
894
+ end
895
+
896
+ # Gets error logs for a specific VNC process.
897
+ #
898
+ # @param process_name [String] Name of the process to get error logs for
899
+ # @return [CogboxApiClient::ProcessErrorsResponse] Process error logs
900
+ # @raise [Cogbox::Sdk::Error] If the operation fails
901
+ #
902
+ # @example
903
+ # errors = sandbox.computer_use.get_process_errors("x11vnc")
904
+ # puts "X11VNC errors: #{errors}"
905
+ def get_process_errors(process_name:)
906
+ toolbox_api.get_process_errors(process_name, sandbox_id)
907
+ rescue StandardError => e
908
+ raise Sdk::Error, "Failed to get process errors: #{e.message}"
909
+ end
910
+
911
+ instrument :start, :stop, :status, :get_process_status, :restart_process,
912
+ :get_process_logs, :get_process_errors,
913
+ component: 'ComputerUse'
914
+
915
+ private
916
+
917
+ # @return [Cogbox::OtelState, nil]
918
+ attr_reader :otel_state
919
+ end
920
+ end