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
|
@@ -37,6 +37,70 @@ module BrainzLab
|
|
|
37
37
|
:pulse_flush_interval,
|
|
38
38
|
:pulse_sample_rate,
|
|
39
39
|
:pulse_excluded_paths,
|
|
40
|
+
:flux_enabled,
|
|
41
|
+
:flux_url,
|
|
42
|
+
:flux_api_key,
|
|
43
|
+
:flux_ingest_key,
|
|
44
|
+
:flux_master_key,
|
|
45
|
+
:flux_auto_provision,
|
|
46
|
+
:flux_buffer_size,
|
|
47
|
+
:flux_flush_interval,
|
|
48
|
+
:signal_enabled,
|
|
49
|
+
:signal_url,
|
|
50
|
+
:signal_api_key,
|
|
51
|
+
:signal_master_key,
|
|
52
|
+
:signal_auto_provision,
|
|
53
|
+
:vault_enabled,
|
|
54
|
+
:vault_url,
|
|
55
|
+
:vault_api_key,
|
|
56
|
+
:vault_master_key,
|
|
57
|
+
:vault_auto_provision,
|
|
58
|
+
:vault_cache_enabled,
|
|
59
|
+
:vault_cache_ttl,
|
|
60
|
+
:vault_auto_load,
|
|
61
|
+
:vault_load_provider_keys,
|
|
62
|
+
:vision_enabled,
|
|
63
|
+
:vision_url,
|
|
64
|
+
:vision_api_key,
|
|
65
|
+
:vision_ingest_key,
|
|
66
|
+
:vision_master_key,
|
|
67
|
+
:vision_auto_provision,
|
|
68
|
+
:vision_default_model,
|
|
69
|
+
:vision_default_browser_provider,
|
|
70
|
+
:cortex_enabled,
|
|
71
|
+
:cortex_url,
|
|
72
|
+
:cortex_api_key,
|
|
73
|
+
:cortex_master_key,
|
|
74
|
+
:cortex_auto_provision,
|
|
75
|
+
:cortex_cache_enabled,
|
|
76
|
+
:cortex_cache_ttl,
|
|
77
|
+
:cortex_default_context,
|
|
78
|
+
:beacon_enabled,
|
|
79
|
+
:beacon_url,
|
|
80
|
+
:beacon_api_key,
|
|
81
|
+
:beacon_master_key,
|
|
82
|
+
:beacon_auto_provision,
|
|
83
|
+
:nerve_enabled,
|
|
84
|
+
:nerve_url,
|
|
85
|
+
:nerve_api_key,
|
|
86
|
+
:nerve_master_key,
|
|
87
|
+
:nerve_auto_provision,
|
|
88
|
+
:dendrite_enabled,
|
|
89
|
+
:dendrite_url,
|
|
90
|
+
:dendrite_api_key,
|
|
91
|
+
:dendrite_master_key,
|
|
92
|
+
:dendrite_auto_provision,
|
|
93
|
+
:sentinel_enabled,
|
|
94
|
+
:sentinel_url,
|
|
95
|
+
:sentinel_api_key,
|
|
96
|
+
:sentinel_agent_key,
|
|
97
|
+
:sentinel_master_key,
|
|
98
|
+
:sentinel_auto_provision,
|
|
99
|
+
:synapse_enabled,
|
|
100
|
+
:synapse_url,
|
|
101
|
+
:synapse_api_key,
|
|
102
|
+
:synapse_master_key,
|
|
103
|
+
:synapse_auto_provision,
|
|
40
104
|
:scrub_fields,
|
|
41
105
|
:logger,
|
|
42
106
|
:instrument_http,
|
|
@@ -49,13 +113,39 @@ module BrainzLab
|
|
|
49
113
|
:instrument_action_mailer,
|
|
50
114
|
:instrument_delayed_job,
|
|
51
115
|
:instrument_grape,
|
|
116
|
+
:instrument_solid_queue,
|
|
117
|
+
:instrument_good_job,
|
|
118
|
+
:instrument_resque,
|
|
119
|
+
:instrument_excon,
|
|
120
|
+
:instrument_typhoeus,
|
|
121
|
+
:instrument_dalli,
|
|
122
|
+
:instrument_aws,
|
|
123
|
+
:instrument_stripe,
|
|
52
124
|
:http_ignore_hosts,
|
|
53
125
|
:redis_ignore_commands,
|
|
54
126
|
:log_formatter_enabled,
|
|
55
127
|
:log_formatter_colors,
|
|
56
128
|
:log_formatter_hide_assets,
|
|
57
129
|
:log_formatter_compact_assets,
|
|
58
|
-
:log_formatter_show_params
|
|
130
|
+
:log_formatter_show_params,
|
|
131
|
+
:disable_self_tracking,
|
|
132
|
+
:devtools_enabled,
|
|
133
|
+
:devtools_error_page_enabled,
|
|
134
|
+
:devtools_debug_panel_enabled,
|
|
135
|
+
:devtools_allowed_environments,
|
|
136
|
+
:devtools_allowed_ips,
|
|
137
|
+
:devtools_asset_path,
|
|
138
|
+
:devtools_panel_position,
|
|
139
|
+
:devtools_expand_by_default
|
|
140
|
+
|
|
141
|
+
# Services that should not track themselves to avoid circular dependencies
|
|
142
|
+
SELF_TRACKING_SERVICES = {
|
|
143
|
+
"recall" => :recall_enabled,
|
|
144
|
+
"reflex" => :reflex_enabled,
|
|
145
|
+
"pulse" => :pulse_enabled,
|
|
146
|
+
"flux" => :flux_enabled,
|
|
147
|
+
"signal" => :signal_enabled
|
|
148
|
+
}.freeze
|
|
59
149
|
|
|
60
150
|
def initialize
|
|
61
151
|
# Authentication
|
|
@@ -76,6 +166,10 @@ module BrainzLab
|
|
|
76
166
|
# Debug mode - enables verbose logging
|
|
77
167
|
@debug = ENV["BRAINZLAB_DEBUG"] == "true"
|
|
78
168
|
|
|
169
|
+
# Disable self-tracking - prevents services from tracking to themselves
|
|
170
|
+
# e.g., Recall won't log to itself, Reflex won't track errors to itself
|
|
171
|
+
@disable_self_tracking = ENV.fetch("BRAINZLAB_DISABLE_SELF_TRACKING", "true") == "true"
|
|
172
|
+
|
|
79
173
|
# Recall settings
|
|
80
174
|
@recall_enabled = true
|
|
81
175
|
@recall_url = ENV["RECALL_URL"] || "https://recall.brainzlab.ai"
|
|
@@ -107,6 +201,90 @@ module BrainzLab
|
|
|
107
201
|
@pulse_sample_rate = nil
|
|
108
202
|
@pulse_excluded_paths = %w[/health /ping /up /assets]
|
|
109
203
|
|
|
204
|
+
# Flux settings
|
|
205
|
+
@flux_enabled = true
|
|
206
|
+
@flux_url = ENV["FLUX_URL"] || "https://flux.brainzlab.ai"
|
|
207
|
+
@flux_api_key = ENV["FLUX_API_KEY"]
|
|
208
|
+
@flux_ingest_key = ENV["FLUX_INGEST_KEY"]
|
|
209
|
+
@flux_master_key = ENV["FLUX_MASTER_KEY"]
|
|
210
|
+
@flux_auto_provision = true
|
|
211
|
+
@flux_buffer_size = 100
|
|
212
|
+
@flux_flush_interval = 5
|
|
213
|
+
|
|
214
|
+
# Signal settings
|
|
215
|
+
@signal_enabled = true
|
|
216
|
+
@signal_url = ENV["SIGNAL_URL"] || "https://signal.brainzlab.ai"
|
|
217
|
+
@signal_api_key = ENV["SIGNAL_API_KEY"]
|
|
218
|
+
@signal_master_key = ENV["SIGNAL_MASTER_KEY"]
|
|
219
|
+
@signal_auto_provision = true
|
|
220
|
+
|
|
221
|
+
# Vault settings
|
|
222
|
+
@vault_enabled = true
|
|
223
|
+
@vault_url = ENV["VAULT_URL"] || "https://vault.brainzlab.ai"
|
|
224
|
+
@vault_api_key = ENV["VAULT_API_KEY"]
|
|
225
|
+
@vault_master_key = ENV["VAULT_MASTER_KEY"]
|
|
226
|
+
@vault_auto_provision = true
|
|
227
|
+
@vault_cache_enabled = true
|
|
228
|
+
@vault_cache_ttl = 300 # 5 minutes
|
|
229
|
+
@vault_auto_load = ENV.fetch("VAULT_AUTO_LOAD", "false") == "true" # Auto-load secrets into ENV
|
|
230
|
+
@vault_load_provider_keys = true # Also load provider keys (OpenAI, etc.)
|
|
231
|
+
|
|
232
|
+
# Vision settings (AI browser automation)
|
|
233
|
+
@vision_enabled = true
|
|
234
|
+
@vision_url = ENV["VISION_URL"] || "https://vision.brainzlab.ai"
|
|
235
|
+
@vision_api_key = ENV["VISION_API_KEY"]
|
|
236
|
+
@vision_ingest_key = ENV["VISION_INGEST_KEY"]
|
|
237
|
+
@vision_master_key = ENV["VISION_MASTER_KEY"]
|
|
238
|
+
@vision_auto_provision = true
|
|
239
|
+
@vision_default_model = ENV["VISION_DEFAULT_MODEL"] || "claude-sonnet-4"
|
|
240
|
+
@vision_default_browser_provider = ENV["VISION_DEFAULT_BROWSER_PROVIDER"] || "local"
|
|
241
|
+
|
|
242
|
+
# Cortex settings (feature flags)
|
|
243
|
+
@cortex_enabled = true
|
|
244
|
+
@cortex_url = ENV["CORTEX_URL"] || "https://cortex.brainzlab.ai"
|
|
245
|
+
@cortex_api_key = ENV["CORTEX_API_KEY"]
|
|
246
|
+
@cortex_master_key = ENV["CORTEX_MASTER_KEY"]
|
|
247
|
+
@cortex_auto_provision = true
|
|
248
|
+
@cortex_cache_enabled = true
|
|
249
|
+
@cortex_cache_ttl = 60 # 1 minute
|
|
250
|
+
@cortex_default_context = {}
|
|
251
|
+
|
|
252
|
+
# Beacon settings (uptime monitoring)
|
|
253
|
+
@beacon_enabled = true
|
|
254
|
+
@beacon_url = ENV["BEACON_URL"] || "https://beacon.brainzlab.ai"
|
|
255
|
+
@beacon_api_key = ENV["BEACON_API_KEY"]
|
|
256
|
+
@beacon_master_key = ENV["BEACON_MASTER_KEY"]
|
|
257
|
+
@beacon_auto_provision = true
|
|
258
|
+
|
|
259
|
+
# Nerve settings (job monitoring)
|
|
260
|
+
@nerve_enabled = true
|
|
261
|
+
@nerve_url = ENV["NERVE_URL"] || "https://nerve.brainzlab.ai"
|
|
262
|
+
@nerve_api_key = ENV["NERVE_API_KEY"]
|
|
263
|
+
@nerve_master_key = ENV["NERVE_MASTER_KEY"]
|
|
264
|
+
@nerve_auto_provision = true
|
|
265
|
+
|
|
266
|
+
# Dendrite settings (AI documentation)
|
|
267
|
+
@dendrite_enabled = true
|
|
268
|
+
@dendrite_url = ENV["DENDRITE_URL"] || "https://dendrite.brainzlab.ai"
|
|
269
|
+
@dendrite_api_key = ENV["DENDRITE_API_KEY"]
|
|
270
|
+
@dendrite_master_key = ENV["DENDRITE_MASTER_KEY"]
|
|
271
|
+
@dendrite_auto_provision = true
|
|
272
|
+
|
|
273
|
+
# Sentinel settings (host monitoring)
|
|
274
|
+
@sentinel_enabled = true
|
|
275
|
+
@sentinel_url = ENV["SENTINEL_URL"] || "https://sentinel.brainzlab.ai"
|
|
276
|
+
@sentinel_api_key = ENV["SENTINEL_API_KEY"]
|
|
277
|
+
@sentinel_agent_key = ENV["SENTINEL_AGENT_KEY"]
|
|
278
|
+
@sentinel_master_key = ENV["SENTINEL_MASTER_KEY"]
|
|
279
|
+
@sentinel_auto_provision = true
|
|
280
|
+
|
|
281
|
+
# Synapse settings (AI development orchestration)
|
|
282
|
+
@synapse_enabled = true
|
|
283
|
+
@synapse_url = ENV["SYNAPSE_URL"] || "https://synapse.brainzlab.ai"
|
|
284
|
+
@synapse_api_key = ENV["SYNAPSE_API_KEY"]
|
|
285
|
+
@synapse_master_key = ENV["SYNAPSE_MASTER_KEY"]
|
|
286
|
+
@synapse_auto_provision = true
|
|
287
|
+
|
|
110
288
|
# Filtering
|
|
111
289
|
@scrub_fields = %i[password password_confirmation token api_key secret]
|
|
112
290
|
|
|
@@ -124,6 +302,14 @@ module BrainzLab
|
|
|
124
302
|
@instrument_action_mailer = true # ActionMailer instrumentation
|
|
125
303
|
@instrument_delayed_job = true # Delayed::Job instrumentation
|
|
126
304
|
@instrument_grape = true # Grape API instrumentation
|
|
305
|
+
@instrument_solid_queue = true # Solid Queue job instrumentation
|
|
306
|
+
@instrument_good_job = true # GoodJob instrumentation
|
|
307
|
+
@instrument_resque = true # Resque instrumentation
|
|
308
|
+
@instrument_excon = true # Excon HTTP client instrumentation
|
|
309
|
+
@instrument_typhoeus = true # Typhoeus HTTP client instrumentation
|
|
310
|
+
@instrument_dalli = true # Dalli/Memcached instrumentation
|
|
311
|
+
@instrument_aws = true # AWS SDK instrumentation
|
|
312
|
+
@instrument_stripe = true # Stripe API instrumentation
|
|
127
313
|
@http_ignore_hosts = %w[localhost 127.0.0.1]
|
|
128
314
|
@redis_ignore_commands = %w[ping info] # Commands to skip tracking
|
|
129
315
|
|
|
@@ -133,6 +319,16 @@ module BrainzLab
|
|
|
133
319
|
@log_formatter_hide_assets = false
|
|
134
320
|
@log_formatter_compact_assets = true
|
|
135
321
|
@log_formatter_show_params = true
|
|
322
|
+
|
|
323
|
+
# DevTools settings (development error page and debug panel)
|
|
324
|
+
@devtools_enabled = true
|
|
325
|
+
@devtools_error_page_enabled = true
|
|
326
|
+
@devtools_debug_panel_enabled = true
|
|
327
|
+
@devtools_allowed_environments = %w[development test]
|
|
328
|
+
@devtools_allowed_ips = ["127.0.0.1", "::1", "172.16.0.0/12", "192.168.0.0/16", "10.0.0.0/8"]
|
|
329
|
+
@devtools_asset_path = "/__brainzlab__"
|
|
330
|
+
@devtools_panel_position = "bottom-right"
|
|
331
|
+
@devtools_expand_by_default = false
|
|
136
332
|
end
|
|
137
333
|
|
|
138
334
|
def recall_min_level=(level)
|
|
@@ -168,10 +364,150 @@ module BrainzLab
|
|
|
168
364
|
pulse_api_key || secret_key
|
|
169
365
|
end
|
|
170
366
|
|
|
367
|
+
def flux_valid?
|
|
368
|
+
key = flux_ingest_key || flux_api_key || secret_key
|
|
369
|
+
!key.nil? && !key.empty?
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
def flux_auth_key
|
|
373
|
+
flux_ingest_key || flux_api_key || secret_key
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
def signal_valid?
|
|
377
|
+
key = signal_api_key || secret_key
|
|
378
|
+
!key.nil? && !key.empty?
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def signal_auth_key
|
|
382
|
+
signal_api_key || secret_key
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
def vault_valid?
|
|
386
|
+
key = vault_api_key || secret_key
|
|
387
|
+
!key.nil? && !key.empty?
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
def vault_auth_key
|
|
391
|
+
vault_api_key || secret_key
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
def vision_valid?
|
|
395
|
+
key = vision_ingest_key || vision_api_key || secret_key
|
|
396
|
+
!key.nil? && !key.empty?
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
def vision_auth_key
|
|
400
|
+
vision_ingest_key || vision_api_key || secret_key
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
def beacon_valid?
|
|
404
|
+
key = beacon_api_key || secret_key
|
|
405
|
+
!key.nil? && !key.empty?
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
def beacon_auth_key
|
|
409
|
+
beacon_api_key || secret_key
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
def nerve_valid?
|
|
413
|
+
key = nerve_api_key || secret_key
|
|
414
|
+
!key.nil? && !key.empty?
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
def nerve_auth_key
|
|
418
|
+
nerve_api_key || secret_key
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
def cortex_valid?
|
|
422
|
+
key = cortex_api_key || secret_key
|
|
423
|
+
!key.nil? && !key.empty?
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
def cortex_auth_key
|
|
427
|
+
cortex_api_key || secret_key
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
def dendrite_valid?
|
|
431
|
+
key = dendrite_api_key || secret_key
|
|
432
|
+
!key.nil? && !key.empty?
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
def dendrite_auth_key
|
|
436
|
+
dendrite_api_key || secret_key
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
def sentinel_valid?
|
|
440
|
+
key = sentinel_api_key || secret_key
|
|
441
|
+
!key.nil? && !key.empty?
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
def sentinel_auth_key
|
|
445
|
+
sentinel_api_key || secret_key
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
def synapse_valid?
|
|
449
|
+
key = synapse_api_key || secret_key
|
|
450
|
+
!key.nil? && !key.empty?
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
def synapse_auth_key
|
|
454
|
+
synapse_api_key || secret_key
|
|
455
|
+
end
|
|
456
|
+
|
|
171
457
|
def debug?
|
|
172
458
|
@debug == true
|
|
173
459
|
end
|
|
174
460
|
|
|
461
|
+
# Check if recall is effectively enabled (considering self-tracking)
|
|
462
|
+
def recall_effectively_enabled?
|
|
463
|
+
return false unless @recall_enabled
|
|
464
|
+
return true unless @disable_self_tracking
|
|
465
|
+
|
|
466
|
+
# Disable if this is the Recall service itself
|
|
467
|
+
normalized_app_name = @app_name.to_s.downcase.strip
|
|
468
|
+
normalized_app_name != "recall"
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
# Check if reflex is effectively enabled (considering self-tracking)
|
|
472
|
+
def reflex_effectively_enabled?
|
|
473
|
+
return false unless @reflex_enabled
|
|
474
|
+
return true unless @disable_self_tracking
|
|
475
|
+
|
|
476
|
+
# Disable if this is the Reflex service itself
|
|
477
|
+
normalized_app_name = @app_name.to_s.downcase.strip
|
|
478
|
+
normalized_app_name != "reflex"
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
# Check if pulse is effectively enabled (considering self-tracking)
|
|
482
|
+
def pulse_effectively_enabled?
|
|
483
|
+
return false unless @pulse_enabled
|
|
484
|
+
return true unless @disable_self_tracking
|
|
485
|
+
|
|
486
|
+
# Disable if this is the Pulse service itself
|
|
487
|
+
normalized_app_name = @app_name.to_s.downcase.strip
|
|
488
|
+
normalized_app_name != "pulse"
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
# Check if flux is effectively enabled (considering self-tracking)
|
|
492
|
+
def flux_effectively_enabled?
|
|
493
|
+
return false unless @flux_enabled
|
|
494
|
+
return true unless @disable_self_tracking
|
|
495
|
+
|
|
496
|
+
# Disable if this is the Flux service itself
|
|
497
|
+
normalized_app_name = @app_name.to_s.downcase.strip
|
|
498
|
+
normalized_app_name != "flux"
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
# Check if signal is effectively enabled (considering self-tracking)
|
|
502
|
+
def signal_effectively_enabled?
|
|
503
|
+
return false unless @signal_enabled
|
|
504
|
+
return true unless @disable_self_tracking
|
|
505
|
+
|
|
506
|
+
# Disable if this is the Signal service itself
|
|
507
|
+
normalized_app_name = @app_name.to_s.downcase.strip
|
|
508
|
+
normalized_app_name != "signal"
|
|
509
|
+
end
|
|
510
|
+
|
|
175
511
|
def debug_log(message)
|
|
176
512
|
return unless debug?
|
|
177
513
|
|
|
@@ -201,13 +537,15 @@ module BrainzLab
|
|
|
201
537
|
end
|
|
202
538
|
|
|
203
539
|
def detect_git_commit
|
|
204
|
-
`git rev-parse HEAD 2>/dev/null`.strip
|
|
540
|
+
result = `git rev-parse HEAD 2>/dev/null`.strip
|
|
541
|
+
result.empty? ? nil : result
|
|
205
542
|
rescue StandardError
|
|
206
543
|
nil
|
|
207
544
|
end
|
|
208
545
|
|
|
209
546
|
def detect_git_branch
|
|
210
|
-
`git rev-parse --abbrev-ref HEAD 2>/dev/null`.strip
|
|
547
|
+
result = `git rev-parse --abbrev-ref HEAD 2>/dev/null`.strip
|
|
548
|
+
result.empty? ? nil : result
|
|
211
549
|
rescue StandardError
|
|
212
550
|
nil
|
|
213
551
|
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BrainzLab
|
|
4
|
+
module Cortex
|
|
5
|
+
class Cache
|
|
6
|
+
def initialize(ttl = 60)
|
|
7
|
+
@ttl = ttl
|
|
8
|
+
@store = {}
|
|
9
|
+
@timestamps = {}
|
|
10
|
+
@mutex = Mutex.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def get(key)
|
|
14
|
+
@mutex.synchronize do
|
|
15
|
+
return nil unless @store.key?(key)
|
|
16
|
+
return nil if expired?(key)
|
|
17
|
+
|
|
18
|
+
@store[key]
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def set(key, value)
|
|
23
|
+
@mutex.synchronize do
|
|
24
|
+
@store[key] = value
|
|
25
|
+
@timestamps[key] = Time.now
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def has?(key)
|
|
30
|
+
@mutex.synchronize do
|
|
31
|
+
@store.key?(key) && !expired?(key)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def delete(key)
|
|
36
|
+
@mutex.synchronize do
|
|
37
|
+
@store.delete(key)
|
|
38
|
+
@timestamps.delete(key)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def clear!
|
|
43
|
+
@mutex.synchronize do
|
|
44
|
+
@store.clear
|
|
45
|
+
@timestamps.clear
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def expired?(key)
|
|
52
|
+
timestamp = @timestamps[key]
|
|
53
|
+
return true unless timestamp
|
|
54
|
+
|
|
55
|
+
Time.now - timestamp > @ttl
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "net/http"
|
|
4
|
+
require "json"
|
|
5
|
+
require "uri"
|
|
6
|
+
require "cgi"
|
|
7
|
+
|
|
8
|
+
module BrainzLab
|
|
9
|
+
module Cortex
|
|
10
|
+
class Client
|
|
11
|
+
def initialize(config)
|
|
12
|
+
@config = config
|
|
13
|
+
@base_url = config.cortex_url || "https://cortex.brainzlab.ai"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Evaluate a single flag
|
|
17
|
+
def evaluate(flag_name, context: {})
|
|
18
|
+
response = request(
|
|
19
|
+
:post,
|
|
20
|
+
"/api/v1/evaluate",
|
|
21
|
+
body: {
|
|
22
|
+
flag: flag_name,
|
|
23
|
+
context: context
|
|
24
|
+
}
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
return nil unless response.is_a?(Net::HTTPSuccess)
|
|
28
|
+
|
|
29
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
|
30
|
+
data[:result]
|
|
31
|
+
rescue StandardError => e
|
|
32
|
+
log_error("evaluate", e)
|
|
33
|
+
nil
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Evaluate multiple flags at once
|
|
37
|
+
def evaluate_all(context: {})
|
|
38
|
+
response = request(
|
|
39
|
+
:post,
|
|
40
|
+
"/api/v1/evaluate/batch",
|
|
41
|
+
body: { context: context }
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
return {} unless response.is_a?(Net::HTTPSuccess)
|
|
45
|
+
|
|
46
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
|
47
|
+
data[:flags] || {}
|
|
48
|
+
rescue StandardError => e
|
|
49
|
+
log_error("evaluate_all", e)
|
|
50
|
+
{}
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# List all flags
|
|
54
|
+
def list
|
|
55
|
+
response = request(:get, "/api/v1/flags")
|
|
56
|
+
|
|
57
|
+
return [] unless response.is_a?(Net::HTTPSuccess)
|
|
58
|
+
|
|
59
|
+
data = JSON.parse(response.body, symbolize_names: true)
|
|
60
|
+
data[:flags] || []
|
|
61
|
+
rescue StandardError => e
|
|
62
|
+
log_error("list", e)
|
|
63
|
+
[]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Get flag details
|
|
67
|
+
def get_flag(flag_name)
|
|
68
|
+
response = request(:get, "/api/v1/flags/#{CGI.escape(flag_name.to_s)}")
|
|
69
|
+
|
|
70
|
+
return nil unless response.is_a?(Net::HTTPSuccess)
|
|
71
|
+
|
|
72
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
73
|
+
rescue StandardError => e
|
|
74
|
+
log_error("get_flag", e)
|
|
75
|
+
nil
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def provision(project_id:, app_name:)
|
|
79
|
+
response = request(
|
|
80
|
+
:post,
|
|
81
|
+
"/api/v1/projects/provision",
|
|
82
|
+
body: { project_id: project_id, app_name: app_name },
|
|
83
|
+
use_service_key: true
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPCreated)
|
|
87
|
+
rescue StandardError => e
|
|
88
|
+
log_error("provision", e)
|
|
89
|
+
false
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
def request(method, path, headers: {}, body: nil, params: nil, use_service_key: false)
|
|
95
|
+
uri = URI.parse("#{@base_url}#{path}")
|
|
96
|
+
|
|
97
|
+
if params
|
|
98
|
+
uri.query = URI.encode_www_form(params)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
102
|
+
http.use_ssl = uri.scheme == "https"
|
|
103
|
+
http.open_timeout = 5
|
|
104
|
+
http.read_timeout = 10
|
|
105
|
+
|
|
106
|
+
request = case method
|
|
107
|
+
when :get
|
|
108
|
+
Net::HTTP::Get.new(uri)
|
|
109
|
+
when :post
|
|
110
|
+
Net::HTTP::Post.new(uri)
|
|
111
|
+
when :put
|
|
112
|
+
Net::HTTP::Put.new(uri)
|
|
113
|
+
when :delete
|
|
114
|
+
Net::HTTP::Delete.new(uri)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Set headers
|
|
118
|
+
request["Content-Type"] = "application/json"
|
|
119
|
+
request["Accept"] = "application/json"
|
|
120
|
+
|
|
121
|
+
if use_service_key
|
|
122
|
+
request["X-Service-Key"] = @config.cortex_master_key || @config.secret_key
|
|
123
|
+
else
|
|
124
|
+
auth_key = @config.cortex_api_key || @config.secret_key
|
|
125
|
+
request["Authorization"] = "Bearer #{auth_key}" if auth_key
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
headers.each { |k, v| request[k] = v }
|
|
129
|
+
|
|
130
|
+
# Set body
|
|
131
|
+
request.body = body.to_json if body
|
|
132
|
+
|
|
133
|
+
http.request(request)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def log_error(operation, error)
|
|
137
|
+
BrainzLab.debug_log("[Cortex::Client] #{operation} failed: #{error.message}")
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BrainzLab
|
|
4
|
+
module Cortex
|
|
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.cortex_auto_provision
|
|
14
|
+
return unless valid_auth?
|
|
15
|
+
|
|
16
|
+
@provisioned = true
|
|
17
|
+
|
|
18
|
+
# Try to provision with Platform project ID
|
|
19
|
+
project_id = detect_project_id
|
|
20
|
+
return unless project_id
|
|
21
|
+
|
|
22
|
+
client = Client.new(@config)
|
|
23
|
+
client.provision(
|
|
24
|
+
project_id: project_id,
|
|
25
|
+
app_name: @config.app_name || @config.service
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
BrainzLab.debug_log("[Cortex::Provisioner] Project provisioned: #{project_id}")
|
|
29
|
+
rescue StandardError => e
|
|
30
|
+
BrainzLab.debug_log("[Cortex::Provisioner] Provisioning failed: #{e.message}")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def valid_auth?
|
|
36
|
+
key = @config.cortex_api_key || @config.cortex_master_key || @config.secret_key
|
|
37
|
+
!key.nil? && !key.empty?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def detect_project_id
|
|
41
|
+
# Try environment variable first
|
|
42
|
+
return ENV["BRAINZLAB_PROJECT_ID"] if ENV["BRAINZLAB_PROJECT_ID"]
|
|
43
|
+
|
|
44
|
+
# Could also detect from Platform API if we have a secret key
|
|
45
|
+
nil
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|