e2b 0.3.3 → 0.3.5
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/README.md +104 -20
- data/lib/e2b/api/http_client.rb +5 -1
- data/lib/e2b/client.rb +3 -5
- data/lib/e2b/configuration.rb +6 -6
- data/lib/e2b/models/process_result.rb +20 -22
- data/lib/e2b/models/template_log_entry.rb +1 -1
- data/lib/e2b/paginator.rb +1 -3
- data/lib/e2b/sandbox.rb +5 -10
- data/lib/e2b/sandbox_helpers.rb +5 -7
- data/lib/e2b/services/base_service.rb +80 -68
- data/lib/e2b/services/command_handle.rb +16 -40
- data/lib/e2b/services/commands.rb +37 -30
- data/lib/e2b/services/envd_base64.rb +22 -0
- data/lib/e2b/services/filesystem.rb +29 -32
- data/lib/e2b/services/git.rb +8 -8
- data/lib/e2b/services/live_streamable.rb +25 -25
- data/lib/e2b/services/pty.rb +17 -18
- data/lib/e2b/template.rb +32 -29
- data/lib/e2b/version.rb +1 -1
- metadata +40 -10
|
@@ -99,9 +99,9 @@ module E2B
|
|
|
99
99
|
# entries.each { |e| puts "#{e.name} (#{e.type})" }
|
|
100
100
|
def list(path, depth: 1, user: nil, request_timeout: 60)
|
|
101
101
|
response = envd_rpc("filesystem.Filesystem", "ListDir",
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
body: { path: path, depth: depth },
|
|
103
|
+
timeout: request_timeout,
|
|
104
|
+
headers: user_auth_headers(user))
|
|
105
105
|
|
|
106
106
|
entries = extract_entries(response)
|
|
107
107
|
entries.map { |e| Models::EntryInfo.from_hash(e) }
|
|
@@ -132,9 +132,9 @@ module E2B
|
|
|
132
132
|
# @return [Models::EntryInfo] File/directory info
|
|
133
133
|
def get_info(path, user: nil, request_timeout: 30)
|
|
134
134
|
response = envd_rpc("filesystem.Filesystem", "Stat",
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
135
|
+
body: { path: path },
|
|
136
|
+
timeout: request_timeout,
|
|
137
|
+
headers: user_auth_headers(user))
|
|
138
138
|
|
|
139
139
|
entry_data = extract_entry(response)
|
|
140
140
|
Models::EntryInfo.from_hash(entry_data)
|
|
@@ -147,9 +147,9 @@ module E2B
|
|
|
147
147
|
# @param request_timeout [Integer] Request timeout in seconds
|
|
148
148
|
def remove(path, user: nil, request_timeout: 30)
|
|
149
149
|
envd_rpc("filesystem.Filesystem", "Remove",
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
150
|
+
body: { path: path },
|
|
151
|
+
timeout: request_timeout,
|
|
152
|
+
headers: user_auth_headers(user))
|
|
153
153
|
end
|
|
154
154
|
|
|
155
155
|
# Rename/move a file or directory
|
|
@@ -161,9 +161,9 @@ module E2B
|
|
|
161
161
|
# @return [Models::EntryInfo] Info about the moved entry
|
|
162
162
|
def rename(old_path, new_path, user: nil, request_timeout: 30)
|
|
163
163
|
response = envd_rpc("filesystem.Filesystem", "Move",
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
164
|
+
body: { source: old_path, destination: new_path },
|
|
165
|
+
timeout: request_timeout,
|
|
166
|
+
headers: user_auth_headers(user))
|
|
167
167
|
|
|
168
168
|
entry_data = extract_entry(response)
|
|
169
169
|
Models::EntryInfo.from_hash(entry_data)
|
|
@@ -177,9 +177,9 @@ module E2B
|
|
|
177
177
|
# @return [Boolean] true if created successfully
|
|
178
178
|
def make_dir(path, user: nil, request_timeout: 30)
|
|
179
179
|
envd_rpc("filesystem.Filesystem", "MakeDir",
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
180
|
+
body: { path: path },
|
|
181
|
+
timeout: request_timeout,
|
|
182
|
+
headers: user_auth_headers(user))
|
|
183
183
|
true
|
|
184
184
|
end
|
|
185
185
|
|
|
@@ -202,13 +202,13 @@ module E2B
|
|
|
202
202
|
def watch_dir(path, recursive: false, user: nil, request_timeout: 30)
|
|
203
203
|
if recursive && !supports_recursive_watch?
|
|
204
204
|
raise E2B::TemplateError,
|
|
205
|
-
|
|
205
|
+
"You need to update the template to use recursive watching. You can do this by running `e2b template build` in the directory with the template."
|
|
206
206
|
end
|
|
207
207
|
|
|
208
208
|
response = envd_rpc("filesystem.Filesystem", "CreateWatcher",
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
209
|
+
body: { path: path, recursive: recursive },
|
|
210
|
+
timeout: request_timeout,
|
|
211
|
+
headers: user_auth_headers(user))
|
|
212
212
|
|
|
213
213
|
watcher_id = response[:events]&.first&.dig("watcherId") ||
|
|
214
214
|
response["watcherId"] ||
|
|
@@ -257,9 +257,8 @@ module E2B
|
|
|
257
257
|
|
|
258
258
|
response = execute_http_request(uri, request, timeout: timeout)
|
|
259
259
|
unless successful_response?(response)
|
|
260
|
-
if response.code.to_i == 404
|
|
261
|
-
|
|
262
|
-
end
|
|
260
|
+
raise E2B::NotFoundError.new("File not found", status_code: 404) if response.code.to_i == 404
|
|
261
|
+
|
|
263
262
|
raise E2B::E2BError, "File read failed: HTTP #{response.code}"
|
|
264
263
|
end
|
|
265
264
|
|
|
@@ -281,9 +280,7 @@ module E2B
|
|
|
281
280
|
apply_request_headers(request)
|
|
282
281
|
|
|
283
282
|
response = execute_http_request(uri, request, timeout: timeout)
|
|
284
|
-
unless successful_response?(response)
|
|
285
|
-
raise E2B::E2BError, "File upload failed: HTTP #{response.code}"
|
|
286
|
-
end
|
|
283
|
+
raise E2B::E2BError, "File upload failed: HTTP #{response.code}" unless successful_response?(response)
|
|
287
284
|
|
|
288
285
|
parse_upload_response(response.body)
|
|
289
286
|
end
|
|
@@ -344,13 +341,11 @@ module E2B
|
|
|
344
341
|
rescue OpenSSL::SSL::SSLError, Errno::ECONNRESET, EOFError, Net::OpenTimeout, Net::ReadTimeout => e
|
|
345
342
|
retry_count += 1
|
|
346
343
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
raise E2B::E2BError, "#{operation} failed after #{max_retries} retries: #{e.message}"
|
|
353
|
-
end
|
|
344
|
+
raise E2B::E2BError, "#{operation} failed after #{max_retries} retries: #{e.message}" unless retry_count <= max_retries
|
|
345
|
+
|
|
346
|
+
sleep_time = 2**retry_count
|
|
347
|
+
sleep(sleep_time)
|
|
348
|
+
retry
|
|
354
349
|
end
|
|
355
350
|
end
|
|
356
351
|
|
|
@@ -365,6 +360,7 @@ module E2B
|
|
|
365
360
|
|
|
366
361
|
events.each do |event|
|
|
367
362
|
next unless event.is_a?(Hash)
|
|
363
|
+
|
|
368
364
|
# Direct entries field
|
|
369
365
|
if event["entries"]
|
|
370
366
|
entries.concat(Array(event["entries"]))
|
|
@@ -400,6 +396,7 @@ module E2B
|
|
|
400
396
|
events.each do |event|
|
|
401
397
|
next unless event.is_a?(Hash)
|
|
402
398
|
return event["watcherId"] || event["watcher_id"] if event["watcherId"] || event["watcher_id"]
|
|
399
|
+
|
|
403
400
|
result = event["result"]
|
|
404
401
|
return result["watcherId"] || result["watcher_id"] if result.is_a?(Hash) && (result["watcherId"] || result["watcher_id"])
|
|
405
402
|
end
|
data/lib/e2b/services/git.rb
CHANGED
|
@@ -84,7 +84,7 @@ module E2B
|
|
|
84
84
|
#
|
|
85
85
|
# @return [Boolean]
|
|
86
86
|
def has_conflicts?
|
|
87
|
-
file_status.any? { |f|
|
|
87
|
+
file_status.any? { |f| %w[u U].include?(f.index_status) }
|
|
88
88
|
end
|
|
89
89
|
|
|
90
90
|
# Number of staged files
|
|
@@ -105,7 +105,7 @@ module E2B
|
|
|
105
105
|
#
|
|
106
106
|
# @return [Integer]
|
|
107
107
|
def conflict_count
|
|
108
|
-
file_status.count { |f|
|
|
108
|
+
file_status.count { |f| %w[u U].include?(f.index_status) }
|
|
109
109
|
end
|
|
110
110
|
|
|
111
111
|
# Number of modified files (in the working tree)
|
|
@@ -571,7 +571,7 @@ module E2B
|
|
|
571
571
|
envs: nil, user: nil, cwd: nil, timeout: nil)
|
|
572
572
|
# Configure credential helper to use the store
|
|
573
573
|
set_config("credential.helper", "store", scope: "global",
|
|
574
|
-
|
|
574
|
+
envs: envs, user: user, cwd: cwd, timeout: timeout)
|
|
575
575
|
|
|
576
576
|
# Write credentials to the credential store via git credential approve
|
|
577
577
|
credential_input = [
|
|
@@ -583,7 +583,7 @@ module E2B
|
|
|
583
583
|
].join("\n")
|
|
584
584
|
|
|
585
585
|
escaped_input = Shellwords.escape(credential_input)
|
|
586
|
-
args = [
|
|
586
|
+
args = %w[credential approve]
|
|
587
587
|
cmd = build_git_command(args, nil)
|
|
588
588
|
full_cmd = "echo #{escaped_input} | #{cmd}"
|
|
589
589
|
|
|
@@ -604,9 +604,9 @@ module E2B
|
|
|
604
604
|
# @return [void]
|
|
605
605
|
def configure_user(name, email, scope: "global", path: nil, envs: nil, user: nil, cwd: nil, timeout: nil)
|
|
606
606
|
set_config("user.name", name, scope: scope, path: path,
|
|
607
|
-
|
|
607
|
+
envs: envs, user: user, cwd: cwd, timeout: timeout)
|
|
608
608
|
set_config("user.email", email, scope: scope, path: path,
|
|
609
|
-
|
|
609
|
+
envs: envs, user: user, cwd: cwd, timeout: timeout)
|
|
610
610
|
end
|
|
611
611
|
|
|
612
612
|
private
|
|
@@ -781,7 +781,7 @@ module E2B
|
|
|
781
781
|
def validate_scope!(scope)
|
|
782
782
|
return if VALID_SCOPES.include?(scope)
|
|
783
783
|
|
|
784
|
-
raise E2B::E2BError, "Invalid git config scope '#{scope}'. Must be one of: #{VALID_SCOPES.join(
|
|
784
|
+
raise E2B::E2BError, "Invalid git config scope '#{scope}'. Must be one of: #{VALID_SCOPES.join(", ")}"
|
|
785
785
|
end
|
|
786
786
|
|
|
787
787
|
# Convert a scope name to its git CLI flag
|
|
@@ -835,7 +835,7 @@ module E2B
|
|
|
835
835
|
file_status << GitFileStatus.new(path: filepath.split("\t").first, index_status: idx, work_tree_status: wt)
|
|
836
836
|
when /\Au (.)(.) .+ .+ .+ .+ .+ (.+)\z/
|
|
837
837
|
# Unmerged entry
|
|
838
|
-
|
|
838
|
+
Regexp.last_match(1)
|
|
839
839
|
wt = Regexp.last_match(2)
|
|
840
840
|
filepath = Regexp.last_match(3)
|
|
841
841
|
file_status << GitFileStatus.new(path: filepath, index_status: "u", work_tree_status: wt)
|
|
@@ -22,31 +22,31 @@ module E2B
|
|
|
22
22
|
|
|
23
23
|
begin
|
|
24
24
|
envd_rpc("process.Process", rpc_method,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
25
|
+
body: body,
|
|
26
|
+
timeout: timeout,
|
|
27
|
+
headers: headers,
|
|
28
|
+
on_event: lambda { |event_data|
|
|
29
|
+
stream_event = event_data[:event]
|
|
30
|
+
stream.push(stream_event) if stream_event
|
|
31
|
+
|
|
32
|
+
stdout_chunk = event_data[:stdout]
|
|
33
|
+
stderr_chunk = event_data[:stderr]
|
|
34
|
+
|
|
35
|
+
on_stdout&.call(stdout_chunk) if stdout_chunk && !stdout_chunk.empty?
|
|
36
|
+
on_stderr&.call(stderr_chunk) if stderr_chunk && !stderr_chunk.empty?
|
|
37
|
+
|
|
38
|
+
stream_block&.call(:stdout, stdout_chunk) if stdout_chunk && !stdout_chunk.empty?
|
|
39
|
+
stream_block&.call(:stderr, stderr_chunk) if stderr_chunk && !stderr_chunk.empty?
|
|
40
|
+
|
|
41
|
+
unless start_signal_sent
|
|
42
|
+
extracted_pid = extract_pid_from_event(stream_event)
|
|
43
|
+
if extracted_pid
|
|
44
|
+
pid = extracted_pid
|
|
45
|
+
start_signal_sent = true
|
|
46
|
+
start_queue << [:pid, pid]
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
})
|
|
50
50
|
|
|
51
51
|
unless start_signal_sent
|
|
52
52
|
start_signal_sent = true
|
data/lib/e2b/services/pty.rb
CHANGED
|
@@ -54,6 +54,7 @@ module E2B
|
|
|
54
54
|
# handle = pty.connect(pid)
|
|
55
55
|
class Pty < BaseService
|
|
56
56
|
include LiveStreamable
|
|
57
|
+
|
|
57
58
|
# Default shell to use for PTY sessions
|
|
58
59
|
DEFAULT_SHELL = "/bin/bash"
|
|
59
60
|
|
|
@@ -98,7 +99,8 @@ module E2B
|
|
|
98
99
|
process: process_spec,
|
|
99
100
|
pty: {
|
|
100
101
|
size: size.to_h
|
|
101
|
-
}
|
|
102
|
+
},
|
|
103
|
+
stdin: false
|
|
102
104
|
}
|
|
103
105
|
|
|
104
106
|
build_live_handle(
|
|
@@ -154,9 +156,9 @@ module E2B
|
|
|
154
156
|
def send_stdin(pid, data, headers: nil)
|
|
155
157
|
encoded = Base64.strict_encode64(data.is_a?(String) ? data : data.to_s)
|
|
156
158
|
envd_rpc("process.Process", "SendInput", body: {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
159
|
+
process: { pid: pid },
|
|
160
|
+
input: { pty: encoded }
|
|
161
|
+
}, headers: headers)
|
|
160
162
|
end
|
|
161
163
|
|
|
162
164
|
# Kill a PTY process with SIGKILL.
|
|
@@ -168,9 +170,9 @@ module E2B
|
|
|
168
170
|
# sandbox.pty.kill(12345)
|
|
169
171
|
def kill(pid, headers: nil)
|
|
170
172
|
envd_rpc("process.Process", "SendSignal", body: {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
173
|
+
process: { pid: pid },
|
|
174
|
+
signal: 9 # SIGKILL
|
|
175
|
+
}, headers: headers)
|
|
174
176
|
true
|
|
175
177
|
rescue E2B::E2BError
|
|
176
178
|
false
|
|
@@ -190,11 +192,11 @@ module E2B
|
|
|
190
192
|
# sandbox.pty.resize(pid, PtySize.new(cols: 120, rows: 40))
|
|
191
193
|
def resize(pid, size)
|
|
192
194
|
envd_rpc("process.Process", "Update", body: {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
195
|
+
process: { pid: pid },
|
|
196
|
+
pty: {
|
|
197
|
+
size: size.to_h
|
|
198
|
+
}
|
|
199
|
+
})
|
|
198
200
|
end
|
|
199
201
|
|
|
200
202
|
# Close the stdin of a PTY process.
|
|
@@ -207,8 +209,8 @@ module E2B
|
|
|
207
209
|
# @raise [E2B::E2BError] if the process is not found
|
|
208
210
|
def close_stdin(pid)
|
|
209
211
|
envd_rpc("process.Process", "CloseStdin", body: {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
+
process: { pid: pid }
|
|
213
|
+
})
|
|
212
214
|
end
|
|
213
215
|
|
|
214
216
|
# List running processes in the sandbox.
|
|
@@ -234,13 +236,10 @@ module E2B
|
|
|
234
236
|
result["LANG"] = "C.UTF-8"
|
|
235
237
|
result["LC_ALL"] = "C.UTF-8"
|
|
236
238
|
|
|
237
|
-
if envs.is_a?(Hash)
|
|
238
|
-
envs.each { |k, v| result[k.to_s] = v.to_s }
|
|
239
|
-
end
|
|
239
|
+
envs.each { |k, v| result[k.to_s] = v.to_s } if envs.is_a?(Hash)
|
|
240
240
|
|
|
241
241
|
result
|
|
242
242
|
end
|
|
243
|
-
|
|
244
243
|
end
|
|
245
244
|
end
|
|
246
245
|
end
|
data/lib/e2b/template.rb
CHANGED
|
@@ -45,9 +45,9 @@ module E2B
|
|
|
45
45
|
credentials = resolve_credentials(api_key: api_key, access_token: access_token)
|
|
46
46
|
http_client = build_http_client(**credentials, domain: resolve_domain(domain))
|
|
47
47
|
response = http_client.post("/templates/tags", body: {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
target: target_name,
|
|
49
|
+
tags: normalize_tags(tags)
|
|
50
|
+
})
|
|
51
51
|
|
|
52
52
|
Models::TemplateTagInfo.from_hash(response)
|
|
53
53
|
end
|
|
@@ -56,9 +56,9 @@ module E2B
|
|
|
56
56
|
credentials = resolve_credentials(api_key: api_key, access_token: access_token)
|
|
57
57
|
http_client = build_http_client(**credentials, domain: resolve_domain(domain))
|
|
58
58
|
http_client.delete("/templates/tags", body: {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
name: name,
|
|
60
|
+
tags: normalize_tags(tags)
|
|
61
|
+
})
|
|
62
62
|
nil
|
|
63
63
|
end
|
|
64
64
|
|
|
@@ -179,7 +179,7 @@ module E2B
|
|
|
179
179
|
credentials = resolve_credentials(api_key: api_key, access_token: access_token)
|
|
180
180
|
http_client = build_http_client(**credentials, domain: resolve_domain(domain))
|
|
181
181
|
|
|
182
|
-
tags_message = Array(tags).any? ? " with tags #{Array(tags).join(
|
|
182
|
+
tags_message = Array(tags).any? ? " with tags #{Array(tags).join(", ")}" : ""
|
|
183
183
|
on_build_logs&.call(log_entry("Requesting build for template: #{resolved_name}#{tags_message}"))
|
|
184
184
|
|
|
185
185
|
body = {
|
|
@@ -338,11 +338,11 @@ module E2B
|
|
|
338
338
|
|
|
339
339
|
if build_info.is_a?(Hash)
|
|
340
340
|
resolved_template_id = build_info[:template_id] || build_info["template_id"] ||
|
|
341
|
-
|
|
341
|
+
build_info[:templateID] || build_info["templateID"]
|
|
342
342
|
resolved_build_id = build_info[:build_id] || build_info["build_id"] ||
|
|
343
|
-
|
|
343
|
+
build_info[:buildID] || build_info["buildID"]
|
|
344
344
|
resolved_build_step_origins ||= build_info[:build_step_origins] || build_info["build_step_origins"] ||
|
|
345
|
-
|
|
345
|
+
build_info[:buildStepOrigins] || build_info["buildStepOrigins"]
|
|
346
346
|
|
|
347
347
|
return [resolved_template_id, resolved_build_id, Array(resolved_build_step_origins).compact]
|
|
348
348
|
end
|
|
@@ -404,12 +404,12 @@ module E2B
|
|
|
404
404
|
end
|
|
405
405
|
|
|
406
406
|
def resolve_credentials(api_key:, access_token:)
|
|
407
|
-
resolved_api_key = api_key || E2B.configuration&.api_key || ENV
|
|
408
|
-
resolved_access_token = access_token || E2B.configuration&.access_token || ENV
|
|
407
|
+
resolved_api_key = api_key || E2B.configuration&.api_key || ENV.fetch("E2B_API_KEY", nil)
|
|
408
|
+
resolved_access_token = access_token || E2B.configuration&.access_token || ENV.fetch("E2B_ACCESS_TOKEN", nil)
|
|
409
409
|
|
|
410
410
|
unless (resolved_api_key && !resolved_api_key.empty?) || (resolved_access_token && !resolved_access_token.empty?)
|
|
411
411
|
raise ConfigurationError,
|
|
412
|
-
|
|
412
|
+
"E2B credentials are required. Set E2B_API_KEY or E2B_ACCESS_TOKEN, or pass api_key:/access_token:."
|
|
413
413
|
end
|
|
414
414
|
|
|
415
415
|
{ api_key: resolved_api_key, access_token: resolved_access_token }
|
|
@@ -638,7 +638,7 @@ module E2B
|
|
|
638
638
|
|
|
639
639
|
def pip_install(packages = nil, g: true)
|
|
640
640
|
package_list = packages.nil? ? nil : Array(packages).map(&:to_s)
|
|
641
|
-
args = [
|
|
641
|
+
args = %w[pip install]
|
|
642
642
|
args << "--user" unless g
|
|
643
643
|
args.concat(package_list || ["."])
|
|
644
644
|
run_cmd(args.join(" "), user: g ? "root" : nil)
|
|
@@ -646,7 +646,7 @@ module E2B
|
|
|
646
646
|
|
|
647
647
|
def npm_install(packages = nil, g: false, dev: false)
|
|
648
648
|
package_list = packages.nil? ? nil : Array(packages).map(&:to_s)
|
|
649
|
-
args = [
|
|
649
|
+
args = %w[npm install]
|
|
650
650
|
args << "-g" if g
|
|
651
651
|
args << "--save-dev" if dev
|
|
652
652
|
args.concat(package_list) if package_list
|
|
@@ -655,7 +655,7 @@ module E2B
|
|
|
655
655
|
|
|
656
656
|
def bun_install(packages = nil, g: false, dev: false)
|
|
657
657
|
package_list = packages.nil? ? nil : Array(packages).map(&:to_s)
|
|
658
|
-
args = [
|
|
658
|
+
args = %w[bun install]
|
|
659
659
|
args << "-g" if g
|
|
660
660
|
args << "--dev" if dev
|
|
661
661
|
args.concat(package_list) if package_list
|
|
@@ -668,7 +668,7 @@ module E2B
|
|
|
668
668
|
run_cmd(
|
|
669
669
|
[
|
|
670
670
|
"apt-get update",
|
|
671
|
-
"DEBIAN_FRONTEND=noninteractive DEBCONF_NOWARNINGS=yes apt-get install -y #{install_flags}#{package_list.join(
|
|
671
|
+
"DEBIAN_FRONTEND=noninteractive DEBCONF_NOWARNINGS=yes apt-get install -y #{install_flags}#{package_list.join(" ")}"
|
|
672
672
|
],
|
|
673
673
|
user: "root"
|
|
674
674
|
)
|
|
@@ -683,7 +683,7 @@ module E2B
|
|
|
683
683
|
end
|
|
684
684
|
|
|
685
685
|
server_list = Array(servers).map(&:to_s)
|
|
686
|
-
run_cmd("mcp-gateway pull #{server_list.join(
|
|
686
|
+
run_cmd("mcp-gateway pull #{server_list.join(" ")}", user: "root")
|
|
687
687
|
end
|
|
688
688
|
|
|
689
689
|
def git_clone(url, path = nil, branch: nil, depth: nil, user: nil)
|
|
@@ -730,7 +730,7 @@ module E2B
|
|
|
730
730
|
|
|
731
731
|
def make_dir(path, mode: nil, user: nil)
|
|
732
732
|
args = ["mkdir", "-p"]
|
|
733
|
-
args << "-m #{format(
|
|
733
|
+
args << "-m #{format("%04o", mode)}" if mode
|
|
734
734
|
args.concat(Array(path).map(&:to_s))
|
|
735
735
|
run_cmd(args.join(" "), user: user)
|
|
736
736
|
end
|
|
@@ -777,9 +777,12 @@ module E2B
|
|
|
777
777
|
)
|
|
778
778
|
end
|
|
779
779
|
|
|
780
|
-
|
|
780
|
+
unless @base_image
|
|
781
|
+
raise template_error("No base image specified for template",
|
|
782
|
+
source_location: capture_source_location)
|
|
783
|
+
end
|
|
781
784
|
|
|
782
|
-
dockerfile =
|
|
785
|
+
dockerfile = "FROM #{@base_image}\n"
|
|
783
786
|
@instructions.each do |instruction|
|
|
784
787
|
case instruction[:type]
|
|
785
788
|
when "RUN"
|
|
@@ -788,9 +791,9 @@ module E2B
|
|
|
788
791
|
dockerfile << "COPY #{instruction[:args][0]} #{instruction[:args][1]}\n"
|
|
789
792
|
when "ENV"
|
|
790
793
|
values = instruction[:args].each_slice(2).map { |key, value| "#{key}=#{value}" }
|
|
791
|
-
dockerfile << "ENV #{values.join(
|
|
794
|
+
dockerfile << "ENV #{values.join(" ")}\n"
|
|
792
795
|
else
|
|
793
|
-
dockerfile << "#{instruction[:type]} #{instruction[:args].join(
|
|
796
|
+
dockerfile << "#{instruction[:type]} #{instruction[:args].join(" ")}\n"
|
|
794
797
|
end
|
|
795
798
|
end
|
|
796
799
|
dockerfile << "ENTRYPOINT #{@start_cmd}\n" if @start_cmd
|
|
@@ -913,7 +916,7 @@ module E2B
|
|
|
913
916
|
|
|
914
917
|
def collect_files(src)
|
|
915
918
|
matches = Dir.glob(src, base: @file_context_path, flags: File::FNM_DOTMATCH)
|
|
916
|
-
|
|
919
|
+
.reject { |entry| [".", ".."].include?(entry) }
|
|
917
920
|
|
|
918
921
|
files = []
|
|
919
922
|
matches.each do |match|
|
|
@@ -991,14 +994,14 @@ module E2B
|
|
|
991
994
|
end
|
|
992
995
|
|
|
993
996
|
def normalize_ignore_pattern(pattern)
|
|
994
|
-
normalized = pattern.to_s.tr(File::SEPARATOR, "/").sub(
|
|
995
|
-
return "/#{normalized.sub(%r{\A/+},
|
|
997
|
+
normalized = pattern.to_s.tr(File::SEPARATOR, "/").sub(%r{\A\./}, "")
|
|
998
|
+
return "/#{normalized.sub(%r{\A/+}, "")}" if normalized.start_with?("/")
|
|
996
999
|
|
|
997
1000
|
normalized.sub(%r{\A/+}, "")
|
|
998
1001
|
end
|
|
999
1002
|
|
|
1000
1003
|
def normalize_ignore_path(path)
|
|
1001
|
-
path.to_s.tr(File::SEPARATOR, "/").sub(
|
|
1004
|
+
path.to_s.tr(File::SEPARATOR, "/").sub(%r{\A\./}, "").sub(%r{\A/+}, "")
|
|
1002
1005
|
end
|
|
1003
1006
|
|
|
1004
1007
|
def build_step_origins
|
|
@@ -1013,8 +1016,8 @@ module E2B
|
|
|
1013
1016
|
return [] unless File.exist?(dockerignore_path)
|
|
1014
1017
|
|
|
1015
1018
|
File.readlines(dockerignore_path, chomp: true)
|
|
1016
|
-
|
|
1017
|
-
|
|
1019
|
+
.map(&:strip)
|
|
1020
|
+
.reject { |line| line.empty? || line.start_with?("#") }
|
|
1018
1021
|
end
|
|
1019
1022
|
|
|
1020
1023
|
def read_gcp_service_account_json(path_or_content)
|
data/lib/e2b/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: e2b
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tao Luo
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-05-
|
|
10
|
+
date: 2026-05-10 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: base64
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.2'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0.2'
|
|
12
26
|
- !ruby/object:Gem::Dependency
|
|
13
27
|
name: faraday
|
|
14
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -44,35 +58,49 @@ dependencies:
|
|
|
44
58
|
- !ruby/object:Gem::Version
|
|
45
59
|
version: '1.0'
|
|
46
60
|
- !ruby/object:Gem::Dependency
|
|
47
|
-
name:
|
|
61
|
+
name: rake
|
|
48
62
|
requirement: !ruby/object:Gem::Requirement
|
|
49
63
|
requirements:
|
|
50
64
|
- - "~>"
|
|
51
65
|
- !ruby/object:Gem::Version
|
|
52
|
-
version: '0
|
|
53
|
-
type: :
|
|
66
|
+
version: '13.0'
|
|
67
|
+
type: :development
|
|
54
68
|
prerelease: false
|
|
55
69
|
version_requirements: !ruby/object:Gem::Requirement
|
|
56
70
|
requirements:
|
|
57
71
|
- - "~>"
|
|
58
72
|
- !ruby/object:Gem::Version
|
|
59
|
-
version: '0
|
|
73
|
+
version: '13.0'
|
|
60
74
|
- !ruby/object:Gem::Dependency
|
|
61
|
-
name:
|
|
75
|
+
name: rspec
|
|
62
76
|
requirement: !ruby/object:Gem::Requirement
|
|
63
77
|
requirements:
|
|
64
78
|
- - "~>"
|
|
65
79
|
- !ruby/object:Gem::Version
|
|
66
|
-
version: '
|
|
80
|
+
version: '3.0'
|
|
67
81
|
type: :development
|
|
68
82
|
prerelease: false
|
|
69
83
|
version_requirements: !ruby/object:Gem::Requirement
|
|
70
84
|
requirements:
|
|
71
85
|
- - "~>"
|
|
72
86
|
- !ruby/object:Gem::Version
|
|
73
|
-
version: '
|
|
87
|
+
version: '3.0'
|
|
74
88
|
- !ruby/object:Gem::Dependency
|
|
75
|
-
name:
|
|
89
|
+
name: rubocop
|
|
90
|
+
requirement: !ruby/object:Gem::Requirement
|
|
91
|
+
requirements:
|
|
92
|
+
- - "~>"
|
|
93
|
+
- !ruby/object:Gem::Version
|
|
94
|
+
version: '1.72'
|
|
95
|
+
type: :development
|
|
96
|
+
prerelease: false
|
|
97
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
98
|
+
requirements:
|
|
99
|
+
- - "~>"
|
|
100
|
+
- !ruby/object:Gem::Version
|
|
101
|
+
version: '1.72'
|
|
102
|
+
- !ruby/object:Gem::Dependency
|
|
103
|
+
name: rubocop-rspec
|
|
76
104
|
requirement: !ruby/object:Gem::Requirement
|
|
77
105
|
requirements:
|
|
78
106
|
- - "~>"
|
|
@@ -134,6 +162,7 @@ files:
|
|
|
134
162
|
- lib/e2b/services/base_service.rb
|
|
135
163
|
- lib/e2b/services/command_handle.rb
|
|
136
164
|
- lib/e2b/services/commands.rb
|
|
165
|
+
- lib/e2b/services/envd_base64.rb
|
|
137
166
|
- lib/e2b/services/filesystem.rb
|
|
138
167
|
- lib/e2b/services/git.rb
|
|
139
168
|
- lib/e2b/services/live_streamable.rb
|
|
@@ -150,6 +179,7 @@ metadata:
|
|
|
150
179
|
source_code_uri: https://github.com/ya-luotao/e2b-ruby
|
|
151
180
|
documentation_uri: https://e2b.dev/docs
|
|
152
181
|
changelog_uri: https://github.com/ya-luotao/e2b-ruby/blob/main/CHANGELOG.md
|
|
182
|
+
rubygems_mfa_required: 'true'
|
|
153
183
|
rdoc_options: []
|
|
154
184
|
require_paths:
|
|
155
185
|
- lib
|