nightona 0.191.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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +22 -0
- data/.ruby-version +1 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/LICENSE +190 -0
- data/README.md +184 -0
- data/Rakefile +12 -0
- data/lib/nightona/code_interpreter.rb +359 -0
- data/lib/nightona/common/charts.rb +124 -0
- data/lib/nightona/common/code_interpreter.rb +56 -0
- data/lib/nightona/common/code_language.rb +14 -0
- data/lib/nightona/common/file_system.rb +26 -0
- data/lib/nightona/common/git.rb +19 -0
- data/lib/nightona/common/image.rb +500 -0
- data/lib/nightona/common/nightona.rb +230 -0
- data/lib/nightona/common/process.rb +149 -0
- data/lib/nightona/common/pty.rb +309 -0
- data/lib/nightona/common/resources.rb +39 -0
- data/lib/nightona/common/response.rb +83 -0
- data/lib/nightona/common/snapshot.rb +124 -0
- data/lib/nightona/computer_use.rb +919 -0
- data/lib/nightona/config.rb +116 -0
- data/lib/nightona/file_system.rb +451 -0
- data/lib/nightona/file_transfer.rb +383 -0
- data/lib/nightona/git.rb +334 -0
- data/lib/nightona/lsp_server.rb +139 -0
- data/lib/nightona/nightona.rb +336 -0
- data/lib/nightona/object_storage.rb +172 -0
- data/lib/nightona/otel.rb +183 -0
- data/lib/nightona/process.rb +550 -0
- data/lib/nightona/sandbox.rb +751 -0
- data/lib/nightona/sdk/version.rb +10 -0
- data/lib/nightona/sdk.rb +56 -0
- data/lib/nightona/snapshot_service.rb +238 -0
- data/lib/nightona/util.rb +80 -0
- data/lib/nightona/volume.rb +46 -0
- data/lib/nightona/volume_service.rb +61 -0
- data/lib/nightona.rb +10 -0
- data/project.json +100 -0
- data/scripts/generate-docs.rb +402 -0
- data/sig/nightona/sdk.rbs +6 -0
- metadata +242 -0
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
# Copyright Daytona Platforms Inc.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
# frozen_string_literal: true
|
|
5
|
+
|
|
6
|
+
require 'uri'
|
|
7
|
+
|
|
8
|
+
module Nightona
|
|
9
|
+
class Process # rubocop:disable Metrics/ClassLength
|
|
10
|
+
include Instrumentation
|
|
11
|
+
|
|
12
|
+
# @return [String] The ID of the Sandbox
|
|
13
|
+
attr_reader :sandbox_id
|
|
14
|
+
|
|
15
|
+
# @return [NightonaToolboxApiClient::ProcessApi] API client for Sandbox operations
|
|
16
|
+
attr_reader :toolbox_api
|
|
17
|
+
|
|
18
|
+
# @return [Proc] Function to get preview link for a port
|
|
19
|
+
attr_reader :get_preview_link
|
|
20
|
+
|
|
21
|
+
# @return [String] The language for code execution (e.g. 'python', 'typescript', 'javascript')
|
|
22
|
+
attr_reader :language
|
|
23
|
+
|
|
24
|
+
# Initialize a new Process instance
|
|
25
|
+
#
|
|
26
|
+
# @param sandbox_id [String] The ID of the Sandbox
|
|
27
|
+
# @param toolbox_api [NightonaToolboxApiClient::ProcessApi] API client for Sandbox operations
|
|
28
|
+
# @param get_preview_link [Proc] Function to get preview link for a port
|
|
29
|
+
# @param language [String] The language for code execution
|
|
30
|
+
# @param otel_state [Nightona::OtelState, nil]
|
|
31
|
+
def initialize(sandbox_id:, toolbox_api:, get_preview_link:, language: 'python', otel_state: nil)
|
|
32
|
+
@sandbox_id = sandbox_id
|
|
33
|
+
@toolbox_api = toolbox_api
|
|
34
|
+
@get_preview_link = get_preview_link
|
|
35
|
+
@language = language
|
|
36
|
+
@otel_state = otel_state
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Execute a shell command in the Sandbox
|
|
40
|
+
#
|
|
41
|
+
# @param command [String] Shell command to execute
|
|
42
|
+
# @param cwd [String, nil] Working directory for command execution. If not specified, uses the sandbox working directory
|
|
43
|
+
# @param env [Hash<String, String>, nil] Environment variables to set for the command
|
|
44
|
+
# @param timeout [Integer, nil] Maximum time in seconds to wait for the command to complete.
|
|
45
|
+
# @return [ExecuteResponse] Command execution results containing exit_code, result, and artifacts
|
|
46
|
+
#
|
|
47
|
+
# @example
|
|
48
|
+
# # Simple command
|
|
49
|
+
# response = sandbox.process.exec("echo 'Hello'")
|
|
50
|
+
# puts response.artifacts.stdout
|
|
51
|
+
# => "Hello\n"
|
|
52
|
+
#
|
|
53
|
+
# # Command with working directory
|
|
54
|
+
# result = sandbox.process.exec("ls", cwd: "workspace/src")
|
|
55
|
+
#
|
|
56
|
+
# # Command with timeout
|
|
57
|
+
# result = sandbox.process.exec("sleep 10", timeout: 5)
|
|
58
|
+
def exec(command:, cwd: nil, env: nil, timeout: nil)
|
|
59
|
+
envs = env&.empty? ? nil : env
|
|
60
|
+
|
|
61
|
+
response = toolbox_api.execute_command(NightonaToolboxApiClient::ExecuteRequest.new(command:, cwd:, envs:,
|
|
62
|
+
timeout:))
|
|
63
|
+
result = response.result || ''
|
|
64
|
+
ExecuteResponse.new(
|
|
65
|
+
exit_code: response.exit_code,
|
|
66
|
+
result:,
|
|
67
|
+
artifacts: ExecutionArtifacts.new(result, [])
|
|
68
|
+
)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Execute code in the Sandbox using the appropriate language runtime
|
|
72
|
+
#
|
|
73
|
+
# @param code [String] Code to execute
|
|
74
|
+
# @param params [CodeRunParams, nil] Parameters for code execution
|
|
75
|
+
# @param timeout [Integer, nil] Maximum time in seconds to wait for the code to complete. 0 means wait indefinitely
|
|
76
|
+
# @return [ExecuteResponse] Code execution result containing exit_code, result, and artifacts
|
|
77
|
+
#
|
|
78
|
+
# @example
|
|
79
|
+
# # Run Python code
|
|
80
|
+
# response = sandbox.process.code_run(<<~CODE)
|
|
81
|
+
# x = 10
|
|
82
|
+
# y = 20
|
|
83
|
+
# print(f"Sum: {x + y}")
|
|
84
|
+
# CODE
|
|
85
|
+
# puts response.artifacts.stdout # Prints: Sum: 30
|
|
86
|
+
def code_run(code:, params: nil, timeout: nil)
|
|
87
|
+
response = toolbox_api.code_run(
|
|
88
|
+
NightonaToolboxApiClient::CodeRunRequest.new(
|
|
89
|
+
code:, language:, argv: params&.argv, envs: params&.env, timeout:
|
|
90
|
+
)
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
ExecuteResponse.new(
|
|
94
|
+
exit_code: response.exit_code,
|
|
95
|
+
result: response.result,
|
|
96
|
+
artifacts: ExecutionArtifacts.new(response.result, (response.artifacts&.charts || []).map do |c|
|
|
97
|
+
Charts.parse_chart(c)
|
|
98
|
+
end)
|
|
99
|
+
)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Creates a new long-running background session in the Sandbox
|
|
103
|
+
#
|
|
104
|
+
# Sessions are background processes that maintain state between commands, making them ideal for
|
|
105
|
+
# scenarios requiring multiple related commands or persistent environment setup.
|
|
106
|
+
#
|
|
107
|
+
# @param session_id [String] Unique identifier for the new session
|
|
108
|
+
# @return [void]
|
|
109
|
+
#
|
|
110
|
+
# @example
|
|
111
|
+
# # Create a new session
|
|
112
|
+
# session_id = "my-session"
|
|
113
|
+
# sandbox.process.create_session(session_id)
|
|
114
|
+
# session = sandbox.process.get_session(session_id)
|
|
115
|
+
# # Do work...
|
|
116
|
+
# sandbox.process.delete_session(session_id)
|
|
117
|
+
def create_session(session_id)
|
|
118
|
+
toolbox_api.create_session(NightonaToolboxApiClient::CreateSessionRequest.new(session_id:))
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Gets a session in the Sandbox
|
|
122
|
+
#
|
|
123
|
+
# @param session_id [String] Unique identifier of the session to retrieve
|
|
124
|
+
# @return [NightonaApiClient::Session] Session information including session_id and commands
|
|
125
|
+
#
|
|
126
|
+
# @example
|
|
127
|
+
# session = sandbox.process.get_session("my-session")
|
|
128
|
+
# session.commands.each do |cmd|
|
|
129
|
+
# puts "Command: #{cmd.command}"
|
|
130
|
+
# end
|
|
131
|
+
def get_session(session_id) = toolbox_api.get_session(session_id)
|
|
132
|
+
|
|
133
|
+
# Gets the Sandbox entrypoint session
|
|
134
|
+
#
|
|
135
|
+
# @return [NightonaApiClient::Session] Entrypoint session information including session_id and commands
|
|
136
|
+
#
|
|
137
|
+
# @example
|
|
138
|
+
# session = sandbox.process.get_entrypoint_session()
|
|
139
|
+
# session.commands.each do |cmd|
|
|
140
|
+
# puts "Command: #{cmd.command}"
|
|
141
|
+
# end
|
|
142
|
+
def get_entrypoint_session = toolbox_api.get_entrypoint_session
|
|
143
|
+
|
|
144
|
+
# Gets information about a specific command executed in a session
|
|
145
|
+
#
|
|
146
|
+
# @param session_id [String] Unique identifier of the session
|
|
147
|
+
# @param command_id [String] Unique identifier of the command
|
|
148
|
+
# @return [NightonaApiClient::Command] Command information including id, command, and exit_code
|
|
149
|
+
#
|
|
150
|
+
# @example
|
|
151
|
+
# cmd = sandbox.process.get_session_command(session_id: "my-session", command_id: "cmd-123")
|
|
152
|
+
# if cmd.exit_code == 0
|
|
153
|
+
# puts "Command #{cmd.command} completed successfully"
|
|
154
|
+
# end
|
|
155
|
+
def get_session_command(session_id:, command_id:)
|
|
156
|
+
toolbox_api.get_session_command(session_id, command_id)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Executes a command in the session
|
|
160
|
+
#
|
|
161
|
+
# @param session_id [String] Unique identifier of the session to use
|
|
162
|
+
# @param req [Nightona::SessionExecuteRequest] Command execution request containing command and run_async
|
|
163
|
+
# @return [Nightona::SessionExecuteResponse] Command execution results containing cmd_id, output, stdout, stderr, and exit_code
|
|
164
|
+
#
|
|
165
|
+
# @example
|
|
166
|
+
# # Execute commands in sequence, maintaining state
|
|
167
|
+
# session_id = "my-session"
|
|
168
|
+
#
|
|
169
|
+
# # Change directory
|
|
170
|
+
# req = Nightona::SessionExecuteRequest.new(command: "cd /workspace")
|
|
171
|
+
# sandbox.process.execute_session_command(session_id:, req:)
|
|
172
|
+
#
|
|
173
|
+
# # Create a file
|
|
174
|
+
# req = Nightona::SessionExecuteRequest.new(command: "echo 'Hello' > test.txt")
|
|
175
|
+
# sandbox.process.execute_session_command(session_id:, req:)
|
|
176
|
+
#
|
|
177
|
+
# # Read the file
|
|
178
|
+
# req = Nightona::SessionExecuteRequest.new(command: "cat test.txt")
|
|
179
|
+
# result = sandbox.process.execute_session_command(session_id:, req:)
|
|
180
|
+
# puts "Command stdout: #{result.stdout}"
|
|
181
|
+
# puts "Command stderr: #{result.stderr}"
|
|
182
|
+
def execute_session_command(session_id:, req:) # rubocop:disable Metrics/MethodLength
|
|
183
|
+
response = toolbox_api.session_execute_command(
|
|
184
|
+
session_id,
|
|
185
|
+
NightonaToolboxApiClient::SessionExecuteRequest.new(command: req.command, run_async: req.run_async,
|
|
186
|
+
suppress_input_echo: req.suppress_input_echo)
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
SessionExecuteResponse.new(
|
|
190
|
+
cmd_id: response.cmd_id,
|
|
191
|
+
output: response.output,
|
|
192
|
+
stdout: response.stdout || '',
|
|
193
|
+
stderr: response.stderr || '',
|
|
194
|
+
exit_code: response.exit_code,
|
|
195
|
+
additional_properties: {}
|
|
196
|
+
)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Get the logs for a command executed in a session
|
|
200
|
+
#
|
|
201
|
+
# @param session_id [String] Unique identifier of the session
|
|
202
|
+
# @param command_id [String] Unique identifier of the command
|
|
203
|
+
# @return [Nightona::SessionCommandLogsResponse] Command logs including output, stdout, and stderr
|
|
204
|
+
#
|
|
205
|
+
# @example
|
|
206
|
+
# logs = sandbox.process.get_session_command_logs(session_id: "my-session", command_id: "cmd-123")
|
|
207
|
+
# puts "Command stdout: #{logs.stdout}"
|
|
208
|
+
# puts "Command stderr: #{logs.stderr}"
|
|
209
|
+
def get_session_command_logs(session_id:, command_id:)
|
|
210
|
+
response = toolbox_api.get_session_command_logs(session_id, command_id)
|
|
211
|
+
SessionCommandLogsResponse.new(output: response.output, stdout: response.stdout, stderr: response.stderr)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Asynchronously retrieves and processes the logs for a command executed in a session as they become available
|
|
215
|
+
#
|
|
216
|
+
# @param session_id [String] Unique identifier of the session
|
|
217
|
+
# @param command_id [String] Unique identifier of the command
|
|
218
|
+
# @param on_stdout [Proc] Callback function to handle stdout log chunks as they arrive
|
|
219
|
+
# @param on_stderr [Proc] Callback function to handle stderr log chunks as they arrive
|
|
220
|
+
# @return [WebSocket::Client::Simple::Client]
|
|
221
|
+
#
|
|
222
|
+
# @example
|
|
223
|
+
# sandbox.process.get_session_command_logs_async(
|
|
224
|
+
# session_id: "my-session",
|
|
225
|
+
# command_id: "cmd-123",
|
|
226
|
+
# on_stdout: ->(log) { puts "[STDOUT]: #{log}" },
|
|
227
|
+
# on_stderr: ->(log) { puts "[STDERR]: #{log}" }
|
|
228
|
+
# )
|
|
229
|
+
def get_session_command_logs_async(session_id:, command_id:, on_stdout:, on_stderr:) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
230
|
+
preview_link = get_preview_link.call(WS_PORT)
|
|
231
|
+
url = URI.parse(preview_link.url)
|
|
232
|
+
url.scheme = url.scheme == 'https' ? 'wss' : 'ws'
|
|
233
|
+
url.path = "/process/session/#{session_id}/command/#{command_id}/logs"
|
|
234
|
+
url.query = 'follow=true'
|
|
235
|
+
|
|
236
|
+
completion_queue = Queue.new
|
|
237
|
+
|
|
238
|
+
WebSocket::Client::Simple.connect(
|
|
239
|
+
url.to_s,
|
|
240
|
+
headers: toolbox_api.api_client.default_headers.dup.merge(
|
|
241
|
+
'X-Nightona-Preview-Token' => preview_link.token,
|
|
242
|
+
'Content-Type' => 'text/plain',
|
|
243
|
+
'Accept' => 'text/plain'
|
|
244
|
+
)
|
|
245
|
+
) do |client|
|
|
246
|
+
client.on(:message) do |message|
|
|
247
|
+
if message.type == :close
|
|
248
|
+
client.close
|
|
249
|
+
completion_queue.push(:close)
|
|
250
|
+
else
|
|
251
|
+
stdout, stderr = Util.demux(message.data.to_s)
|
|
252
|
+
|
|
253
|
+
on_stdout.call(stdout) unless stdout.empty?
|
|
254
|
+
on_stderr.call(stderr) unless stderr.empty?
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
client.on(:close) do
|
|
259
|
+
completion_queue.push(:close)
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
client.on(:error) do |e|
|
|
263
|
+
completion_queue.push(:error)
|
|
264
|
+
raise Sdk::Error, "WebSocket error: #{e.message}"
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# Wait for completion
|
|
269
|
+
completion_queue.pop
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# Get the sandbox entrypoint logs
|
|
273
|
+
#
|
|
274
|
+
# @return [Nightona::SessionCommandLogsResponse] Entrypoint logs including output, stdout, and stderr
|
|
275
|
+
#
|
|
276
|
+
# @example
|
|
277
|
+
# logs = sandbox.process.get_entrypoint_logs()
|
|
278
|
+
# puts "Command stdout: #{logs.stdout}"
|
|
279
|
+
# puts "Command stderr: #{logs.stderr}"
|
|
280
|
+
def get_entrypoint_logs
|
|
281
|
+
response = toolbox_api.get_entrypoint_logs
|
|
282
|
+
SessionCommandLogsResponse.new(output: response.output, stdout: response.stdout, stderr: response.stderr)
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
# Asynchronously retrieves and processes the sandbox entrypoint logs as they become available
|
|
286
|
+
#
|
|
287
|
+
# @param on_stdout [Proc] Callback function to handle stdout log chunks as they arrive
|
|
288
|
+
# @param on_stderr [Proc] Callback function to handle stderr log chunks as they arrive
|
|
289
|
+
# @return [WebSocket::Client::Simple::Client]
|
|
290
|
+
#
|
|
291
|
+
# @example
|
|
292
|
+
# sandbox.process.get_entrypoint_logs_async(
|
|
293
|
+
# on_stdout: ->(log) { puts "[STDOUT]: #{log}" },
|
|
294
|
+
# on_stderr: ->(log) { puts "[STDERR]: #{log}" }
|
|
295
|
+
# )
|
|
296
|
+
def get_entrypoint_logs_async(on_stdout:, on_stderr:) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
297
|
+
preview_link = get_preview_link.call(WS_PORT)
|
|
298
|
+
url = URI.parse(preview_link.url)
|
|
299
|
+
url.scheme = url.scheme == 'https' ? 'wss' : 'ws'
|
|
300
|
+
url.path = '/process/session/entrypoint/logs'
|
|
301
|
+
url.query = 'follow=true'
|
|
302
|
+
|
|
303
|
+
completion_queue = Queue.new
|
|
304
|
+
|
|
305
|
+
WebSocket::Client::Simple.connect(
|
|
306
|
+
url.to_s,
|
|
307
|
+
headers: toolbox_api.api_client.default_headers.dup.merge(
|
|
308
|
+
'X-Nightona-Preview-Token' => preview_link.token,
|
|
309
|
+
'Content-Type' => 'text/plain',
|
|
310
|
+
'Accept' => 'text/plain'
|
|
311
|
+
)
|
|
312
|
+
) do |client|
|
|
313
|
+
client.on(:message) do |message|
|
|
314
|
+
if message.type == :close
|
|
315
|
+
client.close
|
|
316
|
+
completion_queue.push(:close)
|
|
317
|
+
else
|
|
318
|
+
stdout, stderr = Util.demux(message.data.to_s)
|
|
319
|
+
|
|
320
|
+
on_stdout.call(stdout) unless stdout.empty?
|
|
321
|
+
on_stderr.call(stderr) unless stderr.empty?
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
client.on(:close) do
|
|
326
|
+
completion_queue.push(:close)
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
client.on(:error) do |e|
|
|
330
|
+
completion_queue.push(:error)
|
|
331
|
+
raise Sdk::Error, "WebSocket error: #{e.message}"
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
# Wait for completion
|
|
336
|
+
completion_queue.pop
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
# Sends input data to a command executed in a session
|
|
340
|
+
#
|
|
341
|
+
# This method allows you to send input to an interactive command running in a session,
|
|
342
|
+
# such as responding to prompts or providing data to stdin.
|
|
343
|
+
#
|
|
344
|
+
# @param session_id [String] Unique identifier of the session
|
|
345
|
+
# @param command_id [String] Unique identifier of the command
|
|
346
|
+
# @param data [String] Input data to send to the command
|
|
347
|
+
# @return [void]
|
|
348
|
+
def send_session_command_input(session_id:, command_id:, data:)
|
|
349
|
+
toolbox_api.send_input(
|
|
350
|
+
session_id,
|
|
351
|
+
command_id,
|
|
352
|
+
NightonaToolboxApiClient::SessionSendInputRequest.new(data:)
|
|
353
|
+
)
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
#
|
|
357
|
+
# @return [Array<NightonaApiClient::Session>] List of all sessions in the Sandbox
|
|
358
|
+
#
|
|
359
|
+
# @example
|
|
360
|
+
# sessions = sandbox.process.list_sessions
|
|
361
|
+
# sessions.each do |session|
|
|
362
|
+
# puts "Session #{session.session_id}:"
|
|
363
|
+
# puts " Commands: #{session.commands.length}"
|
|
364
|
+
# end
|
|
365
|
+
def list_sessions = toolbox_api.list_sessions
|
|
366
|
+
|
|
367
|
+
# Terminates and removes a session from the Sandbox, cleaning up any resources associated with it
|
|
368
|
+
#
|
|
369
|
+
# @param session_id [String] Unique identifier of the session to delete
|
|
370
|
+
#
|
|
371
|
+
# @example
|
|
372
|
+
# # Create and use a session
|
|
373
|
+
# sandbox.process.create_session("temp-session")
|
|
374
|
+
# # ... use the session ...
|
|
375
|
+
#
|
|
376
|
+
# # Clean up when done
|
|
377
|
+
# sandbox.process.delete_session("temp-session")
|
|
378
|
+
def delete_session(session_id) = toolbox_api.delete_session(session_id)
|
|
379
|
+
|
|
380
|
+
# Creates a new PTY (pseudo-terminal) session in the Sandbox.
|
|
381
|
+
#
|
|
382
|
+
# Creates an interactive terminal session that can execute commands and handle user input.
|
|
383
|
+
# The PTY session behaves like a real terminal, supporting features like command history.
|
|
384
|
+
#
|
|
385
|
+
# @param id [String] Unique identifier for the PTY session. Must be unique within the Sandbox.
|
|
386
|
+
# @param cwd [String, nil] Working directory for the PTY session. Defaults to the sandbox's working directory.
|
|
387
|
+
# @param envs [Hash<String, String>, nil] Environment variables to set in the PTY session. These will be merged with
|
|
388
|
+
# the Sandbox's default environment variables.
|
|
389
|
+
# @param pty_size [PtySize, nil] Terminal size configuration. Defaults to 80x24 if not specified.
|
|
390
|
+
# @return [PtyHandle] Handle for managing the created PTY session. Use this to send input,
|
|
391
|
+
# receive output, resize the terminal, and manage the session lifecycle.
|
|
392
|
+
#
|
|
393
|
+
# @example
|
|
394
|
+
# # Create a basic PTY session
|
|
395
|
+
# pty_handle = sandbox.process.create_pty_session(id: "my-pty")
|
|
396
|
+
#
|
|
397
|
+
# # Create a PTY session with specific size and environment
|
|
398
|
+
# pty_size = Nightona::PtySize.new(rows: 30, cols: 120)
|
|
399
|
+
# pty_handle = sandbox.process.create_pty_session(
|
|
400
|
+
# id: "my-pty",
|
|
401
|
+
# cwd: "/workspace",
|
|
402
|
+
# envs: {"NODE_ENV" => "development"},
|
|
403
|
+
# pty_size: pty_size
|
|
404
|
+
# )
|
|
405
|
+
#
|
|
406
|
+
# # Use the PTY session
|
|
407
|
+
# pty_handle.wait_for_connection
|
|
408
|
+
# pty_handle.send_input("ls -la\n")
|
|
409
|
+
# result = pty_handle.wait
|
|
410
|
+
# pty_handle.disconnect
|
|
411
|
+
#
|
|
412
|
+
# @raise [Nightona::Sdk::Error] If the PTY session creation fails or the session ID is already in use.
|
|
413
|
+
def create_pty_session(id:, cwd: nil, envs: nil, pty_size: nil) # rubocop:disable Metrics/MethodLength
|
|
414
|
+
response = toolbox_api.create_pty_session(
|
|
415
|
+
NightonaToolboxApiClient::PtyCreateRequest.new(
|
|
416
|
+
id:,
|
|
417
|
+
cwd:,
|
|
418
|
+
envs:,
|
|
419
|
+
cols: pty_size&.cols,
|
|
420
|
+
rows: pty_size&.rows,
|
|
421
|
+
lazy_start: true
|
|
422
|
+
)
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
connect_pty_session(response.session_id)
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
# Connects to an existing PTY session in the Sandbox.
|
|
429
|
+
#
|
|
430
|
+
# Establishes a WebSocket connection to an existing PTY session, allowing you to
|
|
431
|
+
# interact with a previously created terminal session.
|
|
432
|
+
#
|
|
433
|
+
# @param session_id [String] Unique identifier of the PTY session to connect to.
|
|
434
|
+
# @return [PtyHandle] Handle for managing the connected PTY session.
|
|
435
|
+
#
|
|
436
|
+
# @example
|
|
437
|
+
# # Connect to an existing PTY session
|
|
438
|
+
# pty_handle = sandbox.process.connect_pty_session("my-pty-session")
|
|
439
|
+
# pty_handle.wait_for_connection
|
|
440
|
+
# pty_handle.send_input("echo 'Hello World'\n")
|
|
441
|
+
# result = pty_handle.wait
|
|
442
|
+
# pty_handle.disconnect
|
|
443
|
+
#
|
|
444
|
+
# @raise [Nightona::Sdk::Error] If the PTY session doesn't exist or connection fails.
|
|
445
|
+
def connect_pty_session(session_id) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
446
|
+
preview_link = get_preview_link.call(WS_PORT)
|
|
447
|
+
url = URI.parse(preview_link.url)
|
|
448
|
+
url.scheme = url.scheme == 'https' ? 'wss' : 'ws'
|
|
449
|
+
url.path = "/process/pty/#{session_id}/connect"
|
|
450
|
+
|
|
451
|
+
handle = nil
|
|
452
|
+
WebSocket::Client::Simple.connect(
|
|
453
|
+
url.to_s,
|
|
454
|
+
headers: toolbox_api.api_client.default_headers.dup.merge(
|
|
455
|
+
'X-Nightona-Preview-Token' => preview_link.token
|
|
456
|
+
)
|
|
457
|
+
) do |client|
|
|
458
|
+
handle = PtyHandle.new(
|
|
459
|
+
client,
|
|
460
|
+
session_id:,
|
|
461
|
+
handle_resize: ->(pty_size) { resize_pty_session(session_id, pty_size) },
|
|
462
|
+
handle_kill: -> { delete_pty_session(session_id) }
|
|
463
|
+
)
|
|
464
|
+
end
|
|
465
|
+
handle.tap(&:wait_for_connection)
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
# Resizes a PTY session to the specified dimensions
|
|
469
|
+
#
|
|
470
|
+
# @param session_id [String] Unique identifier of the PTY session
|
|
471
|
+
# @param pty_size [PtySize] New terminal size
|
|
472
|
+
# @return [NightonaApiClient::PtySessionInfo] Updated PTY session information
|
|
473
|
+
#
|
|
474
|
+
# @example
|
|
475
|
+
# pty_size = Nightona::PtySize.new(rows: 30, cols: 120)
|
|
476
|
+
# session_info = sandbox.process.resize_pty_session("my-pty", pty_size)
|
|
477
|
+
# puts "PTY resized to #{session_info.cols}x#{session_info.rows}"
|
|
478
|
+
def resize_pty_session(session_id, pty_size)
|
|
479
|
+
toolbox_api.resize_pty_session(
|
|
480
|
+
session_id,
|
|
481
|
+
NightonaToolboxApiClient::PtyResizeRequest.new(
|
|
482
|
+
cols: pty_size.cols,
|
|
483
|
+
rows: pty_size.rows
|
|
484
|
+
)
|
|
485
|
+
)
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
# Deletes a PTY session, terminating the associated process
|
|
489
|
+
#
|
|
490
|
+
# @param session_id [String] Unique identifier of the PTY session to delete
|
|
491
|
+
# @return [void]
|
|
492
|
+
#
|
|
493
|
+
# @example
|
|
494
|
+
# sandbox.process.delete_pty_session("my-pty")
|
|
495
|
+
def delete_pty_session(session_id)
|
|
496
|
+
toolbox_api.delete_pty_session(session_id)
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
# Lists all PTY sessions in the Sandbox
|
|
500
|
+
#
|
|
501
|
+
# @return [Array<NightonaApiClient::PtySessionInfo>] List of PTY session information
|
|
502
|
+
#
|
|
503
|
+
# @example
|
|
504
|
+
# sessions = sandbox.process.list_pty_sessions
|
|
505
|
+
# sessions.each do |session|
|
|
506
|
+
# puts "PTY Session #{session.id}: #{session.cols}x#{session.rows}"
|
|
507
|
+
# end
|
|
508
|
+
def list_pty_sessions
|
|
509
|
+
response = toolbox_api.list_pty_sessions
|
|
510
|
+
return [] if response.nil?
|
|
511
|
+
|
|
512
|
+
response.respond_to?(:sessions) ? (response.sessions || []) : response
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
# Gets detailed information about a specific PTY session
|
|
516
|
+
#
|
|
517
|
+
# Retrieves comprehensive information about a PTY session including its current state,
|
|
518
|
+
# configuration, and metadata.
|
|
519
|
+
#
|
|
520
|
+
# @param session_id [String] Unique identifier of the PTY session to retrieve information for
|
|
521
|
+
# @return [NightonaApiClient::PtySessionInfo] Detailed information about the PTY session including ID, state,
|
|
522
|
+
# creation time, working directory, environment variables, and more
|
|
523
|
+
#
|
|
524
|
+
# @example
|
|
525
|
+
# # Get details about a specific PTY session
|
|
526
|
+
# session_info = sandbox.process.get_pty_session_info("my-session")
|
|
527
|
+
# puts "Session ID: #{session_info.id}"
|
|
528
|
+
# puts "Active: #{session_info.active}"
|
|
529
|
+
# puts "Working Directory: #{session_info.cwd}"
|
|
530
|
+
# puts "Terminal Size: #{session_info.cols}x#{session_info.rows}"
|
|
531
|
+
def get_pty_session_info(session_id)
|
|
532
|
+
toolbox_api.get_pty_session(session_id)
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
instrument :exec, :code_run, :create_session, :get_session, :get_session_command,
|
|
536
|
+
:execute_session_command, :get_session_command_logs, :get_session_command_logs_async,
|
|
537
|
+
:send_session_command_input, :list_sessions, :delete_session,
|
|
538
|
+
:create_pty_session, :connect_pty_session, :resize_pty_session,
|
|
539
|
+
:delete_pty_session, :list_pty_sessions, :get_pty_session_info,
|
|
540
|
+
component: 'Process'
|
|
541
|
+
|
|
542
|
+
private
|
|
543
|
+
|
|
544
|
+
# @return [Nightona::OtelState, nil]
|
|
545
|
+
attr_reader :otel_state
|
|
546
|
+
|
|
547
|
+
WS_PORT = 2280
|
|
548
|
+
private_constant :WS_PORT
|
|
549
|
+
end
|
|
550
|
+
end
|