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
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-toolsPour 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
- Architecture Détaillée : DYNAMIC_TOOLS_SYSTEM.md
- Guide Migration : MIGRATION_GUIDE.md
- Examples : /examples
- API Docs : https://docs.devana.ai
🤝 Support & Communauté
- GitHub Issues : github.com/Scriptor-Group/devana.ai/issues
- Discord : discord.gg/devana
- Email : support@devana.ai
🔄 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