rails-agent-stack 0.1.1

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 (91) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/README.md +16 -0
  3. data/.github/workflows/ci.yml +32 -0
  4. data/.gitignore +8 -0
  5. data/.rspec +3 -0
  6. data/AGENTS.md +18 -0
  7. data/CHANGELOG.md +40 -0
  8. data/Gemfile +15 -0
  9. data/MIT-LICENSE +21 -0
  10. data/README.md +228 -0
  11. data/Rakefile +8 -0
  12. data/app/assets/images/rails_agents/kip-logo.png +0 -0
  13. data/app/assets/images/rails_agents/kip-waving.png +0 -0
  14. data/app/assets/stylesheets/rails_agents/application.css +411 -0
  15. data/app/controllers/rails_agents/application_controller.rb +25 -0
  16. data/app/controllers/rails_agents/connect_controller.rb +36 -0
  17. data/app/controllers/rails_agents/dashboard_controller.rb +70 -0
  18. data/app/controllers/rails_agents/handshake_controller.rb +55 -0
  19. data/app/controllers/rails_agents/open_wire_inbound_controller.rb +153 -0
  20. data/app/controllers/rails_agents/pulls_controller.rb +42 -0
  21. data/app/controllers/rails_agents/registrations_controller.rb +96 -0
  22. data/app/controllers/rails_agents/schema_controller.rb +123 -0
  23. data/app/controllers/rails_agents/sessions_controller.rb +76 -0
  24. data/app/controllers/rails_agents/tool_bridge_controller.rb +67 -0
  25. data/app/views/layouts/rails_agents/application.html.erb +18 -0
  26. data/app/views/rails_agents/dashboard/show.html.erb +139 -0
  27. data/app/views/rails_agents/registrations/new.html.erb +83 -0
  28. data/app/views/rails_agents/sessions/new.html.erb +73 -0
  29. data/app/views/rails_agents/shared/_auth_brand.html.erb +20 -0
  30. data/config/routes.rb +34 -0
  31. data/docs/AGENTS.md +220 -0
  32. data/docs/PRD.md +89 -0
  33. data/docs/assets/hero-banner.png +0 -0
  34. data/examples/support/agent.rb +14 -0
  35. data/examples/support/channels/.keep +0 -0
  36. data/examples/support/evals/smoke.yml +4 -0
  37. data/examples/support/knowledge/shipping.md +1 -0
  38. data/examples/support/memory.rb +3 -0
  39. data/examples/support/prompt.md +9 -0
  40. data/examples/support/skills/.keep +0 -0
  41. data/examples/support/tools/.keep +0 -0
  42. data/exe/rails-agents +6 -0
  43. data/lib/generators/rails_agents/agent/agent_generator.rb +80 -0
  44. data/lib/generators/rails_agents/agent/templates/agent.rb.tt +14 -0
  45. data/lib/generators/rails_agents/agent/templates/agent_database.rb.tt +42 -0
  46. data/lib/generators/rails_agents/agent/templates/channels/slack.rb.tt +8 -0
  47. data/lib/generators/rails_agents/agent/templates/evals/smoke.yml.tt +4 -0
  48. data/lib/generators/rails_agents/agent/templates/imports.yml.tt +9 -0
  49. data/lib/generators/rails_agents/agent/templates/memory.rb.tt +6 -0
  50. data/lib/generators/rails_agents/agent/templates/prompt.md.tt +10 -0
  51. data/lib/generators/rails_agents/agent/templates/prompt_database.md.tt +14 -0
  52. data/lib/generators/rails_agents/install/INSTALL +10 -0
  53. data/lib/generators/rails_agents/install/install_generator.rb +72 -0
  54. data/lib/generators/rails_agents/install/templates/AGENTS.md.tt +46 -0
  55. data/lib/generators/rails_agents/install/templates/initializer.rb.tt +8 -0
  56. data/lib/generators/rails_agents/install/templates/library_README.md.tt +28 -0
  57. data/lib/generators/rails_agents/install/templates/library_manifest.yml.tt +5 -0
  58. data/lib/generators/rails_agents/install/templates/rails_agents_autoload.rb.tt +10 -0
  59. data/lib/rails-agent-stack.rb +4 -0
  60. data/lib/rails_agents/background_agent.rb +15 -0
  61. data/lib/rails_agents/base.rb +294 -0
  62. data/lib/rails_agents/chat_agent.rb +15 -0
  63. data/lib/rails_agents/cli.rb +245 -0
  64. data/lib/rails_agents/client.rb +216 -0
  65. data/lib/rails_agents/configuration.rb +41 -0
  66. data/lib/rails_agents/creation_agent.rb +10 -0
  67. data/lib/rails_agents/credentials_writer.rb +38 -0
  68. data/lib/rails_agents/engine.rb +19 -0
  69. data/lib/rails_agents/knowledge_agent.rb +10 -0
  70. data/lib/rails_agents/library_imports.rb +163 -0
  71. data/lib/rails_agents/local_auth.rb +56 -0
  72. data/lib/rails_agents/local_sync.rb +113 -0
  73. data/lib/rails_agents/monitoring_agent.rb +10 -0
  74. data/lib/rails_agents/open_wire_adapter.rb +31 -0
  75. data/lib/rails_agents/operations_agent.rb +10 -0
  76. data/lib/rails_agents/railtie.rb +14 -0
  77. data/lib/rails_agents/tasks.rake +8 -0
  78. data/lib/rails_agents/version.rb +5 -0
  79. data/lib/rails_agents/workflow_agent.rb +11 -0
  80. data/lib/rails_agents.rb +57 -0
  81. data/rails-agent-stack.gemspec +49 -0
  82. data/rails-agents.code-workspace +25 -0
  83. data/spec/generators/agent_generator_spec.rb +36 -0
  84. data/spec/rails_agents/base_spec.rb +202 -0
  85. data/spec/rails_agents/client_spec.rb +121 -0
  86. data/spec/rails_agents/configuration_spec.rb +55 -0
  87. data/spec/rails_agents/library_imports_spec.rb +80 -0
  88. data/spec/rails_agents/local_sync_spec.rb +63 -0
  89. data/spec/rails_agents/taxonomy_spec.rb +52 -0
  90. data/spec/spec_helper.rb +27 -0
  91. metadata +229 -0
@@ -0,0 +1,411 @@
1
+ /*
2
+ * Rails Agent engine — matches rails-agent.com design system
3
+ * cream paper · ruby · ink · Fraunces + Inter
4
+ */
5
+
6
+ :root {
7
+ --ruby: #c23a3a;
8
+ --ruby-deep: #9e2a2a;
9
+ --cream: #fdfbf7;
10
+ --paper: #f7f3eb;
11
+ --ink: #1c1917;
12
+ --muted: #6b6560;
13
+ --border: rgba(28, 25, 23, 0.12);
14
+ --card: #fffcf8;
15
+ --ember: #e8a060;
16
+ }
17
+
18
+ *,
19
+ *::before,
20
+ *::after {
21
+ box-sizing: border-box;
22
+ }
23
+
24
+ .rails-agents-body {
25
+ margin: 0;
26
+ min-height: 100vh;
27
+ background: var(--cream);
28
+ color: var(--ink);
29
+ font-family: "Inter", system-ui, -apple-system, sans-serif;
30
+ line-height: 1.55;
31
+ -webkit-font-smoothing: antialiased;
32
+ }
33
+
34
+ .rails-agents-body a:not(.rails-agents-btn) {
35
+ color: var(--ruby);
36
+ }
37
+
38
+ code {
39
+ font-family: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
40
+ font-size: 0.9em;
41
+ background: rgba(28, 25, 23, 0.06);
42
+ padding: 0.1em 0.4em;
43
+ border-radius: 4px;
44
+ }
45
+
46
+ /* ——— Connect: split screen ——— */
47
+
48
+ .rails-agents-connect {
49
+ display: grid;
50
+ min-height: 100vh;
51
+ grid-template-columns: 1fr;
52
+ }
53
+
54
+ @media (min-width: 960px) {
55
+ .rails-agents-connect {
56
+ grid-template-columns: 1fr 1fr;
57
+ }
58
+ }
59
+
60
+ .rails-agents-connect__brand {
61
+ position: relative;
62
+ display: flex;
63
+ flex-direction: column;
64
+ justify-content: space-between;
65
+ padding: 1.5rem 1.5rem 2rem;
66
+ background:
67
+ radial-gradient(ellipse 80% 50% at 50% 100%, rgba(194, 58, 58, 0.12), transparent 70%),
68
+ var(--cream);
69
+ overflow: hidden;
70
+ }
71
+
72
+ @media (min-width: 640px) {
73
+ .rails-agents-connect__brand {
74
+ padding: 2.5rem;
75
+ }
76
+ }
77
+
78
+ .rails-agents-connect__form-pane {
79
+ display: flex;
80
+ align-items: center;
81
+ justify-content: center;
82
+ padding: 1.5rem;
83
+ background: var(--paper);
84
+ border-top: 1px solid var(--border);
85
+ }
86
+
87
+ @media (min-width: 960px) {
88
+ .rails-agents-connect__form-pane {
89
+ border-top: 0;
90
+ border-left: 1px solid var(--border);
91
+ padding: 2.5rem;
92
+ background: var(--cream);
93
+ }
94
+ }
95
+
96
+ .rails-agents-logo-lockup {
97
+ display: inline-flex;
98
+ align-items: center;
99
+ gap: 0.65rem;
100
+ text-decoration: none;
101
+ color: inherit;
102
+ }
103
+
104
+ .rails-agents-logo-mark {
105
+ position: relative;
106
+ display: inline-flex;
107
+ width: 2.5rem;
108
+ height: 2.5rem;
109
+ align-items: center;
110
+ justify-content: center;
111
+ overflow: hidden;
112
+ border-radius: 0.75rem;
113
+ background: var(--ruby);
114
+ color: #fff;
115
+ font-family: "Fraunces", Georgia, serif;
116
+ font-size: 0.75rem;
117
+ font-weight: 700;
118
+ letter-spacing: -0.04em;
119
+ box-shadow: 0 0 0 1px rgba(28, 25, 23, 0.1);
120
+ }
121
+
122
+ .rails-agents-logo-mark img {
123
+ width: 100%;
124
+ height: 100%;
125
+ object-fit: cover;
126
+ object-position: top;
127
+ }
128
+
129
+ .rails-agents-logo-mark::after {
130
+ content: "";
131
+ position: absolute;
132
+ right: -2px;
133
+ bottom: -2px;
134
+ width: 0.55rem;
135
+ height: 0.55rem;
136
+ border-radius: 999px;
137
+ background: var(--ruby);
138
+ box-shadow: 0 0 0 2px var(--cream);
139
+ }
140
+
141
+ .rails-agents-logo-word {
142
+ font-family: "Fraunces", Georgia, serif;
143
+ font-size: 1.2rem;
144
+ font-weight: 600;
145
+ letter-spacing: -0.02em;
146
+ line-height: 1;
147
+ }
148
+
149
+ .rails-agents-logo-word em {
150
+ font-style: normal;
151
+ color: var(--ruby);
152
+ }
153
+
154
+ .rails-agents-connect__copy {
155
+ max-width: 28rem;
156
+ margin: 2rem 0;
157
+ }
158
+
159
+ @media (min-width: 960px) {
160
+ .rails-agents-connect__copy {
161
+ margin: 0;
162
+ }
163
+ }
164
+
165
+ .rails-agents-connect__copy h1 {
166
+ margin: 0;
167
+ font-family: "Fraunces", Georgia, serif;
168
+ font-size: clamp(2rem, 4.5vw, 3.25rem);
169
+ font-weight: 600;
170
+ line-height: 1.08;
171
+ letter-spacing: -0.02em;
172
+ text-wrap: balance;
173
+ }
174
+
175
+ .rails-agents-connect__copy h1 em {
176
+ font-style: italic;
177
+ color: var(--ruby);
178
+ font-weight: 600;
179
+ }
180
+
181
+ .rails-agents-connect__copy > p {
182
+ margin: 1.25rem 0 0;
183
+ font-size: 1.05rem;
184
+ color: var(--muted);
185
+ max-width: 26rem;
186
+ }
187
+
188
+ .rails-agents-connect__meta {
189
+ margin: 1.5rem 0 0;
190
+ font-family: "JetBrains Mono", ui-monospace, monospace;
191
+ font-size: 0.6875rem;
192
+ letter-spacing: 0.2em;
193
+ text-transform: uppercase;
194
+ color: var(--muted);
195
+ }
196
+
197
+ .rails-agents-connect__card {
198
+ width: 100%;
199
+ max-width: 22rem;
200
+ }
201
+
202
+ .rails-agents-connect__card h2 {
203
+ margin: 0;
204
+ font-family: "Fraunces", Georgia, serif;
205
+ font-size: 1.75rem;
206
+ font-weight: 600;
207
+ letter-spacing: -0.02em;
208
+ }
209
+
210
+ .rails-agents-connect__card > .rails-agents-lede {
211
+ margin: 0.4rem 0 0;
212
+ font-size: 0.9rem;
213
+ color: var(--muted);
214
+ }
215
+
216
+ .rails-agents-btn {
217
+ display: inline-flex;
218
+ width: 100%;
219
+ align-items: center;
220
+ justify-content: center;
221
+ gap: 0.5rem;
222
+ padding: 0.8rem 1rem;
223
+ border-radius: 0.65rem;
224
+ border: 1px solid transparent;
225
+ font: inherit;
226
+ font-size: 0.9rem;
227
+ font-weight: 550;
228
+ text-decoration: none;
229
+ cursor: pointer;
230
+ transition: filter 0.15s ease, background 0.15s ease, border-color 0.15s ease;
231
+ }
232
+
233
+ .rails-agents-btn--primary {
234
+ background: var(--ruby);
235
+ color: #fffaf6;
236
+ }
237
+
238
+ .rails-agents-btn--primary:hover,
239
+ .rails-agents-btn--primary:visited {
240
+ background: var(--ruby);
241
+ color: #fffaf6;
242
+ filter: brightness(1.06);
243
+ }
244
+
245
+ .rails-agents-btn--ghost {
246
+ background: var(--card);
247
+ color: var(--ink);
248
+ border-color: var(--border);
249
+ }
250
+
251
+ .rails-agents-btn--ghost:hover,
252
+ .rails-agents-btn--ghost:visited {
253
+ background: rgba(28, 25, 23, 0.04);
254
+ color: var(--ink);
255
+ }
256
+
257
+ .rails-agents-btn--ink {
258
+ background: var(--ink);
259
+ color: var(--cream);
260
+ }
261
+
262
+ .rails-agents-btn--ink:hover {
263
+ filter: brightness(1.15);
264
+ color: var(--cream);
265
+ }
266
+
267
+ .rails-agents-btn:disabled {
268
+ opacity: 0.6;
269
+ cursor: not-allowed;
270
+ }
271
+
272
+ .rails-agents-btn--spaced {
273
+ margin-top: 1.5rem;
274
+ }
275
+
276
+ .rails-agents-divider {
277
+ display: flex;
278
+ align-items: center;
279
+ gap: 0.75rem;
280
+ margin: 1.35rem 0;
281
+ font-size: 0.7rem;
282
+ font-weight: 500;
283
+ letter-spacing: 0.15em;
284
+ text-transform: uppercase;
285
+ color: var(--muted);
286
+ }
287
+
288
+ .rails-agents-divider::before,
289
+ .rails-agents-divider::after {
290
+ content: "";
291
+ flex: 1;
292
+ height: 1px;
293
+ background: var(--border);
294
+ }
295
+
296
+ .rails-agents-form {
297
+ display: grid;
298
+ gap: 0.75rem;
299
+ }
300
+
301
+ .rails-agents-form label {
302
+ display: grid;
303
+ gap: 0.35rem;
304
+ font-size: 0.8rem;
305
+ font-weight: 500;
306
+ color: var(--ink);
307
+ }
308
+
309
+ .rails-agents-form label span {
310
+ font-weight: 400;
311
+ color: var(--muted);
312
+ }
313
+
314
+ .rails-agents-form input {
315
+ width: 100%;
316
+ padding: 0.7rem 0.85rem;
317
+ border: 1px solid var(--border);
318
+ border-radius: 0.65rem;
319
+ background: var(--card);
320
+ color: var(--ink);
321
+ font: inherit;
322
+ font-size: 0.9rem;
323
+ outline: none;
324
+ transition: box-shadow 0.15s ease, border-color 0.15s ease;
325
+ }
326
+
327
+ .rails-agents-form input:focus {
328
+ border-color: var(--ruby);
329
+ box-shadow: 0 0 0 3px rgba(194, 58, 58, 0.18);
330
+ }
331
+
332
+ .rails-agents-form .rails-agents-btn {
333
+ margin-top: 0.35rem;
334
+ }
335
+
336
+ .rails-agents-muted {
337
+ font-size: 0.875rem;
338
+ color: var(--muted);
339
+ }
340
+
341
+ .rails-agents-flash {
342
+ margin-bottom: 1rem;
343
+ padding: 0.75rem 1rem;
344
+ border-radius: 0.65rem;
345
+ background: rgba(194, 58, 58, 0.08);
346
+ color: var(--ink);
347
+ font-size: 0.875rem;
348
+ }
349
+
350
+ .rails-agents-flash--alert {
351
+ background: rgba(194, 58, 58, 0.12);
352
+ color: var(--ruby-deep);
353
+ }
354
+
355
+ .rails-agents-connect__foot {
356
+ margin-top: 1.5rem;
357
+ text-align: center;
358
+ font-size: 0.875rem;
359
+ color: var(--muted);
360
+ }
361
+
362
+ .rails-agents-connect__foot a {
363
+ color: var(--ruby);
364
+ text-decoration: none;
365
+ font-weight: 500;
366
+ }
367
+
368
+ .rails-agents-connect__foot a:hover {
369
+ text-decoration: underline;
370
+ }
371
+
372
+ .rails-agents-github-icon {
373
+ width: 1rem;
374
+ height: 1rem;
375
+ fill: currentColor;
376
+ }
377
+
378
+ /* ——— Embed dashboard chrome ——— */
379
+
380
+ .rails-agents-main--embed {
381
+ max-width: none;
382
+ padding: 0;
383
+ margin: 0;
384
+ }
385
+
386
+ .rails-agents-embed-shell {
387
+ position: relative;
388
+ display: flex;
389
+ flex-direction: column;
390
+ min-height: 100vh;
391
+ }
392
+
393
+ .rails-agents-sync-status--sr {
394
+ position: absolute;
395
+ width: 1px;
396
+ height: 1px;
397
+ padding: 0;
398
+ margin: -1px;
399
+ overflow: hidden;
400
+ clip: rect(0, 0, 0, 0);
401
+ white-space: nowrap;
402
+ border: 0;
403
+ }
404
+
405
+ .rails-agents-embed {
406
+ flex: 1;
407
+ width: 100%;
408
+ min-height: 100vh;
409
+ border: 0;
410
+ background: var(--cream);
411
+ }
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAgents
4
+ class ApplicationController < ActionController::Base
5
+ protect_from_forgery with: :exception
6
+ layout "rails_agents/application"
7
+
8
+ helper_method :dashboard_url, :configured?
9
+
10
+ private
11
+
12
+ def configured?
13
+ RailsAgents.config.configured?
14
+ end
15
+
16
+ def dashboard_url(path = "/dashboard")
17
+ base = RailsAgents.config.dashboard_base
18
+ "#{base.chomp("/")}#{path}"
19
+ end
20
+
21
+ def cloud_redirect(path)
22
+ redirect_to dashboard_url(path), allow_other_host: true
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ module RailsAgents
6
+ class ConnectController < ApplicationController
7
+ # GET /agents/connect?code=… — finishes cloud signup/signin for this Rails app.
8
+ def show
9
+ code = params[:code].to_s.strip
10
+ if code.blank?
11
+ redirect_to rails_agents.root_path, alert: "Missing connect code. Sign up or sign in again."
12
+ return
13
+ end
14
+
15
+ response = Client.new.claim_connect(code)
16
+ data = response["data"] || response
17
+ api_key = data["apiKey"] || data["api_key"]
18
+ project_id = data["projectId"] || data["project_id"]
19
+ embed_token = data["embedToken"] || data["embed_token"]
20
+
21
+ raise Client::Error.new("No API key returned") if api_key.blank?
22
+
23
+ CredentialsWriter.write!(api_key: api_key, project_id: project_id)
24
+ RailsAgents.configure do |config|
25
+ config.api_key = api_key
26
+ config.project_id = project_id if project_id
27
+ end
28
+
29
+ session[:ra_embed_token] = embed_token if embed_token.present?
30
+ redirect_to rails_agents.dashboard_path, notice: "Connected to Rails Agent Cloud."
31
+ rescue Client::Error => e
32
+ flash[:alert] = e.message
33
+ redirect_to rails_agents.root_path
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ module RailsAgents
6
+ class DashboardController < ApplicationController
7
+ def show
8
+ load_local_credentials!
9
+ unless configured?
10
+ redirect_to rails_agents.signup_path
11
+ return
12
+ end
13
+
14
+ @embed_token = take_embed_token!
15
+ @dashboard_url = build_embed_url("/dashboard", dashboard_query)
16
+ end
17
+
18
+ def proxy
19
+ load_local_credentials!
20
+ unless configured?
21
+ redirect_to rails_agents.signup_path
22
+ return
23
+ end
24
+
25
+ @embed_token = take_embed_token!
26
+ path = "/dashboard/#{params[:path]}"
27
+ @dashboard_url = build_embed_url(path, dashboard_query)
28
+ render :show
29
+ end
30
+
31
+ private
32
+
33
+ def take_embed_token!
34
+ session.delete(:ra_embed_token).presence ||
35
+ params[:embed_token].presence ||
36
+ params[:token].presence
37
+ end
38
+
39
+ def load_local_credentials!
40
+ path = Rails.root.join("config/rails_agents_credentials.yml")
41
+ return unless path.exist?
42
+
43
+ data = YAML.safe_load(path.read) || {}
44
+ RailsAgents.configure do |config|
45
+ config.api_key = data["api_key"] if data["api_key"].present?
46
+ config.project_id = data["project_id"] if data["project_id"].present?
47
+ end
48
+ rescue Psych::SyntaxError
49
+ nil
50
+ end
51
+
52
+ def dashboard_query
53
+ request.query_parameters.except("embed_token", "token")
54
+ end
55
+
56
+ def build_embed_url(path, query = {})
57
+ base = RailsAgents.config.dashboard_base.to_s.chomp("/")
58
+ embed_query = query.merge("embed" => "1")
59
+ dashboard_path = "#{path}?#{embed_query.to_query}"
60
+ # Bootstrap sets the cloud session cookie, then redirects to a clean dashboard URL.
61
+ if @embed_token.present?
62
+ return "#{base}/api/v1/auth/embed?#{
63
+ { token: @embed_token, next: dashboard_path }.to_query
64
+ }"
65
+ end
66
+
67
+ "#{base}#{dashboard_path}"
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsAgents
4
+ class HandshakeController < ApplicationController
5
+ skip_before_action :verify_authenticity_token, only: :create
6
+
7
+ def create
8
+ api_key_param = params[:api_key].to_s.strip.presence
9
+
10
+ if api_key_param
11
+ CredentialsWriter.write!(api_key: api_key_param, project_id: params[:project_id].presence)
12
+ apply_runtime_config!(api_key: api_key_param, project_id: params[:project_id].presence)
13
+ redirect_to rails_agents.root_path, notice: "Connected with API key."
14
+ return
15
+ end
16
+
17
+ email = params.require(:email).to_s.strip
18
+ password = params.require(:password).to_s
19
+ workspace = params[:workspace].presence || (defined?(Rails) ? Rails.application.class.module_parent_name : "App")
20
+
21
+ response = Client.new.handshake(
22
+ email: email,
23
+ password: password,
24
+ workspace: workspace
25
+ )
26
+ data = response["data"] || response
27
+ api_key = data["api_key"] || data.dig("apiKey", "token") || data["apiKey"]
28
+ project_id = data["project_id"] || data.dig("project", "id") || data["projectId"]
29
+ embed_token = data["embed_token"] || data["embedToken"]
30
+
31
+ raise Client::Error.new("No API key returned") if api_key.blank?
32
+
33
+ CredentialsWriter.write!(api_key: api_key, project_id: project_id)
34
+ apply_runtime_config!(api_key: api_key, project_id: project_id)
35
+
36
+ session[:ra_embed_token] = embed_token if embed_token.present?
37
+ redirect_to rails_agents.dashboard_path, notice: "Connected to Rails Agent Cloud."
38
+ rescue Client::Error => e
39
+ flash[:alert] = e.message
40
+ redirect_to rails_agents.root_path
41
+ rescue ActionController::ParameterMissing => e
42
+ flash[:alert] = e.message
43
+ redirect_to rails_agents.root_path
44
+ end
45
+
46
+ private
47
+
48
+ def apply_runtime_config!(api_key:, project_id:)
49
+ RailsAgents.configure do |config|
50
+ config.api_key = api_key
51
+ config.project_id = project_id if project_id
52
+ end
53
+ end
54
+ end
55
+ end