> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/moeru-ai/airi/llms.txt
> Use this file to discover all available pages before exploring further.

# Server SDK Package

> Client SDK for connecting to AIRI server runtime

## Overview

The `@proj-airi/server-sdk` package provides a client SDK for connecting to the AIRI server runtime. It handles WebSocket communication, authentication, heartbeat management, and event routing.

## Installation

```bash theme={null}
npm install @proj-airi/server-sdk
```

## Quick Start

Connect to a server runtime:

```typescript theme={null}
import { Client } from '@proj-airi/server-sdk'

const client = new Client({
  url: 'ws://localhost:6121/ws',
  name: 'my-module',
  token: 'your-auth-token'
})

// Listen for events
client.onEvent('registry:modules:sync', (event) => {
  console.log('Modules:', event.data.modules)
})

// Send events
client.send({
  type: 'custom:event',
  data: { message: 'Hello' }
})
```

## Client Class

### Constructor

Create a new client instance.

```typescript theme={null}
const client = new Client<CustomData>(options)
```

<ParamField path="options.url" type="string" optional default="ws://localhost:6121/ws">
  WebSocket server URL
</ParamField>

<ParamField path="options.name" type="string" required>
  Module name for identification
</ParamField>

<ParamField path="options.token" type="string" optional>
  Authentication token (if server requires auth)
</ParamField>

<ParamField path="options.identity" type="MetadataEventSource" optional>
  Custom module identity (auto-generated if not provided)
</ParamField>

<ParamField path="options.possibleEvents" type="string[]" optional>
  List of event types this module can emit
</ParamField>

<ParamField path="options.dependencies" type="ModuleDependency[]" optional>
  Module dependencies
</ParamField>

<ParamField path="options.configSchema" type="ModuleConfigSchema" optional>
  Configuration schema for this module
</ParamField>

<ParamField path="options.autoConnect" type="boolean" optional default="true">
  Connect automatically on construction
</ParamField>

<ParamField path="options.autoReconnect" type="boolean" optional default="true">
  Reconnect automatically on disconnect
</ParamField>

<ParamField path="options.maxReconnectAttempts" type="number" optional default="-1">
  Maximum reconnection attempts (-1 for unlimited)
</ParamField>

<ParamField path="options.heartbeat.readTimeout" type="number" optional default="30000">
  Heartbeat interval in milliseconds
</ParamField>

<ParamField path="options.heartbeat.message" type="MessageHeartbeat | string" optional>
  Custom heartbeat message
</ParamField>

<ParamField path="options.onError" type="(error: unknown) => void" optional>
  Error handler callback
</ParamField>

<ParamField path="options.onClose" type="() => void" optional>
  Connection close callback
</ParamField>

<ParamField path="options.onAnyMessage" type="(data: WebSocketEvent) => void" optional>
  Handler for all incoming messages
</ParamField>

<ParamField path="options.onAnySend" type="(data: WebSocketEvent) => void" optional>
  Handler for all outgoing messages
</ParamField>

### Methods

#### connect()

Manually connect to the server.

```typescript theme={null}
await client.connect()
```

#### send()

Send an event to the server.

```typescript theme={null}
client.send({
  type: 'custom:event',
  data: { message: 'Hello' },
  route: {
    destinations: ['target-module']
  }
})
```

<ParamField path="data.type" type="string" required>
  Event type identifier
</ParamField>

<ParamField path="data.data" type="T" required>
  Event payload data
</ParamField>

<ParamField path="data.route" type="object" optional>
  Routing information
</ParamField>

<ParamField path="data.metadata" type="object" optional>
  Event metadata (auto-populated if not provided)
</ParamField>

#### sendRaw()

Send raw data without serialization.

```typescript theme={null}
client.sendRaw('raw string data')
client.sendRaw(new Uint8Array([1, 2, 3]))
```

#### onEvent()

Register an event handler.

```typescript theme={null}
client.onEvent('module:configure', async (event) => {
  const config = event.data.config
  // Handle configuration
})
```

<ParamField path="event" type="string" required>
  Event type to listen for
</ParamField>

<ParamField path="callback" type="function" required>
  Event handler function
</ParamField>

#### offEvent()

Unregister an event handler.

```typescript theme={null}
const handler = (event) => {
  // Handle event
}

client.onEvent('custom:event', handler)

// Later: remove specific handler
client.offEvent('custom:event', handler)

// Or remove all handlers for an event
client.offEvent('custom:event')
```

#### close()

Close the connection.

```typescript theme={null}
client.close()
```

## TypeScript Generics

Type your custom event data:

```typescript theme={null}
interface CustomEvents {
  'custom:greeting': { name: string }
  'custom:data': { value: number }
}

const client = new Client<CustomEvents>({
  url: 'ws://localhost:6121/ws',
  name: 'typed-module'
})

// Type-safe event handling
client.onEvent('custom:greeting', (event) => {
  const name = event.data.name  // TypeScript knows this is a string
})

// Type-safe sending
client.send({
  type: 'custom:greeting',
  data: { name: 'Alice' }  // TypeScript enforces correct shape
})
```

## Event Types

The SDK re-exports event types from `@proj-airi/server-shared`:

```typescript theme={null}
import type {
  WebSocketEvent,
  MetadataEventSource,
  ModuleDependency,
  ModuleConfigSchema
} from '@proj-airi/server-sdk'

import {
  WebSocketEventSource,
  ContextUpdateStrategy,
  MessageHeartbeat,
  MessageHeartbeatKind
} from '@proj-airi/server-sdk'
```

## Connection Lifecycle

The client manages connection lifecycle automatically:

```mermaid theme={null}
stateDiagram-v2
    [*] --> Connecting
    Connecting --> Connected: Success
    Connecting --> Reconnecting: Failure
    Connected --> Authenticating: Token Present
    Connected --> Announced: No Token
    Authenticating --> Announced: Success
    Authenticating --> Reconnecting: Failure
    Announced --> Ready: Registry Sync
    Ready --> Disconnected: Close
    Disconnected --> Reconnecting: Auto Reconnect
    Reconnecting --> Connecting: Backoff Complete
    Ready --> [*]: Manual Close
```

## Authentication Flow

```typescript theme={null}
const client = new Client({
  url: 'ws://localhost:6121/ws',
  name: 'secure-module',
  token: 'secret-token'
})

// Client automatically:
// 1. Connects to server
// 2. Sends module:authenticate with token
// 3. Waits for module:authenticated response
// 4. Announces module to registry
// 5. Receives registry:modules:sync
```

## Heartbeat Management

Heartbeats maintain connection health:

```typescript theme={null}
const client = new Client({
  url: 'ws://localhost:6121/ws',
  name: 'monitored-module',
  heartbeat: {
    readTimeout: 30000,  // Send ping every 30s
    message: MessageHeartbeat.Ping
  }
})

// Client automatically sends heartbeat pings
// Server responds with pongs
// Connection is maintained
```

## Error Handling

```typescript theme={null}
const client = new Client({
  url: 'ws://localhost:6121/ws',
  name: 'resilient-module',
  onError: (error) => {
    console.error('Client error:', error)
  },
  onClose: () => {
    console.log('Connection closed')
  },
  autoReconnect: true,
  maxReconnectAttempts: 10
})

// Handle specific error events
client.onEvent('error', (event) => {
  console.error('Server error:', event.data.message)
  
  if (event.data.message === 'not authenticated') {
    // Handle authentication failure
  }
})
```

## Reconnection with Backoff

The client implements exponential backoff:

```typescript theme={null}
// Attempt 1: 1s delay
// Attempt 2: 2s delay
// Attempt 3: 4s delay
// Attempt 4: 8s delay
// Attempt 5: 16s delay
// Attempt 6+: 30s delay (capped)
```

Configure reconnection:

```typescript theme={null}
const client = new Client({
  url: 'ws://localhost:6121/ws',
  name: 'persistent-module',
  autoReconnect: true,
  maxReconnectAttempts: -1  // Unlimited attempts
})
```

## Event Routing

Route events to specific destinations:

```typescript theme={null}
// Route to specific modules
client.send({
  type: 'custom:request',
  data: { query: 'data' },
  route: {
    destinations: ['processor-module', 'logger-module']
  }
})

// Route by module instance
client.send({
  type: 'custom:request',
  data: { query: 'data' },
  route: {
    destinations: [
      { name: 'worker-module', index: 0 },
      { name: 'worker-module', index: 1 }
    ]
  }
})

// Route by labels
client.send({
  type: 'custom:broadcast',
  data: { message: 'Hello' },
  route: {
    destinations: [
      { labels: { role: 'processor' } }
    ]
  }
})

// Broadcast to all (default)
client.send({
  type: 'custom:broadcast',
  data: { message: 'Everyone' }
})
```

## Module Registry

Receive and track connected modules:

```typescript theme={null}
const modules = new Map()

client.onEvent('registry:modules:sync', (event) => {
  for (const module of event.data.modules) {
    modules.set(module.identity.id, module)
  }
  
  console.log('Known modules:', Array.from(modules.keys()))
})
```

## Configuration Handling

```typescript theme={null}
client.onEvent('module:configure', async (event) => {
  const config = event.data.config
  
  try {
    // Validate configuration
    if (!config.apiKey) {
      throw new Error('API key required')
    }
    
    // Apply configuration
    await applyConfig(config)
    
    // Acknowledge
    client.send({
      type: 'module:configuration:configured',
      data: {
        identity: client.identity,
        config
      }
    })
  } catch (error) {
    // Report error
    client.send({
      type: 'error',
      data: {
        message: error.message
      }
    })
  }
})
```

## Message Interception

Intercept all messages for logging or debugging:

```typescript theme={null}
const client = new Client({
  url: 'ws://localhost:6121/ws',
  name: 'debug-module',
  onAnyMessage: (event) => {
    console.log('Received:', event.type, event.data)
  },
  onAnySend: (event) => {
    console.log('Sending:', event.type, event.data)
  }
})
```

## Node.js Utilities

For Node.js environments:

```typescript theme={null}
import { /* node utilities */ } from '@proj-airi/server-sdk/utils/node'

// Node-specific utilities (implementation details in source)
```

## Complete Example

AI module connecting to server:

```typescript theme={null}
import { Client } from '@proj-airi/server-sdk'

interface AIModuleEvents {
  'ai:generate': { prompt: string, model: string }
  'ai:response': { text: string, tokens: number }
}

const client = new Client<AIModuleEvents>({
  url: 'ws://localhost:6121/ws',
  name: 'ai-module',
  token: process.env.AUTH_TOKEN,
  identity: {
    kind: 'plugin',
    plugin: {
      id: 'ai-module',
      version: '1.0.0',
      labels: { role: 'processor', tier: 'premium' }
    },
    id: 'ai-module-1'
  },
  possibleEvents: ['ai:response'],
  onError: (error) => {
    console.error('Connection error:', error)
  },
  autoReconnect: true
})

// Handle generation requests
client.onEvent('ai:generate', async (event) => {
  const { prompt, model } = event.data
  
  try {
    // Generate response
    const response = await generateText(prompt, model)
    
    // Send response
    client.send({
      type: 'ai:response',
      data: {
        text: response.text,
        tokens: response.tokens
      },
      metadata: {
        event: {
          parentId: event.metadata.event.id
        }
      }
    })
  } catch (error) {
    // Send error
    client.send({
      type: 'error',
      data: {
        message: error.message
      },
      metadata: {
        event: {
          parentId: event.metadata.event.id
        }
      }
    })
  }
})

// Graceful shutdown
process.on('SIGTERM', () => {
  console.log('Shutting down...')
  client.close()
  process.exit(0)
})
```

## Best Practices

<AccordionGroup>
  <Accordion title="Handle reconnection">
    Always enable `autoReconnect` for production services to handle network issues gracefully.
  </Accordion>

  <Accordion title="Use event IDs for tracing">
    Include `parentId` in responses to enable request-response correlation and distributed tracing.
  </Accordion>

  <Accordion title="Implement heartbeat">
    Configure appropriate heartbeat intervals based on your network conditions (default: 30s).
  </Accordion>

  <Accordion title="Type your events">
    Use TypeScript generics to type-check event data and catch errors at compile time.
  </Accordion>

  <Accordion title="Handle authentication errors">
    Listen for authentication errors and handle them appropriately (retry with new token, notify user, etc.).
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Server Runtime" icon="server" href="/api/server-runtime/overview">
    Learn about the server runtime
  </Card>

  <Card title="WebSocket Protocol" icon="network-wired" href="/api/server-runtime/websocket">
    Understand the WebSocket protocol
  </Card>
</CardGroup>
