brainzlab 0.1.1 → 0.1.2
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 +8 -0
- data/lib/brainzlab/beacon/client.rb +209 -0
- data/lib/brainzlab/beacon/provisioner.rb +44 -0
- data/lib/brainzlab/beacon.rb +215 -0
- data/lib/brainzlab/configuration.rb +341 -3
- data/lib/brainzlab/cortex/cache.rb +59 -0
- data/lib/brainzlab/cortex/client.rb +141 -0
- data/lib/brainzlab/cortex/provisioner.rb +49 -0
- data/lib/brainzlab/cortex.rb +227 -0
- data/lib/brainzlab/dendrite/client.rb +232 -0
- data/lib/brainzlab/dendrite/provisioner.rb +44 -0
- data/lib/brainzlab/dendrite.rb +195 -0
- data/lib/brainzlab/devtools/assets/devtools.css +1106 -0
- data/lib/brainzlab/devtools/assets/devtools.js +322 -0
- data/lib/brainzlab/devtools/assets/logo.svg +6 -0
- data/lib/brainzlab/devtools/assets/templates/debug_panel.html.erb +500 -0
- data/lib/brainzlab/devtools/assets/templates/error_page.html.erb +1086 -0
- data/lib/brainzlab/devtools/data/collector.rb +248 -0
- data/lib/brainzlab/devtools/middleware/asset_server.rb +63 -0
- data/lib/brainzlab/devtools/middleware/database_handler.rb +180 -0
- data/lib/brainzlab/devtools/middleware/debug_panel.rb +126 -0
- data/lib/brainzlab/devtools/middleware/error_page.rb +376 -0
- data/lib/brainzlab/devtools/renderers/debug_panel_renderer.rb +155 -0
- data/lib/brainzlab/devtools/renderers/error_page_renderer.rb +94 -0
- data/lib/brainzlab/devtools.rb +75 -0
- data/lib/brainzlab/flux/buffer.rb +96 -0
- data/lib/brainzlab/flux/client.rb +70 -0
- data/lib/brainzlab/flux/provisioner.rb +57 -0
- data/lib/brainzlab/flux.rb +174 -0
- data/lib/brainzlab/instrumentation/active_record.rb +18 -1
- data/lib/brainzlab/instrumentation/aws.rb +179 -0
- data/lib/brainzlab/instrumentation/dalli.rb +108 -0
- data/lib/brainzlab/instrumentation/excon.rb +152 -0
- data/lib/brainzlab/instrumentation/good_job.rb +102 -0
- data/lib/brainzlab/instrumentation/resque.rb +115 -0
- data/lib/brainzlab/instrumentation/solid_queue.rb +198 -0
- data/lib/brainzlab/instrumentation/stripe.rb +164 -0
- data/lib/brainzlab/instrumentation/typhoeus.rb +104 -0
- data/lib/brainzlab/instrumentation.rb +72 -0
- data/lib/brainzlab/nerve/client.rb +217 -0
- data/lib/brainzlab/nerve/provisioner.rb +44 -0
- data/lib/brainzlab/nerve.rb +219 -0
- data/lib/brainzlab/pulse/instrumentation.rb +35 -2
- data/lib/brainzlab/pulse/propagation.rb +1 -1
- data/lib/brainzlab/pulse/tracer.rb +1 -1
- data/lib/brainzlab/pulse.rb +1 -1
- data/lib/brainzlab/rails/log_subscriber.rb +1 -2
- data/lib/brainzlab/rails/railtie.rb +36 -3
- data/lib/brainzlab/recall/provisioner.rb +17 -0
- data/lib/brainzlab/recall.rb +6 -1
- data/lib/brainzlab/reflex.rb +2 -2
- data/lib/brainzlab/sentinel/client.rb +218 -0
- data/lib/brainzlab/sentinel/provisioner.rb +44 -0
- data/lib/brainzlab/sentinel.rb +165 -0
- data/lib/brainzlab/signal/client.rb +62 -0
- data/lib/brainzlab/signal/provisioner.rb +55 -0
- data/lib/brainzlab/signal.rb +136 -0
- data/lib/brainzlab/synapse/client.rb +290 -0
- data/lib/brainzlab/synapse/provisioner.rb +44 -0
- data/lib/brainzlab/synapse.rb +270 -0
- data/lib/brainzlab/utilities/circuit_breaker.rb +265 -0
- data/lib/brainzlab/utilities/health_check.rb +296 -0
- data/lib/brainzlab/utilities/log_formatter.rb +256 -0
- data/lib/brainzlab/utilities/rate_limiter.rb +230 -0
- data/lib/brainzlab/utilities.rb +17 -0
- data/lib/brainzlab/vault/cache.rb +80 -0
- data/lib/brainzlab/vault/client.rb +198 -0
- data/lib/brainzlab/vault/provisioner.rb +49 -0
- data/lib/brainzlab/vault.rb +268 -0
- data/lib/brainzlab/version.rb +1 -1
- data/lib/brainzlab/vision/client.rb +128 -0
- data/lib/brainzlab/vision/provisioner.rb +136 -0
- data/lib/brainzlab/vision.rb +157 -0
- data/lib/brainzlab.rb +101 -0
- metadata +60 -1
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "net/http"
|
|
4
|
+
require "json"
|
|
5
|
+
require "uri"
|
|
6
|
+
|
|
7
|
+
module BrainzLab
|
|
8
|
+
module Synapse
|
|
9
|
+
class Client
|
|
10
|
+
def initialize(config)
|
|
11
|
+
@config = config
|
|
12
|
+
@base_url = config.synapse_url || "https://synapse.brainzlab.ai"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# List all projects
|
|
16
|
+
def list_projects(status: nil, page: 1, per_page: 20)
|
|
17
|
+
params = { page: page, per_page: per_page }
|
|
18
|
+
params[:status] = status if status
|
|
19
|
+
|
|
20
|
+
response = request(:get, "/api/v1/projects", params: params)
|
|
21
|
+
|
|
22
|
+
return [] unless response.is_a?(Net::HTTPSuccess)
|
|
23
|
+
|
|
24
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
|
25
|
+
data[:projects] || []
|
|
26
|
+
rescue StandardError => e
|
|
27
|
+
log_error("list_projects", e)
|
|
28
|
+
[]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Get project details
|
|
32
|
+
def get_project(project_id)
|
|
33
|
+
response = request(:get, "/api/v1/projects/#{project_id}")
|
|
34
|
+
|
|
35
|
+
return nil unless response.is_a?(Net::HTTPSuccess)
|
|
36
|
+
|
|
37
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
38
|
+
rescue StandardError => e
|
|
39
|
+
log_error("get_project", e)
|
|
40
|
+
nil
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Create a new project
|
|
44
|
+
def create_project(name:, repos: [], description: nil, **options)
|
|
45
|
+
response = request(
|
|
46
|
+
:post,
|
|
47
|
+
"/api/v1/projects",
|
|
48
|
+
body: {
|
|
49
|
+
name: name,
|
|
50
|
+
description: description,
|
|
51
|
+
repos: repos,
|
|
52
|
+
**options
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
return nil unless response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPCreated)
|
|
57
|
+
|
|
58
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
59
|
+
rescue StandardError => e
|
|
60
|
+
log_error("create_project", e)
|
|
61
|
+
nil
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Start project containers
|
|
65
|
+
def start_project(project_id)
|
|
66
|
+
response = request(:post, "/api/v1/projects/#{project_id}/up")
|
|
67
|
+
response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPAccepted)
|
|
68
|
+
rescue StandardError => e
|
|
69
|
+
log_error("start_project", e)
|
|
70
|
+
false
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Stop project containers
|
|
74
|
+
def stop_project(project_id)
|
|
75
|
+
response = request(:post, "/api/v1/projects/#{project_id}/down")
|
|
76
|
+
response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPAccepted)
|
|
77
|
+
rescue StandardError => e
|
|
78
|
+
log_error("stop_project", e)
|
|
79
|
+
false
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Restart project containers
|
|
83
|
+
def restart_project(project_id)
|
|
84
|
+
response = request(:post, "/api/v1/projects/#{project_id}/restart")
|
|
85
|
+
response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPAccepted)
|
|
86
|
+
rescue StandardError => e
|
|
87
|
+
log_error("restart_project", e)
|
|
88
|
+
false
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Deploy project to environment
|
|
92
|
+
def deploy(project_id, environment:, options: {})
|
|
93
|
+
response = request(
|
|
94
|
+
:post,
|
|
95
|
+
"/api/v1/projects/#{project_id}/deploy",
|
|
96
|
+
body: {
|
|
97
|
+
environment: environment,
|
|
98
|
+
**options
|
|
99
|
+
}
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
return nil unless response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPAccepted)
|
|
103
|
+
|
|
104
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
105
|
+
rescue StandardError => e
|
|
106
|
+
log_error("deploy", e)
|
|
107
|
+
nil
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Get deployment status
|
|
111
|
+
def get_deployment(deployment_id)
|
|
112
|
+
response = request(:get, "/api/v1/deployments/#{deployment_id}")
|
|
113
|
+
|
|
114
|
+
return nil unless response.is_a?(Net::HTTPSuccess)
|
|
115
|
+
|
|
116
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
117
|
+
rescue StandardError => e
|
|
118
|
+
log_error("get_deployment", e)
|
|
119
|
+
nil
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Create an AI task
|
|
123
|
+
def create_task(project_id:, description:, type: nil, priority: nil, **options)
|
|
124
|
+
response = request(
|
|
125
|
+
:post,
|
|
126
|
+
"/api/v1/tasks",
|
|
127
|
+
body: {
|
|
128
|
+
project_id: project_id,
|
|
129
|
+
description: description,
|
|
130
|
+
type: type,
|
|
131
|
+
priority: priority,
|
|
132
|
+
**options
|
|
133
|
+
}
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
return nil unless response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPCreated)
|
|
137
|
+
|
|
138
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
139
|
+
rescue StandardError => e
|
|
140
|
+
log_error("create_task", e)
|
|
141
|
+
nil
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Get task status
|
|
145
|
+
def get_task(task_id)
|
|
146
|
+
response = request(:get, "/api/v1/tasks/#{task_id}")
|
|
147
|
+
|
|
148
|
+
return nil unless response.is_a?(Net::HTTPSuccess)
|
|
149
|
+
|
|
150
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
151
|
+
rescue StandardError => e
|
|
152
|
+
log_error("get_task", e)
|
|
153
|
+
nil
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Get task status and progress
|
|
157
|
+
def get_task_status(task_id)
|
|
158
|
+
response = request(:get, "/api/v1/tasks/#{task_id}/status")
|
|
159
|
+
|
|
160
|
+
return nil unless response.is_a?(Net::HTTPSuccess)
|
|
161
|
+
|
|
162
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
163
|
+
rescue StandardError => e
|
|
164
|
+
log_error("get_task_status", e)
|
|
165
|
+
nil
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# List tasks for a project
|
|
169
|
+
def list_tasks(project_id: nil, status: nil, page: 1, per_page: 20)
|
|
170
|
+
params = { page: page, per_page: per_page }
|
|
171
|
+
params[:project_id] = project_id if project_id
|
|
172
|
+
params[:status] = status if status
|
|
173
|
+
|
|
174
|
+
response = request(:get, "/api/v1/tasks", params: params)
|
|
175
|
+
|
|
176
|
+
return [] unless response.is_a?(Net::HTTPSuccess)
|
|
177
|
+
|
|
178
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
|
179
|
+
data[:tasks] || []
|
|
180
|
+
rescue StandardError => e
|
|
181
|
+
log_error("list_tasks", e)
|
|
182
|
+
[]
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Cancel a running task
|
|
186
|
+
def cancel_task(task_id)
|
|
187
|
+
response = request(:post, "/api/v1/tasks/#{task_id}/cancel")
|
|
188
|
+
response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPAccepted)
|
|
189
|
+
rescue StandardError => e
|
|
190
|
+
log_error("cancel_task", e)
|
|
191
|
+
false
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Get container logs
|
|
195
|
+
def get_logs(project_id, container: nil, lines: 100, since: nil)
|
|
196
|
+
params = { lines: lines }
|
|
197
|
+
params[:container] = container if container
|
|
198
|
+
params[:since] = since if since
|
|
199
|
+
|
|
200
|
+
response = request(:get, "/api/v1/projects/#{project_id}/logs", params: params)
|
|
201
|
+
|
|
202
|
+
return nil unless response.is_a?(Net::HTTPSuccess)
|
|
203
|
+
|
|
204
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
205
|
+
rescue StandardError => e
|
|
206
|
+
log_error("get_logs", e)
|
|
207
|
+
nil
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Execute command in container
|
|
211
|
+
def exec(project_id, command:, container: nil, timeout: 30)
|
|
212
|
+
response = request(
|
|
213
|
+
:post,
|
|
214
|
+
"/api/v1/projects/#{project_id}/exec",
|
|
215
|
+
body: {
|
|
216
|
+
command: command,
|
|
217
|
+
container: container,
|
|
218
|
+
timeout: timeout
|
|
219
|
+
}
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
return nil unless response.is_a?(Net::HTTPSuccess)
|
|
223
|
+
|
|
224
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
225
|
+
rescue StandardError => e
|
|
226
|
+
log_error("exec", e)
|
|
227
|
+
nil
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def provision(project_id:, app_name:)
|
|
231
|
+
response = request(
|
|
232
|
+
:post,
|
|
233
|
+
"/api/v1/projects/provision",
|
|
234
|
+
body: { project_id: project_id, app_name: app_name },
|
|
235
|
+
use_service_key: true
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPCreated)
|
|
239
|
+
rescue StandardError => e
|
|
240
|
+
log_error("provision", e)
|
|
241
|
+
false
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
private
|
|
245
|
+
|
|
246
|
+
def request(method, path, headers: {}, body: nil, params: nil, use_service_key: false)
|
|
247
|
+
uri = URI.parse("#{@base_url}#{path}")
|
|
248
|
+
|
|
249
|
+
if params
|
|
250
|
+
uri.query = URI.encode_www_form(params)
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
254
|
+
http.use_ssl = uri.scheme == "https"
|
|
255
|
+
http.open_timeout = 10
|
|
256
|
+
http.read_timeout = 120 # Longer timeout for AI tasks
|
|
257
|
+
|
|
258
|
+
request = case method
|
|
259
|
+
when :get
|
|
260
|
+
Net::HTTP::Get.new(uri)
|
|
261
|
+
when :post
|
|
262
|
+
Net::HTTP::Post.new(uri)
|
|
263
|
+
when :put
|
|
264
|
+
Net::HTTP::Put.new(uri)
|
|
265
|
+
when :delete
|
|
266
|
+
Net::HTTP::Delete.new(uri)
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
request["Content-Type"] = "application/json"
|
|
270
|
+
request["Accept"] = "application/json"
|
|
271
|
+
|
|
272
|
+
if use_service_key
|
|
273
|
+
request["X-Service-Key"] = @config.synapse_master_key || @config.secret_key
|
|
274
|
+
else
|
|
275
|
+
auth_key = @config.synapse_api_key || @config.secret_key
|
|
276
|
+
request["Authorization"] = "Bearer #{auth_key}" if auth_key
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
headers.each { |k, v| request[k] = v }
|
|
280
|
+
request.body = body.to_json if body
|
|
281
|
+
|
|
282
|
+
http.request(request)
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def log_error(operation, error)
|
|
286
|
+
BrainzLab.debug_log("[Synapse::Client] #{operation} failed: #{error.message}")
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BrainzLab
|
|
4
|
+
module Synapse
|
|
5
|
+
class Provisioner
|
|
6
|
+
def initialize(config)
|
|
7
|
+
@config = config
|
|
8
|
+
@provisioned = false
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def ensure_project!
|
|
12
|
+
return if @provisioned
|
|
13
|
+
return unless @config.synapse_auto_provision
|
|
14
|
+
return unless valid_auth?
|
|
15
|
+
|
|
16
|
+
@provisioned = true
|
|
17
|
+
|
|
18
|
+
project_id = detect_project_id
|
|
19
|
+
return unless project_id
|
|
20
|
+
|
|
21
|
+
client = Client.new(@config)
|
|
22
|
+
client.provision(
|
|
23
|
+
project_id: project_id,
|
|
24
|
+
app_name: @config.app_name || @config.service
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
BrainzLab.debug_log("[Synapse::Provisioner] Project provisioned: #{project_id}")
|
|
28
|
+
rescue StandardError => e
|
|
29
|
+
BrainzLab.debug_log("[Synapse::Provisioner] Provisioning failed: #{e.message}")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def valid_auth?
|
|
35
|
+
key = @config.synapse_api_key || @config.synapse_master_key || @config.secret_key
|
|
36
|
+
!key.nil? && !key.empty?
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def detect_project_id
|
|
40
|
+
ENV["BRAINZLAB_PROJECT_ID"]
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "synapse/client"
|
|
4
|
+
require_relative "synapse/provisioner"
|
|
5
|
+
|
|
6
|
+
module BrainzLab
|
|
7
|
+
module Synapse
|
|
8
|
+
class << self
|
|
9
|
+
# List all projects
|
|
10
|
+
# @param status [String] Filter by status (running, stopped, deploying)
|
|
11
|
+
# @return [Array<Hash>] List of projects
|
|
12
|
+
#
|
|
13
|
+
# @example
|
|
14
|
+
# projects = BrainzLab::Synapse.projects(status: "running")
|
|
15
|
+
#
|
|
16
|
+
def projects(status: nil, page: 1, per_page: 20)
|
|
17
|
+
return [] unless enabled?
|
|
18
|
+
|
|
19
|
+
ensure_provisioned!
|
|
20
|
+
return [] unless BrainzLab.configuration.synapse_valid?
|
|
21
|
+
|
|
22
|
+
client.list_projects(status: status, page: page, per_page: per_page)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Get project details
|
|
26
|
+
# @param project_id [String] Project ID
|
|
27
|
+
# @return [Hash, nil] Project details
|
|
28
|
+
def project(project_id)
|
|
29
|
+
return nil unless enabled?
|
|
30
|
+
|
|
31
|
+
ensure_provisioned!
|
|
32
|
+
return nil unless BrainzLab.configuration.synapse_valid?
|
|
33
|
+
|
|
34
|
+
client.get_project(project_id)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Create a new project
|
|
38
|
+
# @param name [String] Project name
|
|
39
|
+
# @param repos [Array<Hash>] Repository configurations
|
|
40
|
+
# @param description [String] Project description
|
|
41
|
+
# @return [Hash, nil] Created project
|
|
42
|
+
#
|
|
43
|
+
# @example
|
|
44
|
+
# project = BrainzLab::Synapse.create_project(
|
|
45
|
+
# name: "My App",
|
|
46
|
+
# repos: [
|
|
47
|
+
# { url: "https://github.com/org/api", type: "rails" },
|
|
48
|
+
# { url: "https://github.com/org/frontend", type: "react" }
|
|
49
|
+
# ]
|
|
50
|
+
# )
|
|
51
|
+
#
|
|
52
|
+
def create_project(name:, repos: [], description: nil, **options)
|
|
53
|
+
return nil unless enabled?
|
|
54
|
+
|
|
55
|
+
ensure_provisioned!
|
|
56
|
+
return nil unless BrainzLab.configuration.synapse_valid?
|
|
57
|
+
|
|
58
|
+
client.create_project(name: name, repos: repos, description: description, **options)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Start project containers
|
|
62
|
+
# @param project_id [String] Project ID
|
|
63
|
+
# @return [Boolean] True if started
|
|
64
|
+
def up(project_id)
|
|
65
|
+
return false unless enabled?
|
|
66
|
+
|
|
67
|
+
ensure_provisioned!
|
|
68
|
+
return false unless BrainzLab.configuration.synapse_valid?
|
|
69
|
+
|
|
70
|
+
client.start_project(project_id)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Stop project containers
|
|
74
|
+
# @param project_id [String] Project ID
|
|
75
|
+
# @return [Boolean] True if stopped
|
|
76
|
+
def down(project_id)
|
|
77
|
+
return false unless enabled?
|
|
78
|
+
|
|
79
|
+
ensure_provisioned!
|
|
80
|
+
return false unless BrainzLab.configuration.synapse_valid?
|
|
81
|
+
|
|
82
|
+
client.stop_project(project_id)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Restart project containers
|
|
86
|
+
# @param project_id [String] Project ID
|
|
87
|
+
# @return [Boolean] True if restarted
|
|
88
|
+
def restart(project_id)
|
|
89
|
+
return false unless enabled?
|
|
90
|
+
|
|
91
|
+
ensure_provisioned!
|
|
92
|
+
return false unless BrainzLab.configuration.synapse_valid?
|
|
93
|
+
|
|
94
|
+
client.restart_project(project_id)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Deploy project to environment
|
|
98
|
+
# @param project_id [String] Project ID
|
|
99
|
+
# @param environment [Symbol] Target environment (:staging, :production)
|
|
100
|
+
# @param options [Hash] Deployment options
|
|
101
|
+
# @return [Hash, nil] Deployment info
|
|
102
|
+
#
|
|
103
|
+
# @example
|
|
104
|
+
# deployment = BrainzLab::Synapse.deploy(project_id, environment: :staging)
|
|
105
|
+
#
|
|
106
|
+
def deploy(project_id, environment:, **options)
|
|
107
|
+
return nil unless enabled?
|
|
108
|
+
|
|
109
|
+
ensure_provisioned!
|
|
110
|
+
return nil unless BrainzLab.configuration.synapse_valid?
|
|
111
|
+
|
|
112
|
+
client.deploy(project_id, environment: environment, options: options)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Get deployment status
|
|
116
|
+
# @param deployment_id [String] Deployment ID
|
|
117
|
+
# @return [Hash, nil] Deployment details
|
|
118
|
+
def deployment(deployment_id)
|
|
119
|
+
return nil unless enabled?
|
|
120
|
+
|
|
121
|
+
ensure_provisioned!
|
|
122
|
+
return nil unless BrainzLab.configuration.synapse_valid?
|
|
123
|
+
|
|
124
|
+
client.get_deployment(deployment_id)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Create an AI development task
|
|
128
|
+
# @param project_id [String] Project ID
|
|
129
|
+
# @param description [String] Task description in natural language
|
|
130
|
+
# @param type [Symbol] Task type (:feature, :bugfix, :refactor, :test)
|
|
131
|
+
# @param priority [Symbol] Priority (:low, :medium, :high, :urgent)
|
|
132
|
+
# @return [Hash, nil] Created task
|
|
133
|
+
#
|
|
134
|
+
# @example
|
|
135
|
+
# task = BrainzLab::Synapse.task(
|
|
136
|
+
# project_id: project_id,
|
|
137
|
+
# description: "Add user authentication with OAuth",
|
|
138
|
+
# type: :feature,
|
|
139
|
+
# priority: :high
|
|
140
|
+
# )
|
|
141
|
+
#
|
|
142
|
+
def task(project_id:, description:, type: nil, priority: nil, **options)
|
|
143
|
+
return nil unless enabled?
|
|
144
|
+
|
|
145
|
+
ensure_provisioned!
|
|
146
|
+
return nil unless BrainzLab.configuration.synapse_valid?
|
|
147
|
+
|
|
148
|
+
client.create_task(
|
|
149
|
+
project_id: project_id,
|
|
150
|
+
description: description,
|
|
151
|
+
type: type,
|
|
152
|
+
priority: priority,
|
|
153
|
+
**options
|
|
154
|
+
)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Get task details
|
|
158
|
+
# @param task_id [String] Task ID
|
|
159
|
+
# @return [Hash, nil] Task details
|
|
160
|
+
def get_task(task_id)
|
|
161
|
+
return nil unless enabled?
|
|
162
|
+
|
|
163
|
+
ensure_provisioned!
|
|
164
|
+
return nil unless BrainzLab.configuration.synapse_valid?
|
|
165
|
+
|
|
166
|
+
client.get_task(task_id)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Get task status and progress
|
|
170
|
+
# @param task_id [String] Task ID
|
|
171
|
+
# @return [Hash, nil] Task status with progress
|
|
172
|
+
def task_status(task_id)
|
|
173
|
+
return nil unless enabled?
|
|
174
|
+
|
|
175
|
+
ensure_provisioned!
|
|
176
|
+
return nil unless BrainzLab.configuration.synapse_valid?
|
|
177
|
+
|
|
178
|
+
client.get_task_status(task_id)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# List tasks
|
|
182
|
+
# @param project_id [String] Optional filter by project
|
|
183
|
+
# @param status [String] Filter by status (pending, running, completed, failed)
|
|
184
|
+
# @return [Array<Hash>] List of tasks
|
|
185
|
+
def tasks(project_id: nil, status: nil, page: 1, per_page: 20)
|
|
186
|
+
return [] unless enabled?
|
|
187
|
+
|
|
188
|
+
ensure_provisioned!
|
|
189
|
+
return [] unless BrainzLab.configuration.synapse_valid?
|
|
190
|
+
|
|
191
|
+
client.list_tasks(project_id: project_id, status: status, page: page, per_page: per_page)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Cancel a running task
|
|
195
|
+
# @param task_id [String] Task ID
|
|
196
|
+
# @return [Boolean] True if cancelled
|
|
197
|
+
def cancel_task(task_id)
|
|
198
|
+
return false unless enabled?
|
|
199
|
+
|
|
200
|
+
ensure_provisioned!
|
|
201
|
+
return false unless BrainzLab.configuration.synapse_valid?
|
|
202
|
+
|
|
203
|
+
client.cancel_task(task_id)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Get container logs
|
|
207
|
+
# @param project_id [String] Project ID
|
|
208
|
+
# @param container [String] Optional container name
|
|
209
|
+
# @param lines [Integer] Number of lines (default: 100)
|
|
210
|
+
# @param since [String] Start time (ISO8601)
|
|
211
|
+
# @return [Hash, nil] Log data
|
|
212
|
+
def logs(project_id, container: nil, lines: 100, since: nil)
|
|
213
|
+
return nil unless enabled?
|
|
214
|
+
|
|
215
|
+
ensure_provisioned!
|
|
216
|
+
return nil unless BrainzLab.configuration.synapse_valid?
|
|
217
|
+
|
|
218
|
+
client.get_logs(project_id, container: container, lines: lines, since: since)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Execute command in container
|
|
222
|
+
# @param project_id [String] Project ID
|
|
223
|
+
# @param command [String] Command to execute
|
|
224
|
+
# @param container [String] Optional container name
|
|
225
|
+
# @param timeout [Integer] Timeout in seconds
|
|
226
|
+
# @return [Hash, nil] Command output
|
|
227
|
+
#
|
|
228
|
+
# @example
|
|
229
|
+
# result = BrainzLab::Synapse.exec(project_id, command: "rails db:migrate")
|
|
230
|
+
#
|
|
231
|
+
def exec(project_id, command:, container: nil, timeout: 30)
|
|
232
|
+
return nil unless enabled?
|
|
233
|
+
|
|
234
|
+
ensure_provisioned!
|
|
235
|
+
return nil unless BrainzLab.configuration.synapse_valid?
|
|
236
|
+
|
|
237
|
+
client.exec(project_id, command: command, container: container, timeout: timeout)
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# === INTERNAL ===
|
|
241
|
+
|
|
242
|
+
def ensure_provisioned!
|
|
243
|
+
return if @provisioned
|
|
244
|
+
|
|
245
|
+
@provisioned = true
|
|
246
|
+
provisioner.ensure_project!
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def provisioner
|
|
250
|
+
@provisioner ||= Provisioner.new(BrainzLab.configuration)
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def client
|
|
254
|
+
@client ||= Client.new(BrainzLab.configuration)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def reset!
|
|
258
|
+
@client = nil
|
|
259
|
+
@provisioner = nil
|
|
260
|
+
@provisioned = false
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
private
|
|
264
|
+
|
|
265
|
+
def enabled?
|
|
266
|
+
BrainzLab.configuration.synapse_enabled
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
end
|