daytona 0.155.0 → 0.157.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_toolbox/sandbox_js_code_toolbox.rb +5 -3
- data/lib/daytona/code_toolbox/sandbox_python_code_toolbox.rb +5 -6
- data/lib/daytona/code_toolbox/sandbox_ts_code_toolbox.rb +12 -5
- data/lib/daytona/config.rb +31 -30
- data/lib/daytona/daytona.rb +5 -2
- data/lib/daytona/process.rb +13 -9
- data/lib/daytona/sandbox.rb +3 -2
- data/lib/daytona/sdk/version.rb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0e4b054d1b0090d6425189733b471adfb8de35c814cbebfe9ba1df64dd7cb37e
|
|
4
|
+
data.tar.gz: 69fdfe1572c89aa67bf804a2bd751edb2b73f8c6d2b52add512f79ec0459b5ec
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 249970151c4a5c4043ebff0bcaf3c885ed723b7b0742b6016af95c4bbdfa3e8d23c287936ce32a0b7b4dfc2bc6ab0261b75e8fc29421485528fda670f44960c5
|
|
7
|
+
data.tar.gz: 396607d006c45ea9add617beba268f15c0176af2778249b1c94414c9ab2e80f0be831df2cfe7c520c61173f79e15ca1d70b827e24c3c8a1760d07ceba229bb6d
|
|
@@ -5,15 +5,17 @@ require 'base64'
|
|
|
5
5
|
module Daytona
|
|
6
6
|
class SandboxJsCodeToolbox
|
|
7
7
|
def get_run_command(code, params = nil)
|
|
8
|
+
# Prepend argv fix: node - places '-' at argv[1]; splice it out to match legacy node -e behaviour
|
|
8
9
|
# Encode the provided code in base64
|
|
9
|
-
base64_code = Base64.strict_encode64(code)
|
|
10
|
+
base64_code = Base64.strict_encode64("process.argv.splice(1, 1);\n" + code)
|
|
10
11
|
|
|
11
12
|
# Build command-line arguments string
|
|
12
13
|
argv = ''
|
|
13
14
|
argv = params.argv.join(' ') if params&.argv && !params.argv.empty?
|
|
14
15
|
|
|
15
|
-
#
|
|
16
|
-
|
|
16
|
+
# Pipe the base64-encoded code via stdin to avoid OS ARG_MAX limits on large payloads
|
|
17
|
+
# Use node - to read from stdin (node /dev/stdin does not work when stdin is a pipe)
|
|
18
|
+
"printf '%s' '#{base64_code}' | base64 -d | node - #{argv}"
|
|
17
19
|
end
|
|
18
20
|
end
|
|
19
21
|
end
|
|
@@ -10,22 +10,21 @@ module Daytona
|
|
|
10
10
|
# @param params [Daytona::CodeRunParams, nil] Optional parameters for code execution
|
|
11
11
|
# @return [String] The command to run the Python code
|
|
12
12
|
def get_run_command(code, params = nil)
|
|
13
|
-
encoded_code = Base64.
|
|
13
|
+
encoded_code = Base64.strict_encode64(code)
|
|
14
14
|
|
|
15
15
|
# Override plt.show() method if matplotlib is imported
|
|
16
16
|
if matplotlib_imported?(code)
|
|
17
|
-
encoded_code = Base64.
|
|
17
|
+
encoded_code = Base64.strict_encode64(
|
|
18
18
|
Base64.decode64(PYTHON_CODE_WRAPPER).gsub('{encoded_code}', encoded_code)
|
|
19
19
|
)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
argv = params&.argv&.join(' ') || ''
|
|
23
23
|
|
|
24
|
-
#
|
|
24
|
+
# Pipe the base64-encoded code via stdin to avoid OS ARG_MAX limits on large payloads
|
|
25
|
+
# printf is a shell builtin that does not invoke execve(), so the base64 string bypasses the kernel ARG_MAX limit
|
|
25
26
|
# Use -u flag to ensure unbuffered output for real-time error reporting
|
|
26
|
-
|
|
27
|
-
" sh -c 'python3 -u -c \"exec(__import__(\\\"base64\\\").b64decode(\\\"\\\"\\\"#{encoded_code}\\\"\\\"\\\")" \
|
|
28
|
-
".decode())\" #{argv}' "
|
|
27
|
+
"printf '%s' '#{encoded_code}' | base64 -d | python3 -u - #{argv}"
|
|
29
28
|
end
|
|
30
29
|
|
|
31
30
|
private
|
|
@@ -10,14 +10,21 @@ module Daytona
|
|
|
10
10
|
# @param params [Daytona::CodeRunParams, nil] Optional parameters for code execution
|
|
11
11
|
# @return [String] The command to run the TypeScript code
|
|
12
12
|
def get_run_command(code, params = nil)
|
|
13
|
-
|
|
13
|
+
# Prepend argv fix: ts-node places the script path at argv[1]; splice it out to match legacy node -e behaviour
|
|
14
|
+
encoded_code = Base64.strict_encode64("process.argv.splice(1, 1);\n" + code)
|
|
14
15
|
|
|
15
16
|
argv = params&.argv&.join(' ') || ''
|
|
16
17
|
|
|
17
|
-
#
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
# Pipe the base64-encoded code via stdin to avoid OS ARG_MAX limits on large payloads
|
|
19
|
+
# ts-node does not support - for stdin; use shell PID ($$) for the temp file — each code_run spawns its own
|
|
20
|
+
# shell process so $$ is unique across concurrent calls; cleaned up before exit
|
|
21
|
+
# npm_config_loglevel=error suppresses npm notice/warn output at source, preserving streaming and real errors
|
|
22
|
+
'_f=/tmp/dtn_$$.ts; ' \
|
|
23
|
+
"printf '%s' '#{encoded_code}' | base64 -d > \"$_f\"; " \
|
|
24
|
+
"npm_config_loglevel=error npx ts-node -T --ignore-diagnostics 5107 -O '{\"module\":\"CommonJS\"}' \"$_f\" #{argv}; " \
|
|
25
|
+
'_dtn_ec=$?; ' \
|
|
26
|
+
'rm -f "$_f"; ' \
|
|
27
|
+
'exit $_dtn_ec'
|
|
21
28
|
end
|
|
22
29
|
end
|
|
23
30
|
end
|
data/lib/daytona/config.rb
CHANGED
|
@@ -52,46 +52,47 @@ module Daytona
|
|
|
52
52
|
target: nil,
|
|
53
53
|
_experimental: nil
|
|
54
54
|
)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
@
|
|
60
|
-
@
|
|
61
|
-
@
|
|
62
|
-
@target = target || ENV.fetch('DAYTONA_TARGET', nil)
|
|
63
|
-
@organization_id = organization_id || ENV.fetch('DAYTONA_ORGANIZATION_ID', nil)
|
|
55
|
+
@env_reader = daytona_env_reader
|
|
56
|
+
|
|
57
|
+
@api_key = api_key || @env_reader.call('DAYTONA_API_KEY')
|
|
58
|
+
@jwt_token = jwt_token || @env_reader.call('DAYTONA_JWT_TOKEN')
|
|
59
|
+
@api_url = api_url || @env_reader.call('DAYTONA_API_URL') || API_URL
|
|
60
|
+
@target = target || @env_reader.call('DAYTONA_TARGET')
|
|
61
|
+
@organization_id = organization_id || @env_reader.call('DAYTONA_ORGANIZATION_ID')
|
|
64
62
|
@_experimental = _experimental
|
|
65
63
|
end
|
|
66
64
|
|
|
67
|
-
|
|
65
|
+
# Reads a DAYTONA_-prefixed environment variable using the same precedence
|
|
66
|
+
# as the Config initializer: runtime ENV first, then .env.local, then .env.
|
|
67
|
+
# Only names starting with DAYTONA_ are accepted.
|
|
68
|
+
#
|
|
69
|
+
# @param name [String] The environment variable name. Must start with DAYTONA_.
|
|
70
|
+
# @return [String, nil] The value of the environment variable, or nil if not set.
|
|
71
|
+
# @raise [ArgumentError] If name does not start with DAYTONA_.
|
|
72
|
+
def read_env(name)
|
|
73
|
+
@env_reader.call(name)
|
|
74
|
+
end
|
|
68
75
|
|
|
69
|
-
|
|
70
|
-
# Only loads variables that are not already set in the runtime environment
|
|
71
|
-
# .env.local overrides .env
|
|
72
|
-
# Files are loaded from the current working directory
|
|
73
|
-
def load_env_files
|
|
74
|
-
# Daytona-specific variables we want to load
|
|
75
|
-
daytona_vars = %w[
|
|
76
|
-
DAYTONA_API_KEY
|
|
77
|
-
DAYTONA_API_URL
|
|
78
|
-
DAYTONA_TARGET
|
|
79
|
-
DAYTONA_JWT_TOKEN
|
|
80
|
-
DAYTONA_ORGANIZATION_ID
|
|
81
|
-
]
|
|
76
|
+
private
|
|
82
77
|
|
|
78
|
+
# Returns a lambda that looks up DAYTONA_-prefixed env vars without writing to ENV.
|
|
79
|
+
# Files are parsed once; lookups check runtime env first, then .env.local, then .env.
|
|
80
|
+
def daytona_env_reader
|
|
81
|
+
file_vars = {}
|
|
83
82
|
env_file = File.join(Dir.pwd, '.env')
|
|
83
|
+
file_vars.merge!(daytona_filter(Dotenv.parse(env_file))) if File.exist?(env_file)
|
|
84
84
|
env_local_file = File.join(Dir.pwd, '.env.local')
|
|
85
|
+
file_vars.merge!(daytona_filter(Dotenv.parse(env_local_file))) if File.exist?(env_local_file)
|
|
85
86
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
env_from_file.merge!(Dotenv.parse(env_file)) if File.exist?(env_file)
|
|
89
|
-
env_from_file.merge!(Dotenv.parse(env_local_file)) if File.exist?(env_local_file)
|
|
87
|
+
lambda do |name|
|
|
88
|
+
raise ArgumentError, "Variable must start with 'DAYTONA_', got '#{name}'" unless name.start_with?('DAYTONA_')
|
|
90
89
|
|
|
91
|
-
|
|
92
|
-
daytona_vars.each do |var|
|
|
93
|
-
ENV[var] = env_from_file[var] if env_from_file.key?(var) && !ENV.key?(var)
|
|
90
|
+
ENV.key?(name) ? ENV[name] : file_vars[name]
|
|
94
91
|
end
|
|
95
92
|
end
|
|
93
|
+
|
|
94
|
+
def daytona_filter(env_hash)
|
|
95
|
+
env_hash.select { |k, _| k.start_with?('DAYTONA_') }
|
|
96
|
+
end
|
|
96
97
|
end
|
|
97
98
|
end
|
data/lib/daytona/daytona.rb
CHANGED
|
@@ -33,7 +33,7 @@ module Daytona
|
|
|
33
33
|
@config = config
|
|
34
34
|
ensure_access_token_defined
|
|
35
35
|
|
|
36
|
-
otel_enabled = config._experimental&.dig('otel_enabled') ||
|
|
36
|
+
otel_enabled = config._experimental&.dig('otel_enabled') || config.read_env('DAYTONA_EXPERIMENTAL_OTEL_ENABLED') == 'true'
|
|
37
37
|
@otel_state = (::Daytona.init_otel(Sdk::VERSION) if otel_enabled)
|
|
38
38
|
|
|
39
39
|
@api_client = build_api_client
|
|
@@ -149,10 +149,13 @@ module Daytona
|
|
|
149
149
|
raise Sdk::Error, 'auto_archive_interval must be a non-negative integer'
|
|
150
150
|
end
|
|
151
151
|
|
|
152
|
+
labels = params.labels&.dup || {}
|
|
153
|
+
labels[LABEL_CODE_TOOLBOX_LANGUAGE] = params.language.to_s if params.language
|
|
154
|
+
|
|
152
155
|
create_sandbox = DaytonaApiClient::CreateSandbox.new(
|
|
153
156
|
user: params.os_user,
|
|
154
157
|
env: params.env_vars || {},
|
|
155
|
-
labels:
|
|
158
|
+
labels: labels,
|
|
156
159
|
public: params.public,
|
|
157
160
|
target: config.target,
|
|
158
161
|
auto_stop_interval: params.auto_stop_interval,
|
data/lib/daytona/process.rb
CHANGED
|
@@ -54,21 +54,23 @@ module Daytona
|
|
|
54
54
|
#
|
|
55
55
|
# # Command with timeout
|
|
56
56
|
# result = sandbox.process.exec("sleep 10", timeout: 5)
|
|
57
|
-
def exec(command:, cwd: nil, env: nil, timeout: nil) # rubocop:disable Metrics/
|
|
58
|
-
command = "echo '#{Base64.encode64(command)}' | base64 -d | sh"
|
|
59
|
-
|
|
57
|
+
def exec(command:, cwd: nil, env: nil, timeout: nil) # rubocop:disable Metrics/MethodLength
|
|
60
58
|
if env && !env.empty?
|
|
59
|
+
env.each_key do |key|
|
|
60
|
+
unless key.match?(/\A[A-Za-z_][A-Za-z0-9_]*\z/)
|
|
61
|
+
raise ArgumentError,
|
|
62
|
+
"Invalid environment variable name: '#{key}'"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
61
65
|
safe_env_exports = env.map do |key, value|
|
|
62
|
-
"export #{key}
|
|
63
|
-
end.join(';')
|
|
66
|
+
"export #{key}=\"$(echo '#{Base64.strict_encode64(value)}' | base64 -d)\""
|
|
67
|
+
end.join('; ')
|
|
64
68
|
command = "#{safe_env_exports}; #{command}"
|
|
65
69
|
end
|
|
66
70
|
|
|
67
|
-
command = "sh -c \"#{command}\""
|
|
68
|
-
|
|
69
71
|
response = toolbox_api.execute_command(DaytonaToolboxApiClient::ExecuteRequest.new(command:, cwd:, timeout:))
|
|
70
72
|
# Post-process the output to extract ExecutionArtifacts
|
|
71
|
-
artifacts = parse_output(response.result.split("\n"))
|
|
73
|
+
artifacts = parse_output(response.result.split("\n", -1))
|
|
72
74
|
|
|
73
75
|
# Create new response with processed output and charts
|
|
74
76
|
ExecuteResponse.new(
|
|
@@ -546,15 +548,17 @@ module Daytona
|
|
|
546
548
|
# @return [Daytona::ExecutionArtifacts] The artifacts from the command execution
|
|
547
549
|
def parse_output(lines)
|
|
548
550
|
artifacts = ExecutionArtifacts.new('', [])
|
|
551
|
+
stdout_lines = []
|
|
549
552
|
|
|
550
553
|
lines.each do |line|
|
|
551
554
|
if line.start_with?(ARTIFACT_PREFIX)
|
|
552
555
|
parse_json_line(line:, artifacts:)
|
|
553
556
|
else
|
|
554
|
-
|
|
557
|
+
stdout_lines << line
|
|
555
558
|
end
|
|
556
559
|
end
|
|
557
560
|
|
|
561
|
+
artifacts.stdout = stdout_lines.join("\n")
|
|
558
562
|
artifacts
|
|
559
563
|
end
|
|
560
564
|
|
data/lib/daytona/sandbox.rb
CHANGED
|
@@ -365,13 +365,14 @@ module Daytona
|
|
|
365
365
|
# Stops the Sandbox and waits for it to be stopped.
|
|
366
366
|
#
|
|
367
367
|
# @param timeout [Numeric] Maximum wait time in seconds (defaults to 60 s).
|
|
368
|
+
# @param force [Boolean] If true, uses SIGKILL instead of SIGTERM (defaults to false).
|
|
368
369
|
# @return [void]
|
|
369
|
-
def stop(timeout = DEFAULT_TIMEOUT) # rubocop:disable Metrics/MethodLength
|
|
370
|
+
def stop(timeout = DEFAULT_TIMEOUT, force: false) # rubocop:disable Metrics/MethodLength
|
|
370
371
|
with_timeout(
|
|
371
372
|
timeout:,
|
|
372
373
|
message: "Sandbox #{id} failed to become stopped within the #{timeout} seconds timeout period",
|
|
373
374
|
setup: proc {
|
|
374
|
-
sandbox_api.stop_sandbox(id)
|
|
375
|
+
sandbox_api.stop_sandbox(id, { force: force })
|
|
375
376
|
refresh
|
|
376
377
|
}
|
|
377
378
|
) do
|
data/lib/daytona/sdk/version.rb
CHANGED
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.157.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daytona Platforms Inc.
|
|
@@ -85,28 +85,28 @@ dependencies:
|
|
|
85
85
|
requirements:
|
|
86
86
|
- - '='
|
|
87
87
|
- !ruby/object:Gem::Version
|
|
88
|
-
version: 0.
|
|
88
|
+
version: 0.157.0
|
|
89
89
|
type: :runtime
|
|
90
90
|
prerelease: false
|
|
91
91
|
version_requirements: !ruby/object:Gem::Requirement
|
|
92
92
|
requirements:
|
|
93
93
|
- - '='
|
|
94
94
|
- !ruby/object:Gem::Version
|
|
95
|
-
version: 0.
|
|
95
|
+
version: 0.157.0
|
|
96
96
|
- !ruby/object:Gem::Dependency
|
|
97
97
|
name: daytona_toolbox_api_client
|
|
98
98
|
requirement: !ruby/object:Gem::Requirement
|
|
99
99
|
requirements:
|
|
100
100
|
- - '='
|
|
101
101
|
- !ruby/object:Gem::Version
|
|
102
|
-
version: 0.
|
|
102
|
+
version: 0.157.0
|
|
103
103
|
type: :runtime
|
|
104
104
|
prerelease: false
|
|
105
105
|
version_requirements: !ruby/object:Gem::Requirement
|
|
106
106
|
requirements:
|
|
107
107
|
- - '='
|
|
108
108
|
- !ruby/object:Gem::Version
|
|
109
|
-
version: 0.
|
|
109
|
+
version: 0.157.0
|
|
110
110
|
- !ruby/object:Gem::Dependency
|
|
111
111
|
name: dotenv
|
|
112
112
|
requirement: !ruby/object:Gem::Requirement
|