JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 36
  • Score
    100M100P100Q88891F
  • License MIT

WebSocket client library for Devana Dynamic Tools - Connect any app to Devana AI agents

Package Exports

  • @devana/ws-tools
  • @devana/ws-tools/dist/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@devana/ws-tools) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

@devana/ws-tools

npm version License: MIT

Bibliothèque JavaScript/TypeScript pour connecter n'importe quelle application à Devana AI via WebSocket et exposer des tools dynamiques que l'agent IA peut utiliser en temps réel.

🌟 Qu'est-ce que @devana/ws-tools ?

Cette bibliothèque permet de :

  • Connecter votre application à Devana AI via WebSocket
  • Exposer des fonctionnalités de votre app comme des "tools" pour l'IA
  • Permettre à l'agent IA d'interagir avec votre application en temps réel
  • Supporter multi-sessions et multi-documents avec un seul client

🚀 Installation

npm install @devana/ws-tools
# ou
yarn add @devana/ws-tools
# ou
pnpm add @devana/ws-tools

Pour Node.js, installez également :

npm install ws

📝 Guide de Démarrage Rapide

1. Créer un Client WebSocket

import { DevanaWSClient } from '@devana/ws-tools';

const client = new DevanaWSClient({
  apiUrl: 'https://api.devana.ai',
  clientType: 'my-app',              // Type de votre application
  documentName: 'Document Principal', // Optionnel : nom du document
  bearerToken: 'eyJhbGciOi...',      // Optionnel : Token JWT (le serveur extrait automatiquement le userId)
  metadata: {                        // Optionnel : métadonnées
    version: '1.0.0',
    platform: 'web'
  },
  onConnected: (sessionId) => {
    console.log('✅ Connecté avec session:', sessionId);
  },
  onDisconnected: () => {
    console.log('❌ Déconnecté');
  },
  onError: (error) => {
    console.error('⚠️ Erreur:', error);
  }
});

2. Enregistrer des Tools (Fonctionnalités)

Les tools sont des fonctionnalités que vous exposez à l'agent IA :

// Tool simple
client.registerTool('get_current_time', {
  description: 'Obtenir l\'heure actuelle',
  schema: {
    type: 'object',
    properties: {},
    required: []
  },
  handler: async () => {
    return new Date().toLocaleTimeString();
  }
});

// Tool avec paramètres
client.registerTool('create_document', {
  description: 'Créer un nouveau document',
  schema: {
    type: 'object',
    properties: {
      title: {
        type: 'string',
        description: 'Titre du document'
      },
      content: {
        type: 'string',
        description: 'Contenu initial'
      },
      format: {
        type: 'string',
        enum: ['txt', 'md', 'html', 'json'],
        description: 'Format du document'
      }
    },
    required: ['title', 'content']
  },
  handler: async (data) => {
    // Votre logique métier
    const doc = await createDocumentInYourApp(data);
    return `Document "${data.title}" créé avec succès`;
  }
});

// Enregistrer plusieurs tools d'un coup
client.registerTools([
  {
    name: 'save_file',
    description: 'Sauvegarder un fichier',
    schema: { /* ... */ },
    handler: async (data) => { /* ... */ }
  },
  {
    name: 'delete_file',
    description: 'Supprimer un fichier',
    schema: { /* ... */ },
    handler: async (data) => { /* ... */ }
  }
]);

3. Se Connecter au Serveur

// Connexion asynchrone
await client.connect();

// Vérifier la connexion
if (client.isConnected()) {
  console.log('Session ID:', client.getSessionId());
  console.log('Document:', client.getDocumentName());
}

4. Utiliser avec l'API Devana

import { callDevanaWithTools } from '@devana/ws-tools';

// Envoyer une requête au LLM avec vos tools
const response = await callDevanaWithTools(
  'https://api.devana.ai',
  'votre-api-key',
  {
    agentId: 'devana',                    // ID de l'agent
    message: 'Crée un document test.md',  // Message utilisateur
    sessionId: client.getSessionId()!,    // Session WebSocket
    tools: client.getToolsConfig(),       // Vos tools
    conversationId: 'conv-123',           // Optionnel
    stream: false                         // Optionnel
  }
);

// L'agent utilisera automatiquement vos tools pour accomplir la tâche !
const result = await response.json();
console.log(result);

🎨 Exemples Complets

Exemple 1 : Microsoft Word Add-in

import { DevanaWSClient } from '@devana/ws-tools';

class WordAddIn {
  private client: DevanaWSClient;

  constructor() {
    this.client = new DevanaWSClient({
      apiUrl: 'https://api.devana.ai',
      clientType: 'word',
      documentName: this.getDocumentName(),
      onConnected: (sessionId) => {
        this.updateUI('connected', sessionId);
      }
    });

    this.registerWordTools();
  }

  private getDocumentName(): string {
    // Obtenir le nom du document Word actuel
    return Office.context.document.url || 'Sans titre';
  }

  private registerWordTools() {
    // Ajouter du texte avec style
    this.client.registerTool('add_paragraph', {
      description: 'Ajouter un paragraphe avec un style spécifique',
      schema: {
        type: 'object',
        properties: {
          text: {
            type: 'string',
            description: 'Texte du paragraphe'
          },
          style: {
            type: 'string',
            enum: ['Title', 'Heading1', 'Heading2', 'Heading3', 'Normal', 'Quote'],
            description: 'Style du paragraphe'
          },
          alignment: {
            type: 'string',
            enum: ['Left', 'Center', 'Right', 'Justified'],
            description: 'Alignement du texte'
          }
        },
        required: ['text', 'style']
      },
      handler: async (data) => {
        return await Word.run(async (context) => {
          const body = context.document.body;
          const paragraph = body.insertParagraph(
            data.text,
            Word.InsertLocation.end
          );

          paragraph.style = data.style;
          if (data.alignment) {
            paragraph.alignment = data.alignment;
          }

          await context.sync();
          return `Paragraphe ajouté avec le style ${data.style}`;
        });
      }
    });

    // Insérer un tableau
    this.client.registerTool('insert_table', {
      description: 'Insérer un tableau dans le document',
      schema: {
        type: 'object',
        properties: {
          rows: {
            type: 'number',
            description: 'Nombre de lignes',
            minimum: 1,
            maximum: 100
          },
          columns: {
            type: 'number',
            description: 'Nombre de colonnes',
            minimum: 1,
            maximum: 20
          },
          data: {
            type: 'array',
            description: 'Données du tableau (optionnel)',
            items: {
              type: 'array',
              items: { type: 'string' }
            }
          }
        },
        required: ['rows', 'columns']
      },
      handler: async (data) => {
        return await Word.run(async (context) => {
          const body = context.document.body;
          const table = body.insertTable(
            data.rows,
            data.columns,
            Word.InsertLocation.end
          );

          // Remplir avec les données si fournies
          if (data.data) {
            for (let i = 0; i < data.data.length && i < data.rows; i++) {
              for (let j = 0; j < data.data[i].length && j < data.columns; j++) {
                table.getCell(i, j).value = data.data[i][j];
              }
            }
          }

          await context.sync();
          return `Tableau ${data.rows}x${data.columns} inséré`;
        });
      }
    });

    // Obtenir les statistiques du document
    this.client.registerTool('get_document_stats', {
      description: 'Obtenir les statistiques du document',
      schema: {
        type: 'object',
        properties: {},
        required: []
      },
      handler: async () => {
        return await Word.run(async (context) => {
          const body = context.document.body;
          const paragraphs = body.paragraphs;
          const tables = body.tables;

          body.load('text');
          paragraphs.load('items');
          tables.load('items');

          await context.sync();

          const text = body.text;
          const words = text.trim().split(/\s+/).filter(w => w.length > 0);
          const characters = text.length;

          return {
            paragraphs: paragraphs.items.length,
            tables: tables.items.length,
            words: words.length,
            characters: characters,
            charactersNoSpaces: text.replace(/\s/g, '').length
          };
        });
      }
    });
  }

  async connect() {
    await this.client.connect();
  }

  getClient() {
    return this.client;
  }
}

// Utilisation
Office.onReady(async () => {
  const wordAddIn = new WordAddIn();
  await wordAddIn.connect();

  // Maintenant l'agent IA peut contrôler Word !
});

Exemple 2 : Application IoT / Domotique

import { DevanaWSClient } from '@devana/ws-tools';

class SmartHomeClient {
  private client: DevanaWSClient;
  private devices: Map<string, any> = new Map();

  constructor() {
    this.client = new DevanaWSClient({
      apiUrl: 'https://api.devana.ai',
      clientType: 'smart-home',
      bearerToken: 'eyJhbGciOi...',
      metadata: {
        location: 'Maison principale',
        rooms: ['salon', 'cuisine', 'chambres']
      }
    });

    this.registerSmartHomeTools();
  }

  private registerSmartHomeTools() {
    // Contrôle des lumières
    this.client.registerTool('control_lights', {
      description: 'Contrôler les lumières de la maison',
      schema: {
        type: 'object',
        properties: {
          room: {
            type: 'string',
            enum: ['salon', 'cuisine', 'chambre1', 'chambre2', 'all'],
            description: 'Pièce cible'
          },
          action: {
            type: 'string',
            enum: ['on', 'off', 'dim'],
            description: 'Action à effectuer'
          },
          brightness: {
            type: 'number',
            minimum: 0,
            maximum: 100,
            description: 'Niveau de luminosité (pour dim)'
          }
        },
        required: ['room', 'action']
      },
      handler: async (data) => {
        // Logique de contrôle des lumières
        const devices = data.room === 'all'
          ? this.getAllLights()
          : this.getLightsByRoom(data.room);

        for (const device of devices) {
          switch (data.action) {
            case 'on':
              await device.turnOn();
              break;
            case 'off':
              await device.turnOff();
              break;
            case 'dim':
              await device.setBrightness(data.brightness || 50);
              break;
          }
        }

        return `Lumières ${data.room}: ${data.action}`;
      }
    });

    // Thermostat
    this.client.registerTool('set_temperature', {
      description: 'Régler la température',
      schema: {
        type: 'object',
        properties: {
          zone: {
            type: 'string',
            enum: ['salon', 'chambres', 'global'],
            description: 'Zone de température'
          },
          temperature: {
            type: 'number',
            minimum: 15,
            maximum: 30,
            description: 'Température en Celsius'
          },
          mode: {
            type: 'string',
            enum: ['heat', 'cool', 'auto'],
            description: 'Mode de climatisation'
          }
        },
        required: ['zone', 'temperature']
      },
      handler: async (data) => {
        const thermostat = this.getThermostat(data.zone);
        await thermostat.setTemperature(data.temperature);
        if (data.mode) {
          await thermostat.setMode(data.mode);
        }
        return `Température ${data.zone} réglée à ${data.temperature}°C`;
      }
    });

    // Sécurité
    this.client.registerTool('security_control', {
      description: 'Contrôler le système de sécurité',
      schema: {
        type: 'object',
        properties: {
          action: {
            type: 'string',
            enum: ['arm', 'disarm', 'status', 'lock_doors', 'unlock_doors'],
            description: 'Action de sécurité'
          },
          code: {
            type: 'string',
            description: 'Code de sécurité (pour arm/disarm)'
          }
        },
        required: ['action']
      },
      handler: async (data) => {
        switch (data.action) {
          case 'arm':
            return await this.armSecuritySystem(data.code);
          case 'disarm':
            return await this.disarmSecuritySystem(data.code);
          case 'status':
            return await this.getSecurityStatus();
          case 'lock_doors':
            return await this.lockAllDoors();
          case 'unlock_doors':
            return await this.unlockDoors(data.code);
        }
      }
    });

    // Scénarios
    this.client.registerTool('run_scenario', {
      description: 'Exécuter un scénario prédéfini',
      schema: {
        type: 'object',
        properties: {
          scenario: {
            type: 'string',
            enum: ['morning', 'night', 'away', 'movie', 'party'],
            description: 'Scénario à exécuter'
          }
        },
        required: ['scenario']
      },
      handler: async (data) => {
        switch (data.scenario) {
          case 'morning':
            await this.runMorningRoutine();
            break;
          case 'night':
            await this.runNightRoutine();
            break;
          case 'away':
            await this.runAwayMode();
            break;
          case 'movie':
            await this.runMovieMode();
            break;
          case 'party':
            await this.runPartyMode();
            break;
        }
        return `Scénario "${data.scenario}" activé`;
      }
    });
  }

  // Méthodes helper...
  private async runMorningRoutine() {
    // Ouvrir les volets
    // Allumer lumières cuisine
    // Régler température à 21°C
    // Lancer machine à café
  }

  // ... autres méthodes
}

Exemple 3 : Application de Dessin/Design

import { DevanaWSClient } from '@devana/ws-tools';

class DesignAppClient {
  private client: DevanaWSClient;
  private canvas: any;

  constructor(canvas: any) {
    this.canvas = canvas;
    this.client = new DevanaWSClient({
      apiUrl: 'https://api.devana.ai',
      clientType: 'design-app',
      documentName: canvas.projectName
    });

    this.registerDesignTools();
  }

  private registerDesignTools() {
    // Créer des formes
    this.client.registerTool('create_shape', {
      description: 'Créer une forme géométrique',
      schema: {
        type: 'object',
        properties: {
          type: {
            type: 'string',
            enum: ['rectangle', 'circle', 'triangle', 'polygon', 'star'],
            description: 'Type de forme'
          },
          x: { type: 'number', description: 'Position X' },
          y: { type: 'number', description: 'Position Y' },
          width: { type: 'number', description: 'Largeur' },
          height: { type: 'number', description: 'Hauteur' },
          fill: { type: 'string', description: 'Couleur de remplissage' },
          stroke: { type: 'string', description: 'Couleur de bordure' },
          strokeWidth: { type: 'number', description: 'Épaisseur de bordure' }
        },
        required: ['type', 'x', 'y', 'width', 'height']
      },
      handler: async (data) => {
        const shape = this.canvas.createShape(data);
        return `Forme ${data.type} créée avec ID: ${shape.id}`;
      }
    });

    // Ajouter du texte
    this.client.registerTool('add_text', {
      description: 'Ajouter du texte sur le canvas',
      schema: {
        type: 'object',
        properties: {
          text: { type: 'string', description: 'Texte à afficher' },
          x: { type: 'number', description: 'Position X' },
          y: { type: 'number', description: 'Position Y' },
          font: { type: 'string', description: 'Police' },
          size: { type: 'number', description: 'Taille de police' },
          color: { type: 'string', description: 'Couleur' },
          bold: { type: 'boolean', description: 'Gras' },
          italic: { type: 'boolean', description: 'Italique' }
        },
        required: ['text', 'x', 'y']
      },
      handler: async (data) => {
        const textObj = this.canvas.addText(data);
        return `Texte ajouté: "${data.text}"`;
      }
    });

    // Appliquer des filtres
    this.client.registerTool('apply_filter', {
      description: 'Appliquer un filtre à un élément ou au canvas',
      schema: {
        type: 'object',
        properties: {
          target: {
            type: 'string',
            description: 'ID de l\'élément ou "canvas" pour tout'
          },
          filter: {
            type: 'string',
            enum: ['blur', 'brightness', 'contrast', 'grayscale', 'sepia', 'saturate'],
            description: 'Type de filtre'
          },
          value: {
            type: 'number',
            description: 'Valeur du filtre (0-100)'
          }
        },
        required: ['filter', 'value']
      },
      handler: async (data) => {
        if (data.target === 'canvas') {
          this.canvas.applyGlobalFilter(data.filter, data.value);
        } else {
          const element = this.canvas.getElementById(data.target);
          element.applyFilter(data.filter, data.value);
        }
        return `Filtre ${data.filter} appliqué`;
      }
    });
  }
}

📊 Architecture Multi-Sessions

Le système supporte plusieurs sessions simultanées pour un même utilisateur :

// Client 1 : Document Word
const wordClient = new DevanaWSClient({
  apiUrl: 'https://api.devana.ai',
  clientType: 'word',
  documentName: 'Rapport Q4 2024',
  bearerToken: 'eyJhbGciOi...'
});

// Client 2 : Document Excel
const excelClient = new DevanaWSClient({
  apiUrl: 'https://api.devana.ai',
  clientType: 'excel',
  documentName: 'Budget 2024',
  bearerToken: 'eyJhbGciOi...'  // Même utilisateur
});

// Client 3 : Application IoT
const iotClient = new DevanaWSClient({
  apiUrl: 'https://api.devana.ai',
  clientType: 'smart-home',
  bearerToken: 'eyJhbGciOi...'  // Même utilisateur
});

// L'agent IA peut interagir avec les 3 en même temps !

🔧 Configuration Avancée

Options de Reconnexion

const client = new DevanaWSClient({
  apiUrl: 'https://api.devana.ai',
  clientType: 'my-app',

  // Reconnexion automatique
  autoReconnect: true,              // Par défaut: true
  maxReconnectAttempts: 10,         // Par défaut: 5
  reconnectDelay: 5000,             // Par défaut: 3000ms

  // Callbacks
  onReconnected: (sessionId) => {
    console.log('Reconnecté avec nouvelle session:', sessionId);
    // Réenregistrer l'état si nécessaire
  }
});

Gestion des Erreurs

client.registerTool('risky_operation', {
  description: 'Opération qui peut échouer',
  schema: { /* ... */ },
  handler: async (data) => {
    try {
      // Opération risquée
      const result = await dangerousOperation(data);
      return result;
    } catch (error) {
      // L'erreur sera transmise à l'agent IA
      throw new Error(`Échec de l'opération: ${error.message}`);
    }
  }
});

Heartbeat / Keep-Alive

La bibliothèque gère automatiquement un heartbeat (ping/pong) toutes les 30 secondes pour maintenir la connexion active.

📚 API Reference Complète

DevanaWSClient

Constructor Options

Option Type Description Default
apiUrl string URL de l'API Devana Required
clientType string Type de client (word, excel, custom...) Required
documentName string Nom du document ou contexte undefined
bearerToken string Token JWT pour authentification (le serveur extrait automatiquement le userId) undefined
metadata object Métadonnées supplémentaires {}
autoReconnect boolean Reconnexion automatique si déconnecté true
maxReconnectAttempts number Nombre max de tentatives 5
reconnectDelay number Délai entre tentatives (ms) 3000
onConnected (sessionId: string) => void Callback connexion réussie () => {}
onDisconnected () => void Callback déconnexion () => {}
onReconnected (sessionId: string) => void Callback reconnexion () => {}
onError (error: Error) => void Callback erreur () => {}
onSessionsChanged () => void Callback changement de sessions undefined

Methods

registerTool(name: string, config: ToolDefinition)

Enregistre un tool avec son handler.

client.registerTool('my_tool', {
  description: 'Description du tool',
  schema: { /* JSON Schema */ },
  handler: async (data) => { /* ... */ }
});
registerTools(tools: ToolDefinition[])

Enregistre plusieurs tools en une fois.

getToolsConfig(): ToolConfig[]

Retourne la configuration des tools (sans les handlers) pour envoyer au serveur.

connect(): Promise<string>

Se connecte au serveur WebSocket. Retourne le sessionId.

getSessionId(): string | null

Retourne l'ID de session actuel.

getDocumentName(): string | undefined

Retourne le nom du document associé.

isConnected(): boolean

Vérifie si le client est connecté.

disconnect(): void

Déconnecte proprement le client.

callDevanaWithTools()

Helper pour envoyer une requête à l'API Devana avec support des tools dynamiques.

async function callDevanaWithTools(
  apiUrl: string,
  apiKey: string,
  options: {
    agentId: string;          // ID de l'agent IA
    message: string;          // Message utilisateur
    sessionId: string;        // Session WebSocket
    tools: ToolConfig[];      // Configuration des tools
    conversationId?: string;  // ID de conversation (optionnel)
    stream?: boolean;         // Mode streaming (optionnel)
  }
): Promise<Response>

Types TypeScript

interface ToolSchema {
  type: "object";
  properties: Record<string, {
    type: string;
    description?: string;
    enum?: string[];
    minimum?: number;
    maximum?: number;
    items?: any;
  }>;
  required?: string[];
}

interface ToolConfig {
  name: string;
  description: string;
  schema: ToolSchema;
}

interface ToolDefinition extends ToolConfig {
  handler: (data: Record<string, any>) => Promise<any>;
}

interface DevanaWSClientOptions {
  apiUrl: string;
  clientType: string;
  documentName?: string;
  bearerToken?: string; // Token JWT pour authentification
  metadata?: Record<string, any>;
  autoReconnect?: boolean;
  maxReconnectAttempts?: number;
  reconnectDelay?: number;
  onConnected?: (sessionId: string) => void;
  onDisconnected?: () => void;
  onReconnected?: (sessionId: string) => void;
  onError?: (error: Error) => void;
  onSessionsChanged?: () => void;
}

🔒 Sécurité

Authentification

  • Les sessions WebSocket sont temporaires et uniques
  • Chaque session a un ID unique généré côté client
  • Le serveur valide les permissions via JWT

Isolation

  • Chaque session est isolée
  • Pas d'accès cross-session
  • Les tools sont limités à leur session

Validation

  • Les schémas sont validés côté serveur avec Zod
  • Timeout de 30 secondes par exécution de tool
  • Rate limiting sur les appels

🐛 Debugging

Activer les logs détaillés :

// Les logs sont automatiquement affichés dans la console
// Format: [DevanaWSClient] Message

// Exemples de logs:
// [DevanaWSClient] ✅ Registered
// [DevanaWSClient] 📥 add_paragraph
// [DevanaWSClient] ▶ add_paragraph
// [DevanaWSClient] ✅ add_paragraph done
// [DevanaWSClient] 📤 Response sent (OK)

🚀 Déploiement

Browser (Web)

<script src="https://unpkg.com/@devana/ws-tools"></script>
<script>
  const client = new DevanaWSTools.DevanaWSClient({
    apiUrl: 'https://api.devana.ai',
    clientType: 'web-app'
  });
</script>

Node.js

import { DevanaWSClient } from '@devana/ws-tools';
// ou
const { DevanaWSClient } = require('@devana/ws-tools');

React/Vue/Angular

// Dans un composant React
import { useEffect, useState } from 'react';
import { DevanaWSClient } from '@devana/ws-tools';

function MyComponent() {
  const [client, setClient] = useState<DevanaWSClient | null>(null);

  useEffect(() => {
    const wsClient = new DevanaWSClient({
      apiUrl: process.env.REACT_APP_DEVANA_API,
      clientType: 'react-app'
    });

    wsClient.registerTool(/* ... */);
    wsClient.connect();

    setClient(wsClient);

    return () => {
      wsClient.disconnect();
    };
  }, []);

  // ...
}

📖 Documentation & Ressources

🤝 Support & Communauté

🔄 Changelog

v1.0.0 (2024-01)

  • Version initiale
  • Support WebSocket avec auto-reconnect
  • Système de tools dynamiques
  • Support multi-sessions
  • Heartbeat automatique
  • Support Browser et Node.js

📄 License

MIT © Devana.ai


Fait avec ❤️ par l'équipe Devana.ai