rails_console_ai 0.13.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 (44) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +95 -0
  3. data/LICENSE +21 -0
  4. data/README.md +328 -0
  5. data/app/controllers/rails_console_ai/application_controller.rb +28 -0
  6. data/app/controllers/rails_console_ai/sessions_controller.rb +16 -0
  7. data/app/helpers/rails_console_ai/sessions_helper.rb +56 -0
  8. data/app/models/rails_console_ai/session.rb +23 -0
  9. data/app/views/layouts/rails_console_ai/application.html.erb +84 -0
  10. data/app/views/rails_console_ai/sessions/index.html.erb +57 -0
  11. data/app/views/rails_console_ai/sessions/show.html.erb +66 -0
  12. data/config/routes.rb +4 -0
  13. data/lib/generators/rails_console_ai/install_generator.rb +26 -0
  14. data/lib/generators/rails_console_ai/templates/initializer.rb +79 -0
  15. data/lib/rails_console_ai/channel/base.rb +23 -0
  16. data/lib/rails_console_ai/channel/console.rb +457 -0
  17. data/lib/rails_console_ai/channel/slack.rb +182 -0
  18. data/lib/rails_console_ai/configuration.rb +185 -0
  19. data/lib/rails_console_ai/console_methods.rb +277 -0
  20. data/lib/rails_console_ai/context_builder.rb +120 -0
  21. data/lib/rails_console_ai/conversation_engine.rb +1142 -0
  22. data/lib/rails_console_ai/engine.rb +5 -0
  23. data/lib/rails_console_ai/executor.rb +461 -0
  24. data/lib/rails_console_ai/providers/anthropic.rb +122 -0
  25. data/lib/rails_console_ai/providers/base.rb +118 -0
  26. data/lib/rails_console_ai/providers/bedrock.rb +171 -0
  27. data/lib/rails_console_ai/providers/local.rb +112 -0
  28. data/lib/rails_console_ai/providers/openai.rb +114 -0
  29. data/lib/rails_console_ai/railtie.rb +34 -0
  30. data/lib/rails_console_ai/repl.rb +65 -0
  31. data/lib/rails_console_ai/safety_guards.rb +207 -0
  32. data/lib/rails_console_ai/session_logger.rb +90 -0
  33. data/lib/rails_console_ai/slack_bot.rb +473 -0
  34. data/lib/rails_console_ai/storage/base.rb +27 -0
  35. data/lib/rails_console_ai/storage/file_storage.rb +63 -0
  36. data/lib/rails_console_ai/tools/code_tools.rb +126 -0
  37. data/lib/rails_console_ai/tools/memory_tools.rb +136 -0
  38. data/lib/rails_console_ai/tools/model_tools.rb +95 -0
  39. data/lib/rails_console_ai/tools/registry.rb +478 -0
  40. data/lib/rails_console_ai/tools/schema_tools.rb +60 -0
  41. data/lib/rails_console_ai/version.rb +3 -0
  42. data/lib/rails_console_ai.rb +214 -0
  43. data/lib/tasks/rails_console_ai.rake +7 -0
  44. metadata +152 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 98ae3edf9aa6ff90817a965a826abaac229c14cba7a76f5eb27f3fea0e1defc4
4
+ data.tar.gz: 410831834a4a68bcd23693ee185d437c76b648c533b9c2f9d89c83c2ce376d39
5
+ SHA512:
6
+ metadata.gz: ecec76ab83d0bc42a7216e1180d447bddf092556a5e600dad784da6826f613d7145e81e65bd768aafab1858a434a2193e6370315d5601cf668e23da0bcad40d9
7
+ data.tar.gz: eef36ae45dfda23ca47a95524afc7ebb76083a927162c79263c5d3c98a0726ec1bb20b5fcf2e29b03b2c480044484c6bec36dbac466035092e969a6d7d2e5747
data/CHANGELOG.md ADDED
@@ -0,0 +1,95 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [0.13.0]
6
+
7
+ - Rename gem from `console_agent` to `rails_console_ai`
8
+ - Add AWS Bedrock provider
9
+ - Add prompt caching and cache-aware cost tracking for Anthropic
10
+ - Enable prompt caching for Bedrock
11
+
12
+ ## [0.12.0]
13
+
14
+ - Add `slack_allowed_usernames` to restrict Slack channel access
15
+
16
+ ## [0.11.0]
17
+
18
+ - Add Slack channel integration with system instructions and connection pooling
19
+ - Extract channels abstraction and ConversationEngine from Repl
20
+ - Add built-in safety guards with `/danger` bypass and progressive safety allowlists
21
+ - Add local model support with prompt truncation warnings
22
+ - Add `clear!` command to clear bot messages in thread
23
+ - Match code blocks in LLM results
24
+ - Fix long query display and add cost tracking to session viewer
25
+ - Strip quotes from session names when saving
26
+
27
+ ## [0.10.0]
28
+
29
+ - Add `/expand` command to view previous results
30
+ - Exclude previous output from context; add tool for LLM to retrieve it on demand
31
+ - Show summarized info per LLM call in `/debug`
32
+
33
+ ## [0.9.0]
34
+
35
+ - Add `/system` and `/context` commands to inspect what is being sent
36
+ - Omit huge output from tool results
37
+ - Don't cancel code execution on incorrect prompt answers
38
+ - Preserve code blocks when compacting; require manual `/compact`
39
+ - Fix authentication when neither method was applied
40
+ - Remove prompt to upgrade model on excessive tool calls
41
+
42
+ ## [0.8.0]
43
+
44
+ - Add authentication function support so host apps can avoid using basic auth
45
+ - Add `/think` and `/cost` commands with Sonnet vs Opus support
46
+ - Gracefully handle token limit exceeded errors
47
+
48
+ ## [0.7.0]
49
+
50
+ - Include binding variables and their classes in the Rails console context
51
+ - Add `ai_setup` command
52
+ - Add `/compact` mechanism for conversation management
53
+ - Catch errors and attempt to auto-fix them
54
+
55
+ ## [0.6.0]
56
+
57
+ - Add core memory (`rails_console_ai.md`) that persists across sessions in the system prompt
58
+ - Add `ai_init` command to seed core memory
59
+ - Allow reading partial files
60
+ - Fix rspec hanging issues
61
+
62
+ ## [0.5.0]
63
+
64
+ - Auto-accept single-step plans
65
+ - Support `>` shorthand to run code directly
66
+ - Add `script/release` for releases
67
+
68
+ ## [0.4.0]
69
+
70
+ - Fix resuming sessions repeatedly
71
+ - Fix terminal flashing/loading in production (kubectl)
72
+ - Better escaping during thinking output
73
+
74
+ ## [0.3.0]
75
+
76
+ - Add plan mechanism with "auto" execution mode
77
+ - Add session logging to DB with `/rails_console_ai` admin UI
78
+ - List and resume past sessions with pagination
79
+ - Add shift-tab for auto-execute mode
80
+ - Add usage display and debug toggle
81
+ - Store sessions incrementally; improved code segment display
82
+
83
+ ## [0.2.0]
84
+
85
+ - Add memory system with individual file storage
86
+ - Add `ask_user` tool
87
+ - Add registry cache
88
+ - Fix REPL up-key and ctrl-a navigation
89
+ - Show tool usage and model processing info
90
+ - Add token count information and debug ability
91
+ - Use tools-based approach instead of sending everything at once
92
+
93
+ ## [0.1.0]
94
+
95
+ - Initial implementation
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Frank Cort
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,328 @@
1
+ # RailsConsoleAI
2
+
3
+ Claude Code for your Rails Console.
4
+
5
+ ```
6
+ irb> ai "find the 5 most recent orders over $100"
7
+ Thinking...
8
+ -> list_tables
9
+ 12 tables: users, orders, line_items, products...
10
+ -> describe_table("orders")
11
+ 8 columns
12
+
13
+ Order.where("total > ?", 100).order(created_at: :desc).limit(5)
14
+
15
+ Execute? [y/N/edit/danger] y
16
+ => [#<Order id: 4821, ...>, ...]
17
+ ```
18
+
19
+ For complex tasks it builds multi-step plans, executing each step sequentially:
20
+
21
+ ```
22
+ ai> get the most recent salesforce token and count events via the API
23
+ Plan (2 steps):
24
+ 1. Find the most recent active Salesforce OAuth2 token
25
+ token = Oauth2Token.where(provider: "salesforce", active: true)
26
+ .order(updated_at: :desc).first
27
+ 2. Query event count via SOQL
28
+ api = SalesforceApi.new(step1)
29
+ api.query("SELECT COUNT(Id) FROM Event")
30
+
31
+ Accept plan? [y/N/a(uto)] a
32
+ ```
33
+
34
+ No context needed from you — it figures out your app on its own.
35
+
36
+ ## Install
37
+
38
+ ```ruby
39
+ # Gemfile
40
+ gem 'rails_console_ai', group: :development
41
+ ```
42
+
43
+ ```bash
44
+ bundle install
45
+ rails generate rails_console_ai:install
46
+ ```
47
+
48
+ Set your API key in the generated initializer or via env var (`ANTHROPIC_API_KEY`):
49
+
50
+ ```ruby
51
+ # config/initializers/rails_console_ai.rb
52
+ RailsConsoleAI.configure do |config|
53
+ config.api_key = 'sk-ant-...'
54
+ end
55
+ ```
56
+
57
+ ## Commands
58
+
59
+ | Command | What it does |
60
+ |---------|-------------|
61
+ | `ai "query"` | Ask, review generated code, confirm execution |
62
+ | `ai!` | Enter interactive mode (multi-turn conversation) |
63
+ | `ai? "query"` | Explain only, no execution |
64
+ | `ai_init` | Generate app guide for better AI context |
65
+ | `ai_setup` | Install session logging table |
66
+ | `ai_sessions` | List recent sessions |
67
+ | `ai_resume` | Resume a session by name or ID |
68
+ | `ai_memories` | Show stored memories |
69
+ | `ai_status` | Show current configuration |
70
+
71
+ ### Interactive Mode
72
+
73
+ `ai!` starts a conversation. Slash commands available inside:
74
+
75
+ | Command | What it does |
76
+ |---------|-------------|
77
+ | `/auto` | Toggle auto-execute (skip confirmations) |
78
+ | `/danger` | Toggle safe mode off/on (allow side effects) |
79
+ | `/safe` | Show safety guard status |
80
+ | `/compact` | Compress history into a summary (saves tokens) |
81
+ | `/usage` | Show token stats |
82
+ | `/cost` | Show per-model cost breakdown |
83
+ | `/think` | Upgrade to thinking model (Opus) for the rest of the session |
84
+ | `/debug` | Toggle debug summaries (context stats, cost per call) |
85
+ | `/expand <id>` | Show full omitted output |
86
+ | `/context` | Show conversation history as sent to the LLM |
87
+ | `/system` | Show the system prompt |
88
+ | `/name <label>` | Name the session for easy resume |
89
+
90
+ Prefix input with `>` to run Ruby directly (no LLM round-trip). The result is added to conversation context.
91
+
92
+ Say "think harder" in any query to auto-upgrade to the thinking model for that session. After 5+ tool rounds, you'll also be prompted to switch.
93
+
94
+ ## Features
95
+
96
+ - **Tool use** — AI introspects your schema, models, files, and code to write accurate queries
97
+ - **Multi-step plans** — complex tasks are broken into steps, executed sequentially with `step1`/`step2` references
98
+ - **Two-tier models** — defaults to Sonnet for speed/cost; `/think` upgrades to Opus when you need it
99
+ - **Cost tracking** — `/cost` shows per-model token usage and estimated spend
100
+ - **Memories** — AI saves what it learns about your app across sessions
101
+ - **App guide** — `ai_init` generates a guide injected into every system prompt
102
+ - **Sessions** — name, list, and resume interactive conversations (`ai_setup` to enable)
103
+ - **History compaction** — `/compact` summarizes long conversations to reduce cost and latency
104
+ - **Output trimming** — older execution outputs are automatically replaced with references; the LLM can recall them on demand via `recall_output`, and you can `/expand <id>` to see them
105
+ - **Debug mode** — `/debug` shows context breakdown, token counts, and per-call cost estimates before and after each LLM call
106
+ - **Safe mode** — configurable guards that block side effects (DB writes, HTTP mutations, email delivery) during AI code execution
107
+
108
+ ## Safety Guards
109
+
110
+ Safety guards prevent AI-generated code from causing side effects. When a guard blocks an operation, the user is prompted to re-run with safe mode disabled.
111
+
112
+ ### Built-in Guards
113
+
114
+ ```ruby
115
+ RailsConsoleAI.configure do |config|
116
+ config.use_builtin_safety_guard :database_writes # blocks INSERT/UPDATE/DELETE/DROP/etc.
117
+ config.use_builtin_safety_guard :http_mutations # blocks POST/PUT/PATCH/DELETE via Net::HTTP
118
+ config.use_builtin_safety_guard :mailers # disables ActionMailer delivery
119
+ end
120
+ ```
121
+
122
+ - **`:database_writes`** — intercepts the ActiveRecord connection adapter to block write SQL. Works on Rails 5+ with any database adapter.
123
+ - **`:http_mutations`** — intercepts `Net::HTTP#request` to block non-GET/HEAD/OPTIONS requests. Covers libraries built on Net::HTTP (HTTParty, RestClient, Faraday).
124
+ - **`:mailers`** — sets `ActionMailer::Base.perform_deliveries = false` during execution.
125
+
126
+ ### Custom Guards
127
+
128
+ Write your own guards using the around-block pattern:
129
+
130
+ ```ruby
131
+ RailsConsoleAI.configure do |config|
132
+ config.safety_guard :jobs do |&execute|
133
+ Sidekiq::Testing.fake! { execute.call }
134
+ end
135
+ end
136
+ ```
137
+
138
+ Raise `RailsConsoleAI::SafetyError` in your app code to trigger the safe mode prompt:
139
+
140
+ ```ruby
141
+ raise RailsConsoleAI::SafetyError, "Stripe charge blocked"
142
+ ```
143
+
144
+ ### Toggling Safe Mode
145
+
146
+ - **`/danger`** in interactive mode toggles all guards off/on for the session
147
+ - **`d`** at the `Execute? [y/N/edit/danger]` prompt disables guards for that single execution
148
+ - When a guard blocks an operation, the user is prompted: `Re-run with safe mode disabled? [y/N]`
149
+
150
+ ## LLM Providers
151
+
152
+ RailsConsoleAI supports four LLM providers. Each uses a two-tier model system: a default model for speed/cost, and a thinking model activated via `/think` or by saying "think harder".
153
+
154
+ ### Anthropic (default)
155
+
156
+ ```ruby
157
+ RailsConsoleAI.configure do |config|
158
+ config.provider = :anthropic
159
+ config.api_key = 'sk-ant-...' # or set ANTHROPIC_API_KEY env var
160
+ end
161
+ ```
162
+
163
+ Default model: `claude-sonnet-4-6`. Thinking model: `claude-opus-4-6`. Prompt caching is enabled automatically.
164
+
165
+ ### OpenAI
166
+
167
+ ```ruby
168
+ RailsConsoleAI.configure do |config|
169
+ config.provider = :openai
170
+ config.api_key = 'sk-...' # or set OPENAI_API_KEY env var
171
+ end
172
+ ```
173
+
174
+ Default model: `gpt-5.3-codex`. OpenAI applies prompt caching automatically on their end for prompts over 1024 tokens.
175
+
176
+ ### AWS Bedrock
177
+
178
+ Access frontier models (Claude, Mistral, DeepSeek, Llama) via your AWS account with pay-per-token pricing. No API key needed — authentication uses the AWS SDK credential chain (IAM roles, env vars, `~/.aws/credentials`).
179
+
180
+ ```ruby
181
+ # Gemfile
182
+ gem 'aws-sdk-bedrockruntime'
183
+ ```
184
+
185
+ ```ruby
186
+ RailsConsoleAI.configure do |config|
187
+ config.provider = :bedrock
188
+ config.bedrock_region = 'us-east-1'
189
+ # config.model = 'us.anthropic.claude-sonnet-4-6' # default
190
+ # config.thinking_model = 'us.anthropic.claude-opus-4-6-v1' # default
191
+ end
192
+ ```
193
+
194
+ Bedrock model IDs use the `us.` prefix for cross-region inference profiles (required for on-demand Anthropic models). Non-Anthropic models use their bare ID:
195
+
196
+ ```ruby
197
+ config.model = 'mistral.devstral-2-123b'
198
+ config.model = 'deepseek.v3.2'
199
+ ```
200
+
201
+ **Setup checklist:**
202
+ 1. Add `aws-sdk-bedrockruntime` to your Gemfile (it is not a hard dependency of the gem)
203
+ 2. Ensure AWS credentials are available to the SDK (env vars, IAM role, or `~/.aws/credentials`)
204
+ 3. For Anthropic models, submit the use case form in the Bedrock console (one-time, per account)
205
+ 4. The IAM role/user needs `bedrock:InvokeModel` permission
206
+
207
+ Prompt caching is automatically enabled for Anthropic models on Bedrock, reducing cost on multi-turn tool use conversations.
208
+
209
+ ### Local (Ollama / vLLM / OpenAI-compatible)
210
+
211
+ Run against a local model server. No API key required.
212
+
213
+ ```ruby
214
+ RailsConsoleAI.configure do |config|
215
+ config.provider = :local
216
+ config.local_url = 'http://localhost:11434'
217
+ config.local_model = 'qwen2.5:7b'
218
+ # config.local_api_key = nil # if your server requires auth
219
+ end
220
+ ```
221
+
222
+ Timeout is automatically raised to 300s minimum for local models to account for slower inference.
223
+
224
+ ## Configuration
225
+
226
+ ```ruby
227
+ RailsConsoleAI.configure do |config|
228
+ config.provider = :anthropic # :anthropic, :openai, :bedrock, :local
229
+ config.auto_execute = false # true to skip confirmations
230
+ config.session_logging = true # requires ai_setup
231
+ config.temperature = 0.2
232
+ config.timeout = 30 # HTTP timeout in seconds
233
+ config.max_tool_rounds = 200 # safety cap on tool-use loops
234
+ end
235
+ ```
236
+
237
+ ## Web UI Authentication
238
+
239
+ The engine mounts a session viewer at `/rails_console_ai`. By default it's open — you can protect it with basic auth or a custom authentication function.
240
+
241
+ ### Basic Auth
242
+
243
+ ```ruby
244
+ RailsConsoleAI.configure do |config|
245
+ config.admin_username = 'admin'
246
+ config.admin_password = ENV['CONSOLE_AGENT_PASSWORD']
247
+ end
248
+ ```
249
+
250
+ ### Custom Authentication
251
+
252
+ For apps with their own auth system, pass a proc to `authenticate`. It runs in the controller context, so you have access to `session`, `request`, `redirect_to`, etc.
253
+
254
+ ```ruby
255
+ RailsConsoleAI.configure do |config|
256
+ config.authenticate = proc {
257
+ user = User.find_by(id: session[:user_id])
258
+ unless user&.admin?
259
+ redirect_to '/login'
260
+ end
261
+ }
262
+ end
263
+ ```
264
+
265
+ When `authenticate` is set, `admin_username` / `admin_password` are ignored.
266
+
267
+ ## Additional Channels
268
+
269
+ RailsConsoleAI can run through different channels beyond the Rails console. Each channel is a separate process that connects the same AI engine to a different interface.
270
+
271
+ ### Slack
272
+
273
+ Run RailsConsoleAI as a Slack bot. Each Slack thread becomes an independent AI session with full tool use, multi-step plans, and safety guards always on.
274
+
275
+ #### Slack App Setup
276
+
277
+ 1. Create a new app at https://api.slack.com/apps → **Create New App** → **From scratch**
278
+
279
+ 2. **Enable Socket Mode** — Settings → Socket Mode → toggle ON. Generate an App-Level Token with the `connections:write` scope. Copy the `xapp-...` token.
280
+
281
+ 3. **Bot Token Scopes** — OAuth & Permissions → Bot Token Scopes, add:
282
+ - `chat:write`
283
+ - `channels:history` (public channels)
284
+ - `groups:history` (private channels, optional)
285
+ - `im:history` (direct messages)
286
+ - `users:read`
287
+
288
+ 4. **Event Subscriptions** — Event Subscriptions → toggle ON, then under "Subscribe to bot events" add:
289
+ - `message.channels` (public channels)
290
+ - `message.groups` (private channels, optional)
291
+ - `message.im` (direct messages)
292
+
293
+ 5. **App Home** — Show Tabs → toggle **Messages Tab** ON and check **"Allow users to send Slash commands and messages from the messages tab"**
294
+
295
+ 6. **Install to workspace** — Install App → Install to Workspace. Copy the `xoxb-...` Bot User OAuth Token.
296
+
297
+ 7. **Invite the bot** to a channel with `/invite @YourBotName`, or DM it directly.
298
+
299
+ #### Configuration
300
+
301
+ ```ruby
302
+ RailsConsoleAI.configure do |config|
303
+ config.slack_bot_token = ENV['SLACK_BOT_TOKEN'] # xoxb-...
304
+ config.slack_app_token = ENV['SLACK_APP_TOKEN'] # xapp-...
305
+
306
+ # Optional: restrict to specific Slack channel IDs
307
+ # config.slack_channel_ids = 'C1234567890,C0987654321'
308
+
309
+ # Required: which users the bot responds to (by display name)
310
+ config.slack_allowed_usernames = ['alice', 'bob'] # or 'ALL' for everyone
311
+ end
312
+ ```
313
+
314
+ #### Running
315
+
316
+ ```bash
317
+ bundle exec rake rails_console_ai:slack
318
+ ```
319
+
320
+ This starts a long-running process (run it separately from your web server). Each new message creates a session; threaded replies continue the conversation. The bot auto-executes code with safety guards always enabled — there is no `/danger` equivalent in Slack.
321
+
322
+ ## Requirements
323
+
324
+ Ruby >= 2.5, Rails >= 5.0, Faraday >= 1.0. For Bedrock: `aws-sdk-bedrockruntime` (loaded lazily, not a hard dependency).
325
+
326
+ ## License
327
+
328
+ MIT
@@ -0,0 +1,28 @@
1
+ module RailsConsoleAI
2
+ class ApplicationController < ActionController::Base
3
+ protect_from_forgery with: :exception
4
+
5
+ before_action :rails_console_ai_authenticate!
6
+
7
+ private
8
+
9
+ def rails_console_ai_authenticate!
10
+ if (auth = RailsConsoleAI.configuration.authenticate)
11
+ instance_exec(&auth)
12
+ else
13
+ username = RailsConsoleAI.configuration.admin_username
14
+ password = RailsConsoleAI.configuration.admin_password
15
+
16
+ unless username && password
17
+ head :unauthorized
18
+ return
19
+ end
20
+
21
+ authenticate_or_request_with_http_basic('RailsConsoleAI Admin') do |u, p|
22
+ ActiveSupport::SecurityUtils.secure_compare(u, username) &
23
+ ActiveSupport::SecurityUtils.secure_compare(p, password)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,16 @@
1
+ module RailsConsoleAI
2
+ class SessionsController < ApplicationController
3
+ PER_PAGE = 50
4
+
5
+ def index
6
+ @page = [params[:page].to_i, 1].max
7
+ @total = Session.count
8
+ @total_pages = (@total / PER_PAGE.to_f).ceil
9
+ @sessions = Session.recent.offset((@page - 1) * PER_PAGE).limit(PER_PAGE)
10
+ end
11
+
12
+ def show
13
+ @session = Session.find(params[:id])
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,56 @@
1
+ module RailsConsoleAI
2
+ module SessionsHelper
3
+ def estimated_cost(session)
4
+ pricing = Configuration::PRICING[session.model]
5
+ return nil unless pricing
6
+
7
+ (session.input_tokens * pricing[:input]) + (session.output_tokens * pricing[:output])
8
+ end
9
+
10
+ def format_cost(session)
11
+ cost = estimated_cost(session)
12
+ return '-' unless cost
13
+
14
+ cost < 0.01 ? "<$0.01" : "$#{'%.2f' % cost}"
15
+ end
16
+
17
+ # Convert ANSI escape codes to HTML spans for terminal-style rendering
18
+ def ansi_to_html(text)
19
+ return '' if text.nil? || text.empty?
20
+
21
+ color_map = {
22
+ '30' => '#000', '31' => '#e74c3c', '32' => '#2ecc71', '33' => '#f39c12',
23
+ '34' => '#3498db', '35' => '#9b59b6', '36' => '#1abc9c', '37' => '#ecf0f1',
24
+ '90' => '#888', '91' => '#ff6b6b', '92' => '#69db7c', '93' => '#ffd43b',
25
+ '94' => '#74c0fc', '95' => '#da77f2', '96' => '#63e6be', '97' => '#fff'
26
+ }
27
+
28
+ escaped = h(text).to_str
29
+
30
+ # Process ANSI codes: colors, bold, dim, reset
31
+ escaped.gsub!(/\e\[([0-9;]+)m/) do
32
+ codes = $1.split(';')
33
+ if codes.include?('0') || $1 == '0'
34
+ '</span>'
35
+ else
36
+ styles = []
37
+ codes.each do |code|
38
+ case code
39
+ when '1' then styles << 'font-weight:bold'
40
+ when '2' then styles << 'opacity:0.6'
41
+ when '4' then styles << 'text-decoration:underline'
42
+ else
43
+ styles << "color:#{color_map[code]}" if color_map[code]
44
+ end
45
+ end
46
+ styles.empty? ? '' : "<span style=\"#{styles.join(';')}\">"
47
+ end
48
+ end
49
+
50
+ # Clean up any remaining escape sequences
51
+ escaped.gsub!(/\e\[[0-9;]*[A-Za-z]/, '')
52
+
53
+ escaped.html_safe
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,23 @@
1
+ module RailsConsoleAI
2
+ class Session < ActiveRecord::Base
3
+ self.table_name = 'rails_console_ai_sessions'
4
+
5
+ validates :query, presence: true
6
+ validates :conversation, presence: true
7
+ validates :mode, presence: true, inclusion: { in: %w[one_shot interactive explain slack] }
8
+
9
+ scope :recent, -> { order(created_at: :desc) }
10
+ scope :named, ->(name) { where(name: name) }
11
+ scope :search, ->(q) { where("name LIKE ? OR query LIKE ?", "%#{q}%", "%#{q}%") }
12
+
13
+ def self.connection
14
+ klass = RailsConsoleAI.configuration.connection_class
15
+ if klass
16
+ klass = Object.const_get(klass) if klass.is_a?(String)
17
+ klass.connection
18
+ else
19
+ super
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,84 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>RailsConsoleAI Admin</title>
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <style>
7
+ * { margin: 0; padding: 0; box-sizing: border-box; }
8
+ body {
9
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
10
+ background: #f5f5f5;
11
+ color: #333;
12
+ line-height: 1.6;
13
+ }
14
+ .header {
15
+ background: #1a1a2e;
16
+ color: #fff;
17
+ padding: 16px 24px;
18
+ display: flex;
19
+ align-items: center;
20
+ justify-content: space-between;
21
+ }
22
+ .header h1 { font-size: 18px; font-weight: 600; }
23
+ .header a { color: #8be9fd; text-decoration: none; font-size: 14px; }
24
+ .container { max-width: 1200px; margin: 24px auto; padding: 0 24px; }
25
+ table { width: 100%; border-collapse: collapse; background: #fff; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
26
+ th { background: #f8f9fa; text-align: left; padding: 12px 16px; font-size: 13px; font-weight: 600; color: #555; border-bottom: 2px solid #e9ecef; }
27
+ td { padding: 10px 16px; border-bottom: 1px solid #f0f0f0; font-size: 14px; }
28
+ tr:hover td { background: #f8f9fa; }
29
+ a { color: #4a6fa5; text-decoration: none; }
30
+ a:hover { text-decoration: underline; }
31
+ .badge {
32
+ display: inline-block; padding: 2px 8px; border-radius: 12px;
33
+ font-size: 11px; font-weight: 600; text-transform: uppercase;
34
+ }
35
+ .badge-one_shot { background: #d4edda; color: #155724; }
36
+ .badge-interactive { background: #cce5ff; color: #004085; }
37
+ .badge-explain { background: #fff3cd; color: #856404; }
38
+ .meta-card {
39
+ background: #fff; border-radius: 8px; padding: 20px;
40
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1); margin-bottom: 24px;
41
+ }
42
+ .meta-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; }
43
+ .meta-item label { font-size: 11px; font-weight: 600; color: #888; text-transform: uppercase; display: block; }
44
+ .meta-item span { font-size: 14px; color: #333; }
45
+ .terminal {
46
+ background: #1e1e1e; border-radius: 8px; overflow: hidden;
47
+ box-shadow: 0 2px 8px rgba(0,0,0,0.2);
48
+ }
49
+ .terminal pre {
50
+ background: transparent; color: #d4d4d4;
51
+ padding: 16px 20px; margin: 0;
52
+ font-family: "SF Mono", "Monaco", "Menlo", "Consolas", monospace;
53
+ font-size: 13px; line-height: 1.5;
54
+ overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;
55
+ }
56
+ .mono { font-family: "SF Mono", "Monaco", "Menlo", "Consolas", monospace; font-size: 13px; }
57
+ .query-cell { max-width: 400px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
58
+ .text-muted { color: #888; }
59
+ .back-link { margin-bottom: 16px; display: inline-block; }
60
+ .check { color: #28a745; }
61
+ .cross { color: #dc3545; }
62
+ .pagination {
63
+ display: flex; justify-content: space-between; align-items: center;
64
+ margin-top: 16px; padding: 12px 0;
65
+ }
66
+ .pagination a {
67
+ padding: 6px 14px; background: #fff; border: 1px solid #ddd; border-radius: 4px;
68
+ font-size: 13px; color: #4a6fa5;
69
+ }
70
+ .pagination a:hover { background: #f8f9fa; text-decoration: none; }
71
+ .pagination .disabled { padding: 6px 14px; font-size: 13px; color: #ccc; }
72
+ .pagination .page-info { font-size: 13px; color: #888; }
73
+ </style>
74
+ </head>
75
+ <body>
76
+ <div class="header">
77
+ <h1>RailsConsoleAI Admin</h1>
78
+ <a href="<%= rails_console_ai.root_path %>">Sessions</a>
79
+ </div>
80
+ <div class="container">
81
+ <%= yield %>
82
+ </div>
83
+ </body>
84
+ </html>