robomotion 0.1.3

Official Rust SDK for building Robomotion RPA packages
Documentation
# 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
| Type | Purpose | Access |
|------|---------|--------|
| `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