# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
robomotion-rust is the official Rust SDK for building Robomotion packages. It provides a runtime framework for creating plugin-based automation nodes that communicate with the Robomotion platform via gRPC.
## Core Architecture
### Plugin Architecture
- Uses HashiCorp's go-plugin compatible protocol for IPC between host and plugins
- Plugins communicate via gRPC with protobuf-defined interfaces
- Each node is a Rust struct that derives `Node` and implements `MessageHandler`
### Key Components
**Runtime Package (`src/runtime/`)**
- `node.rs` - Base NodeBase struct with common properties (GUID, delays, error handling)
- `handler.rs` - MessageHandler trait and node handler registry
- `factory.rs` - Node factory pattern for dynamic node creation
- `variable.rs` - Strongly-typed variable system (InVariable, OutVariable, OptVariable)
- `grpc.rs` - gRPC server implementation
- `client.rs` - RuntimeHelper gRPC client
- `spec.rs` - Node specification generation
- `registry.rs` - Node registration and startup
- `lmo.rs` - Large Message Object support for payloads >256KB
- `oauth.rs` - OAuth2 dialog support
- `tool.rs` / `tool_interceptor.rs` / `tool_response.rs` - AI tool support
**Message System**
- `src/message/mod.rs` - Context for data flow between nodes
**Debug Package (`src/debug/`)**
- `mod.rs` - Development-time debugging with attach/detach support
**Derive Macros (`robomotion-derive/`)**
- Procedural macros for deriving Node specs from struct attributes
## Development Commands
### Building
```bash
# Build the library
cargo build
# Build with release optimizations
cargo build --release
# Cross-compile for different platforms
cargo build --target x86_64-pc-windows-gnu
cargo build --target aarch64-apple-darwin
```
### Running/Testing
```bash
# Run tests
cargo test
# Run a specific test
cargo test test_context_new
# Run with logging
RUST_LOG=robomotion=debug cargo test
```
### Generating Proto Code
Proto code is generated automatically during build via `build.rs`.
## Node Development Pattern
### Basic Node Structure
```rust
use robomotion::prelude::*;
#[derive(Node, Default)]
#[node(id = "Namespace.MyNode", name = "My Node", icon = "mdiIcon", color = "#3498db")]
struct MyNode {
node: NodeBase,
// Options (user-configurable in Designer)
#[option(title = "My Option", value = "default")]
my_option: String,
// Inputs
#[input(title = "Input Data", var_type = "string", scope = "Message", name = "data")]
in_data: InVariable<String>,
// Outputs
#[output(title = "Result", var_type = "string", scope = "Message", name = "result")]
out_result: OutVariable<String>,
}
#[async_trait]
impl MessageHandler for MyNode {
async fn on_create(&mut self) -> Result<()> {
Ok(())
}
async fn on_message(&mut self, ctx: &mut Context) -> Result<()> {
let data = self.in_data.get(ctx).await?;
self.out_result.set(ctx, format!("Processed: {}", data)).await?;
Ok(())
}
async fn on_close(&mut self) -> Result<()> {
Ok(())
}
}
```
### Node Registration
All nodes must be registered in `main.rs`:
```rust
use robomotion::prelude::*;
#[tokio::main]
async fn main() {
register_nodes![MyNode, AnotherNode];
start().await;
}
```
### AI Tool Support
Nodes can be exposed as AI tools for cross-language agent integration:
```rust
#[derive(Node, Default)]
#[node(id = "Namespace.MyTool", name = "My Tool", icon = "mdiRobot", color = "#9b59b6")]
#[tool(name = "my_tool", description = "Does something useful")]
struct MyToolNode {
node: NodeBase,
// AI scope allows LLM to provide input values
#[input(title = "Query", var_type = "string", scope = "AI", name = "query")]
in_query: InVariable<String>,
// Output is automatically collected for tool response
#[output(title = "Result", var_type = "string", scope = "AI", name = "result")]
out_result: OutVariable<String>,
}
```
## Key Conventions
### Node Attributes
- Node identification: `id = "Namespace.NodeName"` (must be unique)
- UI properties: `name`, `icon`, `color`, `inputs`, `outputs`
### Variable Attributes
- `title` - Display name
- `var_type` - Type (string, integer, boolean, array, object)
- `scope` - Variable scope (Message, Custom, AI)
- `name` - Message context key
### Variable Types and Scopes
| `InVariable<T>` | Required input | `.get(ctx).await` returns `Result<T>` |
| `OptVariable<T>` | Optional input | `.get(ctx).await` returns `Result<Option<T>>` |
| `OutVariable<T>` | Output | `.set(ctx, value).await` |
| `Credential` | Vault item | `.get(ctx).await` returns `Result<HashMap<String, Value>>` |
**Scopes:**
- `Message` - Data from previous nodes in flow
- `Custom` - User-configured constants in Designer
- `AI` - Parameters provided by LLM agents
### Error Handling
- Return `Err` from lifecycle methods to stop flow execution
- Use `continue_on_error: true` on NodeBase to continue on errors
- Use `emit_error()` to emit errors without stopping flow
## File Structure Conventions
```
package-root/
├── Cargo.toml # Package manifest
├── config.json # Package metadata
├── src/
│ ├── main.rs # Entry point with node registration
│ └── nodes/ # Node implementations
│ ├── mod.rs
│ └── my_node.rs
├── icon.png # Package icon
└── target/ # Build outputs
```
## Common Patterns
### Accessing Runtime Services
```rust
use robomotion::runtime::*;
// Emit output to next node
emit_output(guid, &data, port).await?;
// Get vault item
let creds = get_vault_item(vault_id, item_id).await?;
// App request
let response = app_request(&request_data, timeout).await?;
```
### Large Data Handling
```rust
// For payloads >256KB, LMO is handled automatically
// Manual packing if needed:
let packed = pack_message_bytes(&large_data).await?;
```
### OAuth2 Authentication
```rust
use robomotion::runtime::oauth::*;
async fn on_create(&mut self) -> Result<()> {
let config = OAuth2Config {
client_id: "your-client-id".to_string(),
client_secret: "your-client-secret".to_string(),
auth_url: "https://provider.com/oauth2/auth".to_string(),
token_url: "https://provider.com/oauth2/token".to_string(),
scopes: vec!["scope1".to_string()],
};
let code = open_oauth_dialog(&config).await?;
// Exchange code for token...
Ok(())
}
```
## Dependencies
Key external dependencies:
- `tonic` - gRPC framework
- `prost` - Protocol buffers
- `tokio` - Async runtime
- `serde` / `serde_json` - Serialization
- `tracing` - Logging
- `oauth2` - OAuth2 support
## Debugging
Use the attach mode for development:
1. Build: `cargo build`
2. Run: `./target/debug/package-name -a`
3. Execute flows in Designer - logs appear in console
4. Use `tracing` macros for debug output