e2b 0.2.0 → 0.3.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/README.md +6 -2
- data/lib/e2b/api/http_client.rb +30 -19
- data/lib/e2b/client.rb +79 -36
- data/lib/e2b/configuration.rb +12 -6
- data/lib/e2b/dockerfile_parser.rb +179 -0
- data/lib/e2b/errors.rb +24 -1
- data/lib/e2b/models/build_info.rb +29 -0
- data/lib/e2b/models/build_status_reason.rb +27 -0
- data/lib/e2b/models/sandbox_info.rb +19 -2
- data/lib/e2b/models/snapshot_info.rb +19 -0
- data/lib/e2b/models/template_build_status_response.rb +31 -0
- data/lib/e2b/models/template_log_entry.rb +54 -0
- data/lib/e2b/models/template_tag.rb +34 -0
- data/lib/e2b/models/template_tag_info.rb +21 -0
- data/lib/e2b/paginator.rb +97 -0
- data/lib/e2b/ready_cmd.rb +36 -0
- data/lib/e2b/sandbox.rb +217 -66
- data/lib/e2b/sandbox_helpers.rb +100 -0
- data/lib/e2b/services/base_service.rb +64 -15
- data/lib/e2b/services/command_handle.rb +189 -36
- data/lib/e2b/services/commands.rb +37 -50
- data/lib/e2b/services/filesystem.rb +70 -23
- data/lib/e2b/services/live_streamable.rb +94 -0
- data/lib/e2b/services/pty.rb +13 -64
- data/lib/e2b/services/watch_handle.rb +6 -3
- data/lib/e2b/template.rb +1089 -0
- data/lib/e2b/template_logger.rb +52 -0
- data/lib/e2b/version.rb +1 -1
- data/lib/e2b.rb +16 -0
- metadata +44 -2
|
@@ -5,6 +5,7 @@ require "net/http"
|
|
|
5
5
|
require "openssl"
|
|
6
6
|
require "uri"
|
|
7
7
|
require "json"
|
|
8
|
+
require "stringio"
|
|
8
9
|
|
|
9
10
|
module E2B
|
|
10
11
|
module Services
|
|
@@ -24,8 +25,6 @@ module E2B
|
|
|
24
25
|
# entries = sandbox.files.list("/home/user")
|
|
25
26
|
class Filesystem < BaseService
|
|
26
27
|
# Default username for file operations
|
|
27
|
-
DEFAULT_USER = "user"
|
|
28
|
-
|
|
29
28
|
# Read file content
|
|
30
29
|
#
|
|
31
30
|
# @param path [String] File path in the sandbox
|
|
@@ -36,10 +35,20 @@ module E2B
|
|
|
36
35
|
#
|
|
37
36
|
# @example
|
|
38
37
|
# content = sandbox.files.read("/home/user/config.json")
|
|
39
|
-
def read(path, format: "text", user:
|
|
38
|
+
def read(path, format: "text", user: nil, request_timeout: 120)
|
|
40
39
|
url = build_file_url("/files", path: path, user: user)
|
|
41
40
|
response = rest_get(url, timeout: request_timeout)
|
|
42
|
-
|
|
41
|
+
|
|
42
|
+
case format
|
|
43
|
+
when "text"
|
|
44
|
+
response.dup.force_encoding("UTF-8")
|
|
45
|
+
when "bytes"
|
|
46
|
+
response.b
|
|
47
|
+
when "stream"
|
|
48
|
+
StringIO.new(response.b)
|
|
49
|
+
else
|
|
50
|
+
raise ArgumentError, "Unsupported read format '#{format}'"
|
|
51
|
+
end
|
|
43
52
|
end
|
|
44
53
|
|
|
45
54
|
# Write content to a file using REST upload
|
|
@@ -48,14 +57,15 @@ module E2B
|
|
|
48
57
|
# @param data [String, IO] Content to write (string or IO object)
|
|
49
58
|
# @param user [String] Username context for the operation
|
|
50
59
|
# @param request_timeout [Integer] Request timeout in seconds
|
|
51
|
-
# @return [Models::
|
|
60
|
+
# @return [Models::WriteInfo] Info about the written file
|
|
52
61
|
#
|
|
53
62
|
# @example
|
|
54
63
|
# sandbox.files.write("/home/user/output.txt", "Hello, World!")
|
|
55
|
-
def write(path, data, user:
|
|
64
|
+
def write(path, data, user: nil, request_timeout: 120)
|
|
56
65
|
url = build_file_url("/files", path: path, user: user)
|
|
57
66
|
content = data.is_a?(IO) || data.respond_to?(:read) ? data.read : data.to_s
|
|
58
|
-
rest_upload(url, content, timeout: request_timeout)
|
|
67
|
+
result = rest_upload(url, content, timeout: request_timeout)
|
|
68
|
+
build_write_info(result, default_path: path)
|
|
59
69
|
end
|
|
60
70
|
|
|
61
71
|
# Write multiple files at once
|
|
@@ -70,7 +80,7 @@ module E2B
|
|
|
70
80
|
# { path: "/home/user/a.txt", data: "Content A" },
|
|
71
81
|
# { path: "/home/user/b.txt", data: "Content B" }
|
|
72
82
|
# ])
|
|
73
|
-
def write_files(files, user:
|
|
83
|
+
def write_files(files, user: nil, request_timeout: 120)
|
|
74
84
|
files.map do |file|
|
|
75
85
|
write(file[:path], file[:data] || file[:content], user: user, request_timeout: request_timeout)
|
|
76
86
|
end
|
|
@@ -87,10 +97,11 @@ module E2B
|
|
|
87
97
|
# @example
|
|
88
98
|
# entries = sandbox.files.list("/home/user")
|
|
89
99
|
# entries.each { |e| puts "#{e.name} (#{e.type})" }
|
|
90
|
-
def list(path, depth: 1, user:
|
|
100
|
+
def list(path, depth: 1, user: nil, request_timeout: 60)
|
|
91
101
|
response = envd_rpc("filesystem.Filesystem", "ListDir",
|
|
92
102
|
body: { path: path, depth: depth },
|
|
93
|
-
timeout: request_timeout
|
|
103
|
+
timeout: request_timeout,
|
|
104
|
+
headers: user_auth_headers(user))
|
|
94
105
|
|
|
95
106
|
entries = extract_entries(response)
|
|
96
107
|
entries.map { |e| Models::EntryInfo.from_hash(e) }
|
|
@@ -102,7 +113,7 @@ module E2B
|
|
|
102
113
|
# @param user [String] Username context
|
|
103
114
|
# @param request_timeout [Integer] Request timeout in seconds
|
|
104
115
|
# @return [Boolean]
|
|
105
|
-
def exists?(path, user:
|
|
116
|
+
def exists?(path, user: nil, request_timeout: 30)
|
|
106
117
|
get_info(path, user: user, request_timeout: request_timeout)
|
|
107
118
|
true
|
|
108
119
|
rescue E2B::NotFoundError, E2B::E2BError
|
|
@@ -115,10 +126,11 @@ module E2B
|
|
|
115
126
|
# @param user [String] Username context
|
|
116
127
|
# @param request_timeout [Integer] Request timeout in seconds
|
|
117
128
|
# @return [Models::EntryInfo] File/directory info
|
|
118
|
-
def get_info(path, user:
|
|
129
|
+
def get_info(path, user: nil, request_timeout: 30)
|
|
119
130
|
response = envd_rpc("filesystem.Filesystem", "Stat",
|
|
120
131
|
body: { path: path },
|
|
121
|
-
timeout: request_timeout
|
|
132
|
+
timeout: request_timeout,
|
|
133
|
+
headers: user_auth_headers(user))
|
|
122
134
|
|
|
123
135
|
entry_data = extract_entry(response)
|
|
124
136
|
Models::EntryInfo.from_hash(entry_data)
|
|
@@ -129,10 +141,11 @@ module E2B
|
|
|
129
141
|
# @param path [String] Path to remove
|
|
130
142
|
# @param user [String] Username context
|
|
131
143
|
# @param request_timeout [Integer] Request timeout in seconds
|
|
132
|
-
def remove(path, user:
|
|
144
|
+
def remove(path, user: nil, request_timeout: 30)
|
|
133
145
|
envd_rpc("filesystem.Filesystem", "Remove",
|
|
134
146
|
body: { path: path },
|
|
135
|
-
timeout: request_timeout
|
|
147
|
+
timeout: request_timeout,
|
|
148
|
+
headers: user_auth_headers(user))
|
|
136
149
|
end
|
|
137
150
|
|
|
138
151
|
# Rename/move a file or directory
|
|
@@ -142,10 +155,11 @@ module E2B
|
|
|
142
155
|
# @param user [String] Username context
|
|
143
156
|
# @param request_timeout [Integer] Request timeout in seconds
|
|
144
157
|
# @return [Models::EntryInfo] Info about the moved entry
|
|
145
|
-
def rename(old_path, new_path, user:
|
|
158
|
+
def rename(old_path, new_path, user: nil, request_timeout: 30)
|
|
146
159
|
response = envd_rpc("filesystem.Filesystem", "Move",
|
|
147
160
|
body: { source: old_path, destination: new_path },
|
|
148
|
-
timeout: request_timeout
|
|
161
|
+
timeout: request_timeout,
|
|
162
|
+
headers: user_auth_headers(user))
|
|
149
163
|
|
|
150
164
|
entry_data = extract_entry(response)
|
|
151
165
|
Models::EntryInfo.from_hash(entry_data)
|
|
@@ -157,10 +171,11 @@ module E2B
|
|
|
157
171
|
# @param user [String] Username context
|
|
158
172
|
# @param request_timeout [Integer] Request timeout in seconds
|
|
159
173
|
# @return [Boolean] true if created successfully
|
|
160
|
-
def make_dir(path, user:
|
|
174
|
+
def make_dir(path, user: nil, request_timeout: 30)
|
|
161
175
|
envd_rpc("filesystem.Filesystem", "MakeDir",
|
|
162
176
|
body: { path: path },
|
|
163
|
-
timeout: request_timeout
|
|
177
|
+
timeout: request_timeout,
|
|
178
|
+
headers: user_auth_headers(user))
|
|
164
179
|
true
|
|
165
180
|
end
|
|
166
181
|
|
|
@@ -180,10 +195,16 @@ module E2B
|
|
|
180
195
|
# events = handle.get_new_events
|
|
181
196
|
# events.each { |e| puts "#{e.type}: #{e.name}" }
|
|
182
197
|
# handle.stop
|
|
183
|
-
def watch_dir(path, recursive: false, user:
|
|
198
|
+
def watch_dir(path, recursive: false, user: nil, request_timeout: 30)
|
|
199
|
+
if recursive && !supports_recursive_watch?
|
|
200
|
+
raise E2B::TemplateError,
|
|
201
|
+
"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."
|
|
202
|
+
end
|
|
203
|
+
|
|
184
204
|
response = envd_rpc("filesystem.Filesystem", "CreateWatcher",
|
|
185
205
|
body: { path: path, recursive: recursive },
|
|
186
|
-
timeout: request_timeout
|
|
206
|
+
timeout: request_timeout,
|
|
207
|
+
headers: user_auth_headers(user))
|
|
187
208
|
|
|
188
209
|
watcher_id = response[:events]&.first&.dig("watcherId") ||
|
|
189
210
|
response["watcherId"] ||
|
|
@@ -192,7 +213,11 @@ module E2B
|
|
|
192
213
|
raise E2B::E2BError, "Failed to create watcher: no watcher_id returned" unless watcher_id
|
|
193
214
|
|
|
194
215
|
rpc_proc = method(:envd_rpc)
|
|
195
|
-
WatchHandle.new(
|
|
216
|
+
WatchHandle.new(
|
|
217
|
+
watcher_id: watcher_id,
|
|
218
|
+
envd_rpc_proc: rpc_proc,
|
|
219
|
+
headers: user_auth_headers(user)
|
|
220
|
+
)
|
|
196
221
|
end
|
|
197
222
|
|
|
198
223
|
# Backward-compatible aliases
|
|
@@ -209,6 +234,7 @@ module E2B
|
|
|
209
234
|
|
|
210
235
|
# Build URL for file operations
|
|
211
236
|
def build_file_url(endpoint, path: nil, user: nil)
|
|
237
|
+
user = resolve_username(user)
|
|
212
238
|
base = "https://#{ENVD_PORT}-#{@sandbox_id}.#{@sandbox_domain}"
|
|
213
239
|
url = "#{base}#{endpoint}"
|
|
214
240
|
params = []
|
|
@@ -255,7 +281,7 @@ module E2B
|
|
|
255
281
|
raise E2B::E2BError, "File upload failed: HTTP #{response.code}"
|
|
256
282
|
end
|
|
257
283
|
|
|
258
|
-
|
|
284
|
+
parse_upload_response(response.body)
|
|
259
285
|
end
|
|
260
286
|
end
|
|
261
287
|
|
|
@@ -287,6 +313,14 @@ module E2B
|
|
|
287
313
|
request["User-Agent"] = "e2b-ruby-sdk/#{E2B::VERSION}"
|
|
288
314
|
end
|
|
289
315
|
|
|
316
|
+
def parse_upload_response(body)
|
|
317
|
+
return [] if body.nil? || body.empty?
|
|
318
|
+
|
|
319
|
+
JSON.parse(body)
|
|
320
|
+
rescue JSON::ParserError
|
|
321
|
+
[]
|
|
322
|
+
end
|
|
323
|
+
|
|
290
324
|
def ssl_verify_mode
|
|
291
325
|
ssl_verify = ENV.fetch("E2B_SSL_VERIFY", "true").downcase != "false"
|
|
292
326
|
ssl_verify ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
|
|
@@ -368,6 +402,19 @@ module E2B
|
|
|
368
402
|
|
|
369
403
|
nil
|
|
370
404
|
end
|
|
405
|
+
|
|
406
|
+
def build_write_info(result, default_path:)
|
|
407
|
+
case result
|
|
408
|
+
when Array
|
|
409
|
+
entry = result.first
|
|
410
|
+
return build_write_info(entry, default_path: default_path) if entry
|
|
411
|
+
when Hash
|
|
412
|
+
path = result["path"] || result[:path] || default_path
|
|
413
|
+
return Models::WriteInfo.new(path: path)
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
Models::WriteInfo.new(path: default_path)
|
|
417
|
+
end
|
|
371
418
|
end
|
|
372
419
|
end
|
|
373
420
|
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module E2B
|
|
4
|
+
module Services
|
|
5
|
+
# Shared live-stream handle builder for Commands and Pty.
|
|
6
|
+
#
|
|
7
|
+
# Both services need to start a background thread that runs an envd RPC,
|
|
8
|
+
# extract the PID from the first Start event, and return a CommandHandle
|
|
9
|
+
# wired to a LiveEventStream. This module keeps that logic in one place.
|
|
10
|
+
module LiveStreamable
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def build_live_handle(rpc_method:, body:, timeout:, headers: nil, on_stdout: nil, on_stderr: nil, &block)
|
|
14
|
+
stream = LiveEventStream.new
|
|
15
|
+
start_queue = Queue.new
|
|
16
|
+
start_signal_sent = false
|
|
17
|
+
pid = nil
|
|
18
|
+
stream_block = block
|
|
19
|
+
|
|
20
|
+
stream_thread = Thread.new do
|
|
21
|
+
Thread.current.report_on_exception = false if Thread.current.respond_to?(:report_on_exception=)
|
|
22
|
+
|
|
23
|
+
begin
|
|
24
|
+
envd_rpc("process.Process", rpc_method,
|
|
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
|
+
|
|
51
|
+
unless start_signal_sent
|
|
52
|
+
start_signal_sent = true
|
|
53
|
+
start_queue << [:error, E2BError.new("Failed to start process: expected start event")]
|
|
54
|
+
end
|
|
55
|
+
rescue StandardError => e
|
|
56
|
+
unless start_signal_sent
|
|
57
|
+
start_signal_sent = true
|
|
58
|
+
start_queue << [:error, e]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
stream.fail(e)
|
|
62
|
+
ensure
|
|
63
|
+
stream.close
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
start_state, start_value = start_queue.pop
|
|
68
|
+
raise start_value if start_state == :error
|
|
69
|
+
|
|
70
|
+
CommandHandle.new(
|
|
71
|
+
pid: pid,
|
|
72
|
+
handle_kill: -> { kill(pid, headers: headers) },
|
|
73
|
+
handle_send_stdin: ->(data) { send_stdin(pid, data, headers: headers) },
|
|
74
|
+
handle_disconnect: -> { disconnect_live_stream(stream_thread, stream) },
|
|
75
|
+
events_proc: ->(&events_block) { stream.each(&events_block) }
|
|
76
|
+
)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def extract_pid_from_event(event)
|
|
80
|
+
return nil unless event.is_a?(Hash) && event["event"].is_a?(Hash)
|
|
81
|
+
|
|
82
|
+
start_event = event["event"]["Start"] || event["event"]["start"]
|
|
83
|
+
return nil unless start_event && start_event["pid"]
|
|
84
|
+
|
|
85
|
+
start_event["pid"].to_i
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def disconnect_live_stream(stream_thread, stream)
|
|
89
|
+
stream.close(discard_pending: true)
|
|
90
|
+
stream_thread.kill if stream_thread&.alive?
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
data/lib/e2b/services/pty.rb
CHANGED
|
@@ -53,6 +53,7 @@ module E2B
|
|
|
53
53
|
# @example Connect to an existing PTY
|
|
54
54
|
# handle = pty.connect(pid)
|
|
55
55
|
class Pty < BaseService
|
|
56
|
+
include LiveStreamable
|
|
56
57
|
# Default shell to use for PTY sessions
|
|
57
58
|
DEFAULT_SHELL = "/bin/bash"
|
|
58
59
|
|
|
@@ -84,6 +85,7 @@ module E2B
|
|
|
84
85
|
def create(size: PtySize.new, user: nil, cwd: nil, envs: nil,
|
|
85
86
|
cmd: DEFAULT_SHELL, args: DEFAULT_SHELL_ARGS, timeout: 60)
|
|
86
87
|
envs = build_pty_envs(envs)
|
|
88
|
+
headers = user_auth_headers(user)
|
|
87
89
|
|
|
88
90
|
process_spec = {
|
|
89
91
|
cmd: cmd,
|
|
@@ -99,34 +101,11 @@ module E2B
|
|
|
99
101
|
}
|
|
100
102
|
}
|
|
101
103
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
# Use streaming RPC to capture the StartEvent and extract the PID
|
|
105
|
-
on_event = ->(event_data) {
|
|
106
|
-
event = event_data[:event]
|
|
107
|
-
if event.is_a?(Hash) && event["event"]
|
|
108
|
-
start_event = event["event"]["Start"] || event["event"]["start"]
|
|
109
|
-
if start_event && start_event["pid"]
|
|
110
|
-
pid = start_event["pid"]
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
response = envd_rpc(
|
|
116
|
-
"process.Process", "Start",
|
|
104
|
+
build_live_handle(
|
|
105
|
+
rpc_method: "Start",
|
|
117
106
|
body: body,
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
# If PID was not captured from streaming, try the accumulated result
|
|
123
|
-
pid ||= extract_pid_from_result(response)
|
|
124
|
-
|
|
125
|
-
CommandHandle.new(
|
|
126
|
-
pid: pid,
|
|
127
|
-
handle_kill: -> { kill(pid) },
|
|
128
|
-
handle_send_stdin: ->(data) { send_stdin(pid, data) },
|
|
129
|
-
result: response
|
|
107
|
+
headers: headers,
|
|
108
|
+
timeout: timeout + 30
|
|
130
109
|
)
|
|
131
110
|
end
|
|
132
111
|
|
|
@@ -148,18 +127,12 @@ module E2B
|
|
|
148
127
|
process: { pid: pid }
|
|
149
128
|
}
|
|
150
129
|
|
|
151
|
-
|
|
152
|
-
|
|
130
|
+
build_live_handle(
|
|
131
|
+
rpc_method: "Connect",
|
|
153
132
|
body: body,
|
|
133
|
+
headers: user_auth_headers(nil),
|
|
154
134
|
timeout: timeout + 30
|
|
155
135
|
)
|
|
156
|
-
|
|
157
|
-
CommandHandle.new(
|
|
158
|
-
pid: pid,
|
|
159
|
-
handle_kill: -> { kill(pid) },
|
|
160
|
-
handle_send_stdin: ->(data) { send_stdin(pid, data) },
|
|
161
|
-
result: response
|
|
162
|
-
)
|
|
163
136
|
end
|
|
164
137
|
|
|
165
138
|
# Send input data to a PTY.
|
|
@@ -178,12 +151,12 @@ module E2B
|
|
|
178
151
|
#
|
|
179
152
|
# @example Send Ctrl+C
|
|
180
153
|
# sandbox.pty.send_stdin(pid, "\x03")
|
|
181
|
-
def send_stdin(pid, data)
|
|
154
|
+
def send_stdin(pid, data, headers: nil)
|
|
182
155
|
encoded = Base64.strict_encode64(data.is_a?(String) ? data : data.to_s)
|
|
183
156
|
envd_rpc("process.Process", "SendInput", body: {
|
|
184
157
|
process: { pid: pid },
|
|
185
158
|
input: { pty: encoded }
|
|
186
|
-
})
|
|
159
|
+
}, headers: headers)
|
|
187
160
|
end
|
|
188
161
|
|
|
189
162
|
# Kill a PTY process with SIGKILL.
|
|
@@ -193,11 +166,11 @@ module E2B
|
|
|
193
166
|
#
|
|
194
167
|
# @example
|
|
195
168
|
# sandbox.pty.kill(12345)
|
|
196
|
-
def kill(pid)
|
|
169
|
+
def kill(pid, headers: nil)
|
|
197
170
|
envd_rpc("process.Process", "SendSignal", body: {
|
|
198
171
|
process: { pid: pid },
|
|
199
172
|
signal: 9 # SIGKILL
|
|
200
|
-
})
|
|
173
|
+
}, headers: headers)
|
|
201
174
|
true
|
|
202
175
|
rescue E2B::E2BError
|
|
203
176
|
false
|
|
@@ -268,30 +241,6 @@ module E2B
|
|
|
268
241
|
result
|
|
269
242
|
end
|
|
270
243
|
|
|
271
|
-
# Extract PID from the StartEvent in a pre-materialized RPC result.
|
|
272
|
-
#
|
|
273
|
-
# The result hash from {EnvdHttpClient#handle_streaming_rpc} or
|
|
274
|
-
# {EnvdHttpClient#handle_rpc_response} contains an :events array.
|
|
275
|
-
# The first event with a Start sub-event carries the PID.
|
|
276
|
-
#
|
|
277
|
-
# @param response [Hash] RPC response hash with :events key
|
|
278
|
-
# @return [Integer, nil] Process ID, or nil if not found
|
|
279
|
-
def extract_pid_from_result(response)
|
|
280
|
-
return nil unless response.is_a?(Hash)
|
|
281
|
-
|
|
282
|
-
events = response[:events] || []
|
|
283
|
-
events.each do |event_hash|
|
|
284
|
-
next unless event_hash.is_a?(Hash) && event_hash["event"]
|
|
285
|
-
|
|
286
|
-
event = event_hash["event"]
|
|
287
|
-
start_event = event["Start"] || event["start"]
|
|
288
|
-
if start_event && start_event["pid"]
|
|
289
|
-
return start_event["pid"]
|
|
290
|
-
end
|
|
291
|
-
end
|
|
292
|
-
|
|
293
|
-
nil
|
|
294
|
-
end
|
|
295
244
|
end
|
|
296
245
|
end
|
|
297
246
|
end
|
|
@@ -38,9 +38,10 @@ module E2B
|
|
|
38
38
|
# @param envd_rpc_proc [Proc] A callable that performs RPC calls. It must accept
|
|
39
39
|
# three positional arguments (service, method) and keyword arguments (body:, timeout:).
|
|
40
40
|
# Typically a lambda wrapping {BaseService#envd_rpc}.
|
|
41
|
-
def initialize(watcher_id:, envd_rpc_proc:)
|
|
41
|
+
def initialize(watcher_id:, envd_rpc_proc:, headers: nil)
|
|
42
42
|
@watcher_id = watcher_id
|
|
43
43
|
@envd_rpc_proc = envd_rpc_proc
|
|
44
|
+
@headers = headers
|
|
44
45
|
@stopped = false
|
|
45
46
|
end
|
|
46
47
|
|
|
@@ -56,7 +57,8 @@ module E2B
|
|
|
56
57
|
|
|
57
58
|
response = @envd_rpc_proc.call(
|
|
58
59
|
"filesystem.Filesystem", "GetWatcherEvents",
|
|
59
|
-
body: { watcherId: @watcher_id }
|
|
60
|
+
body: { watcherId: @watcher_id },
|
|
61
|
+
headers: @headers
|
|
60
62
|
)
|
|
61
63
|
|
|
62
64
|
events = extract_events(response)
|
|
@@ -75,7 +77,8 @@ module E2B
|
|
|
75
77
|
|
|
76
78
|
@envd_rpc_proc.call(
|
|
77
79
|
"filesystem.Filesystem", "RemoveWatcher",
|
|
78
|
-
body: { watcherId: @watcher_id }
|
|
80
|
+
body: { watcherId: @watcher_id },
|
|
81
|
+
headers: @headers
|
|
79
82
|
)
|
|
80
83
|
@stopped = true
|
|
81
84
|
rescue StandardError
|