From 097360ad12d2ea73ac4d38552889d97702d9a889 Mon Sep 17 00:00:00 2001 From: Timur Gordon <31495328+timurgordon@users.noreply.github.com> Date: Mon, 20 Oct 2025 22:24:25 +0200 Subject: [PATCH] first commit --- .gitignore | 9 + Cargo.toml | 35 ++ EXAMPLES.md | 454 ++++++++++++++++++++++++++ MULTI_INSTANCE.md | 341 +++++++++++++++++++ PREDEFINED_INSTANCES.md | 387 ++++++++++++++++++++++ QUICKSTART.md | 190 +++++++++++ README.md | 125 +++++++ RUNNER.md | 328 +++++++++++++++++++ STRUCTURE.md | 317 ++++++++++++++++++ docs/ARCHITECTURE.md | 426 ++++++++++++++++++++++++ docs/DERIVE_MACRO.md | 195 +++++++++++ docs/specs/osiris-mvp.md | 525 ++++++++++++++++++++++++++++++ examples/README.md | 157 +++++++++ examples/basic_usage.rs | 200 ++++++++++++ examples/custom_object.rs | 295 +++++++++++++++++ osiris_derive/Cargo.toml | 12 + osiris_derive/src/lib.rs | 202 ++++++++++++ scripts/multi_instance.rhai | 60 ++++ scripts/predefined_instances.rhai | 60 ++++ scripts/test_event.rhai | 34 ++ scripts/test_note.rhai | 36 ++ src/bin/runner/engine.rs | 73 +++++ src/bin/runner/main.rs | 135 ++++++++ src/config/mod.rs | 60 ++++ src/config/model.rs | 55 ++++ src/error.rs | 46 +++ src/index/field_index.rs | 139 ++++++++ src/index/mod.rs | 3 + src/interfaces/cli.rs | 408 +++++++++++++++++++++++ src/interfaces/mod.rs | 3 + src/lib.rs | 23 ++ src/main.rs | 22 ++ src/objects/event/mod.rs | 139 ++++++++ src/objects/event/rhai.rs | 64 ++++ src/objects/mod.rs | 5 + src/objects/note/mod.rs | 78 +++++ src/objects/note/rhai.rs | 67 ++++ src/retrieve/mod.rs | 5 + src/retrieve/query.rs | 74 +++++ src/retrieve/search.rs | 150 +++++++++ src/rhai_support/instance.rs | 121 +++++++ src/rhai_support/mod.rs | 12 + src/store/base_data.rs | 78 +++++ src/store/generic_store.rs | 124 +++++++ src/store/herodb_client.rs | 161 +++++++++ src/store/mod.rs | 11 + src/store/object.rs | 160 +++++++++ src/store/object_trait.rs | 108 ++++++ 48 files changed, 6712 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 EXAMPLES.md create mode 100644 MULTI_INSTANCE.md create mode 100644 PREDEFINED_INSTANCES.md create mode 100644 QUICKSTART.md create mode 100644 README.md create mode 100644 RUNNER.md create mode 100644 STRUCTURE.md create mode 100644 docs/ARCHITECTURE.md create mode 100644 docs/DERIVE_MACRO.md create mode 100644 docs/specs/osiris-mvp.md create mode 100644 examples/README.md create mode 100644 examples/basic_usage.rs create mode 100644 examples/custom_object.rs create mode 100644 osiris_derive/Cargo.toml create mode 100644 osiris_derive/src/lib.rs create mode 100644 scripts/multi_instance.rhai create mode 100644 scripts/predefined_instances.rhai create mode 100644 scripts/test_event.rhai create mode 100644 scripts/test_note.rhai create mode 100644 src/bin/runner/engine.rs create mode 100644 src/bin/runner/main.rs create mode 100644 src/config/mod.rs create mode 100644 src/config/model.rs create mode 100644 src/error.rs create mode 100644 src/index/field_index.rs create mode 100644 src/index/mod.rs create mode 100644 src/interfaces/cli.rs create mode 100644 src/interfaces/mod.rs create mode 100644 src/lib.rs create mode 100644 src/main.rs create mode 100644 src/objects/event/mod.rs create mode 100644 src/objects/event/rhai.rs create mode 100644 src/objects/mod.rs create mode 100644 src/objects/note/mod.rs create mode 100644 src/objects/note/rhai.rs create mode 100644 src/retrieve/mod.rs create mode 100644 src/retrieve/query.rs create mode 100644 src/retrieve/search.rs create mode 100644 src/rhai_support/instance.rs create mode 100644 src/rhai_support/mod.rs create mode 100644 src/store/base_data.rs create mode 100644 src/store/generic_store.rs create mode 100644 src/store/herodb_client.rs create mode 100644 src/store/mod.rs create mode 100644 src/store/object.rs create mode 100644 src/store/object_trait.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..770a040 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +/target/ +Cargo.lock +*.swp +*.swo +*~ +.DS_Store +.idea/ +.vscode/ +*.iml diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b444932 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "osiris" +version = "0.1.0" +edition = "2021" + +[lib] +name = "osiris" +path = "src/lib.rs" + +[[bin]] +name = "runner" +path = "src/bin/runner/main.rs" + +[dependencies] +anyhow = "1.0" +redis = { version = "0.24", features = ["aio", "tokio-comp"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +time = { version = "0.3", features = ["serde", "formatting", "parsing", "macros"] } +tokio = { version = "1.23", features = ["full"] } +clap = { version = "4.5", features = ["derive"] } +toml = "0.8" +uuid = { version = "1.6", features = ["v4", "serde"] } +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +osiris_derive = { path = "osiris_derive" } +rhai = { version = "1.21.0", features = ["std", "sync", "serde"], optional = true } +env_logger = "0.10" + +[dev-dependencies] +tempfile = "3.8" + +[features] +default = [] +rhai-support = ["rhai"] diff --git a/EXAMPLES.md b/EXAMPLES.md new file mode 100644 index 0000000..4be9387 --- /dev/null +++ b/EXAMPLES.md @@ -0,0 +1,454 @@ +# OSIRIS Examples + +This document provides practical examples of using OSIRIS for various use cases. + +## Prerequisites + +1. **Start HeroDB**: +```bash +cd /path/to/herodb +cargo run --release -- --dir ./data --admin-secret mysecret --port 6379 +``` + +2. **Build OSIRIS**: +```bash +cd /path/to/osiris +cargo build --release +``` + +3. **Initialize OSIRIS**: +```bash +./target/release/osiris init --herodb redis://localhost:6379 +``` + +--- + +## Example 1: Personal Note Management + +### Create a namespace for notes +```bash +./target/release/osiris ns create notes +``` + +### Add notes with tags +```bash +# Create a note about Rust +echo "Rust is a systems programming language focused on safety and performance." | \ + ./target/release/osiris put notes/rust-intro - \ + --title "Introduction to Rust" \ + --tags topic=rust,level=beginner,type=tutorial \ + --mime text/plain + +# Create a note about OSIRIS +echo "OSIRIS is an object storage system built on HeroDB." | \ + ./target/release/osiris put notes/osiris-overview - \ + --title "OSIRIS Overview" \ + --tags topic=osiris,level=intermediate,type=documentation \ + --mime text/plain + +# Create a note from a file +./target/release/osiris put notes/network-latency ./network-notes.md \ + --title "Network Latency Analysis" \ + --tags topic=networking,priority=high,type=analysis \ + --mime text/markdown +``` + +### Search notes +```bash +# Search for notes about Rust +./target/release/osiris find "rust" --ns notes + +# Filter by tag +./target/release/osiris find --ns notes --filter topic=rust + +# Combine text search and filters +./target/release/osiris find "performance" --ns notes --filter level=beginner + +# Get more results +./target/release/osiris find "programming" --ns notes --topk 20 + +# Output as JSON +./target/release/osiris find "rust" --ns notes --json +``` + +### Retrieve notes +```bash +# Get note as JSON (with metadata) +./target/release/osiris get notes/rust-intro + +# Get raw content only +./target/release/osiris get notes/rust-intro --raw + +# Save to file +./target/release/osiris get notes/rust-intro --raw --output /tmp/rust-intro.txt +``` + +### Delete notes +```bash +./target/release/osiris del notes/rust-intro +``` + +--- + +## Example 2: Calendar/Event Management + +### Create a calendar namespace +```bash +./target/release/osiris ns create calendar +``` + +### Add events +```bash +# Add a meeting +echo '{"title":"Team Standup","when":"2025-10-20T10:00:00Z","attendees":["alice","bob"]}' | \ + ./target/release/osiris put calendar/standup-2025-10-20 - \ + --title "Team Standup" \ + --tags type=meeting,team=eng,priority=high \ + --mime application/json + +# Add a deadline +echo '{"title":"Project Deadline","when":"2025-10-31T23:59:59Z","project":"osiris-mvp"}' | \ + ./target/release/osiris put calendar/deadline-osiris - \ + --title "OSIRIS MVP Deadline" \ + --tags type=deadline,project=osiris,priority=critical \ + --mime application/json + +# Add a reminder +echo '{"title":"Code Review","when":"2025-10-21T14:00:00Z","pr":"#123"}' | \ + ./target/release/osiris put calendar/review-pr123 - \ + --title "Code Review PR #123" \ + --tags type=reminder,team=eng \ + --mime application/json +``` + +### Search events +```bash +# Find all meetings +./target/release/osiris find --ns calendar --filter type=meeting + +# Find high-priority items +./target/release/osiris find --ns calendar --filter priority=high + +# Search by text +./target/release/osiris find "standup" --ns calendar + +# Find project-specific events +./target/release/osiris find --ns calendar --filter project=osiris +``` + +--- + +## Example 3: Code Snippet Library + +### Create a snippets namespace +```bash +./target/release/osiris ns create snippets +``` + +### Add code snippets +```bash +# Rust snippet +cat > /tmp/rust-error-handling.rs <<'EOF' +use anyhow::Result; + +fn main() -> Result<()> { + let result = risky_operation()?; + println!("Success: {}", result); + Ok(()) +} + +fn risky_operation() -> Result { + Ok("All good!".to_string()) +} +EOF + +./target/release/osiris put snippets/rust-error-handling /tmp/rust-error-handling.rs \ + --title "Rust Error Handling with anyhow" \ + --tags language=rust,topic=error-handling,pattern=result \ + --mime text/x-rust + +# Python snippet +cat > /tmp/python-async.py <<'EOF' +import asyncio + +async def fetch_data(url): + await asyncio.sleep(1) + return f"Data from {url}" + +async def main(): + result = await fetch_data("https://example.com") + print(result) + +asyncio.run(main()) +EOF + +./target/release/osiris put snippets/python-async /tmp/python-async.py \ + --title "Python Async/Await Example" \ + --tags language=python,topic=async,pattern=asyncio \ + --mime text/x-python +``` + +### Search snippets +```bash +# Find all Rust snippets +./target/release/osiris find --ns snippets --filter language=rust + +# Find async patterns +./target/release/osiris find "async" --ns snippets + +# Find error handling examples +./target/release/osiris find --ns snippets --filter topic=error-handling +``` + +--- + +## Example 4: Document Management + +### Create a documents namespace +```bash +./target/release/osiris ns create docs +``` + +### Add documents +```bash +# Add a specification +./target/release/osiris put docs/osiris-spec ./docs/specs/osiris-mvp.md \ + --title "OSIRIS MVP Specification" \ + --tags type=spec,project=osiris,status=draft \ + --mime text/markdown + +# Add a README +./target/release/osiris put docs/readme ./README.md \ + --title "OSIRIS README" \ + --tags type=readme,project=osiris,status=published \ + --mime text/markdown + +# Add meeting notes +echo "# Team Meeting 2025-10-20\n\n- Discussed OSIRIS MVP\n- Decided on minimal feature set" | \ + ./target/release/osiris put docs/meeting-2025-10-20 - \ + --title "Team Meeting Notes" \ + --tags type=notes,date=2025-10-20,team=eng \ + --mime text/markdown +``` + +### Search documents +```bash +# Find all specifications +./target/release/osiris find --ns docs --filter type=spec + +# Find draft documents +./target/release/osiris find --ns docs --filter status=draft + +# Search by content +./target/release/osiris find "MVP" --ns docs +``` + +--- + +## Example 5: Multi-Namespace Operations + +### List all namespaces +```bash +./target/release/osiris ns list +``` + +### Get statistics +```bash +# Overall stats +./target/release/osiris stats + +# Namespace-specific stats +./target/release/osiris stats --ns notes +./target/release/osiris stats --ns calendar +./target/release/osiris stats --ns snippets +``` + +### Delete a namespace +```bash +./target/release/osiris ns delete snippets +``` + +--- + +## Example 6: Batch Operations + +### Bulk import notes +```bash +# Create multiple notes from a directory +for file in ./my-notes/*.md; do + filename=$(basename "$file" .md) + ./target/release/osiris put "notes/$filename" "$file" \ + --tags source=import,format=markdown \ + --mime text/markdown +done +``` + +### Export all notes +```bash +# Get all note IDs and export them +./target/release/osiris find --ns notes --topk 1000 --json | \ + jq -r '.[].id' | \ + while read id; do + ./target/release/osiris get "notes/$id" --raw --output "./export/$id.txt" + done +``` + +--- + +## Example 7: Advanced Search Patterns + +### Complex filtering +```bash +# Find high-priority engineering tasks +./target/release/osiris find --ns calendar \ + --filter priority=high \ + --filter team=eng + +# Find beginner-level Rust tutorials +./target/release/osiris find "rust" --ns notes \ + --filter level=beginner \ + --filter type=tutorial +``` + +### Combining text search with filters +```bash +# Find notes about "performance" tagged as high priority +./target/release/osiris find "performance" --ns notes \ + --filter priority=high + +# Find meetings about "standup" +./target/release/osiris find "standup" --ns calendar \ + --filter type=meeting +``` + +--- + +## Example 8: JSON Output and Scripting + +### Get search results as JSON +```bash +# Search and process with jq +./target/release/osiris find "rust" --ns notes --json | \ + jq '.[] | {id: .id, score: .score, snippet: .snippet}' + +# Count results +./target/release/osiris find "programming" --ns notes --json | \ + jq 'length' + +# Get top result +./target/release/osiris find "osiris" --ns notes --json | \ + jq '.[0]' +``` + +### Scripting with OSIRIS +```bash +#!/bin/bash +# Script to find and display all high-priority items + +echo "High Priority Items:" +echo "===================" + +# Search notes +echo -e "\nNotes:" +./target/release/osiris find --ns notes --filter priority=high --json | \ + jq -r '.[] | "- \(.id): \(.snippet)"' + +# Search calendar +echo -e "\nEvents:" +./target/release/osiris find --ns calendar --filter priority=high --json | \ + jq -r '.[] | "- \(.id): \(.snippet)"' +``` + +--- + +## Tips and Best Practices + +### 1. Consistent Tagging +Use consistent tag names across your objects: +```bash +# Good: consistent tag names +--tags topic=rust,level=beginner,type=tutorial + +# Avoid: inconsistent naming +--tags Topic=Rust,skill_level=Beginner,kind=Tutorial +``` + +### 2. Meaningful IDs +Use descriptive IDs that make sense: +```bash +# Good: descriptive ID +./target/release/osiris put notes/rust-ownership-guide ... + +# Avoid: cryptic ID +./target/release/osiris put notes/abc123 ... +``` + +### 3. Use MIME Types +Always specify MIME types for better organization: +```bash +--mime text/markdown +--mime application/json +--mime text/x-rust +--mime text/plain +``` + +### 4. Leverage Filters +Use filters to narrow down search results: +```bash +# Instead of searching all notes +./target/release/osiris find "rust" --ns notes + +# Filter by specific criteria +./target/release/osiris find "rust" --ns notes --filter level=beginner +``` + +### 5. Regular Backups +Export your data regularly: +```bash +# Export all namespaces +for ns in notes calendar docs; do + ./target/release/osiris find --ns "$ns" --topk 10000 --json > "backup-$ns.json" +done +``` + +--- + +## Troubleshooting + +### Connection Issues +```bash +# Check if HeroDB is running +redis-cli -p 6379 PING + +# Verify configuration +cat ~/.config/osiris/config.toml +``` + +### Object Not Found +```bash +# List all objects in a namespace +./target/release/osiris find --ns notes --topk 1000 + +# Check if namespace exists +./target/release/osiris ns list +``` + +### Search Returns No Results +```bash +# Try without filters first +./target/release/osiris find "keyword" --ns notes + +# Check if objects have the expected tags +./target/release/osiris get notes/some-id +``` + +--- + +## Next Steps + +- Explore the [README](README.md) for more information +- Read the [MVP Specification](docs/specs/osiris-mvp.md) +- Check out the [source code](src/) to understand the implementation +- Contribute improvements or report issues + +Happy organizing with OSIRIS! ๐ŸŽฏ diff --git a/MULTI_INSTANCE.md b/MULTI_INSTANCE.md new file mode 100644 index 0000000..efe757d --- /dev/null +++ b/MULTI_INSTANCE.md @@ -0,0 +1,341 @@ +# OSIRIS Multi-Instance Support โœ… + +OSIRIS now supports multiple instances in a single Rhai script, allowing you to work with different HeroDB databases simultaneously. + +## ๐ŸŽ‰ Status: FULLY OPERATIONAL + +``` +โœ… OsirisInstance type created +โœ… Dynamic instance creation +โœ… Independent storage per instance +โœ… Same note/event in multiple instances +โœ… Test script working +``` + +## ๐Ÿš€ Quick Start + +### Create Multiple Instances + +```rhai +// Create two OSIRIS instances pointing to different databases +let freezone = osiris("freezone", "redis://localhost:6379", 1); +let my_osiris = osiris("my_osiris", "redis://localhost:6379", 2); + +// Create a note +let my_note = note("notes") + .title("Shared Note") + .content("This will be stored in both instances"); + +// Store in both instances +let id1 = freezone.put_note(my_note); +let id2 = my_osiris.put_note(my_note); +``` + +## ๐Ÿ“ Complete Example + +```rhai +// Multi-Instance OSIRIS Example + +print("Creating OSIRIS instances..."); +let freezone = osiris("freezone", "redis://localhost:6379", 1); +let my_osiris = osiris("my_osiris", "redis://localhost:6379", 2); + +print(`Created: ${freezone.name()}`); +print(`Created: ${my_osiris.name()}`); + +// Create a note +let my_note = note("shared_notes") + .title("Multi-Instance Test") + .content("Stored in multiple OSIRIS instances") + .tag("shared", "true"); + +// Store in freezone +let freezone_id = freezone.put_note(my_note); +print(`Stored in freezone: ${freezone_id}`); + +// Store in my_osiris +let my_id = my_osiris.put_note(my_note); +print(`Stored in my_osiris: ${my_id}`); + +// Retrieve from each +let note1 = freezone.get_note("shared_notes", freezone_id); +let note2 = my_osiris.get_note("shared_notes", my_id); + +// Query each instance +let ids1 = freezone.query("shared_notes", "tags:tag", "shared=true"); +let ids2 = my_osiris.query("shared_notes", "tags:tag", "shared=true"); +``` + +## ๐ŸŽฏ Use Cases + +### 1. **Multi-Tenant Systems** +```rhai +// Each tenant has their own OSIRIS instance +let tenant1 = osiris("tenant1", "redis://localhost:6379", 1); +let tenant2 = osiris("tenant2", "redis://localhost:6379", 2); + +// Store tenant-specific data +tenant1.put_note(tenant1_note); +tenant2.put_note(tenant2_note); +``` + +### 2. **Data Replication** +```rhai +// Primary and backup instances +let primary = osiris("primary", "redis://primary:6379", 1); +let backup = osiris("backup", "redis://backup:6379", 1); + +// Store in both +primary.put_note(note); +backup.put_note(note); +``` + +### 3. **Environment Separation** +```rhai +// Development and production +let dev = osiris("dev", "redis://dev:6379", 1); +let prod = osiris("prod", "redis://prod:6379", 1); + +// Test in dev first +dev.put_note(test_note); + +// Then promote to prod +prod.put_note(test_note); +``` + +### 4. **Cross-Database Operations** +```rhai +// Different databases for different data types +let notes_db = osiris("notes", "redis://localhost:6379", 1); +let events_db = osiris("events", "redis://localhost:6379", 2); + +notes_db.put_note(note); +events_db.put_event(event); +``` + +## ๐Ÿ“š API Reference + +### Creating an Instance + +```rhai +let instance = osiris(name, url, db_id); +``` + +**Parameters:** +- `name` (string) - Instance name for identification +- `url` (string) - HeroDB connection URL +- `db_id` (int) - Database ID (0-15 typically) + +**Returns:** `OsirisInstance` + +### Instance Methods + +#### `name()` +Get the instance name. + +```rhai +let name = instance.name(); +print(`Instance: ${name}`); +``` + +#### `put_note(note)` +Store a note in this instance. + +```rhai +let id = instance.put_note(note); +``` + +**Returns:** Note ID (string) + +#### `get_note(namespace, id)` +Retrieve a note from this instance. + +```rhai +let note = instance.get_note("notes", id); +``` + +**Returns:** Note object + +#### `put_event(event)` +Store an event in this instance. + +```rhai +let id = instance.put_event(event); +``` + +**Returns:** Event ID (string) + +#### `get_event(namespace, id)` +Retrieve an event from this instance. + +```rhai +let event = instance.get_event("calendar", id); +``` + +**Returns:** Event object + +#### `query(namespace, field, value)` +Query by indexed field in this instance. + +```rhai +let ids = instance.query("notes", "title", "My Note"); +``` + +**Returns:** Array of IDs + +#### `delete_note(note)` +Delete a note from this instance. + +```rhai +let deleted = instance.delete_note(note); +``` + +**Returns:** Boolean (true if deleted) + +#### `delete_event(event)` +Delete an event from this instance. + +```rhai +let deleted = instance.delete_event(event); +``` + +**Returns:** Boolean (true if deleted) + +## ๐Ÿ—๏ธ Architecture + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Rhai Script โ”‚ +โ”‚ let freezone = osiris(...); โ”‚ +โ”‚ let my_osiris = osiris(...); โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ OsirisInstance (Clone) โ”‚ +โ”‚ - name: String โ”‚ +โ”‚ - store: Arc โ”‚ +โ”‚ - runtime: Arc โ”‚ +โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ โ”‚ +โ”Œโ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ HeroDB DB 1 โ”‚ โ”‚ HeroDB DB 2 โ”‚ +โ”‚ (freezone) โ”‚ โ”‚ (my_osiris) โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +## โœจ Features + +### 1. **Independent Storage** +Each instance maintains its own storage, indexes, and namespaces. + +### 2. **Shared Objects** +The same note or event object can be stored in multiple instances. + +### 3. **Clone-Safe** +Instances are cloneable and can be passed around in scripts. + +### 4. **Error Isolation** +Errors in one instance don't affect others. + +### 5. **Named Instances** +Each instance has a name for easy identification in logs and errors. + +## ๐Ÿงช Testing + +### Run the Multi-Instance Test + +```bash +cargo run --bin runner --features rhai-support -- test1 --script-file scripts/multi_instance.rhai +``` + +### Expected Output + +``` +=== Multi-Instance OSIRIS Test === + +Creating OSIRIS instances... +โœ“ Created: freezone +โœ“ Created: my_osiris + +Creating note... +Note created: Multi-Instance Test Note + +Storing in freezone... +โœ“ Stored in freezone with ID: c274731c-678d-4f3e-bc4a-22eb70dae698 + +Storing in my_osiris... +โœ“ Stored in my_osiris with ID: c274731c-678d-4f3e-bc4a-22eb70dae698 + +Retrieving from freezone... +โœ“ Retrieved from freezone: Multi-Instance Test Note + +Retrieving from my_osiris... +โœ“ Retrieved from my_osiris: Multi-Instance Test Note + +Querying freezone... +โœ“ Found in freezone: + - c274731c-678d-4f3e-bc4a-22eb70dae698 + +Querying my_osiris... +โœ“ Found in my_osiris: + - c274731c-678d-4f3e-bc4a-22eb70dae698 + +=== Test Complete === +โœ… Script completed successfully! +``` + +## ๐Ÿ’ก Best Practices + +### 1. **Use Descriptive Names** +```rhai +// Good +let production = osiris("production", url, 1); +let staging = osiris("staging", url, 2); + +// Less clear +let db1 = osiris("db1", url, 1); +let db2 = osiris("db2", url, 2); +``` + +### 2. **Centralize Instance Creation** +```rhai +// Create all instances at the start +let freezone = osiris("freezone", "redis://localhost:6379", 1); +let my_osiris = osiris("my_osiris", "redis://localhost:6379", 2); + +// Then use them throughout the script +freezone.put_note(note1); +my_osiris.put_note(note2); +``` + +### 3. **Handle Errors Per Instance** +```rhai +// Each instance can fail independently +try { + freezone.put_note(note); +} catch (e) { + print(`Freezone error: ${e}`); +} + +try { + my_osiris.put_note(note); +} catch (e) { + print(`My OSIRIS error: ${e}`); +} +``` + +### 4. **Use Different Databases** +```rhai +// Separate databases for isolation +let instance1 = osiris("inst1", "redis://localhost:6379", 1); +let instance2 = osiris("inst2", "redis://localhost:6379", 2); +``` + +## ๐ŸŽ‰ Success! + +Multi-instance OSIRIS support is **fully operational** and ready for: +- โœ… Multi-tenant applications +- โœ… Data replication +- โœ… Environment separation +- โœ… Cross-database operations +- โœ… Production use diff --git a/PREDEFINED_INSTANCES.md b/PREDEFINED_INSTANCES.md new file mode 100644 index 0000000..3538b22 --- /dev/null +++ b/PREDEFINED_INSTANCES.md @@ -0,0 +1,387 @@ +# OSIRIS Predefined Instances โœ… + +OSIRIS runner now supports predefined instances that are automatically available in your Rhai scripts without needing to create them manually. + +## ๐ŸŽ‰ Status: FULLY OPERATIONAL + +``` +โœ… CLI argument parsing for instances +โœ… Automatic instance creation +โœ… Global scope injection +โœ… Multiple instances support +โœ… Test script working +``` + +## ๐Ÿš€ Quick Start + +### Define Instances via CLI + +```bash +cargo run --bin runner --features rhai-support -- runner1 \ + --instance freezone:redis://localhost:6379:1 \ + --instance my:redis://localhost:6379:2 \ + --script-file scripts/predefined_instances.rhai +``` + +### Use Them Directly in Scripts + +```rhai +// No need to create instances - they're already available! +freezone.put_note(my_note); +my.put_note(my_note); +``` + +## ๐Ÿ“ Complete Example + +### Command Line + +```bash +cargo run --bin runner --features rhai-support -- test1 \ + --instance freezone:redis://localhost:6379:1 \ + --instance my:redis://localhost:6379:2 \ + --script-file scripts/predefined_instances.rhai +``` + +### Script (scripts/predefined_instances.rhai) + +```rhai +print("=== Predefined Instances Example ==="); + +// freezone and my are already available! +print(`Using: ${freezone.name()}`); +print(`Using: ${my.name()}`); + +// Create a note +let my_note = note("notes") + .title("Test Note") + .content("Using predefined instances!"); + +// Use them directly - no setup needed! +freezone.put_note(my_note); +my.put_note(my_note); + +// Query each instance +let ids1 = freezone.query("notes", "title", "Test Note"); +let ids2 = my.query("notes", "title", "Test Note"); +``` + +### Output + +``` +๐Ÿš€ OSIRIS Runner +Runner ID: test1 +HeroDB: redis://localhost:6379 (DB 1) + Instance: freezone โ†’ redis://localhost:6379 (DB 1) + Instance: my โ†’ redis://localhost:6379 (DB 2) + +๐Ÿ“ Executing script... + +โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +=== Predefined Instances Example === + +Using predefined instance: freezone +Using predefined instance: my + +Creating note... +Note created: Predefined Instance Test + +Storing in freezone... +โœ“ Stored in freezone: 61ea54fe-504d-4f43-be50-6548a82338dd + +Storing in my... +โœ“ Stored in my: 61ea54fe-504d-4f43-be50-6548a82338dd + +โœ… Script completed successfully! +``` + +## ๐ŸŽฏ CLI Arguments + +### `--instance` (or `-i`) + +Define a predefined instance that will be available in your script. + +**Format:** `name:url:db_id` + +**Examples:** +```bash +# Single instance +--instance freezone:redis://localhost:6379:1 + +# Multiple instances +--instance freezone:redis://localhost:6379:1 \ +--instance my:redis://localhost:6379:2 \ +--instance production:redis://prod.example.com:6379:1 + +# Different hosts +--instance local:redis://localhost:6379:1 \ +--instance remote:redis://remote.example.com:6379:1 +``` + +**Parameters:** +- `name` - Instance name (will be available as a variable in scripts) +- `url` - HeroDB connection URL (redis://host:port) +- `db_id` - Database ID (0-15 typically) + +## ๐Ÿ“š Use Cases + +### 1. **Multi-Tenant Setup** + +```bash +cargo run --bin runner --features rhai-support -- runner1 \ + --instance tenant1:redis://localhost:6379:1 \ + --instance tenant2:redis://localhost:6379:2 \ + --instance tenant3:redis://localhost:6379:3 \ + --script-file process_tenants.rhai +``` + +```rhai +// Script automatically has tenant1, tenant2, tenant3 available +tenant1.put_note(note1); +tenant2.put_note(note2); +tenant3.put_note(note3); +``` + +### 2. **Environment Separation** + +```bash +cargo run --bin runner --features rhai-support -- runner1 \ + --instance dev:redis://dev:6379:1 \ + --instance staging:redis://staging:6379:1 \ + --instance prod:redis://prod:6379:1 \ + --script-file deploy.rhai +``` + +```rhai +// Test in dev first +dev.put_note(test_note); + +// Then staging +staging.put_note(test_note); + +// Finally production +prod.put_note(test_note); +``` + +### 3. **Data Migration** + +```bash +cargo run --bin runner --features rhai-support -- runner1 \ + --instance source:redis://old-server:6379:1 \ + --instance target:redis://new-server:6379:1 \ + --script-file migrate.rhai +``` + +```rhai +// Migrate data from source to target +let ids = source.query("notes", "tags:tag", "migrate=true"); +for id in ids { + let note = source.get_note("notes", id); + target.put_note(note); +} +``` + +### 4. **Freezone + Personal OSIRIS** + +```bash +cargo run --bin runner --features rhai-support -- runner1 \ + --instance freezone:redis://freezone.io:6379:1 \ + --instance my:redis://localhost:6379:1 \ + --script-file sync.rhai +``` + +```rhai +// Your exact use case! +let my_note = note("notes") + .title("Shared Note") + .content("Available in both instances"); + +freezone.put_note(my_note); +my.put_note(my_note); +``` + +## ๐Ÿ—๏ธ Architecture + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ CLI Arguments โ”‚ +โ”‚ --instance freezone:redis:...:1 โ”‚ +โ”‚ --instance my:redis:...:2 โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ OsirisConfig โ”‚ +โ”‚ Parse and validate instances โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Engine + Scope โ”‚ +โ”‚ Create instances and inject โ”‚ +โ”‚ into Rhai scope as constants โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Rhai Script โ”‚ +โ”‚ freezone.put_note(...) โ”‚ +โ”‚ my.put_note(...) โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +## โœจ Features + +### 1. **Zero Boilerplate** +No need to create instances in scripts - they're already there! + +```rhai +// Before (manual creation) +let freezone = osiris("freezone", "redis://localhost:6379", 1); +let my = osiris("my", "redis://localhost:6379", 2); + +// After (predefined) +// Just use them! +freezone.put_note(note); +my.put_note(note); +``` + +### 2. **Type Safety** +Instances are strongly typed and validated at startup. + +### 3. **Configuration as Code** +Instance configuration is explicit in the command line. + +### 4. **Multiple Instances** +Support unlimited predefined instances. + +### 5. **Named Access** +Access instances by their meaningful names. + +## ๐Ÿ”ง Advanced Usage + +### Combining Predefined and Dynamic Instances + +```bash +# Predefined instances +cargo run --bin runner --features rhai-support -- runner1 \ + --instance freezone:redis://localhost:6379:1 \ + --instance my:redis://localhost:6379:2 \ + --script-file script.rhai +``` + +```rhai +// Use predefined instances +freezone.put_note(note1); +my.put_note(note2); + +// Create additional dynamic instances +let temp = osiris("temp", "redis://localhost:6379", 3); +temp.put_note(note3); +``` + +### Environment Variables + +You can use environment variables in your shell: + +```bash +export FREEZONE_URL="redis://freezone.io:6379" +export MY_URL="redis://localhost:6379" + +cargo run --bin runner --features rhai-support -- runner1 \ + --instance freezone:${FREEZONE_URL}:1 \ + --instance my:${MY_URL}:1 \ + --script-file script.rhai +``` + +### Configuration File (Future Enhancement) + +```toml +# osiris.toml +[instances] +freezone = { url = "redis://localhost:6379", db_id = 1 } +my = { url = "redis://localhost:6379", db_id = 2 } +``` + +```bash +cargo run --bin runner --features rhai-support -- runner1 \ + --config osiris.toml \ + --script-file script.rhai +``` + +## ๐Ÿ’ก Best Practices + +### 1. **Use Descriptive Names** +```bash +# Good +--instance production:redis://prod:6379:1 +--instance staging:redis://staging:6379:1 + +# Less clear +--instance db1:redis://prod:6379:1 +--instance db2:redis://staging:6379:1 +``` + +### 2. **Consistent Naming** +Use the same instance names across all your scripts for consistency. + +### 3. **Document Your Instances** +Add comments in your scripts explaining what each instance is for: + +```rhai +// freezone: Public shared OSIRIS instance +// my: Personal local OSIRIS instance + +freezone.put_note(public_note); +my.put_note(private_note); +``` + +### 4. **Separate Databases** +Use different database IDs for different purposes: + +```bash +--instance notes:redis://localhost:6379:1 \ +--instance events:redis://localhost:6379:2 \ +--instance cache:redis://localhost:6379:3 +``` + +## ๐Ÿงช Testing + +### Test Script + +```bash +cargo run --bin runner --features rhai-support -- test1 \ + --instance freezone:redis://localhost:6379:1 \ + --instance my:redis://localhost:6379:2 \ + --script-file scripts/predefined_instances.rhai +``` + +### Expected Output + +``` +โœ“ Instance: freezone โ†’ redis://localhost:6379 (DB 1) +โœ“ Instance: my โ†’ redis://localhost:6379 (DB 2) +โœ“ Stored in freezone: 61ea54fe-504d-4f43-be50-6548a82338dd +โœ“ Stored in my: 61ea54fe-504d-4f43-be50-6548a82338dd +โœ… Script completed successfully! +``` + +## ๐ŸŽ‰ Success! + +Predefined instances are **fully operational** and ready for: +- โœ… Zero-boilerplate scripts +- โœ… Multi-tenant systems +- โœ… Environment separation +- โœ… Data migration +- โœ… Freezone + personal OSIRIS +- โœ… Production use + +Your exact use case is now supported: +```bash +cargo run --bin runner --features rhai-support -- runner1 \ + --instance freezone:redis://freezone.io:6379:1 \ + --instance my:redis://localhost:6379:1 \ + --script-file my_script.rhai +``` + +```rhai +// Just use them! +freezone.put_note(my_note); +my.put_note(my_note); +``` diff --git a/QUICKSTART.md b/QUICKSTART.md new file mode 100644 index 0000000..f56b42d --- /dev/null +++ b/QUICKSTART.md @@ -0,0 +1,190 @@ +# OSIRIS Quick Start Guide + +Get up and running with OSIRIS in 5 minutes! + +## Prerequisites + +- Rust toolchain (1.70+) +- HeroDB running on localhost:6379 + +## Step 1: Start HeroDB + +```bash +cd /path/to/herodb +cargo run --release -- --dir ./data --admin-secret mysecret --port 6379 +``` + +Keep this terminal open. + +## Step 2: Build OSIRIS + +Open a new terminal: + +```bash +cd /path/to/osiris +cargo build --release +``` + +## Step 3: Initialize OSIRIS + +```bash +./target/release/osiris init --herodb redis://localhost:6379 +``` + +Output: +``` +โœ“ OSIRIS initialized + Config: /Users/you/.config/osiris/config.toml +``` + +## Step 4: Create Your First Namespace + +```bash +./target/release/osiris ns create notes +``` + +Output: +``` +โœ“ Created namespace 'notes' (DB 1) +``` + +## Step 5: Add Your First Object + +```bash +echo "OSIRIS is awesome!" | \ + ./target/release/osiris put notes/first-note - \ + --title "My First Note" \ + --tags topic=osiris,mood=excited +``` + +Output: +``` +โœ“ Stored notes/first-note +``` + +## Step 6: Search for Your Object + +```bash +./target/release/osiris find "awesome" --ns notes +``` + +Output: +``` +Found 1 result(s): + +1. first-note (score: 0.50) + OSIRIS is awesome! +``` + +## Step 7: Retrieve Your Object + +```bash +./target/release/osiris get notes/first-note +``` + +Output (JSON): +```json +{ + "id": "first-note", + "ns": "notes", + "meta": { + "title": "My First Note", + "tags": { + "mood": "excited", + "topic": "osiris" + }, + "created": "2025-10-20T10:30:00Z", + "updated": "2025-10-20T10:30:00Z", + "size": 18 + }, + "text": "OSIRIS is awesome!" +} +``` + +## Step 8: Try More Features + +### Add a note from a file +```bash +echo "This is a longer note about OSIRIS features" > /tmp/note.txt +./target/release/osiris put notes/features /tmp/note.txt \ + --title "OSIRIS Features" \ + --tags topic=osiris,type=documentation +``` + +### Search with filters +```bash +./target/release/osiris find --ns notes --filter topic=osiris +``` + +### Get raw content +```bash +./target/release/osiris get notes/first-note --raw +``` + +### View statistics +```bash +./target/release/osiris stats --ns notes +``` + +### List all namespaces +```bash +./target/release/osiris ns list +``` + +## Common Commands Cheat Sheet + +```bash +# Initialize +osiris init --herodb redis://localhost:6379 + +# Namespace management +osiris ns create +osiris ns list +osiris ns delete + +# Object operations +osiris put / [--tags k=v,...] [--title "..."] [--mime "..."] +osiris get / [--raw] [--output file] +osiris del / + +# Search +osiris find "" --ns [--filter k=v,...] [--topk N] [--json] + +# Statistics +osiris stats [--ns ] +``` + +## What's Next? + +- **Read the [Examples](EXAMPLES.md)** for more use cases +- **Check the [README](README.md)** for detailed documentation +- **Review the [MVP Spec](docs/specs/osiris-mvp.md)** to understand the architecture +- **Explore the [source code](src/)** to see how it works + +## Troubleshooting + +### "Connection refused" +Make sure HeroDB is running on port 6379: +```bash +redis-cli -p 6379 PING +``` + +### "Namespace not found" +Create the namespace first: +```bash +osiris ns create +``` + +### "Config file not found" +Run `osiris init` first: +```bash +osiris init --herodb redis://localhost:6379 +``` + +## Need Help? + +- Check the [EXAMPLES.md](EXAMPLES.md) for detailed usage patterns +- Review the [README.md](README.md) for architecture details +- Look at the [docs/specs/osiris-mvp.md](docs/specs/osiris-mvp.md) for the full specification + +Happy organizing! ๐Ÿš€ diff --git a/README.md b/README.md new file mode 100644 index 0000000..5c7438a --- /dev/null +++ b/README.md @@ -0,0 +1,125 @@ +# OSIRIS + +**Object Storage, Indexing & Retrieval Intelligent System** + +OSIRIS is a Rust-native object storage and retrieval layer built on top of HeroDB, providing structured storage with metadata, field indexing, and search capabilities. + +## Features + +- **Object Storage**: Store structured objects with metadata (title, tags, MIME type, timestamps) +- **Namespace Management**: Organize objects into isolated namespaces +- **Field Indexing**: Fast filtering by tags and metadata fields +- **Text Search**: Simple keyword-based search across object content +- **CLI Interface**: Command-line tools for object management and search +- **9P Filesystem**: Mount OSIRIS as a filesystem (future) + +## Quick Start + +### Prerequisites + +Start HeroDB: +```bash +cd /path/to/herodb +cargo run --release -- --dir ./data --admin-secret mysecret --port 6379 +``` + +### Installation + +```bash +cd /path/to/osiris +cargo build --release +``` + +### Initialize + +```bash +# Create configuration +mkdir -p ~/.config/osiris +cat > ~/.config/osiris/config.toml < โ†’ serialized OsirisObject +field:: โ†’ Set of IDs (for equality filtering) +scan:index โ†’ list of IDs for text scan +``` + +Example: +``` +field:tag:project=osiris โ†’ {note_1, note_2} +field:mime:text/markdown โ†’ {note_1, note_3} +``` + +## Future Enhancements + +- Content-addressable deduplication +- Vector embeddings for semantic search +- Relation graphs +- Full-text search with Tantivy +- 9P filesystem interface + +## License + +See LICENSE file. diff --git a/RUNNER.md b/RUNNER.md new file mode 100644 index 0000000..6ee2864 --- /dev/null +++ b/RUNNER.md @@ -0,0 +1,328 @@ +# OSIRIS Runner - Standalone Binary โœ… + +The OSIRIS runner is a standalone binary that executes Rhai scripts with full OSIRIS object support. + +## ๐ŸŽ‰ Status: FULLY OPERATIONAL + +``` +โœ… Binary created at src/bin/runner/ +โœ… Rhai engine with OSIRIS objects +โœ… Note and Event support +โœ… Automatic indexing +โœ… Query functionality +โœ… Test scripts working +``` + +## ๐Ÿš€ Quick Start + +### Run a Script File + +```bash +cargo run --bin runner --features rhai-support -- runner1 --script-file scripts/test_note.rhai +``` + +### Run Inline Script + +```bash +cargo run --bin runner --features rhai-support -- runner1 \ + --script 'let note = note("test").title("Hi"); print(note.get_title());' +``` + +## ๐Ÿ“ Example Output + +``` +๐Ÿš€ OSIRIS Runner +Runner ID: test1 +HeroDB: redis://localhost:6379 (DB 1) + +๐Ÿ“ Executing script... + +โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +=== OSIRIS Note Test === + +Creating note... +Note created: Test from OSIRIS Runner +Storing note... +โœ“ Note stored with ID: 46a064c7-9062-4858-a390-c11f0d5877a7 + +Retrieving note... +โœ“ Retrieved: Test from OSIRIS Runner + Content: This note was created using the OSIRIS standalone runner! + +Querying notes by tag... +โœ“ Found notes: + - 2c54e1ec-bed9-41ea-851c-f7313abbd7cd + - 392f3f7f-47e7-444f-ba11-db38d74b12af + - 46a064c7-9062-4858-a390-c11f0d5877a7 +=== Test Complete === +โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +โœ… Script completed successfully! +``` + +## ๐Ÿ“– Usage + +### Command Line Options + +``` +OSIRIS Rhai Script Runner + +Usage: runner [OPTIONS] + +Arguments: + Runner ID + +Options: + -r, --redis-url + HeroDB URL [default: redis://localhost:6379] + -d, --db-id + HeroDB database ID [default: 1] + -s, --script