nanobot 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: cf9ff959e39a3c59e3e9b1df3ab62d041db357bbf52d6f1627dbec8c644748b6
4
+ data.tar.gz: 04bde9184573f878c85fa948ffcd98cd266e965198278369bb88621aaeb9829d
5
+ SHA512:
6
+ metadata.gz: 389c4d9c5f6d544f8ef8e0366a5cb6404fb3a0edc95c791afa1f094d101350ff0147a904a51e13fc70c3490ad8375816fed3df57b2e0ae1135946bc3c73bfb03
7
+ data.tar.gz: bedcce8584550de7cb9e8a9487511a693ea936be49d8741c3560b2f93b4b7bf0bd60eb2f7a07da8f42f5d68abdfd229d77099f478137f42de2043da6d76c0abc
data/Gemfile ADDED
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ ruby '~> 4.0.1'
6
+
7
+ gem 'faraday', '~> 2.7' # HTTP & Network
8
+ gem 'fugit', '~> 1.8' # Cron/duration/timestamp parsing
9
+ gem 'json', '~> 2.6' # JSON & Serialization
10
+ gem 'logger', '~> 1.5' # Logging
11
+ gem 'nokogiri', '~> 1.15' # Web parsing
12
+ gem 'ruby_llm' # LLM Integration
13
+ gem 'thor', '~> 1.3' # CLI & Commands
14
+ gem 'webrick', '~> 1.8' # HTTP Gateway channel
15
+
16
+ # Optional channel dependencies (install as needed)
17
+ gem 'discordrb', require: false
18
+ gem 'mail', require: false
19
+ gem 'slack-ruby-client', require: false
20
+ gem 'telegram-bot-ruby', require: false
21
+
22
+ # Development & Testing
23
+ group :development, :test do
24
+ gem 'amazing_print'
25
+ gem 'debug', '~> 1.9'
26
+ gem 'rspec', '~> 3.12'
27
+ gem 'rubocop', '~> 1.84'
28
+ gem 'rubocop-rspec', '~> 3.0', require: false
29
+ gem 'simplecov', '~> 0.22', require: false
30
+ gem 'timecop', '~> 0.9'
31
+ gem 'webmock', '~> 3.19'
32
+ end
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nanobot Contributors
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,530 @@
1
+ # Nanobot.rb
2
+
3
+ [![Ruby](https://img.shields.io/badge/Ruby-4.0%2B-blue)](https://www.ruby-lang.org/)
4
+
5
+ A minimal, complete personal AI assistant framework. Small enough to read in
6
+ an afternoon, functional enough to use every day, clean enough to fork and
7
+ build on.
8
+
9
+ ## Overview
10
+
11
+ Nanobot.rb is a Ruby port of [Nanobot](https://github.com/HKUDS/nanobot) - a personal AI assistant framework designed for
12
+ simplicity, privacy, and readability. It provides the essential building blocks of an AI
13
+ agent and stops there. Major new features belong in forks, not in this codebase.
14
+
15
+ - **Multi-provider LLM support** via [RubyLLM](https://rubyllm.com/) - Anthropic, OpenAI, Gemini, DeepSeek, Ollama, OpenRouter, and [many more](https://rubyllm.com/available-models/)
16
+ - **Built-in tools** - file operations, shell execution, web search, web fetch
17
+ - **Task scheduling** - one-shot reminders, recurring intervals, and cron expressions
18
+ - **Six channels** - CLI, Slack, Telegram, Discord, Email, HTTP Gateway
19
+ - **Persistent memory** - long-term memory and daily notes across sessions
20
+ - **Security-aware** - workspace sandboxing, command filtering, access control
21
+
22
+ See [docs/goals.md](docs/goals.md) for the project philosophy and
23
+ [docs/use-cases.md](docs/use-cases.md) for detailed usage scenarios.
24
+
25
+ ## Table of Contents
26
+
27
+ - [Installation](#installation)
28
+ - [Quick Start](#quick-start)
29
+ - [Configuration](#configuration)
30
+ - [Usage](#usage)
31
+ - [Built-in Tools](#built-in-tools)
32
+ - [Workspace Structure](#workspace-structure)
33
+ - [Security](#security)
34
+ - [Development](#development)
35
+ - [Architecture](#architecture)
36
+ - [Forking and Extending](#forking-and-extending)
37
+ - [Contributing](#contributing)
38
+ - [License](#license)
39
+
40
+ ## Installation
41
+
42
+ ### Prerequisites
43
+
44
+ - Ruby 4.0.1 or higher
45
+ - Bundler gem
46
+ - An LLM API key (OpenAI, Anthropic, OpenRouter, etc.)
47
+
48
+ ### From Source
49
+
50
+ ```bash
51
+ # Clone the repository
52
+ git clone https://github.com/nanobot-rb/nanobot.rb
53
+ cd nanobot.rb
54
+
55
+ # Install dependencies
56
+ bundle install
57
+
58
+ # Run tests to verify installation
59
+ bundle exec rspec
60
+ ```
61
+
62
+ ### As a Gem (Coming Soon)
63
+
64
+ ```bash
65
+ gem install nanobot
66
+ ```
67
+
68
+ ## Quick Start
69
+
70
+ ### 1. Initialize Nanobot
71
+
72
+ ```bash
73
+ bundle exec bin/nanobot onboard
74
+ ```
75
+
76
+ This creates the configuration directory at `~/.nanobot/` with:
77
+ - `config.json` - Main configuration file
78
+ - `workspace/` - Agent workspace directory
79
+ - `sessions/` - Conversation history storage
80
+
81
+ ### 2. Configure API Keys
82
+
83
+ Edit `~/.nanobot/config.json` and add your API keys:
84
+
85
+ ```json
86
+ {
87
+ "providers": {
88
+ "anthropic": {
89
+ "api_key": "sk-ant-api03-..."
90
+ },
91
+ "openai": {
92
+ "api_key": "sk-..."
93
+ },
94
+ "gemini": {
95
+ "api_key": "AIza..."
96
+ },
97
+ "deepseek": {
98
+ "api_key": "sk-..."
99
+ },
100
+ "ollama": {
101
+ "api_base": "http://localhost:11434"
102
+ },
103
+ "openrouter": {
104
+ "api_key": "sk-or-v1-..."
105
+ }
106
+ }
107
+ }
108
+ ```
109
+
110
+ You only need to configure the providers you plan to use. For the full list
111
+ of supported providers and models, see [RubyLLM Available Models](https://rubyllm.com/available-models/).
112
+
113
+ ### 3. Start Chatting
114
+
115
+ ```bash
116
+ # Interactive mode
117
+ bundle exec bin/nanobot agent
118
+
119
+ # Single message
120
+ bundle exec bin/nanobot agent -m "What's the weather like?"
121
+
122
+ # With specific model
123
+ bundle exec bin/nanobot agent --model openai/gpt-4o-mini -m "Write a haiku"
124
+ ```
125
+
126
+ ## Configuration
127
+
128
+ Configuration is stored in `~/.nanobot/config.json`:
129
+
130
+ ```json
131
+ {
132
+ "providers": {
133
+ "openrouter": {
134
+ "api_key": "sk-or-v1-...",
135
+ "api_base": "https://openrouter.ai/api/v1"
136
+ },
137
+ "anthropic": {
138
+ "api_key": "sk-ant-..."
139
+ },
140
+ "openai": {
141
+ "api_key": "sk-..."
142
+ }
143
+ },
144
+ "provider": "anthropic",
145
+ "agents": {
146
+ "defaults": {
147
+ "model": "claude-haiku-4-5",
148
+ "workspace": "~/.nanobot/workspace",
149
+ "max_tokens": 4096,
150
+ "temperature": 0.7,
151
+ "max_tool_iterations": 20
152
+ }
153
+ },
154
+ "tools": {
155
+ "web": {
156
+ "search": {
157
+ "api_key": "BRAVE_SEARCH_API_KEY"
158
+ }
159
+ },
160
+ "exec": {
161
+ "timeout": 60
162
+ },
163
+ "restrict_to_workspace": true
164
+ }
165
+ }
166
+ ```
167
+
168
+ ### Configuration Options
169
+
170
+ | Section | Key | Description | Default |
171
+ |--------------------|------------------------|-------------------------------------|--------------------------|
172
+ | `provider ` | | Active provider name | `anthropic` |
173
+ | `agents.defaults ` | `model` | Default LLM model | `claude-haiku-4-5` |
174
+ | | `workspace` | Agent workspace directory | `~/.nanobot/workspace` |
175
+ | | `max_tokens` | Maximum response tokens | `4096` |
176
+ | | `temperature` | LLM temperature (0-1) | `0.7` |
177
+ | | `max_tool_iterations` | Max tool execution cycles | `20` |
178
+ | `tools` | `restrict_to_workspace `| Sandbox file/shell operations | `true` |
179
+ | `tools.exec` | `timeout` | Command execution timeout (seconds) | `60` |
180
+ | `scheduler` | `enabled` | Enable task scheduling | `true` |
181
+ | | `tick_interval` | Seconds between schedule checks | `15` |
182
+
183
+ ## Usage
184
+
185
+ ### Command Line Interface
186
+
187
+ ```bash
188
+ # Start interactive agent
189
+ bundle exec bin/nanobot agent
190
+
191
+ # Single message mode
192
+ bundle exec bin/nanobot agent -m "Calculate fibonacci(10)"
193
+
194
+ # Use specific model
195
+ bundle exec bin/nanobot agent --model openai/gpt-4o-mini
196
+
197
+ # Check configuration
198
+ bundle exec bin/nanobot status
199
+
200
+ # Initialize configuration
201
+ bundle exec bin/nanobot onboard
202
+ ```
203
+
204
+ ### Ruby API
205
+
206
+ ```ruby
207
+ require 'nanobot'
208
+
209
+ # Load configuration
210
+ config = Nanobot::Config::Loader.load
211
+
212
+ # Create provider
213
+ provider = Nanobot::Providers::RubyLLMProvider.new(
214
+ api_key: config.api_key,
215
+ provider: config.provider,
216
+ default_model: config.agents.defaults.model
217
+ )
218
+
219
+ # Create message bus and agent loop
220
+ bus = Nanobot::Bus::MessageBus.new
221
+ agent = Nanobot::Agent::Loop.new(
222
+ bus: bus,
223
+ provider: provider,
224
+ workspace: File.expand_path(config.agents.defaults.workspace)
225
+ )
226
+
227
+ # Process a single message directly
228
+ response = agent.process_direct("What is 2+2?")
229
+ puts response
230
+ ```
231
+
232
+ ## Built-in Tools
233
+
234
+ Nanobot uses **RubyLLM-native tools** that inherit from `RubyLLM::Tool` for seamless integration with the LLM provider layer.
235
+
236
+ ### File Operations
237
+
238
+ - **ReadFile** - Read file contents
239
+ - Supports workspace sandboxing
240
+ - Returns full file content
241
+
242
+ - **WriteFile** - Create or overwrite files
243
+ - Auto-creates parent directories
244
+ - Workspace sandboxing support
245
+
246
+ - **EditFile** - Replace text in files
247
+ - Performs exact string replacement
248
+ - Validates single occurrence to avoid ambiguity
249
+
250
+ - **ListDir** - List directory contents
251
+ - Shows files and directories
252
+ - Workspace sandboxing support
253
+
254
+ ### Shell Execution
255
+
256
+ - **Exec** - Execute shell commands with safety filters
257
+ - Configurable timeout protection
258
+ - Dangerous command blocking (rm -rf, shutdown, etc.)
259
+ - Optional workspace sandboxing
260
+ - Captures stdout, stderr, and exit code
261
+
262
+ ### Web Tools
263
+
264
+ - **WebSearch** - Search the web using Brave Search API
265
+ - Requires `BRAVE_SEARCH_API_KEY` environment variable or config
266
+ - Returns formatted search results
267
+
268
+ - **WebFetch** - Fetch and parse web pages
269
+ - Extracts main content from HTML
270
+ - Removes scripts and styles
271
+ - Returns title, URL, and cleaned text
272
+
273
+ ### Task Scheduling
274
+
275
+ - **ScheduleAdd** - Create scheduled tasks
276
+ - One-shot `at` (ISO 8601 timestamp): "remind me at 3:30pm"
277
+ - Recurring `every` (duration): "check every 30 minutes"
278
+ - Cron expressions: "every weekday at 9am" (`0 9 * * 1-5`)
279
+ - Optional timezone and delivery target (channel + chat)
280
+
281
+ - **ScheduleList** - List all scheduled tasks with status and next run time
282
+
283
+ - **ScheduleRemove** - Remove a scheduled task by full or partial ID
284
+
285
+ Schedules fire by publishing synthetic messages to the message bus, so the
286
+ agent loop processes them like any other message. Results can be routed to a
287
+ specific channel (e.g., Slack) via the `deliver_to` option. Schedule tools
288
+ are only available in `serve` mode where the background scheduler is running.
289
+
290
+ ### Tool Architecture
291
+
292
+ Tools inherit from `RubyLLM::Tool` and follow the pattern:
293
+
294
+ ```ruby
295
+ class MyTool < RubyLLM::Tool
296
+ description 'Tool description for the LLM'
297
+ param :arg_name, desc: 'Argument description', required: true
298
+
299
+ def initialize(**options)
300
+ super()
301
+ @options = options
302
+ end
303
+
304
+ def execute(arg_name:)
305
+ # Tool logic here
306
+ "Result"
307
+ end
308
+ end
309
+ ```
310
+
311
+ The LLM receives properly formatted tool definitions and can call them with structured arguments.
312
+
313
+ ## Workspace Structure
314
+
315
+ ```
316
+ ~/.nanobot/
317
+ ├── config.json # Main configuration
318
+ ├── sessions/
319
+ │ └── *.jsonl # Conversation history
320
+ └── workspace/
321
+ ├── AGENTS.md # Agent personality and behavior
322
+ ├── SOUL.md # Core values and principles
323
+ ├── USER.md # User profile and preferences
324
+ ├── TOOLS.md # Custom tool documentation
325
+ ├── IDENTITY.md # Agent identity (name, vibe, emoji, avatar)
326
+ └── memory/
327
+ ├── MEMORY.md # Long-term persistent memory
328
+ └── YYYY-MM-DD.md # Daily notes (auto-created)
329
+ ```
330
+
331
+ ### Bootstrap Files
332
+
333
+ Bootstrap files in the workspace customize agent behavior:
334
+
335
+ - **AGENTS.md**: Define agent personality, expertise, and response style
336
+ - **SOUL.md**: Core values and ethical guidelines
337
+ - **USER.md**: User preferences and context
338
+ - **TOOLS.md**: Documentation for custom tools
339
+ - **IDENTITY.md**: Agent identity — name, creature type, vibe, emoji, and avatar (see [OpenClaw IDENTITY spec](https://docs.openclaw.ai/reference/templates/IDENTITY))
340
+
341
+ ## Security
342
+
343
+ ### Threat Model
344
+
345
+ Nanobot.rb is designed as a **personal assistant for trusted environments**. It
346
+ is adequate for single-user, self-hosted use with proper configuration. It is
347
+ **not hardened for multi-tenant or adversarial deployments**. If you expose
348
+ channels to untrusted users, additional hardening is required — configure
349
+ `allow_from` whitelists, enable workspace sandboxing, and add tool confirmation
350
+ callbacks.
351
+
352
+ ### Workspace Sandboxing
353
+
354
+ File and shell operations are sandboxed to the workspace directory by default
355
+ (`restrict_to_workspace: true`). This prevents the agent from accessing or
356
+ modifying files outside its workspace. The sandbox resolves symlinks to block
357
+ escape attempts. Set `restrict_to_workspace: false` only if you understand the
358
+ risk and trust the agent with full filesystem access.
359
+
360
+ ### Command Filtering
361
+
362
+ The shell tool blocks common dangerous patterns (`rm -rf`, `shutdown`, `dd`,
363
+ fork bombs, etc.) via a denylist. **This is not a true security boundary** — an
364
+ LLM or attacker can bypass it through nested shells, alternative commands, or
365
+ encoding tricks. It prevents accidents, not attacks. For strong isolation, use
366
+ OS-level sandboxing (containers, seccomp, etc.).
367
+
368
+ ### Access Control
369
+
370
+ - Channel-level user whitelisting via `allow_from`
371
+ - Empty `allow_from` **allows all users** (a warning is logged)
372
+ - Non-empty `allow_from` restricts to specified users only
373
+ - For channels exposed to untrusted networks, always configure explicit
374
+ whitelists
375
+
376
+ ### SSRF Protection
377
+
378
+ The web fetch tool validates URLs and blocks requests to private IP ranges
379
+ (127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, link-local, and
380
+ IPv6 equivalents). Redirects are re-validated at each hop. Responses are
381
+ capped at 1 MB.
382
+
383
+ ### Credential Storage
384
+
385
+ API keys and tokens are stored as **plaintext JSON** in `~/.nanobot/config.json`
386
+ with `0600` file permissions. Session files are similarly protected. There is no
387
+ encryption at rest — if your machine is compromised, credentials are exposed.
388
+ Protect your `~/.nanobot/` directory accordingly.
389
+
390
+ ## Development
391
+
392
+ ### Running Tests
393
+
394
+ ```bash
395
+ # Run all tests
396
+ bundle exec rspec
397
+
398
+ # Run with coverage report
399
+ bundle exec rspec
400
+
401
+ # Run specific test file
402
+ bundle exec rspec spec/agent/loop_spec.rb
403
+ ```
404
+
405
+ Run `bundle exec rspec` to see current test coverage.
406
+
407
+ ### Code Style
408
+
409
+ ```bash
410
+ # Check code style
411
+ bundle exec rubocop
412
+
413
+ # Auto-fix issues
414
+ bundle exec rubocop -A
415
+ ```
416
+
417
+ ### Project Structure
418
+
419
+ ```
420
+ lib/nanobot/
421
+ ├── agent/
422
+ │ ├── loop.rb # Core agent processing loop
423
+ │ ├── context.rb # System prompt builder
424
+ │ ├── memory.rb # Memory management
425
+ │ └── tools/
426
+ │ ├── filesystem.rb # File operations (read, write, edit, list)
427
+ │ ├── schedule.rb # Task scheduling (add, list, remove)
428
+ │ ├── shell.rb # Shell execution with safety filters
429
+ │ └── web.rb # Web search and fetch
430
+ ├── bus/
431
+ │ ├── events.rb # Event definitions
432
+ │ └── message_bus.rb # Message routing
433
+ ├── channels/
434
+ │ ├── base.rb # Channel interface
435
+ │ ├── manager.rb # Channel orchestration
436
+ │ ├── slack.rb # Slack integration
437
+ │ ├── telegram.rb # Telegram integration
438
+ │ ├── discord.rb # Discord integration
439
+ │ ├── email.rb # Email (IMAP/SMTP) integration
440
+ │ └── gateway.rb # HTTP Gateway
441
+ ├── cli/
442
+ │ └── commands.rb # CLI implementation
443
+ ├── config/
444
+ │ ├── schema.rb # Configuration schema
445
+ │ └── loader.rb # Config loading/validation
446
+ ├── providers/
447
+ │ ├── base.rb # Provider interface
448
+ │ └── rubyllm_provider.rb # RubyLLM integration
449
+ ├── scheduler/
450
+ │ ├── store.rb # Schedule CRUD and JSON persistence
451
+ │ └── service.rb # Background tick thread, fires due jobs
452
+ ├── session/
453
+ │ └── manager.rb # Session persistence
454
+ └── version.rb # Version constant
455
+ ```
456
+
457
+ ## Architecture
458
+
459
+ ### Message Flow
460
+
461
+ ```
462
+ User Input → Channel → Message Bus → Agent Loop → LLM Provider
463
+ ↓ ↓
464
+ Session Manager Tool System
465
+ ↓ ↓
466
+ JSONL Storage Tool Execution
467
+ ```
468
+
469
+ ### Core Components
470
+
471
+ - **Message Bus**: Queue-based message routing with pub/sub pattern
472
+ - **Agent Loop**: Tool-calling loop with LLM integration
473
+ - **Tool System**: RubyLLM-based tools directly instantiated by the agent
474
+ - **Session Manager**: JSONL-based conversation persistence
475
+ - **Context Builder**: System prompt assembly from bootstrap files
476
+ - **Memory Store**: Long-term and daily memory management
477
+
478
+ ## Forking and Extending
479
+
480
+ Nanobot.rb is designed to be forked. The architecture is modular so you can
481
+ add tools, channels, providers, or entirely new capabilities without fighting
482
+ the codebase.
483
+
484
+ ### Adding Tools
485
+
486
+ Tools inherit from `RubyLLM::Tool`:
487
+
488
+ ```ruby
489
+ class WeatherTool < RubyLLM::Tool
490
+ description 'Get current weather for a location'
491
+ param :location, desc: 'City name or coordinates', required: true
492
+
493
+ def execute(location:)
494
+ "Weather in #{location}: Sunny, 72F"
495
+ end
496
+ end
497
+ ```
498
+
499
+ ### Adding Channels
500
+
501
+ Channels extend `Nanobot::Channels::BaseChannel` and implement `start`,
502
+ `stop`, and `send`.
503
+
504
+ See [docs/goals.md](docs/goals.md) for what belongs in a fork vs. this repo.
505
+
506
+ ## Contributing
507
+
508
+ Contributions that improve what exists are welcome - bug fixes, test coverage,
509
+ documentation, security hardening, and code clarity. See
510
+ [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
511
+
512
+ New features that expand the scope (streaming, MCP, RAG, multi-agent, etc.)
513
+ belong in a fork. The architecture is designed to support this.
514
+
515
+ ## License
516
+
517
+ MIT License - see [LICENSE](LICENSE) file for details.
518
+
519
+ ## Credits
520
+
521
+ This is a Ruby port of the original [Nanobot](https://github.com/HKUDS/nanobot) Python project by the [Data Intelligence Lab at the University of Hong Kong (HKUDS)](https://github.com/HKUDS). All credit for the original design and architecture goes to them.
522
+
523
+ Multi-provider LLM support is powered by [RubyLLM](https://rubyllm.com/) by [Carmine Paolino](https://github.com/crmne).
524
+
525
+ ## Support
526
+
527
+ - **Issues**: [GitHub Issues](https://github.com/nanobot-rb/nanobot.rb/issues)
528
+ - **Discussions**: [GitHub Discussions](https://github.com/nanobot-rb/nanobot.rb/discussions)
529
+ - **Goals**: [docs/goals.md](docs/goals.md)
530
+ - **Use Cases**: [docs/use-cases.md](docs/use-cases.md)
data/bin/nanobot ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative '../lib/nanobot'
5
+ require_relative '../lib/nanobot/cli/commands'
6
+
7
+ Nanobot::CLI::Commands.start(ARGV)
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Record integration test fixtures against live LLM providers.
4
+ # Requires API keys configured in ~/.nanobot/config.json.
5
+ #
6
+ # Usage:
7
+ # bin/record-integrations # record all models
8
+ # bin/record-integrations anthropic # record a single provider
9
+
10
+ set -euo pipefail
11
+
12
+ MODELS=(
13
+ "anthropic:claude-haiku-4-5"
14
+ "openai:gpt-5-mini"
15
+ "gemini:gemini-flash-latest"
16
+ )
17
+
18
+ run_recording() {
19
+ local provider="${1%%:*}"
20
+ local model="${1##*:}"
21
+
22
+ local cmd="NANOBOT_INTEGRATION_RECORD=true \\
23
+ NANOBOT_INTEGRATION_PROVIDER=${provider} \\
24
+ NANOBOT_INTEGRATION_MODEL=${model} \\
25
+ bundle exec rspec spec/integration"
26
+ echo "==> ${cmd}"
27
+ eval "$cmd"
28
+ echo ""
29
+ }
30
+
31
+ filter="${1:-}"
32
+
33
+ for entry in "${MODELS[@]}"; do
34
+ provider="${entry%%:*}"
35
+ if [ -z "$filter" ] || [ "$filter" = "$provider" ]; then
36
+ run_recording "$entry"
37
+ fi
38
+ done