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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +22 -0
  4. data/.ruby-version +1 -0
  5. data/CODE_OF_CONDUCT.md +132 -0
  6. data/LICENSE +190 -0
  7. data/README.md +184 -0
  8. data/Rakefile +12 -0
  9. data/lib/nightona/code_interpreter.rb +359 -0
  10. data/lib/nightona/common/charts.rb +124 -0
  11. data/lib/nightona/common/code_interpreter.rb +56 -0
  12. data/lib/nightona/common/code_language.rb +14 -0
  13. data/lib/nightona/common/file_system.rb +26 -0
  14. data/lib/nightona/common/git.rb +19 -0
  15. data/lib/nightona/common/image.rb +500 -0
  16. data/lib/nightona/common/nightona.rb +230 -0
  17. data/lib/nightona/common/process.rb +149 -0
  18. data/lib/nightona/common/pty.rb +309 -0
  19. data/lib/nightona/common/resources.rb +39 -0
  20. data/lib/nightona/common/response.rb +83 -0
  21. data/lib/nightona/common/snapshot.rb +124 -0
  22. data/lib/nightona/computer_use.rb +919 -0
  23. data/lib/nightona/config.rb +116 -0
  24. data/lib/nightona/file_system.rb +451 -0
  25. data/lib/nightona/file_transfer.rb +383 -0
  26. data/lib/nightona/git.rb +334 -0
  27. data/lib/nightona/lsp_server.rb +139 -0
  28. data/lib/nightona/nightona.rb +336 -0
  29. data/lib/nightona/object_storage.rb +172 -0
  30. data/lib/nightona/otel.rb +183 -0
  31. data/lib/nightona/process.rb +550 -0
  32. data/lib/nightona/sandbox.rb +751 -0
  33. data/lib/nightona/sdk/version.rb +10 -0
  34. data/lib/nightona/sdk.rb +56 -0
  35. data/lib/nightona/snapshot_service.rb +238 -0
  36. data/lib/nightona/util.rb +80 -0
  37. data/lib/nightona/volume.rb +46 -0
  38. data/lib/nightona/volume_service.rb +61 -0
  39. data/lib/nightona.rb +10 -0
  40. data/project.json +100 -0
  41. data/scripts/generate-docs.rb +402 -0
  42. data/sig/nightona/sdk.rbs +6 -0
  43. metadata +242 -0
@@ -0,0 +1,359 @@
1
+ # Copyright Daytona Platforms Inc.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ # frozen_string_literal: true
5
+
6
+ require 'json'
7
+ require 'websocket-client-simple'
8
+ require 'timeout'
9
+
10
+ module Nightona
11
+ # Handles code interpretation and execution within a Sandbox. Currently supports only Python.
12
+ #
13
+ # This class provides methods to execute code in isolated interpreter contexts,
14
+ # manage contexts, and stream execution output via callbacks. If subsequent code executions
15
+ # are performed in the same context, the variables, imports, and functions defined in
16
+ # the previous execution will be available.
17
+ #
18
+ # For other languages, use the `code_run` method from the `Process` interface,
19
+ # or execute the appropriate command directly in the sandbox terminal.
20
+ class CodeInterpreter
21
+ include Instrumentation
22
+
23
+ WEBSOCKET_TIMEOUT_CODE = 4008
24
+ WS_PORT = 2280
25
+ private_constant :WS_PORT
26
+
27
+ # @param sandbox_id [String]
28
+ # @param toolbox_api [NightonaToolboxApiClient::InterpreterApi]
29
+ # @param get_preview_link [Proc]
30
+ # @param otel_state [Nightona::OtelState, nil]
31
+ def initialize(sandbox_id:, toolbox_api:, get_preview_link:, otel_state: nil)
32
+ @sandbox_id = sandbox_id
33
+ @toolbox_api = toolbox_api
34
+ @get_preview_link = get_preview_link
35
+ @otel_state = otel_state
36
+ end
37
+
38
+ # Execute Python code in the sandbox.
39
+ #
40
+ # By default, code runs in the default shared context which persists variables,
41
+ # imports, and functions across executions. To run in an isolated context,
42
+ # create a new context with `create_context` and pass it as the `context` argument.
43
+ #
44
+ # @param code [String] Code to execute
45
+ # @param context [NightonaToolboxApiClient::InterpreterContext, nil] Context to run code in
46
+ # @param on_stdout [Proc, nil] Callback for stdout messages (receives OutputMessage)
47
+ # @param on_stderr [Proc, nil] Callback for stderr messages (receives OutputMessage)
48
+ # @param on_error [Proc, nil] Callback for execution errors (receives ExecutionError)
49
+ # @param envs [Hash<String, String>, nil] Environment variables for this execution
50
+ # @param timeout [Integer, nil] Timeout in seconds. 0 means no timeout. Default is 10 minutes.
51
+ # @return [Nightona::ExecutionResult]
52
+ # @raise [Nightona::Sdk::Error]
53
+ #
54
+ # @example
55
+ # def handle_stdout(msg)
56
+ # print "STDOUT: #{msg.output}"
57
+ # end
58
+ #
59
+ # def handle_stderr(msg)
60
+ # print "STDERR: #{msg.output}"
61
+ # end
62
+ #
63
+ # def handle_error(err)
64
+ # puts "ERROR: #{err.name}: #{err.value}"
65
+ # end
66
+ #
67
+ # code = <<~PYTHON
68
+ # import sys
69
+ # import time
70
+ # for i in range(5):
71
+ # print(i)
72
+ # time.sleep(1)
73
+ # sys.stderr.write("Counting done!")
74
+ # PYTHON
75
+ #
76
+ # result = sandbox.code_interpreter.run_code(
77
+ # code,
78
+ # on_stdout: method(:handle_stdout),
79
+ # on_stderr: method(:handle_stderr),
80
+ # on_error: method(:handle_error),
81
+ # timeout: 10
82
+ # )
83
+ def run_code(code, context: nil, on_stdout: nil, on_stderr: nil, on_error: nil, envs: nil, timeout: nil) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/ParameterLists
84
+ # Get WebSocket URL via preview link
85
+ preview_link = @get_preview_link.call(WS_PORT)
86
+ url = URI.parse(preview_link.url)
87
+ url.scheme = url.scheme == 'https' ? 'wss' : 'ws'
88
+ url.path = '/process/interpreter/execute'
89
+ ws_url = url.to_s
90
+
91
+ result = ExecutionResult.new
92
+
93
+ # Create request payload
94
+ request = { code: }
95
+ request[:contextId] = context.id if context
96
+ request[:envs] = envs if envs
97
+ request[:timeout] = timeout if timeout
98
+
99
+ # Build headers with preview token
100
+ headers = @toolbox_api.api_client.default_headers.dup.merge(
101
+ 'X-Nightona-Preview-Token' => preview_link.token,
102
+ 'Content-Type' => 'application/json',
103
+ 'Accept' => 'application/json'
104
+ )
105
+
106
+ completion_queue = Queue.new
107
+ interpreter = self
108
+
109
+ puts "[DEBUG] Connecting to WebSocket: #{ws_url}" if ENV['DEBUG']
110
+
111
+ ws = WebSocket::Client::Simple.connect(ws_url, headers:) do |client|
112
+ client.on :open do
113
+ puts '[DEBUG] WebSocket opened, sending request' if ENV['DEBUG']
114
+ client.send(JSON.dump(request))
115
+ end
116
+
117
+ client.on :message do |msg|
118
+ puts "[DEBUG] Received message (length=#{msg.data.length}): #{msg.data.inspect[0..200]}" if ENV['DEBUG']
119
+
120
+ interpreter.send(:handle_message, msg.data, result, on_stdout, on_stderr, on_error, completion_queue)
121
+ end
122
+
123
+ client.on :error do |e|
124
+ puts "[DEBUG] WebSocket error: #{e.message}" if ENV['DEBUG']
125
+ completion_queue.push({ type: :error, error: e })
126
+ end
127
+
128
+ client.on :close do |e|
129
+ if ENV['DEBUG']
130
+ code = e&.code || 'nil'
131
+ reason = e&.reason || 'nil'
132
+ puts "[DEBUG] WebSocket closed: code=#{code}, reason=#{reason}"
133
+ end
134
+ error_info = interpreter.send(:handle_close, e)
135
+ if error_info
136
+ completion_queue.push({ type: :error_from_close, error: error_info })
137
+ else
138
+ completion_queue.push({ type: :close })
139
+ end
140
+ end
141
+ end
142
+
143
+ no_timeout = timeout.is_a?(Numeric) && timeout <= 0
144
+ max_wait = no_timeout ? nil : (timeout || 600) + 30
145
+ start_time = Time.now
146
+ completion_reason = nil
147
+
148
+ loop do
149
+ if max_wait
150
+ remaining = max_wait - (Time.now - start_time)
151
+ if remaining <= 0
152
+ ws.close
153
+ raise Sdk::TimeoutError,
154
+ 'Execution timed out: operation exceeded the configured `timeout`. Provide a larger value if needed.'
155
+ end
156
+ end
157
+
158
+ completion = completion_queue.pop(timeout: max_wait ? remaining : nil)
159
+
160
+ if completion.nil?
161
+ ws.close
162
+ raise Sdk::TimeoutError,
163
+ 'Execution timed out: operation exceeded the configured `timeout`. Provide a larger value if needed.'
164
+ end
165
+
166
+ puts "[DEBUG] Got completion signal: #{completion[:type]}" if ENV['DEBUG']
167
+
168
+ if completion[:type] == :completed
169
+ completion_reason = :completed
170
+ break
171
+ elsif completion[:type] == :error_from_close
172
+ error_msg = completion[:error]
173
+ if error_msg.include?('timed out') || error_msg.include?('Execution timed out')
174
+ raise Sdk::TimeoutError, error_msg
175
+ end
176
+
177
+ raise Sdk::Error, error_msg
178
+ elsif completion[:type] == :close
179
+ elapsed = Time.now - start_time
180
+ if timeout && timeout > 0 && elapsed >= timeout && elapsed < (timeout + 2)
181
+ raise Sdk::TimeoutError,
182
+ 'Execution timed out: operation exceeded the configured `timeout`. Provide a larger value if needed.'
183
+ end
184
+ completion_reason = :close
185
+ break
186
+ elsif completion[:type] == :error
187
+ unless completion[:error].message.include?('stream closed')
188
+ raise Sdk::Error, "WebSocket error: #{completion[:error].message}"
189
+ end
190
+
191
+ completion_reason = :close
192
+ break
193
+ end
194
+ end
195
+
196
+ ws.close if completion_reason != :close
197
+ sleep 0.05
198
+
199
+ result
200
+ rescue Sdk::Error
201
+ # Re-raise SDK errors as-is
202
+ raise
203
+ rescue StandardError => e
204
+ # Wrap unexpected errors
205
+ raise Sdk::Error, "Failed to run code: #{e.message}"
206
+ end
207
+
208
+ # Create a new isolated interpreter context.
209
+ #
210
+ # Contexts provide isolated execution environments with their own global namespace.
211
+ # Variables, imports, and functions defined in one context don't affect others.
212
+ #
213
+ # @param cwd [String, nil] Working directory for the context
214
+ # @return [NightonaToolboxApiClient::InterpreterContext]
215
+ # @raise [Nightona::Sdk::Error]
216
+ #
217
+ # @example
218
+ # # Create isolated context
219
+ # ctx = sandbox.code_interpreter.create_context
220
+ #
221
+ # # Execute code in this context
222
+ # sandbox.code_interpreter.run_code("x = 100", context: ctx)
223
+ #
224
+ # # Variable only exists in this context
225
+ # result = sandbox.code_interpreter.run_code("print(x)", context: ctx) # OK
226
+ #
227
+ # # Won't see the variable in default context
228
+ # result = sandbox.code_interpreter.run_code("print(x)") # NameError
229
+ #
230
+ # # Clean up
231
+ # sandbox.code_interpreter.delete_context(ctx)
232
+ def create_context(cwd: nil)
233
+ request = NightonaToolboxApiClient::CreateContextRequest.new(cwd:)
234
+ @toolbox_api.create_interpreter_context(request)
235
+ rescue StandardError => e
236
+ raise Sdk::Error, "Failed to create interpreter context: #{e.message}"
237
+ end
238
+
239
+ # List all user-created interpreter contexts.
240
+ #
241
+ # The default context is not included in this list. Only contexts created
242
+ # via `create_context` are returned.
243
+ #
244
+ # @return [Array<NightonaToolboxApiClient::InterpreterContext>]
245
+ # @raise [Nightona::Sdk::Error]
246
+ #
247
+ # @example
248
+ # contexts = sandbox.code_interpreter.list_contexts
249
+ # contexts.each do |ctx|
250
+ # puts "Context #{ctx.id}: #{ctx.language} at #{ctx.cwd}"
251
+ # end
252
+ def list_contexts
253
+ response = @toolbox_api.list_interpreter_contexts
254
+ response.contexts || []
255
+ rescue StandardError => e
256
+ raise Sdk::Error, "Failed to list interpreter contexts: #{e.message}"
257
+ end
258
+
259
+ # Delete an interpreter context and shut down all associated processes.
260
+ #
261
+ # This permanently removes the context and all its state (variables, imports, etc.).
262
+ # The default context cannot be deleted.
263
+ #
264
+ # @param context [NightonaToolboxApiClient::InterpreterContext]
265
+ # @return [void]
266
+ # @raise [Nightona::Sdk::Error]
267
+ #
268
+ # @example
269
+ # ctx = sandbox.code_interpreter.create_context
270
+ # # ... use context ...
271
+ # sandbox.code_interpreter.delete_context(ctx)
272
+ def delete_context(context)
273
+ @toolbox_api.delete_interpreter_context(context.id)
274
+ nil
275
+ rescue StandardError => e
276
+ raise Sdk::Error, "Failed to delete interpreter context: #{e.message}"
277
+ end
278
+
279
+ instrument :run_code, :create_context, :list_contexts, :delete_context,
280
+ component: 'CodeInterpreter'
281
+
282
+ private
283
+
284
+ # @return [Nightona::OtelState, nil]
285
+ attr_reader :otel_state
286
+
287
+ # @return [Hash<String, String>]
288
+ def build_headers
289
+ headers = {}
290
+ @toolbox_api.api_client.update_params_for_auth!(headers, nil, ['bearer'])
291
+ headers
292
+ end
293
+
294
+ # @param data [String]
295
+ # @param result [Nightona::ExecutionResult]
296
+ # @param on_stdout [Proc, nil]
297
+ # @param on_stderr [Proc, nil]
298
+ # @param on_error [Proc, nil]
299
+ # @param completion_queue [Queue, nil] Queue to signal completion
300
+ # @return [void]
301
+ def handle_message(data, result, on_stdout, on_stderr, on_error, completion_queue = nil) # rubocop:disable Metrics/AbcSize, Metrics/ParameterLists
302
+ # Empty messages are just keepalives or noise, ignore them
303
+ if data.nil? || data.empty?
304
+ puts '[DEBUG] Received empty message, ignoring' if ENV['DEBUG']
305
+ return
306
+ end
307
+
308
+ chunk = JSON.parse(data)
309
+ chunk_type = chunk['type']
310
+
311
+ case chunk_type
312
+ when 'stdout'
313
+ stdout = chunk['text'] || ''
314
+ result.stdout += stdout
315
+ on_stdout&.call(OutputMessage.new(output: stdout))
316
+ when 'stderr'
317
+ stderr = chunk['text'] || ''
318
+ result.stderr += stderr
319
+ on_stderr&.call(OutputMessage.new(output: stderr))
320
+ when 'error'
321
+ error = ExecutionError.new(
322
+ name: chunk['name'] || '',
323
+ value: chunk['value'] || '',
324
+ traceback: chunk['traceback'] || ''
325
+ )
326
+ result.error = error
327
+ on_error&.call(error)
328
+ when 'control'
329
+ control_text = chunk['text'] || ''
330
+ if %w[completed interrupted].include?(control_text)
331
+ puts "[DEBUG] Received control message: #{control_text}" if ENV['DEBUG']
332
+ completion_queue&.push({ type: :completed })
333
+ end
334
+ end
335
+ rescue JSON::ParserError => e
336
+ # Skip malformed messages
337
+ warn "Warning: Failed to parse message: #{e.message}" if ENV['DEBUG']
338
+ end
339
+
340
+ # @param event [Object]
341
+ # @return [void]
342
+ def handle_close(event)
343
+ return nil unless event # Skip if event is nil (manual close)
344
+
345
+ code = event.respond_to?(:code) ? event.code : nil
346
+ reason = event.respond_to?(:reason) ? event.reason : nil
347
+
348
+ if code == WEBSOCKET_TIMEOUT_CODE
349
+ return 'Execution timed out: operation exceeded the configured `timeout`. Provide a larger value if needed.'
350
+ end
351
+
352
+ return nil if code == 1000 || code.nil? # Normal closure or no code
353
+
354
+ detail = reason.to_s.empty? ? 'WebSocket connection closed unexpectedly' : reason.to_s
355
+ detail = "#{detail} (close code #{code})" if code
356
+ detail
357
+ end
358
+ end
359
+ end
@@ -0,0 +1,124 @@
1
+ # Copyright Daytona Platforms Inc.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ # frozen_string_literal: true
5
+
6
+ module Nightona
7
+ ChartElement = NightonaToolboxApiClient::ChartElement
8
+
9
+ module ChartType
10
+ LINE = 'line'
11
+ SCATTER = 'scatter'
12
+ BAR = 'bar'
13
+ PIE = 'pie'
14
+ BOX_AND_WHISKER = 'box_and_whisker'
15
+ COMPOSITE_CHART = 'composite_chart'
16
+ UNKNOWN = 'unknown'
17
+ end
18
+
19
+ PointData = Struct.new(:label, :points, keyword_init: true)
20
+ BarData = Struct.new(:label, :value, :group, keyword_init: true)
21
+ PieData = Struct.new(:label, :angle, :radius, keyword_init: true)
22
+ BoxAndWhiskerData = Struct.new(:label, :min, :first_quartile, :median, :third_quartile, :max, :outliers,
23
+ keyword_init: true)
24
+
25
+ Chart = Struct.new(:type, :title, :png, :elements, keyword_init: true) do
26
+ def initialize(type: nil, title: nil, png: nil, elements: [])
27
+ super
28
+ end
29
+ end
30
+
31
+ Chart2D = Struct.new(:type, :title, :png, :elements, :x_label, :y_label, keyword_init: true) do
32
+ def initialize(type: nil, title: nil, png: nil, elements: [], x_label: nil, y_label: nil)
33
+ super
34
+ end
35
+ end
36
+
37
+ PointChart = Struct.new(:type, :title, :png, :elements, :x_label, :y_label,
38
+ :x_ticks, :y_ticks, :x_tick_labels, :y_tick_labels,
39
+ :x_scale, :y_scale, keyword_init: true) do
40
+ def initialize(type: nil, title: nil, png: nil, elements: [], x_label: nil, y_label: nil,
41
+ x_ticks: nil, y_ticks: nil, x_tick_labels: nil, y_tick_labels: nil,
42
+ x_scale: nil, y_scale: nil)
43
+ super
44
+ end
45
+ end
46
+
47
+ class LineChart < PointChart
48
+ end
49
+
50
+ class ScatterChart < PointChart
51
+ end
52
+
53
+ class BarChart < Chart2D
54
+ end
55
+
56
+ class PieChart < Chart
57
+ end
58
+
59
+ class BoxAndWhiskerChart < Chart2D
60
+ end
61
+
62
+ class CompositeChart < Chart
63
+ end
64
+
65
+ module Charts
66
+ Chart = Nightona::Chart
67
+ ChartElement = Nightona::ChartElement
68
+ ChartType = Nightona::ChartType
69
+ LineChart = Nightona::LineChart
70
+ ScatterChart = Nightona::ScatterChart
71
+ BarChart = Nightona::BarChart
72
+ PieChart = Nightona::PieChart
73
+ BoxAndWhiskerChart = Nightona::BoxAndWhiskerChart
74
+ CompositeChart = Nightona::CompositeChart
75
+ PointData = Nightona::PointData
76
+ BarData = Nightona::BarData
77
+ PieData = Nightona::PieData
78
+ BoxAndWhiskerData = Nightona::BoxAndWhiskerData
79
+
80
+ def self.parse_chart(chart)
81
+ type = chart.type || ChartType::UNKNOWN
82
+ elements = (chart.elements || []).map { |el| map_element(el, type) }
83
+ common = { type: chart.type, title: chart.title, png: chart.png, elements: elements }
84
+
85
+ case type
86
+ when ChartType::LINE
87
+ LineChart.new(x_label: chart.x_label, y_label: chart.y_label, x_ticks: chart.x_ticks, y_ticks: chart.y_ticks,
88
+ x_tick_labels: chart.x_tick_labels, y_tick_labels: chart.y_tick_labels,
89
+ x_scale: chart.x_scale, y_scale: chart.y_scale, **common)
90
+ when ChartType::SCATTER
91
+ ScatterChart.new(x_label: chart.x_label, y_label: chart.y_label, x_ticks: chart.x_ticks, y_ticks: chart.y_ticks,
92
+ x_tick_labels: chart.x_tick_labels, y_tick_labels: chart.y_tick_labels,
93
+ x_scale: chart.x_scale, y_scale: chart.y_scale, **common)
94
+ when ChartType::BAR
95
+ BarChart.new(x_label: chart.x_label, y_label: chart.y_label, **common)
96
+ when ChartType::PIE
97
+ PieChart.new(**common)
98
+ when ChartType::BOX_AND_WHISKER
99
+ BoxAndWhiskerChart.new(x_label: chart.x_label, y_label: chart.y_label, **common)
100
+ when ChartType::COMPOSITE_CHART
101
+ CompositeChart.new(**common)
102
+ else
103
+ Chart.new(**common)
104
+ end
105
+ end
106
+
107
+ def self.map_element(el, chart_type)
108
+ case chart_type
109
+ when ChartType::LINE, ChartType::SCATTER
110
+ PointData.new(label: el.label, points: el.points)
111
+ when ChartType::BAR
112
+ BarData.new(label: el.label, value: el.value, group: el.group)
113
+ when ChartType::PIE
114
+ PieData.new(label: el.label, angle: el.angle, radius: el.radius)
115
+ when ChartType::BOX_AND_WHISKER
116
+ BoxAndWhiskerData.new(label: el.label, min: el.min, first_quartile: el.first_quartile,
117
+ median: el.median, third_quartile: el.third_quartile,
118
+ max: el.max, outliers: el.outliers)
119
+ else
120
+ el
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,56 @@
1
+ # Copyright Daytona Platforms Inc.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ # frozen_string_literal: true
5
+
6
+ module Nightona
7
+ # Represents stdout or stderr output from code execution
8
+ class OutputMessage
9
+ # @return [String] The output content
10
+ attr_reader :output
11
+
12
+ # @param output [String]
13
+ def initialize(output:)
14
+ @output = output
15
+ end
16
+ end
17
+
18
+ # Represents an error that occurred during code execution
19
+ class ExecutionError
20
+ # @return [String] The error type/class name (e.g., "ValueError", "SyntaxError")
21
+ attr_reader :name
22
+
23
+ # @return [String] The error value
24
+ attr_reader :value
25
+
26
+ # @return [String] Full traceback of the error
27
+ attr_reader :traceback
28
+
29
+ # @param name [String]
30
+ # @param value [String]
31
+ # @param traceback [String]
32
+ def initialize(name:, value:, traceback: '')
33
+ @name = name
34
+ @value = value
35
+ @traceback = traceback
36
+ end
37
+ end
38
+
39
+ # Result of code execution
40
+ class ExecutionResult
41
+ # @return [String] Standard output from the code execution
42
+ attr_accessor :stdout
43
+
44
+ # @return [String] Standard error output from the code execution
45
+ attr_accessor :stderr
46
+
47
+ # @return [ExecutionError, nil] Error details if execution failed, nil otherwise
48
+ attr_accessor :error
49
+
50
+ def initialize(stdout: '', stderr: '', error: nil)
51
+ @stdout = stdout
52
+ @stderr = stderr
53
+ @error = error
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,14 @@
1
+ # Copyright Daytona Platforms Inc.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ # frozen_string_literal: true
5
+
6
+ module Nightona
7
+ module CodeLanguage
8
+ ALL = [
9
+ JAVASCRIPT = :javascript,
10
+ PYTHON = :python,
11
+ TYPESCRIPT = :typescript
12
+ ].freeze
13
+ end
14
+ end
@@ -0,0 +1,26 @@
1
+ # Copyright Daytona Platforms Inc.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ # frozen_string_literal: true
5
+
6
+ module Nightona
7
+ class FileUpload
8
+ # @return [String, IO] File contents as a string/bytes object or a local file path or IO object.
9
+ # If a string path is provided, the file content will be read from disk.
10
+ # If a string content is provided, make sure it fits into memory.
11
+ attr_reader :source
12
+
13
+ # @return [String] Absolute destination path in the Sandbox. Relative paths are resolved based on
14
+ # the sandbox working directory.
15
+ attr_reader :destination
16
+
17
+ # Initializes a new FileUpload instance.
18
+ #
19
+ # @param source [String, IO] File contents as a string/bytes object or a local file path or IO object.
20
+ # @param destination [String] Absolute destination path in the Sandbox.
21
+ def initialize(source, destination)
22
+ @source = source
23
+ @destination = destination
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,19 @@
1
+ # Copyright Daytona Platforms Inc.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ # frozen_string_literal: true
5
+
6
+ module Nightona
7
+ # Response from the git commit.
8
+ class GitCommitResponse
9
+ # @return [String] The SHA of the commit
10
+ attr_reader :sha
11
+
12
+ # Initialize a new GitCommitResponse
13
+ #
14
+ # @param sha [String] The SHA of the commit
15
+ def initialize(sha:)
16
+ @sha = sha
17
+ end
18
+ end
19
+ end