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,334 @@
1
+ # Copyright Daytona Platforms Inc.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ # frozen_string_literal: true
5
+
6
+ module Nightona
7
+ class Git
8
+ include Instrumentation
9
+
10
+ # @return [String] The Sandbox ID
11
+ attr_reader :sandbox_id
12
+
13
+ # @return [NightonaToolboxApiClient::GitApi] API client for Sandbox operations
14
+ attr_reader :toolbox_api
15
+
16
+ # Initializes a new Git handler instance.
17
+ #
18
+ # @param sandbox_id [String] The Sandbox ID.
19
+ # @param toolbox_api [NightonaToolboxApiClient::GitApi] API client for Sandbox operations.
20
+ # @param otel_state [Nightona::OtelState, nil]
21
+ def initialize(sandbox_id:, toolbox_api:, otel_state: nil)
22
+ @sandbox_id = sandbox_id
23
+ @toolbox_api = toolbox_api
24
+ @otel_state = otel_state
25
+ end
26
+
27
+ # Stages the specified files for the next commit, similar to
28
+ # running 'git add' on the command line.
29
+ #
30
+ # @param path [String] Path to the Git repository root. Relative paths are resolved based on
31
+ # the sandbox working directory.
32
+ # @param files [Array<String>] List of file paths or directories to stage, relative to the repository root.
33
+ # @return [void]
34
+ # @raise [Nightona::Sdk::Error] if adding files fails
35
+ #
36
+ # @example
37
+ # # Stage a single file
38
+ # sandbox.git.add("workspace/repo", ["file.txt"])
39
+ #
40
+ # # Stage multiple files
41
+ # sandbox.git.add("workspace/repo", [
42
+ # "src/main.rb",
43
+ # "spec/main_spec.rb",
44
+ # "README.md"
45
+ # ])
46
+ def add(path, files)
47
+ toolbox_api.add_files(NightonaToolboxApiClient::GitAddRequest.new(path:, files:))
48
+ rescue NightonaToolboxApiClient::ApiError => e
49
+ raise map_api_error(e, 'Failed to add files')
50
+ rescue StandardError => e
51
+ raise Sdk::Error, "Failed to add files: #{e.message}"
52
+ end
53
+
54
+ # Lists branches in the repository.
55
+ #
56
+ # @param path [String] Path to the Git repository root. Relative paths are resolved based on
57
+ # the sandbox working directory.
58
+ # @return [NightonaApiClient::ListBranchResponse] List of branches in the repository.
59
+ # @raise [Nightona::Sdk::Error] if listing branches fails
60
+ #
61
+ # @example
62
+ # response = sandbox.git.branches("workspace/repo")
63
+ # puts "Branches: #{response.branches}"
64
+ def branches(path)
65
+ toolbox_api.list_branches(path)
66
+ rescue NightonaToolboxApiClient::ApiError => e
67
+ raise map_api_error(e, 'Failed to list branches')
68
+ rescue StandardError => e
69
+ raise Sdk::Error, "Failed to list branches: #{e.message}"
70
+ end
71
+
72
+ # Clones a Git repository into the specified path. It supports
73
+ # cloning specific branches or commits, and can authenticate with the remote
74
+ # repository if credentials are provided.
75
+ #
76
+ # @param url [String] Repository URL to clone from.
77
+ # @param path [String] Path where the repository should be cloned. Relative paths are resolved
78
+ # based on the sandbox working directory.
79
+ # @param branch [String, nil] Specific branch to clone. If not specified,
80
+ # clones the default branch.
81
+ # @param commit_id [String, nil] Specific commit to clone. If specified,
82
+ # the repository will be left in a detached HEAD state at this commit.
83
+ # @param username [String, nil] Git username for authentication.
84
+ # @param password [String, nil] Git password or token for authentication.
85
+ # @param insecure_skip_tls [Boolean, nil] Skip TLS certificate verification (insecure).
86
+ # Use only for trusted internal Git servers with self-signed or private-CA certs;
87
+ # credentials, if supplied, are transmitted over an unverified TLS connection.
88
+ # @return [void]
89
+ # @raise [Nightona::Sdk::Error] if cloning repository fails
90
+ #
91
+ # @example
92
+ # # Clone the default branch
93
+ # sandbox.git.clone(
94
+ # url: "https://github.com/user/repo.git",
95
+ # path: "workspace/repo"
96
+ # )
97
+ #
98
+ # # Clone a specific branch with authentication
99
+ # sandbox.git.clone(
100
+ # url: "https://github.com/user/private-repo.git",
101
+ # path: "workspace/private",
102
+ # branch: "develop",
103
+ # username: "user",
104
+ # password: "token"
105
+ # )
106
+ #
107
+ # # Clone a specific commit
108
+ # sandbox.git.clone(
109
+ # url: "https://github.com/user/repo.git",
110
+ # path: "workspace/repo-old",
111
+ # commit_id: "abc123"
112
+ # )
113
+ def clone(url:, path:, branch: nil, commit_id: nil, username: nil, password: nil, insecure_skip_tls: nil) # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
114
+ toolbox_api.clone_repository(
115
+ NightonaToolboxApiClient::GitCloneRequest.new(
116
+ url: url,
117
+ branch: branch,
118
+ path: path,
119
+ username: username,
120
+ password: password,
121
+ commit_id: commit_id,
122
+ insecure_skip_tls: insecure_skip_tls
123
+ )
124
+ )
125
+ rescue NightonaToolboxApiClient::ApiError => e
126
+ raise map_api_error(e, 'Failed to clone repository')
127
+ rescue StandardError => e
128
+ raise Sdk::Error, "Failed to clone repository: #{e.message}"
129
+ end
130
+
131
+ # Creates a new commit with the staged changes. Make sure to stage
132
+ # changes using the add() method before committing.
133
+ #
134
+ # @param path [String] Path to the Git repository root. Relative paths are resolved based on
135
+ # the sandbox working directory.
136
+ # @param message [String] Commit message describing the changes.
137
+ # @param author [String] Name of the commit author.
138
+ # @param email [String] Email address of the commit author.
139
+ # @param allow_empty [Boolean] Allow creating an empty commit when no changes are staged. Defaults to false.
140
+ # @return [GitCommitResponse] Response containing the commit SHA.
141
+ # @raise [Nightona::Sdk::Error] if committing changes fails
142
+ #
143
+ # @example
144
+ # # Stage and commit changes
145
+ # sandbox.git.add("workspace/repo", ["README.md"])
146
+ # commit_response = sandbox.git.commit(
147
+ # path: "workspace/repo",
148
+ # message: "Update documentation",
149
+ # author: "John Doe",
150
+ # email: "john@example.com",
151
+ # allow_empty: true
152
+ # )
153
+ # puts "Commit SHA: #{commit_response.sha}"
154
+ def commit(path:, message:, author:, email:, allow_empty: false)
155
+ response = toolbox_api.commit_changes(
156
+ NightonaToolboxApiClient::GitCommitRequest.new(path:, message:, author:, email:, allow_empty:)
157
+ )
158
+ GitCommitResponse.new(sha: response._hash)
159
+ rescue NightonaToolboxApiClient::ApiError => e
160
+ raise map_api_error(e, 'Failed to commit changes')
161
+ rescue StandardError => e
162
+ raise Sdk::Error, "Failed to commit changes: #{e.message}"
163
+ end
164
+
165
+ # Pushes all local commits on the current branch to the remote
166
+ # repository. If the remote repository requires authentication, provide
167
+ # username and password/token.
168
+ #
169
+ # @param path [String] Path to the Git repository root. Relative paths are resolved based on
170
+ # the sandbox working directory.
171
+ # @param username [String, nil] Git username for authentication.
172
+ # @param password [String, nil] Git password or token for authentication.
173
+ # @return [void]
174
+ # @raise [Nightona::Sdk::Error] if pushing changes fails
175
+ #
176
+ # @example
177
+ # # Push without authentication (for public repos or SSH)
178
+ # sandbox.git.push("workspace/repo")
179
+ #
180
+ # # Push with authentication
181
+ # sandbox.git.push(
182
+ # path: "workspace/repo",
183
+ # username: "user",
184
+ # password: "github_token"
185
+ # )
186
+ def push(path:, username: nil, password: nil)
187
+ toolbox_api.push_changes(
188
+ NightonaToolboxApiClient::GitRepoRequest.new(path:, username:, password:)
189
+ )
190
+ rescue NightonaToolboxApiClient::ApiError => e
191
+ raise map_api_error(e, 'Failed to push changes')
192
+ rescue StandardError => e
193
+ raise Sdk::Error, "Failed to push changes: #{e.message}"
194
+ end
195
+
196
+ # Pulls changes from the remote repository. If the remote repository requires authentication,
197
+ # provide username and password/token.
198
+ #
199
+ # @param path [String] Path to the Git repository root. Relative paths are resolved based on
200
+ # the sandbox working directory.
201
+ # @param username [String, nil] Git username for authentication.
202
+ # @param password [String, nil] Git password or token for authentication.
203
+ # @return [void]
204
+ # @raise [Nightona::Sdk::Error] if pulling changes fails
205
+ #
206
+ # @example
207
+ # # Pull without authentication
208
+ # sandbox.git.pull("workspace/repo")
209
+ #
210
+ # # Pull with authentication
211
+ # sandbox.git.pull(
212
+ # path: "workspace/repo",
213
+ # username: "user",
214
+ # password: "github_token"
215
+ # )
216
+ #
217
+ def pull(path:, username: nil, password: nil)
218
+ toolbox_api.pull_changes(
219
+ NightonaToolboxApiClient::GitRepoRequest.new(path:, username:, password:)
220
+ )
221
+ rescue NightonaToolboxApiClient::ApiError => e
222
+ raise map_api_error(e, 'Failed to pull changes')
223
+ rescue StandardError => e
224
+ raise Sdk::Error, "Failed to pull changes: #{e.message}"
225
+ end
226
+
227
+ # Gets the current Git repository status.
228
+ #
229
+ # @param path [String] Path to the Git repository root. Relative paths are resolved based on
230
+ # the sandbox working directory.
231
+ # @return [NightonaToolboxApiClient::GitStatus] Repository status information including:
232
+ # @raise [Nightona::Sdk::Error] if getting status fails
233
+ #
234
+ # @example
235
+ # status = sandbox.git.status("workspace/repo")
236
+ # puts "On branch: #{status.current_branch}"
237
+ # puts "Commits ahead: #{status.ahead}"
238
+ # puts "Commits behind: #{status.behind}"
239
+ def status(path)
240
+ toolbox_api.get_status(path)
241
+ rescue NightonaToolboxApiClient::ApiError => e
242
+ raise map_api_error(e, 'Failed to get status')
243
+ rescue StandardError => e
244
+ raise Sdk::Error, "Failed to get status: #{e.message}"
245
+ end
246
+
247
+ # Checkout branch in the repository.
248
+ #
249
+ # @param path [String] Path to the Git repository root. Relative paths are resolved based on
250
+ # the sandbox working directory.
251
+ # @param branch [String] Name of the branch to checkout
252
+ # @return [void]
253
+ # @raise [Nightona::Sdk::Error] if checking out branch fails
254
+ #
255
+ # @example
256
+ # # Checkout a branch
257
+ # sandbox.git.checkout_branch("workspace/repo", "feature-branch")
258
+ def checkout_branch(path, branch)
259
+ toolbox_api.checkout_branch(
260
+ NightonaToolboxApiClient::GitCheckoutRequest.new(path:, branch:)
261
+ )
262
+ rescue NightonaToolboxApiClient::ApiError => e
263
+ raise map_api_error(e, 'Failed to checkout branch')
264
+ rescue StandardError => e
265
+ raise Sdk::Error, "Failed to checkout branch: #{e.message}"
266
+ end
267
+
268
+ # Create branch in the repository.
269
+ #
270
+ # @param path [String] Path to the Git repository root. Relative paths are resolved based on
271
+ # the sandbox working directory.
272
+ # @param name [String] Name of the new branch to create
273
+ # @return [void]
274
+ # @raise [Nightona::Sdk::Error] if creating branch fails
275
+ #
276
+ # @example
277
+ # # Create a new branch
278
+ # sandbox.git.create_branch("workspace/repo", "new-feature")
279
+ #
280
+ def create_branch(path, name)
281
+ toolbox_api.create_branch(
282
+ NightonaToolboxApiClient::GitBranchRequest.new(path:, name:)
283
+ )
284
+ rescue NightonaToolboxApiClient::ApiError => e
285
+ raise map_api_error(e, 'Failed to create branch')
286
+ rescue StandardError => e
287
+ raise Sdk::Error, "Failed to create branch: #{e.message}"
288
+ end
289
+
290
+ # Delete branch in the repository.
291
+ #
292
+ # @param path [String] Path to the Git repository root. Relative paths are resolved based on
293
+ # the sandbox working directory.
294
+ # @param name [String] Name of the branch to delete
295
+ # @return [void]
296
+ # @raise [Nightona::Sdk::Error] if deleting branch fails
297
+ #
298
+ # @example
299
+ # # Delete a branch
300
+ # sandbox.git.delete_branch("workspace/repo", "old-feature")
301
+ def delete_branch(path, name)
302
+ toolbox_api.delete_branch(
303
+ NightonaToolboxApiClient::GitDeleteBranchRequest.new(path:, name:)
304
+ )
305
+ rescue NightonaToolboxApiClient::ApiError => e
306
+ raise map_api_error(e, 'Failed to delete branch')
307
+ rescue StandardError => e
308
+ raise Sdk::Error, "Failed to delete branch: #{e.message}"
309
+ end
310
+
311
+ instrument :add, :branches, :clone, :commit, :push, :pull, :status,
312
+ :checkout_branch, :create_branch, :delete_branch,
313
+ component: 'Git'
314
+
315
+ private
316
+
317
+ # @return [Nightona::OtelState, nil]
318
+ attr_reader :otel_state
319
+
320
+ def map_api_error(api_error, prefix)
321
+ msg = "#{prefix}: #{api_error.message}"
322
+ case api_error.code
323
+ when 400 then Sdk::ValidationError.new(msg)
324
+ when 401 then Sdk::AuthenticationError.new(msg)
325
+ when 403 then Sdk::ForbiddenError.new(msg)
326
+ when 404 then Sdk::NotFoundError.new(msg)
327
+ when 409 then Sdk::ConflictError.new(msg)
328
+ when 429 then Sdk::RateLimitError.new(msg)
329
+ when 500..599 then Sdk::ServerError.new(msg)
330
+ else Sdk::Error.new(msg)
331
+ end
332
+ end
333
+ end
334
+ end
@@ -0,0 +1,139 @@
1
+ # Copyright Daytona Platforms Inc.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ # frozen_string_literal: true
5
+
6
+ module Nightona
7
+ class LspServer
8
+ include Instrumentation
9
+
10
+ module Language
11
+ ALL = [
12
+ JAVASCRIPT = :javascript,
13
+ PYTHON = :python,
14
+ TYPESCRIPT = :typescript
15
+
16
+ ].freeze
17
+ end
18
+
19
+ # Represents a zero-based position in a text document,
20
+ # specified by line number and character offset.
21
+ Position = Data.define(:line, :character)
22
+
23
+ # @return [Symbol]
24
+ attr_reader :language_id
25
+
26
+ # @return [String]
27
+ attr_reader :path_to_project
28
+
29
+ # @return [NightonaToolboxApiClient::LspApi]
30
+ attr_reader :toolbox_api
31
+
32
+ # @return [String]
33
+ attr_reader :sandbox_id
34
+
35
+ # @param language_id [Symbol]
36
+ # @param path_to_project [String]
37
+ # @param toolbox_api [NightonaToolboxApiClient::LspApi]
38
+ # @param sandbox_id [String]
39
+ # @param otel_state [Nightona::OtelState, nil]
40
+ def initialize(language_id:, path_to_project:, toolbox_api:, sandbox_id:, otel_state: nil)
41
+ @language_id = language_id
42
+ @path_to_project = path_to_project
43
+ @toolbox_api = toolbox_api
44
+ @sandbox_id = sandbox_id
45
+ @otel_state = otel_state
46
+ end
47
+
48
+ # Gets completion suggestions at a position in a file
49
+ #
50
+ # @param path [String]
51
+ # @param position [Nightona::LspServer::Position]
52
+ # @return [NightonaApiClient::CompletionList]
53
+ def completions(path:, position:)
54
+ toolbox_api.completions(
55
+ NightonaToolboxApiClient::LspCompletionParams.new(
56
+ language_id:,
57
+ path_to_project:,
58
+ uri: uri(path),
59
+ position: NightonaApiClient::Position.new(line: position.line, character: position.character)
60
+ )
61
+ )
62
+ end
63
+
64
+ # Notify the language server that a file has been closed.
65
+ # This method should be called when a file is closed in the editor to allow
66
+ # the language server to clean up any resources associated with that file.
67
+ #
68
+ # @param path [String]
69
+ # @return [void]
70
+ def did_close(path)
71
+ toolbox_api.did_close(
72
+ NightonaToolboxApiClient::LspDocumentRequest.new(language_id:, path_to_project:, uri: uri(path))
73
+ )
74
+ end
75
+
76
+ # Notifies the language server that a file has been opened.
77
+ # This method should be called when a file is opened in the editor to enable
78
+ # language features like diagnostics and completions for that file. The server
79
+ # will begin tracking the file's contents and providing language features.
80
+ #
81
+ # @param path [String]
82
+ # @return [void]
83
+ def did_open(path)
84
+ toolbox_api.did_open(
85
+ NightonaToolboxApiClient::LspDocumentRequest.new(language_id:, path_to_project:, uri: uri(path))
86
+ )
87
+ end
88
+
89
+ # Gets symbol information (functions, classes, variables, etc.) from a document.
90
+ #
91
+ # @param path [String]
92
+ # @return [Array<NightonaToolboxApiClient::LspSymbol]
93
+ def document_symbols(path) = toolbox_api.document_symbols(language_id, path_to_project, uri(path))
94
+
95
+ # Searches for symbols matching the query string across all files
96
+ # in the Sandbox.
97
+ #
98
+ # @param query [String]
99
+ # @return [Array<NightonaToolboxApiClient::LspSymbol]
100
+ def sandbox_symbols(query) = toolbox_api.workspace_symbols(query, language_id, path_to_project)
101
+
102
+ # Starts the language server.
103
+ # This method must be called before using any other LSP functionality.
104
+ # It initializes the language server for the specified language and project.
105
+ #
106
+ # @return [void]
107
+ def start
108
+ toolbox_api.start(
109
+ NightonaToolboxApiClient::LspServerRequest.new(language_id:, path_to_project:)
110
+ )
111
+ end
112
+
113
+ # Stops the language server.
114
+ # This method should be called when the LSP server is no longer needed to
115
+ # free up system resources.
116
+ #
117
+ # @return [void]
118
+ def stop
119
+ toolbox_api.stop(
120
+ NightonaToolboxApiClient::LspServerRequest.new(language_id:, path_to_project:)
121
+ )
122
+ end
123
+
124
+ instrument :completions, :did_close, :did_open, :document_symbols, :sandbox_symbols,
125
+ :start, :stop,
126
+ component: 'LspServer'
127
+
128
+ private
129
+
130
+ # @return [Nightona::OtelState, nil]
131
+ attr_reader :otel_state
132
+
133
+ # Convert path to file uri.
134
+ #
135
+ # @param path [String]
136
+ # @return [String]
137
+ def uri(path) = "file://#{path}"
138
+ end
139
+ end