daytona 0.211.2 → 0.214.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 +1 -1
- data/lib/daytona/common/image.rb +49 -30
- data/lib/daytona/common/websocket_dialer.rb +166 -0
- data/lib/daytona/config.rb +41 -2
- data/lib/daytona/process.rb +4 -4
- data/lib/daytona/sdk/version.rb +1 -1
- data/lib/daytona/sdk.rb +1 -0
- metadata +10 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 868afc4f077432e39b074d0249d2a20277206a1e430f64954b42663b74496118
|
|
4
|
+
data.tar.gz: 5e8dc41efd1f588f325f8fd117d8ad985382d5c7a43a73740d58ded6d5def73e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c5fca0242291118c5000ffba802fb450aeab89c8f4b05c2f01cde1192e9c898dff8480edf6d9c12332a317c6282656a77b8b3fc9d75b0172a6bb0a16f1bb3dbc
|
|
7
|
+
data.tar.gz: 11fb9e1f412128cdb6651852bd5af7a7441a49ba1dcd3dab1ae3ce2479ac3769ba5968c3a85e10218b6e6f2ee45bd818adaebd9f3481fdb989e884294def4899
|
|
@@ -108,7 +108,7 @@ module Daytona
|
|
|
108
108
|
|
|
109
109
|
puts "[DEBUG] Connecting to WebSocket: #{ws_url}" if ENV['DEBUG']
|
|
110
110
|
|
|
111
|
-
ws =
|
|
111
|
+
ws = Common::WebSocketDialer.connect(ws_url, headers:) do |client|
|
|
112
112
|
client.on :open do
|
|
113
113
|
puts '[DEBUG] WebSocket opened, sending request' if ENV['DEBUG']
|
|
114
114
|
client.send(JSON.dump(request))
|
data/lib/daytona/common/image.rb
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
require 'digest'
|
|
7
7
|
require 'fileutils'
|
|
8
|
+
require 'json'
|
|
8
9
|
require 'pathname'
|
|
9
10
|
require 'shellwords'
|
|
10
11
|
|
|
@@ -362,7 +363,7 @@ module Daytona
|
|
|
362
363
|
# @return [Array<Array<String>>] The list of the actual file path and its corresponding COPY-command source path
|
|
363
364
|
def extract_copy_sources(dockerfile_content, path_prefix = '') # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
|
|
364
365
|
sources = []
|
|
365
|
-
lines = dockerfile_content
|
|
366
|
+
lines = dockerfile_logical_lines(dockerfile_content)
|
|
366
367
|
|
|
367
368
|
lines.each do |line|
|
|
368
369
|
# Skip empty lines and comments
|
|
@@ -404,47 +405,65 @@ module Daytona
|
|
|
404
405
|
sources
|
|
405
406
|
end
|
|
406
407
|
|
|
408
|
+
# Joins backslash-continued physical lines into logical Dockerfile instruction lines
|
|
409
|
+
#
|
|
410
|
+
# @param dockerfile_content [String] The content of the Dockerfile
|
|
411
|
+
# @return [Array<String>] The logical instruction lines
|
|
412
|
+
def dockerfile_logical_lines(dockerfile_content) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
|
|
413
|
+
logical_lines = []
|
|
414
|
+
current = nil
|
|
415
|
+
|
|
416
|
+
dockerfile_content.each_line(chomp: true) do |physical_line|
|
|
417
|
+
is_comment = physical_line.lstrip.start_with?('#')
|
|
418
|
+
# Docker drops empty and comment lines that appear inside a continued instruction
|
|
419
|
+
next if current && (physical_line.strip.empty? || is_comment)
|
|
420
|
+
|
|
421
|
+
stripped = physical_line.rstrip
|
|
422
|
+
# A trailing backslash on a comment line is literal; comments never continue onto the next line
|
|
423
|
+
continued = !is_comment && stripped.end_with?('\\')
|
|
424
|
+
segment = continued ? stripped[0..-2] : physical_line
|
|
425
|
+
current = current ? current + segment : segment
|
|
426
|
+
next if continued
|
|
427
|
+
|
|
428
|
+
logical_lines << current
|
|
429
|
+
current = nil
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
logical_lines << current if current
|
|
433
|
+
logical_lines
|
|
434
|
+
end
|
|
435
|
+
|
|
407
436
|
# Parses a COPY command to extract sources and destination
|
|
408
437
|
#
|
|
409
438
|
# @param line [String] The line to parse
|
|
410
439
|
# @return [Hash, nil] A hash containing the sources and destination, or nil if parsing fails
|
|
411
|
-
def parse_copy_command(line)
|
|
440
|
+
def parse_copy_command(line)
|
|
412
441
|
# Remove initial "COPY" and strip whitespace
|
|
413
442
|
parts = line.strip[4..].strip
|
|
414
443
|
|
|
415
|
-
#
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
# Parse the JSON-like array format
|
|
419
|
-
elements = Shellwords.split(parts.delete('[]'))
|
|
420
|
-
return nil if elements.length < 2
|
|
421
|
-
|
|
422
|
-
{ 'sources' => elements[0..-2], 'dest' => elements[-1] }
|
|
423
|
-
rescue StandardError
|
|
424
|
-
nil
|
|
425
|
-
end
|
|
426
|
-
end
|
|
444
|
+
# Skip leading flags. Value-taking flags use the --flag=value form (--chown=..., --chmod=...)
|
|
445
|
+
# and boolean flags stand alone (--link), so a flag never consumes the token that follows it.
|
|
446
|
+
parts = parts.sub(/\A\S+\s*/, '') while parts.start_with?('--')
|
|
427
447
|
|
|
428
|
-
# Handle
|
|
429
|
-
parts
|
|
448
|
+
# Handle JSON array format: COPY ["src1", "src2", "dest"]
|
|
449
|
+
return parse_json_copy_command(parts) if parts.start_with?('[')
|
|
430
450
|
|
|
431
|
-
#
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
break unless part.start_with?('--')
|
|
451
|
+
# Handle the whitespace-separated format
|
|
452
|
+
elements = Shellwords.split(parts)
|
|
453
|
+
return nil if elements.length < 2
|
|
435
454
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
i + 1
|
|
441
|
-
end
|
|
442
|
-
end
|
|
455
|
+
{ 'sources' => elements[0..-2], 'dest' => elements[-1] }
|
|
456
|
+
rescue ArgumentError
|
|
457
|
+
nil
|
|
458
|
+
end
|
|
443
459
|
|
|
444
|
-
|
|
445
|
-
|
|
460
|
+
def parse_json_copy_command(parts)
|
|
461
|
+
elements = JSON.parse(parts)
|
|
462
|
+
return nil unless elements.is_a?(Array) && elements.all?(String) && elements.length >= 2
|
|
446
463
|
|
|
447
|
-
{ 'sources' =>
|
|
464
|
+
{ 'sources' => elements[0..-2], 'dest' => elements[-1] }
|
|
465
|
+
rescue JSON::ParserError
|
|
466
|
+
nil
|
|
448
467
|
end
|
|
449
468
|
end
|
|
450
469
|
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright Daytona Platforms Inc.
|
|
4
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
|
|
6
|
+
require 'openssl'
|
|
7
|
+
require 'socket'
|
|
8
|
+
require 'uri'
|
|
9
|
+
require 'websocket'
|
|
10
|
+
require 'websocket-client-simple'
|
|
11
|
+
|
|
12
|
+
module Daytona
|
|
13
|
+
module Common
|
|
14
|
+
# Establishes WebSocket connections with the peer certificate fully verified
|
|
15
|
+
# before any request bytes are written.
|
|
16
|
+
#
|
|
17
|
+
# `websocket-client-simple` builds its own `SSLContext` and never calls
|
|
18
|
+
# `OpenSSL::SSL::SSLContext#set_params`, so it does not pick up Ruby's own
|
|
19
|
+
# defaults (`verify_mode: VERIFY_PEER`, `verify_hostname: true`). It applies
|
|
20
|
+
# `verify_mode` only when the caller passes it, and offers no way to enable
|
|
21
|
+
# hostname verification at all. Because the context is frozen by
|
|
22
|
+
# `SSLSocket.new` and the handshake is written before `connect` returns,
|
|
23
|
+
# there is no caller-side hook to correct this - so the dial itself has to
|
|
24
|
+
# be owned here.
|
|
25
|
+
#
|
|
26
|
+
# Chain verification alone is not sufficient: without hostname verification
|
|
27
|
+
# a peer holding any valid certificate can terminate the connection and
|
|
28
|
+
# receive the request headers.
|
|
29
|
+
module WebSocketDialer
|
|
30
|
+
# Opens a verified WebSocket connection.
|
|
31
|
+
#
|
|
32
|
+
# Mirrors `WebSocket::Client::Simple.connect`: the block, if given, receives
|
|
33
|
+
# the client before the connection is established so handlers can be
|
|
34
|
+
# registered, and the client is returned.
|
|
35
|
+
#
|
|
36
|
+
# @param url [String] The `ws://` or `wss://` URL to dial.
|
|
37
|
+
# @param options [Hash] Passed through to the client; `:headers`, `:ssl_version` and `:cert_store` are honoured.
|
|
38
|
+
# @return [VerifyingClient] The connected client.
|
|
39
|
+
# @raise [OpenSSL::SSL::SSLError] If the peer certificate fails chain or hostname verification.
|
|
40
|
+
def self.connect(url, options = {})
|
|
41
|
+
client = VerifyingClient.new
|
|
42
|
+
yield client if block_given?
|
|
43
|
+
client.connect(url, options)
|
|
44
|
+
client
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# A `websocket-client-simple` client that verifies the peer before writing.
|
|
48
|
+
#
|
|
49
|
+
# `#connect` is reimplemented rather than extended because the upstream
|
|
50
|
+
# method builds the socket, performs the TLS handshake and writes the
|
|
51
|
+
# request in one pass, and returns early when `@socket` is already set.
|
|
52
|
+
#
|
|
53
|
+
# Everything after TLS setup mirrors upstream so that framing, the reader
|
|
54
|
+
# thread and event semantics stay identical. That couples to the instance
|
|
55
|
+
# variables the inherited `#send`, `#close`, `#open?` and `#closed?` read,
|
|
56
|
+
# so the gemspec pins the gem to `~> 0.9.0` - a 0.x minor bump could rename
|
|
57
|
+
# them. `websocket_dialer_spec.rb` drives a full send/close round trip
|
|
58
|
+
# against a real listener, which fails if that contract breaks.
|
|
59
|
+
class VerifyingClient < WebSocket::Client::Simple::Client
|
|
60
|
+
# @param url [String] The `ws://` or `wss://` URL to dial.
|
|
61
|
+
# @param options [Hash] Connection options.
|
|
62
|
+
# @return [void]
|
|
63
|
+
def connect(url, options = {})
|
|
64
|
+
return if @socket
|
|
65
|
+
|
|
66
|
+
@url = url
|
|
67
|
+
uri = URI.parse(url)
|
|
68
|
+
@socket = TCPSocket.new(uri.host, uri.port || (uri.scheme == 'wss' ? 443 : 80))
|
|
69
|
+
@socket = verified_ssl_socket(@socket, uri, options) if %w[https wss].include?(uri.scheme)
|
|
70
|
+
|
|
71
|
+
start_websocket(url, options)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
# Wraps a socket in TLS with the peer certificate verified.
|
|
77
|
+
#
|
|
78
|
+
# @param socket [TCPSocket] The connected plaintext socket.
|
|
79
|
+
# @param uri [URI] The dialed URI, whose host the certificate must match.
|
|
80
|
+
# @param options [Hash] Connection options.
|
|
81
|
+
# @return [OpenSSL::SSL::SSLSocket] The connected, verified socket.
|
|
82
|
+
# @raise [OpenSSL::SSL::SSLError] If chain or hostname verification fails.
|
|
83
|
+
def verified_ssl_socket(socket, uri, options) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
84
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
|
85
|
+
ctx.ssl_version = options[:ssl_version] if options[:ssl_version]
|
|
86
|
+
|
|
87
|
+
# Only seed system roots into a store we own. Adding them to a
|
|
88
|
+
# caller-supplied store would silently widen their trust policy.
|
|
89
|
+
cert_store = options[:cert_store]
|
|
90
|
+
unless cert_store
|
|
91
|
+
cert_store = OpenSSL::X509::Store.new
|
|
92
|
+
cert_store.set_default_paths
|
|
93
|
+
end
|
|
94
|
+
ctx.cert_store = cert_store
|
|
95
|
+
|
|
96
|
+
# Must precede SSLSocket.new, which freezes the context.
|
|
97
|
+
ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
98
|
+
ctx.verify_hostname = true
|
|
99
|
+
|
|
100
|
+
ssl = OpenSSL::SSL::SSLSocket.new(socket, ctx)
|
|
101
|
+
ssl.sync_close = true
|
|
102
|
+
ssl.hostname = uri.host
|
|
103
|
+
begin
|
|
104
|
+
ssl.connect
|
|
105
|
+
# Redundant while verify_hostname holds, but keeps the check explicit
|
|
106
|
+
# and independent of the context surviving future changes.
|
|
107
|
+
ssl.post_connection_check(uri.host)
|
|
108
|
+
rescue StandardError
|
|
109
|
+
ssl.close
|
|
110
|
+
raise
|
|
111
|
+
end
|
|
112
|
+
ssl
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Performs the WebSocket handshake and starts the reader thread.
|
|
116
|
+
#
|
|
117
|
+
# Mirrors `WebSocket::Client::Simple::Client#connect` from the request
|
|
118
|
+
# onward. Only reached once the socket is verified.
|
|
119
|
+
#
|
|
120
|
+
# @param url [String] The dialed URL.
|
|
121
|
+
# @param options [Hash] Connection options.
|
|
122
|
+
# @return [void]
|
|
123
|
+
def start_websocket(url, options) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
124
|
+
WebSocket.should_raise = true
|
|
125
|
+
@handshake = WebSocket::Handshake::Client.new(url: url, headers: options[:headers])
|
|
126
|
+
@handshaked = false
|
|
127
|
+
@pipe_broken = false
|
|
128
|
+
@closed = false
|
|
129
|
+
frame = WebSocket::Frame::Incoming::Client.new
|
|
130
|
+
|
|
131
|
+
once :__close do |err|
|
|
132
|
+
close
|
|
133
|
+
emit :close, err
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
@thread = Thread.new do
|
|
137
|
+
until @closed
|
|
138
|
+
begin
|
|
139
|
+
unless (recv_data = @socket.getc)
|
|
140
|
+
sleep 1
|
|
141
|
+
next
|
|
142
|
+
end
|
|
143
|
+
if @handshaked
|
|
144
|
+
frame << recv_data
|
|
145
|
+
while (msg = frame.next)
|
|
146
|
+
emit :message, msg
|
|
147
|
+
end
|
|
148
|
+
else
|
|
149
|
+
@handshake << recv_data
|
|
150
|
+
if @handshake.finished?
|
|
151
|
+
@handshaked = true
|
|
152
|
+
emit :open
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
rescue StandardError => e
|
|
156
|
+
emit :error, e
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
@socket.write @handshake.to_s
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
data/lib/daytona/config.rb
CHANGED
|
@@ -6,6 +6,45 @@
|
|
|
6
6
|
require 'dotenv'
|
|
7
7
|
|
|
8
8
|
module Daytona
|
|
9
|
+
# Dotenv runs `$(...)` in a value through the shell while parsing, and the working
|
|
10
|
+
# directory is frequently a cloned repository, so parsing `.env` there would execute
|
|
11
|
+
# whatever its author put in it.
|
|
12
|
+
module EnvFile
|
|
13
|
+
# Dotenv::Parser picks its substitutions up from `self.class.substitutions`, and Ruby
|
|
14
|
+
# does not inherit class-level instance variables, so declaring the list here drops
|
|
15
|
+
# command substitution while leaving Dotenv::Parser alone — a host application's own
|
|
16
|
+
# Dotenv.load keeps the behaviour its author chose. `${VAR}` is kept: it reads only
|
|
17
|
+
# keys already parsed or the process environment.
|
|
18
|
+
class Parser < Dotenv::Parser
|
|
19
|
+
@substitutions = [Dotenv::Substitutions::Variable].freeze
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Read with the mode dotenv itself uses, so the accepted format does not narrow: `bom`
|
|
23
|
+
# skips a byte-order mark an editor on Windows may have written, and pinning `utf-8`
|
|
24
|
+
# keeps the file readable under a POSIX locale, where the default external encoding
|
|
25
|
+
# would make one accented byte anywhere — a comment included — raise while it is scanned.
|
|
26
|
+
def self.parse(path)
|
|
27
|
+
verify_suppression!
|
|
28
|
+
Parser.call(File.read(path, mode: 'rb:bom|utf-8'))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# The subclass reaches into dotenv's internals rather than a public API, and dotenv has
|
|
32
|
+
# already reorganised them once inside the range the gemspec allows, so confirm the
|
|
33
|
+
# suppression actually holds in the process that relies on it rather than trusting the
|
|
34
|
+
# version pinned in CI. Checked here rather than on load: this is the operation the
|
|
35
|
+
# guard protects, so a lapse stops it, and a gem that never reads a .env still loads.
|
|
36
|
+
# A failure is not memoised, so it is raised again on the next attempt.
|
|
37
|
+
def self.verify_suppression!
|
|
38
|
+
return if @verified
|
|
39
|
+
|
|
40
|
+
probe = Parser.call('DAYTONA_PROBE=$(echo substituted)')['DAYTONA_PROBE']
|
|
41
|
+
raise "dotenv command substitution is not suppressed (got #{probe.inspect})" unless
|
|
42
|
+
probe == '$(echo substituted)'
|
|
43
|
+
|
|
44
|
+
@verified = true
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
9
48
|
class Config
|
|
10
49
|
API_URL = 'https://app.daytona.io/api'
|
|
11
50
|
|
|
@@ -131,9 +170,9 @@ module Daytona
|
|
|
131
170
|
def parse_dotenv_files
|
|
132
171
|
file_vars = {}
|
|
133
172
|
env_file = File.join(Dir.pwd, '.env')
|
|
134
|
-
file_vars.merge!(daytona_filter(
|
|
173
|
+
file_vars.merge!(daytona_filter(EnvFile.parse(env_file))) if File.exist?(env_file)
|
|
135
174
|
env_local_file = File.join(Dir.pwd, '.env.local')
|
|
136
|
-
file_vars.merge!(daytona_filter(
|
|
175
|
+
file_vars.merge!(daytona_filter(EnvFile.parse(env_local_file))) if File.exist?(env_local_file)
|
|
137
176
|
file_vars
|
|
138
177
|
end
|
|
139
178
|
|
data/lib/daytona/process.rb
CHANGED
|
@@ -257,7 +257,7 @@ module Daytona
|
|
|
257
257
|
|
|
258
258
|
completion_queue = Queue.new
|
|
259
259
|
|
|
260
|
-
|
|
260
|
+
Common::WebSocketDialer.connect(
|
|
261
261
|
url.to_s,
|
|
262
262
|
headers: toolbox_api.api_client.default_headers.dup.merge(
|
|
263
263
|
'X-Daytona-Preview-Token' => preview_link.token,
|
|
@@ -326,7 +326,7 @@ module Daytona
|
|
|
326
326
|
|
|
327
327
|
completion_queue = Queue.new
|
|
328
328
|
|
|
329
|
-
|
|
329
|
+
Common::WebSocketDialer.connect(
|
|
330
330
|
url.to_s,
|
|
331
331
|
headers: toolbox_api.api_client.default_headers.dup.merge(
|
|
332
332
|
'X-Daytona-Preview-Token' => preview_link.token,
|
|
@@ -465,7 +465,7 @@ module Daytona
|
|
|
465
465
|
headers['Sec-WebSocket-Protocol'] = protocols.join(', ')
|
|
466
466
|
|
|
467
467
|
PtyHandle.new(
|
|
468
|
-
|
|
468
|
+
Common::WebSocketDialer.connect(url.to_s, headers:),
|
|
469
469
|
session_id: id,
|
|
470
470
|
handle_resize: ->(pty_size_arg) { resize_pty_session(id, pty_size_arg) },
|
|
471
471
|
handle_kill: -> { delete_pty_session(id) }
|
|
@@ -502,7 +502,7 @@ module Daytona
|
|
|
502
502
|
[headers['Sec-WebSocket-Protocol'], PTY_EXIT_CONTROL_SUBPROTOCOL].compact.join(', ')
|
|
503
503
|
|
|
504
504
|
handle = nil
|
|
505
|
-
|
|
505
|
+
Common::WebSocketDialer.connect(url.to_s, headers:) do |client|
|
|
506
506
|
handle = PtyHandle.new(
|
|
507
507
|
client,
|
|
508
508
|
session_id:,
|
data/lib/daytona/sdk/version.rb
CHANGED
data/lib/daytona/sdk.rb
CHANGED
|
@@ -33,6 +33,7 @@ require_relative 'common/snapshot'
|
|
|
33
33
|
require_relative 'code_interpreter'
|
|
34
34
|
require_relative 'computer_use'
|
|
35
35
|
require_relative 'common/socketio_client'
|
|
36
|
+
require_relative 'common/websocket_dialer'
|
|
36
37
|
require_relative 'common/event_dispatcher'
|
|
37
38
|
require_relative 'common/event_subscription_manager'
|
|
38
39
|
require_relative 'daytona'
|
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.
|
|
4
|
+
version: 0.214.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daytona Platforms Inc.
|
|
@@ -91,42 +91,42 @@ dependencies:
|
|
|
91
91
|
requirements:
|
|
92
92
|
- - '='
|
|
93
93
|
- !ruby/object:Gem::Version
|
|
94
|
-
version: 0.
|
|
94
|
+
version: 0.214.0
|
|
95
95
|
type: :runtime
|
|
96
96
|
prerelease: false
|
|
97
97
|
version_requirements: !ruby/object:Gem::Requirement
|
|
98
98
|
requirements:
|
|
99
99
|
- - '='
|
|
100
100
|
- !ruby/object:Gem::Version
|
|
101
|
-
version: 0.
|
|
101
|
+
version: 0.214.0
|
|
102
102
|
- !ruby/object:Gem::Dependency
|
|
103
103
|
name: daytona_api_client
|
|
104
104
|
requirement: !ruby/object:Gem::Requirement
|
|
105
105
|
requirements:
|
|
106
106
|
- - '='
|
|
107
107
|
- !ruby/object:Gem::Version
|
|
108
|
-
version: 0.
|
|
108
|
+
version: 0.214.0
|
|
109
109
|
type: :runtime
|
|
110
110
|
prerelease: false
|
|
111
111
|
version_requirements: !ruby/object:Gem::Requirement
|
|
112
112
|
requirements:
|
|
113
113
|
- - '='
|
|
114
114
|
- !ruby/object:Gem::Version
|
|
115
|
-
version: 0.
|
|
115
|
+
version: 0.214.0
|
|
116
116
|
- !ruby/object:Gem::Dependency
|
|
117
117
|
name: daytona_toolbox_api_client
|
|
118
118
|
requirement: !ruby/object:Gem::Requirement
|
|
119
119
|
requirements:
|
|
120
120
|
- - '='
|
|
121
121
|
- !ruby/object:Gem::Version
|
|
122
|
-
version: 0.
|
|
122
|
+
version: 0.214.0
|
|
123
123
|
type: :runtime
|
|
124
124
|
prerelease: false
|
|
125
125
|
version_requirements: !ruby/object:Gem::Requirement
|
|
126
126
|
requirements:
|
|
127
127
|
- - '='
|
|
128
128
|
- !ruby/object:Gem::Version
|
|
129
|
-
version: 0.
|
|
129
|
+
version: 0.214.0
|
|
130
130
|
- !ruby/object:Gem::Dependency
|
|
131
131
|
name: dotenv
|
|
132
132
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -181,14 +181,14 @@ dependencies:
|
|
|
181
181
|
requirements:
|
|
182
182
|
- - "~>"
|
|
183
183
|
- !ruby/object:Gem::Version
|
|
184
|
-
version:
|
|
184
|
+
version: 0.9.0
|
|
185
185
|
type: :runtime
|
|
186
186
|
prerelease: false
|
|
187
187
|
version_requirements: !ruby/object:Gem::Requirement
|
|
188
188
|
requirements:
|
|
189
189
|
- - "~>"
|
|
190
190
|
- !ruby/object:Gem::Version
|
|
191
|
-
version:
|
|
191
|
+
version: 0.9.0
|
|
192
192
|
description: 'High-level Ruby SDK for Daytona: sandboxes, git, filesystem, LSP, process,
|
|
193
193
|
and object storage. Requires Ruby >= 3.2.'
|
|
194
194
|
email:
|
|
@@ -221,6 +221,7 @@ files:
|
|
|
221
221
|
- lib/daytona/common/response.rb
|
|
222
222
|
- lib/daytona/common/snapshot.rb
|
|
223
223
|
- lib/daytona/common/socketio_client.rb
|
|
224
|
+
- lib/daytona/common/websocket_dialer.rb
|
|
224
225
|
- lib/daytona/computer_use.rb
|
|
225
226
|
- lib/daytona/config.rb
|
|
226
227
|
- lib/daytona/daytona.rb
|