{
  "asyncapi": "3.0.0",
  "id": "urn:playability:desktop:websocket",
  "info": {
    "title": "PlayAbility Desktop WebSocket API",
    "version": "2.6.0",
    "description": "PlayAbility Desktop WebSocket API. receive: client vers PlayAbility; send: PlayAbility vers client. Les schémas décrivent les messages recommandés. info.version identifie ce contrat; il n’y a pas de négociation de version sur la connexion. Envoyer une seule requête avec réponse à la fois.",
    "contact": {
      "name": "PlayAbility",
      "url": "https://www.playability.gg"
    },
    "externalDocs": {
      "description": "Documentation développeur",
      "url": "https://www.playability.gg/developers/websocket/"
    }
  },
  "defaultContentType": "application/json",
  "servers": {
    "local": {
      "host": "127.0.0.1:13123",
      "protocol": "ws",
      "description": "Connexion locale sur le même ordinateur, sans TLS ni sous-protocole obligatoire. Utiliser le chemin /. Le serveur fonctionne si WebSocket ou MCP est activé. Envoyer AUTH quand CONNECT.authRequired vaut true."
    }
  },
  "channels": {
    "control": {
      "address": "/",
      "servers": [
        {
          "$ref": "#/servers/local"
        }
      ],
      "description": "Canal bidirectionnel unique; toutes les commandes se distinguent par type, sauf InputEvents qui utilise events. Le type state est une chaîne JSON doublement encodée. Les types sont sensibles à la casse.",
      "messages": {
        "AUTH": {
          "$ref": "#/components/messages/AUTH"
        },
        "CONNECT": {
          "$ref": "#/components/messages/CONNECT"
        },
        "AUTH_OK": {
          "$ref": "#/components/messages/AUTH_OK"
        },
        "AUTH_ERROR": {
          "$ref": "#/components/messages/AUTH_ERROR"
        },
        "InputEvents": {
          "$ref": "#/components/messages/InputEvents"
        },
        "getState": {
          "$ref": "#/components/messages/getState"
        },
        "state": {
          "$ref": "#/components/messages/state"
        },
        "EVENTS": {
          "$ref": "#/components/messages/EVENTS"
        },
        "GET_CURRENT_MAPPINGS": {
          "$ref": "#/components/messages/GET_CURRENT_MAPPINGS"
        },
        "CURRENT_MAPPINGS": {
          "$ref": "#/components/messages/CURRENT_MAPPINGS"
        },
        "GET_PROFILE_MAPPINGS": {
          "$ref": "#/components/messages/GET_PROFILE_MAPPINGS"
        },
        "PROFILE_MAPPINGS": {
          "$ref": "#/components/messages/PROFILE_MAPPINGS"
        },
        "MCP_SWITCH_PROFILE": {
          "$ref": "#/components/messages/MCP_SWITCH_PROFILE"
        },
        "MCP_PROFILE_SWITCHED": {
          "$ref": "#/components/messages/MCP_PROFILE_SWITCHED"
        },
        "GET_TEMPLATE_PROFILES": {
          "$ref": "#/components/messages/GET_TEMPLATE_PROFILES"
        },
        "TEMPLATE_PROFILES": {
          "$ref": "#/components/messages/TEMPLATE_PROFILES"
        },
        "MCP_IMPORT_MAPPINGS": {
          "$ref": "#/components/messages/MCP_IMPORT_MAPPINGS"
        },
        "MAPPINGS_IMPORTED": {
          "$ref": "#/components/messages/MAPPINGS_IMPORTED"
        },
        "GET_SETTINGS": {
          "$ref": "#/components/messages/GET_SETTINGS"
        },
        "SETTINGS_STATE": {
          "$ref": "#/components/messages/SETTINGS_STATE"
        },
        "SET_SETTING": {
          "$ref": "#/components/messages/SET_SETTING"
        },
        "SETTING_APPLIED": {
          "$ref": "#/components/messages/SETTING_APPLIED"
        },
        "NAVIGATE_TO": {
          "$ref": "#/components/messages/NAVIGATE_TO"
        },
        "NAVIGATED": {
          "$ref": "#/components/messages/NAVIGATED"
        },
        "GET_CURRENT_PAGE": {
          "$ref": "#/components/messages/GET_CURRENT_PAGE"
        },
        "CURRENT_PAGE": {
          "$ref": "#/components/messages/CURRENT_PAGE"
        },
        "TAKE_SCREENSHOT": {
          "$ref": "#/components/messages/TAKE_SCREENSHOT"
        },
        "SCREENSHOT_DATA": {
          "$ref": "#/components/messages/SCREENSHOT_DATA"
        },
        "LIST_WINDOWS": {
          "$ref": "#/components/messages/LIST_WINDOWS"
        },
        "WINDOWS_LIST": {
          "$ref": "#/components/messages/WINDOWS_LIST"
        },
        "GET_DISPLAYS": {
          "$ref": "#/components/messages/GET_DISPLAYS"
        },
        "DISPLAYS_LIST": {
          "$ref": "#/components/messages/DISPLAYS_LIST"
        },
        "WINDOW_GET_PROPERTIES": {
          "$ref": "#/components/messages/WINDOW_GET_PROPERTIES"
        },
        "WINDOW_PROPERTIES": {
          "$ref": "#/components/messages/WINDOW_PROPERTIES"
        },
        "WINDOW_MOVE": {
          "$ref": "#/components/messages/WINDOW_MOVE"
        },
        "WINDOW_MOVED": {
          "$ref": "#/components/messages/WINDOW_MOVED"
        },
        "WINDOW_RESIZE": {
          "$ref": "#/components/messages/WINDOW_RESIZE"
        },
        "WINDOW_RESIZED": {
          "$ref": "#/components/messages/WINDOW_RESIZED"
        },
        "WINDOW_FOCUS": {
          "$ref": "#/components/messages/WINDOW_FOCUS"
        },
        "WINDOW_FOCUSED": {
          "$ref": "#/components/messages/WINDOW_FOCUSED"
        },
        "GET_MOUSE_CURSOR_POSITION": {
          "$ref": "#/components/messages/GET_MOUSE_CURSOR_POSITION"
        },
        "MOUSE_POSITION": {
          "$ref": "#/components/messages/MOUSE_POSITION"
        },
        "ERROR": {
          "$ref": "#/components/messages/ERROR"
        },
        "SCREENSHOT_ERROR": {
          "$ref": "#/components/messages/SCREENSHOT_ERROR"
        },
        "MOUSE_POSITION_ERROR": {
          "$ref": "#/components/messages/MOUSE_POSITION_ERROR"
        }
      }
    }
  },
  "operations": {
    "receiveAUTH": {
      "action": "receive",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Authentification au niveau message JSON, pas un en-tête HTTP",
      "messages": [
        {
          "$ref": "#/channels/control/messages/AUTH"
        }
      ],
      "x-response-types": [
        "AUTH_OK",
        "AUTH_ERROR"
      ],
      "reply": {
        "channel": {
          "$ref": "#/channels/control"
        },
        "messages": [
          {
            "$ref": "#/channels/control/messages/AUTH_OK"
          },
          {
            "$ref": "#/channels/control/messages/AUTH_ERROR"
          }
        ]
      }
    },
    "sendCONNECT": {
      "action": "send",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Accusé de connexion envoyé automatiquement",
      "messages": [
        {
          "$ref": "#/channels/control/messages/CONNECT"
        }
      ]
    },
    "sendAUTH_OK": {
      "action": "send",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Authentification acceptée pour cette connexion.",
      "messages": [
        {
          "$ref": "#/channels/control/messages/AUTH_OK"
        }
      ]
    },
    "sendAUTH_ERROR": {
      "action": "send",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Clé incorrecte; le serveur ferme ensuite la connexion sans code personnalisé.",
      "messages": [
        {
          "$ref": "#/channels/control/messages/AUTH_ERROR"
        }
      ]
    },
    "receiveInputEvents": {
      "action": "receive",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Applique les entrées dans l’ordre du tableau, sans accusé de traitement",
      "messages": [
        {
          "$ref": "#/channels/control/messages/InputEvents"
        }
      ]
    },
    "receivegetState": {
      "action": "receive",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Demande un état de manette limité aux valeurs non neutres",
      "messages": [
        {
          "$ref": "#/channels/control/messages/getState"
        }
      ],
      "x-response-types": [
        "state"
      ]
    },
    "sendstate": {
      "action": "send",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "État diffusé à tous les clients après getState, y compris avant authentification",
      "messages": [
        {
          "$ref": "#/channels/control/messages/state"
        }
      ]
    },
    "sendEVENTS": {
      "action": "send",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Deltas diffusés si toggleWebsocket ET allowWebsocketOutput sont vrais",
      "messages": [
        {
          "$ref": "#/channels/control/messages/EVENTS"
        }
      ]
    },
    "receiveGET_CURRENT_MAPPINGS": {
      "action": "receive",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Lit le profil actif, la liste des noms de profils et ses mappings.",
      "messages": [
        {
          "$ref": "#/channels/control/messages/GET_CURRENT_MAPPINGS"
        }
      ],
      "x-response-types": [
        "CURRENT_MAPPINGS",
        "ERROR"
      ],
      "reply": {
        "channel": {
          "$ref": "#/channels/control"
        },
        "messages": [
          {
            "$ref": "#/channels/control/messages/CURRENT_MAPPINGS"
          },
          {
            "$ref": "#/channels/control/messages/ERROR"
          }
        ]
      }
    },
    "sendCURRENT_MAPPINGS": {
      "action": "send",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Profil actif, noms des profils et mappings",
      "messages": [
        {
          "$ref": "#/channels/control/messages/CURRENT_MAPPINGS"
        }
      ]
    },
    "receiveGET_PROFILE_MAPPINGS": {
      "action": "receive",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Lit les mappings du nom de profil résolu; un nom inconnu renvoie une liste vide.",
      "messages": [
        {
          "$ref": "#/channels/control/messages/GET_PROFILE_MAPPINGS"
        }
      ],
      "x-response-types": [
        "PROFILE_MAPPINGS",
        "ERROR"
      ],
      "reply": {
        "channel": {
          "$ref": "#/channels/control"
        },
        "messages": [
          {
            "$ref": "#/channels/control/messages/PROFILE_MAPPINGS"
          },
          {
            "$ref": "#/channels/control/messages/ERROR"
          }
        ]
      }
    },
    "sendPROFILE_MAPPINGS": {
      "action": "send",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Nom du profil et mappings",
      "messages": [
        {
          "$ref": "#/channels/control/messages/PROFILE_MAPPINGS"
        }
      ]
    },
    "receiveMCP_SWITCH_PROFILE": {
      "action": "receive",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Change le profil actif",
      "messages": [
        {
          "$ref": "#/channels/control/messages/MCP_SWITCH_PROFILE"
        }
      ],
      "x-response-types": [
        "MCP_PROFILE_SWITCHED",
        "ERROR"
      ],
      "reply": {
        "channel": {
          "$ref": "#/channels/control"
        },
        "messages": [
          {
            "$ref": "#/channels/control/messages/MCP_PROFILE_SWITCHED"
          },
          {
            "$ref": "#/channels/control/messages/ERROR"
          }
        ]
      }
    },
    "sendMCP_PROFILE_SWITCHED": {
      "action": "send",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Profil actif après changement.",
      "messages": [
        {
          "$ref": "#/channels/control/messages/MCP_PROFILE_SWITCHED"
        }
      ]
    },
    "receiveGET_TEMPLATE_PROFILES": {
      "action": "receive",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Lit les profils intégrés dont type vaut template.",
      "messages": [
        {
          "$ref": "#/channels/control/messages/GET_TEMPLATE_PROFILES"
        }
      ],
      "x-response-types": [
        "TEMPLATE_PROFILES",
        "ERROR"
      ],
      "reply": {
        "channel": {
          "$ref": "#/channels/control"
        },
        "messages": [
          {
            "$ref": "#/channels/control/messages/TEMPLATE_PROFILES"
          },
          {
            "$ref": "#/channels/control/messages/ERROR"
          }
        ]
      }
    },
    "sendTEMPLATE_PROFILES": {
      "action": "send",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Liste des modèles intégrés; outputTypes peut être absent.",
      "messages": [
        {
          "$ref": "#/channels/control/messages/TEMPLATE_PROFILES"
        }
      ]
    },
    "receiveMCP_IMPORT_MAPPINGS": {
      "action": "receive",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Importe des mappings",
      "messages": [
        {
          "$ref": "#/channels/control/messages/MCP_IMPORT_MAPPINGS"
        }
      ],
      "x-response-types": [
        "MAPPINGS_IMPORTED",
        "ERROR"
      ],
      "reply": {
        "channel": {
          "$ref": "#/channels/control"
        },
        "messages": [
          {
            "$ref": "#/channels/control/messages/MAPPINGS_IMPORTED"
          },
          {
            "$ref": "#/channels/control/messages/ERROR"
          }
        ]
      }
    },
    "sendMAPPINGS_IMPORTED": {
      "action": "send",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Résultat effectif après import ou choix utilisateur.",
      "messages": [
        {
          "$ref": "#/channels/control/messages/MAPPINGS_IMPORTED"
        }
      ]
    },
    "receiveGET_SETTINGS": {
      "action": "receive",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Lit les paramètres booléens exposés et leurs indications; ne retourne pas la clé d’authentification.",
      "messages": [
        {
          "$ref": "#/channels/control/messages/GET_SETTINGS"
        }
      ],
      "x-response-types": [
        "SETTINGS_STATE",
        "ERROR"
      ],
      "reply": {
        "channel": {
          "$ref": "#/channels/control"
        },
        "messages": [
          {
            "$ref": "#/channels/control/messages/SETTINGS_STATE"
          },
          {
            "$ref": "#/channels/control/messages/ERROR"
          }
        ]
      }
    },
    "sendSETTINGS_STATE": {
      "action": "send",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Paramètres exposés et indications en anglais",
      "messages": [
        {
          "$ref": "#/channels/control/messages/SETTINGS_STATE"
        }
      ]
    },
    "receiveSET_SETTING": {
      "action": "receive",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Définit value ou bascule avec toggle=true, exclusivement",
      "messages": [
        {
          "$ref": "#/channels/control/messages/SET_SETTING"
        }
      ],
      "x-response-types": [
        "SETTING_APPLIED",
        "ERROR"
      ],
      "reply": {
        "channel": {
          "$ref": "#/channels/control"
        },
        "messages": [
          {
            "$ref": "#/channels/control/messages/SETTING_APPLIED"
          },
          {
            "$ref": "#/channels/control/messages/ERROR"
          }
        ]
      }
    },
    "sendSETTING_APPLIED": {
      "action": "send",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Valeur réellement conservée après action.",
      "messages": [
        {
          "$ref": "#/channels/control/messages/SETTING_APPLIED"
        }
      ]
    },
    "receiveNAVIGATE_TO": {
      "action": "receive",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Navigue vers une route de Desktop, avec / initial ajouté si absent",
      "messages": [
        {
          "$ref": "#/channels/control/messages/NAVIGATE_TO"
        }
      ],
      "x-response-types": [
        "NAVIGATED",
        "ERROR"
      ],
      "reply": {
        "channel": {
          "$ref": "#/channels/control"
        },
        "messages": [
          {
            "$ref": "#/channels/control/messages/NAVIGATED"
          },
          {
            "$ref": "#/channels/control/messages/ERROR"
          }
        ]
      }
    },
    "sendNAVIGATED": {
      "action": "send",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Route demandée normalisée avec / initial; tab reprend la demande même si recherche mapping prioritaire.",
      "messages": [
        {
          "$ref": "#/channels/control/messages/NAVIGATED"
        }
      ]
    },
    "receiveGET_CURRENT_PAGE": {
      "action": "receive",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Lit le premier segment de la route courante, son paramètre tab et le pathname.",
      "messages": [
        {
          "$ref": "#/channels/control/messages/GET_CURRENT_PAGE"
        }
      ],
      "x-response-types": [
        "CURRENT_PAGE",
        "ERROR"
      ],
      "reply": {
        "channel": {
          "$ref": "#/channels/control"
        },
        "messages": [
          {
            "$ref": "#/channels/control/messages/CURRENT_PAGE"
          },
          {
            "$ref": "#/channels/control/messages/ERROR"
          }
        ]
      }
    },
    "sendCURRENT_PAGE": {
      "action": "send",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "page sans / initial; racine → dashboard, gameDiscovery → game-discovery",
      "messages": [
        {
          "$ref": "#/channels/control/messages/CURRENT_PAGE"
        }
      ]
    },
    "receiveTAKE_SCREENSHOT": {
      "action": "receive",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Capture la fenêtre active demandée, sinon le titre sélectionné, la région fournie ou l’écran complet",
      "messages": [
        {
          "$ref": "#/channels/control/messages/TAKE_SCREENSHOT"
        }
      ],
      "x-response-types": [
        "SCREENSHOT_DATA",
        "SCREENSHOT_ERROR",
        "ERROR"
      ],
      "reply": {
        "channel": {
          "$ref": "#/channels/control"
        },
        "messages": [
          {
            "$ref": "#/channels/control/messages/SCREENSHOT_DATA"
          },
          {
            "$ref": "#/channels/control/messages/SCREENSHOT_ERROR"
          },
          {
            "$ref": "#/channels/control/messages/ERROR"
          }
        ]
      }
    },
    "sendSCREENSHOT_DATA": {
      "action": "send",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Capture créée: file→filePath, base64→buffer, both→les deux, dataUrl→dataUrl",
      "messages": [
        {
          "$ref": "#/channels/control/messages/SCREENSHOT_DATA"
        }
      ]
    },
    "receiveLIST_WINDOWS": {
      "action": "receive",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Liste les fenêtres avec leur titre et un index temporaire",
      "messages": [
        {
          "$ref": "#/channels/control/messages/LIST_WINDOWS"
        }
      ],
      "x-response-types": [
        "WINDOWS_LIST",
        "ERROR"
      ],
      "reply": {
        "channel": {
          "$ref": "#/channels/control"
        },
        "messages": [
          {
            "$ref": "#/channels/control/messages/WINDOWS_LIST"
          },
          {
            "$ref": "#/channels/control/messages/ERROR"
          }
        ]
      }
    },
    "sendWINDOWS_LIST": {
      "action": "send",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "id est l’index de l’énumération converti en chaîne; il ne peut pas sélectionner une fenêtre",
      "messages": [
        {
          "$ref": "#/channels/control/messages/WINDOWS_LIST"
        }
      ]
    },
    "receiveGET_DISPLAYS": {
      "action": "receive",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Liste les écrans avec leur nom, leurs dimensions, leur position et leur facteur d’échelle.",
      "messages": [
        {
          "$ref": "#/channels/control/messages/GET_DISPLAYS"
        }
      ],
      "x-response-types": [
        "DISPLAYS_LIST",
        "ERROR"
      ],
      "reply": {
        "channel": {
          "$ref": "#/channels/control"
        },
        "messages": [
          {
            "$ref": "#/channels/control/messages/DISPLAYS_LIST"
          },
          {
            "$ref": "#/channels/control/messages/ERROR"
          }
        ]
      }
    },
    "sendDISPLAYS_LIST": {
      "action": "send",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Écrans et géométrie.",
      "messages": [
        {
          "$ref": "#/channels/control/messages/DISPLAYS_LIST"
        }
      ]
    },
    "receiveWINDOW_GET_PROPERTIES": {
      "action": "receive",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Lit le titre et la région de la fenêtre sélectionnée",
      "messages": [
        {
          "$ref": "#/channels/control/messages/WINDOW_GET_PROPERTIES"
        }
      ],
      "x-response-types": [
        "WINDOW_PROPERTIES",
        "ERROR"
      ],
      "reply": {
        "channel": {
          "$ref": "#/channels/control"
        },
        "messages": [
          {
            "$ref": "#/channels/control/messages/WINDOW_PROPERTIES"
          },
          {
            "$ref": "#/channels/control/messages/ERROR"
          }
        ]
      }
    },
    "sendWINDOW_PROPERTIES": {
      "action": "send",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Titre et dimensions de la fenêtre.",
      "messages": [
        {
          "$ref": "#/channels/control/messages/WINDOW_PROPERTIES"
        }
      ]
    },
    "receiveWINDOW_MOVE": {
      "action": "receive",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Déplace la fenêtre; x/y absents valent 0",
      "messages": [
        {
          "$ref": "#/channels/control/messages/WINDOW_MOVE"
        }
      ],
      "x-response-types": [
        "WINDOW_MOVED",
        "ERROR"
      ],
      "reply": {
        "channel": {
          "$ref": "#/channels/control"
        },
        "messages": [
          {
            "$ref": "#/channels/control/messages/WINDOW_MOVED"
          },
          {
            "$ref": "#/channels/control/messages/ERROR"
          }
        ]
      }
    },
    "sendWINDOW_MOVED": {
      "action": "send",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Opération effectuée.",
      "messages": [
        {
          "$ref": "#/channels/control/messages/WINDOW_MOVED"
        }
      ]
    },
    "receiveWINDOW_RESIZE": {
      "action": "receive",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Redimensionne la fenêtre; valeurs absentes 800×600, sinon les dimensions fournies",
      "messages": [
        {
          "$ref": "#/channels/control/messages/WINDOW_RESIZE"
        }
      ],
      "x-response-types": [
        "WINDOW_RESIZED",
        "ERROR"
      ],
      "reply": {
        "channel": {
          "$ref": "#/channels/control"
        },
        "messages": [
          {
            "$ref": "#/channels/control/messages/WINDOW_RESIZED"
          },
          {
            "$ref": "#/channels/control/messages/ERROR"
          }
        ]
      }
    },
    "sendWINDOW_RESIZED": {
      "action": "send",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Opération effectuée.",
      "messages": [
        {
          "$ref": "#/channels/control/messages/WINDOW_RESIZED"
        }
      ]
    },
    "receiveWINDOW_FOCUS": {
      "action": "receive",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Active la fenêtre sélectionnée",
      "messages": [
        {
          "$ref": "#/channels/control/messages/WINDOW_FOCUS"
        }
      ],
      "x-response-types": [
        "WINDOW_FOCUSED",
        "ERROR"
      ],
      "reply": {
        "channel": {
          "$ref": "#/channels/control"
        },
        "messages": [
          {
            "$ref": "#/channels/control/messages/WINDOW_FOCUSED"
          },
          {
            "$ref": "#/channels/control/messages/ERROR"
          }
        ]
      }
    },
    "sendWINDOW_FOCUSED": {
      "action": "send",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Opération effectuée.",
      "messages": [
        {
          "$ref": "#/channels/control/messages/WINDOW_FOCUSED"
        }
      ]
    },
    "receiveGET_MOUSE_CURSOR_POSITION": {
      "action": "receive",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Lit la position de la souris",
      "messages": [
        {
          "$ref": "#/channels/control/messages/GET_MOUSE_CURSOR_POSITION"
        }
      ],
      "x-response-types": [
        "MOUSE_POSITION",
        "MOUSE_POSITION_ERROR",
        "ERROR"
      ],
      "reply": {
        "channel": {
          "$ref": "#/channels/control"
        },
        "messages": [
          {
            "$ref": "#/channels/control/messages/MOUSE_POSITION"
          },
          {
            "$ref": "#/channels/control/messages/MOUSE_POSITION_ERROR"
          },
          {
            "$ref": "#/channels/control/messages/ERROR"
          }
        ]
      }
    },
    "sendMOUSE_POSITION": {
      "action": "send",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Coordonnées de la souris renvoyées par le système.",
      "messages": [
        {
          "$ref": "#/channels/control/messages/MOUSE_POSITION"
        }
      ]
    },
    "sendERROR": {
      "action": "send",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "Erreur de format, d’authentification, de validation ou d’exécution",
      "messages": [
        {
          "$ref": "#/channels/control/messages/ERROR"
        }
      ]
    },
    "sendSCREENSHOT_ERROR": {
      "action": "send",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "La capture n’a pas pu être effectuée",
      "messages": [
        {
          "$ref": "#/channels/control/messages/SCREENSHOT_ERROR"
        }
      ]
    },
    "sendMOUSE_POSITION_ERROR": {
      "action": "send",
      "channel": {
        "$ref": "#/channels/control"
      },
      "summary": "La position du curseur n’a pas pu être lue",
      "messages": [
        {
          "$ref": "#/channels/control/messages/MOUSE_POSITION_ERROR"
        }
      ]
    }
  },
  "components": {
    "schemas": {
      "Point": {
        "type": "object",
        "properties": {
          "x": {
            "type": "number"
          },
          "y": {
            "type": "number"
          }
        },
        "required": [
          "x",
          "y"
        ]
      },
      "Bounds": {
        "type": "object",
        "properties": {
          "x": {
            "type": "number"
          },
          "y": {
            "type": "number"
          },
          "width": {
            "type": "number"
          },
          "height": {
            "type": "number"
          }
        },
        "required": [
          "x",
          "y",
          "width",
          "height"
        ]
      },
      "WindowRegion": {
        "type": "object",
        "properties": {
          "left": {
            "type": "number"
          },
          "top": {
            "type": "number"
          },
          "width": {
            "type": "number"
          },
          "height": {
            "type": "number"
          }
        },
        "required": [
          "left",
          "top",
          "width",
          "height"
        ]
      },
      "ButtonCode": {
        "type": "string",
        "enum": [
          "A",
          "B",
          "X",
          "Y",
          "LEFT_SHOULDER",
          "RIGHT_SHOULDER",
          "LEFT_THUMB",
          "RIGHT_THUMB",
          "BACK",
          "START",
          "GUIDE",
          "DPAD_UP",
          "DPAD_DOWN",
          "DPAD_LEFT",
          "DPAD_RIGHT",
          "LEFT_STICK_UP",
          "LEFT_STICK_DOWN",
          "LEFT_STICK_LEFT",
          "LEFT_STICK_RIGHT",
          "RIGHT_STICK_UP",
          "RIGHT_STICK_DOWN",
          "RIGHT_STICK_LEFT",
          "RIGHT_STICK_RIGHT"
        ]
      },
      "AxisCode": {
        "type": "string",
        "enum": [
          "leftX",
          "leftY",
          "rightX",
          "rightY",
          "dpadHorz",
          "dpadVert",
          "mouseX",
          "mouseY"
        ],
        "description": "Codes canoniques. Les variantes x-aliases sont reconnues sans tenir compte de la casse; utiliser exactement leftX/leftY/rightX/rightY pour les codes canoniques.",
        "x-aliases": {
          "leftX": [
            "LeftStickX",
            "LEFT_STICK_X"
          ],
          "leftY": [
            "LeftStickY",
            "LEFT_STICK_Y"
          ],
          "rightX": [
            "RightStickX",
            "RIGHT_STICK_X"
          ],
          "rightY": [
            "RightStickY",
            "RIGHT_STICK_Y"
          ],
          "dpadHorz": [
            "dpadhorz",
            "dpad_horz",
            "dpad_x"
          ],
          "dpadVert": [
            "dpadvert",
            "dpad_vert",
            "dpad_y"
          ]
        }
      },
      "TriggerCode": {
        "type": "string",
        "enum": [
          "leftTrigger",
          "rightTrigger"
        ],
        "x-aliases": {
          "leftTrigger": [
            "lefttrigger",
            "left_trigger"
          ],
          "rightTrigger": [
            "righttrigger",
            "right_trigger"
          ]
        }
      },
      "KeyboardCode": {
        "type": "string",
        "enum": [
          "a",
          "b",
          "c",
          "d",
          "e",
          "f",
          "g",
          "h",
          "i",
          "j",
          "k",
          "l",
          "m",
          "n",
          "o",
          "p",
          "q",
          "r",
          "s",
          "t",
          "u",
          "v",
          "w",
          "x",
          "y",
          "z",
          "0",
          "1",
          "2",
          "3",
          "4",
          "5",
          "6",
          "7",
          "8",
          "9",
          "numpad_0",
          "numpad_1",
          "numpad_2",
          "numpad_3",
          "numpad_4",
          "numpad_5",
          "numpad_6",
          "numpad_7",
          "numpad_8",
          "numpad_9",
          "backspace",
          "delete",
          "enter",
          "tab",
          "escape",
          "up",
          "down",
          "right",
          "left",
          "home",
          "end",
          "pageup",
          "pagedown",
          "f1",
          "f2",
          "f3",
          "f4",
          "f5",
          "f6",
          "f7",
          "f8",
          "f9",
          "f10",
          "f11",
          "f12",
          "f13",
          "f14",
          "f15",
          "f16",
          "f17",
          "f18",
          "f19",
          "f20",
          "f21",
          "f22",
          "f23",
          "f24",
          "alt",
          "control",
          "shift",
          "space",
          "windows",
          "right_control",
          "right_shift",
          "right_alt",
          "right_windows",
          "play",
          "pause",
          "mute",
          "fn",
          "grave",
          "minus",
          "equal",
          "leftbracket",
          "rightbracket",
          "backslash",
          "semicolon",
          "quote",
          "comma",
          "period",
          "slash"
        ]
      },
      "MouseButtonCode": {
        "type": "string",
        "enum": [
          "leftClick",
          "middleClick",
          "rightClick",
          "leftDoubleClick",
          "middleDoubleClick",
          "rightDoubleClick",
          "scrollUp",
          "scrollDown"
        ]
      },
      "MouseMotionCode": {
        "type": "string",
        "enum": [
          "mouseMotionOut",
          "mouseLeft",
          "mouseRight",
          "mouseUp",
          "mouseDown"
        ]
      },
      "ExposedSettingKey": {
        "type": "string",
        "enum": [
          "toggleOutput",
          "toggleGamepad",
          "toggleKeyboard",
          "toggleMouse2Stick",
          "toggleHeadStick",
          "toggleDetectionSystem",
          "toggleGamepadForwarding",
          "toggleMouseOutput",
          "toggleMouseMotionOutput",
          "toggleHandMouseOutput",
          "toggleHeadMouseOutput",
          "toggleOpenTrackOutput",
          "calibrateHeadStick",
          "calibrateFaceBlendshapes",
          "releaseAll",
          "toggleMiniView",
          "toggleMesh",
          "toggleFaceLandmarker",
          "toggleHandLandmarker",
          "togglePoseLandmarker",
          "toggleDebug",
          "toggleWidget",
          "toggleDarkTheme",
          "autoStartFaceDetection",
          "autoStartMainOutput",
          "autoStartOverlay",
          "toggleOverlay",
          "toggleWebsocket",
          "allowWebsocketInput",
          "allowWebsocketOutput",
          "websocketFullSpeed",
          "toggleVideoShow",
          "enableExternalAI",
          "allowSerialOutput",
          "toggleSerial",
          "allowHIDOutput",
          "isKeyboardInAI",
          "autoStart",
          "centerMouse",
          "hideForPerformance",
          "voiceActivation",
          "toggleVoiceOutput",
          "toggleFaceDetectionOutput",
          "autoStartVoice",
          "autoStartOpenTrackOutput",
          "autoStartEyeTracker",
          "enableAnalytics",
          "enablePostHog",
          "enableCrisp",
          "requireFacingCamera",
          "ignoreOverlayMouseEvents",
          "toggleMcpServer",
          "requireWebsocketAuth",
          "autoStartWebRTC",
          "toggleInputBlocking",
          "unlockInputMute"
        ]
      },
      "WritableSettingKey": {
        "type": "string",
        "enum": [
          "toggleOutput",
          "toggleGamepad",
          "toggleKeyboard",
          "toggleMouse2Stick",
          "toggleHeadStick",
          "toggleDetectionSystem",
          "toggleGamepadForwarding",
          "toggleMouseOutput",
          "toggleMouseMotionOutput",
          "toggleHandMouseOutput",
          "toggleHeadMouseOutput",
          "toggleOpenTrackOutput",
          "calibrateHeadStick",
          "calibrateFaceBlendshapes",
          "releaseAll",
          "toggleMiniView",
          "toggleMesh",
          "toggleFaceLandmarker",
          "toggleHandLandmarker",
          "togglePoseLandmarker",
          "toggleDebug",
          "toggleWidget",
          "toggleDarkTheme",
          "autoStartFaceDetection",
          "autoStartMainOutput",
          "autoStartOverlay",
          "toggleOverlay",
          "toggleWebsocket",
          "allowWebsocketInput",
          "allowWebsocketOutput",
          "websocketFullSpeed",
          "toggleVideoShow",
          "enableExternalAI",
          "toggleSerial",
          "isKeyboardInAI",
          "autoStart",
          "centerMouse",
          "hideForPerformance",
          "voiceActivation",
          "toggleVoiceOutput",
          "toggleFaceDetectionOutput",
          "autoStartVoice",
          "autoStartOpenTrackOutput",
          "autoStartEyeTracker",
          "requireFacingCamera",
          "ignoreOverlayMouseEvents",
          "toggleMcpServer",
          "autoStartWebRTC"
        ],
        "description": "Clés acceptées par SET_SETTING. Les huit clés présentes dans x-read-only-keys sont uniquement lisibles.",
        "x-read-only-keys": [
          "allowSerialOutput",
          "allowHIDOutput",
          "unlockInputMute",
          "toggleInputBlocking",
          "requireWebsocketAuth",
          "enableAnalytics",
          "enablePostHog",
          "enableCrisp"
        ]
      },
      "Delay": {
        "type": "integer",
        "minimum": 0,
        "maximum": 5000,
        "description": "Délai avant relâchement, après application immédiate. Utiliser un entier de 1 à 5000 ms. Sans effet pour une valeur relâchée/nulle, ni pour mouseScroll, mousePosition ou reset. Prévoir aussi un relâchement explicite."
      },
      "ButtonEvent": {
        "type": "object",
        "properties": {
          "type": {
            "const": "button"
          },
          "code": {
            "$ref": "#/components/schemas/ButtonCode"
          },
          "value": {
            "oneOf": [
              {
                "type": "boolean"
              },
              {
                "type": "number",
                "minimum": 0,
                "maximum": 1
              }
            ],
            "description": "Booléen pour un bouton; amplitude de 0 à 1 pour LEFT_STICK_* et RIGHT_STICK_*."
          },
          "delay": {
            "$ref": "#/components/schemas/Delay"
          }
        },
        "required": [
          "type",
          "code",
          "value"
        ]
      },
      "AxisEvent": {
        "type": "object",
        "properties": {
          "type": {
            "const": "axis"
          },
          "code": {
            "$ref": "#/components/schemas/AxisCode"
          },
          "value": {
            "type": "number",
            "minimum": -1,
            "maximum": 1
          },
          "delay": {
            "$ref": "#/components/schemas/Delay"
          }
        },
        "required": [
          "type",
          "code",
          "value"
        ]
      },
      "TriggerEvent": {
        "type": "object",
        "properties": {
          "type": {
            "const": "trigger"
          },
          "code": {
            "$ref": "#/components/schemas/TriggerCode"
          },
          "value": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "description": "Valeurs 0..1 acceptées; actuellement, 0 relâche et toute valeur non nulle active entièrement la gâchette. Utiliser 0 ou 1."
          },
          "delay": {
            "$ref": "#/components/schemas/Delay"
          }
        },
        "required": [
          "type",
          "code",
          "value"
        ]
      },
      "KeyboardEvent": {
        "type": "object",
        "properties": {
          "type": {
            "const": "keyboard"
          },
          "code": {
            "$ref": "#/components/schemas/KeyboardCode"
          },
          "value": {
            "type": "boolean"
          },
          "delay": {
            "$ref": "#/components/schemas/Delay"
          }
        },
        "required": [
          "type",
          "code",
          "value"
        ]
      },
      "MouseButtonEvent": {
        "type": "object",
        "properties": {
          "type": {
            "const": "mouseButton"
          },
          "code": {
            "$ref": "#/components/schemas/MouseButtonCode"
          },
          "value": {
            "type": "boolean"
          },
          "delay": {
            "$ref": "#/components/schemas/Delay"
          }
        },
        "required": [
          "type",
          "code",
          "value"
        ]
      },
      "MouseMotionOutEvent": {
        "type": "object",
        "properties": {
          "type": {
            "const": "mouseMotionOut"
          },
          "code": {
            "$ref": "#/components/schemas/MouseMotionCode"
          },
          "value": {
            "type": "boolean"
          },
          "delay": {
            "$ref": "#/components/schemas/Delay"
          }
        },
        "required": [
          "type",
          "code",
          "value"
        ],
        "x-legacy": true
      },
      "SettingEvent": {
        "type": "object",
        "properties": {
          "type": {
            "const": "setting"
          },
          "code": {
            "type": "string",
            "minLength": 1,
            "description": "Action historique déclenchée au passage de false à true. Préférer SET_SETTING pour modifier un paramètre exposé et MCP_SWITCH_PROFILE pour choisir un profil. Les permissions de SET_SETTING ne définissent pas celles de cette action."
          },
          "value": {
            "type": "boolean"
          },
          "delay": {
            "$ref": "#/components/schemas/Delay"
          }
        },
        "required": [
          "type",
          "code",
          "value"
        ],
        "x-legacy": true
      },
      "MouseScrollEvent": {
        "type": "object",
        "properties": {
          "type": {
            "const": "mouseScroll"
          },
          "code": {
            "type": "string",
            "enum": [
              "up",
              "down",
              "left",
              "right"
            ]
          },
          "value": {
            "type": "number",
            "minimum": 0,
            "description": "Valeur > 0 arrondie; absente, nulle, négative ou non numérique: 120. Pas de relâchement ni delay."
          }
        },
        "required": [
          "type",
          "code"
        ]
      },
      "MousePositionEvent": {
        "type": "object",
        "properties": {
          "type": {
            "const": "mousePosition"
          },
          "code": {
            "const": "mousePosition"
          },
          "value": {
            "$ref": "#/components/schemas/Point"
          }
        },
        "required": [
          "type",
          "value"
        ],
        "description": "Coordonnées logiques de l’écran courant, bornées entre 0 et width/height − 1 puis mises à l’échelle et arrondies. code est ignoré. Pas de délai ni relâchement."
      },
      "ResetEvent": {
        "type": "object",
        "properties": {
          "type": {
            "const": "reset"
          }
        },
        "required": [
          "type"
        ],
        "description": "Réinitialise l’état des commandes. Pour la libération globale, utiliser SET_SETTING avec releaseAll=true."
      },
      "InputEvent": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/ButtonEvent"
          },
          {
            "$ref": "#/components/schemas/AxisEvent"
          },
          {
            "$ref": "#/components/schemas/TriggerEvent"
          },
          {
            "$ref": "#/components/schemas/KeyboardEvent"
          },
          {
            "$ref": "#/components/schemas/MouseButtonEvent"
          },
          {
            "$ref": "#/components/schemas/MouseScrollEvent"
          },
          {
            "$ref": "#/components/schemas/MousePositionEvent"
          },
          {
            "$ref": "#/components/schemas/ResetEvent"
          },
          {
            "$ref": "#/components/schemas/MouseMotionOutEvent"
          },
          {
            "$ref": "#/components/schemas/SettingEvent"
          }
        ],
        "description": "Formes recommandées. Des entrées hors schéma peuvent être ignorées sans erreur réseau; utiliser les types, codes et valeurs documentés."
      },
      "OutputEvent": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "button",
              "axis",
              "trigger",
              "keyboard",
              "mouseButton"
            ]
          },
          "code": {
            "type": "string"
          },
          "value": {
            "type": [
              "boolean",
              "number"
            ]
          }
        },
        "required": [
          "type",
          "code",
          "value"
        ],
        "description": "Delta d’état. Les directions de stick sont de type button et de valeur numérique; les autres boutons/clavier/souris sont booléens. Les valeurs numériques sont arrondies à deux décimales."
      },
      "MappingInput": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "keyboard",
              "faceLandmark",
              "mouse",
              "word",
              "sound",
              "gamepad",
              "",
              "mouseMotion",
              "headMotion",
              "handGesture",
              "handMotion",
              "handMotionMouse",
              "headMotionMouse",
              "bodyGesture",
              "websocket",
              "dwell",
              "cursorStabilizer",
              "overlay"
            ]
          },
          "code": {
            "type": "string"
          },
          "settings": {
            "type": "object",
            "additionalProperties": true,
            "description": "Paramètres propres au type d’entrée. Reprendre ceux d’un profil exporté ou de GET_TEMPLATE_PROFILES."
          }
        },
        "required": [
          "type",
          "code"
        ]
      },
      "ImportMappingOutput": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "type": {
                "const": "button"
              },
              "code": {
                "type": "string",
                "pattern": "^(?:A|B|X|Y|LEFT_SHOULDER|RIGHT_SHOULDER|LEFT_THUMB|RIGHT_THUMB|BACK|START|GUIDE|DPAD_UP|DPAD_DOWN|DPAD_LEFT|DPAD_RIGHT|LEFT_STICK_UP|LEFT_STICK_DOWN|LEFT_STICK_LEFT|LEFT_STICK_RIGHT|RIGHT_STICK_UP|RIGHT_STICK_DOWN|RIGHT_STICK_LEFT|RIGHT_STICK_RIGHT)(?:\\|(?:A|B|X|Y|LEFT_SHOULDER|RIGHT_SHOULDER|LEFT_THUMB|RIGHT_THUMB|BACK|START|GUIDE|DPAD_UP|DPAD_DOWN|DPAD_LEFT|DPAD_RIGHT|LEFT_STICK_UP|LEFT_STICK_DOWN|LEFT_STICK_LEFT|LEFT_STICK_RIGHT|RIGHT_STICK_UP|RIGHT_STICK_DOWN|RIGHT_STICK_LEFT|RIGHT_STICK_RIGHT))*$",
                "description": "Code unique ou combinaison de codes du même type séparés par |; les espaces autour de chaque membre sont supprimés à l’import."
              },
              "value": {
                "type": "boolean"
              }
            },
            "required": [
              "type",
              "code"
            ]
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "const": "axis"
              },
              "code": {
                "type": "string",
                "enum": [
                  "leftX",
                  "leftY",
                  "rightX",
                  "rightY",
                  "dpadHorz",
                  "dpadVert",
                  "mouseX",
                  "mouseY"
                ]
              },
              "value": {
                "type": "number"
              }
            },
            "required": [
              "type",
              "code"
            ]
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "const": "trigger"
              },
              "code": {
                "type": "string",
                "pattern": "^(?:leftTrigger|rightTrigger)(?:\\|(?:leftTrigger|rightTrigger))*$",
                "description": "Code unique ou combinaison de codes du même type séparés par |; les espaces autour de chaque membre sont supprimés à l’import."
              },
              "value": {
                "type": "number"
              }
            },
            "required": [
              "type",
              "code"
            ]
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "const": "keyboard"
              },
              "code": {
                "type": "string",
                "pattern": "^(?:a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|0|1|2|3|4|5|6|7|8|9|numpad_0|numpad_1|numpad_2|numpad_3|numpad_4|numpad_5|numpad_6|numpad_7|numpad_8|numpad_9|backspace|delete|enter|tab|escape|up|down|right|left|home|end|pageup|pagedown|f1|f2|f3|f4|f5|f6|f7|f8|f9|f10|f11|f12|f13|f14|f15|f16|f17|f18|f19|f20|f21|f22|f23|f24|alt|control|shift|space|windows|right_control|right_shift|right_alt|right_windows|play|pause|mute|fn|grave|minus|equal|leftbracket|rightbracket|backslash|semicolon|quote|comma|period|slash)(?:\\|(?:a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|0|1|2|3|4|5|6|7|8|9|numpad_0|numpad_1|numpad_2|numpad_3|numpad_4|numpad_5|numpad_6|numpad_7|numpad_8|numpad_9|backspace|delete|enter|tab|escape|up|down|right|left|home|end|pageup|pagedown|f1|f2|f3|f4|f5|f6|f7|f8|f9|f10|f11|f12|f13|f14|f15|f16|f17|f18|f19|f20|f21|f22|f23|f24|alt|control|shift|space|windows|right_control|right_shift|right_alt|right_windows|play|pause|mute|fn|grave|minus|equal|leftbracket|rightbracket|backslash|semicolon|quote|comma|period|slash))*$",
                "description": "Code unique ou combinaison de codes du même type séparés par |; les espaces autour de chaque membre sont supprimés à l’import."
              },
              "value": {
                "type": "boolean"
              }
            },
            "required": [
              "type",
              "code"
            ]
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "const": "mouseButton"
              },
              "code": {
                "type": "string",
                "pattern": "^(?:leftClick|middleClick|rightClick|leftDoubleClick|middleDoubleClick|rightDoubleClick|scrollUp|scrollDown)(?:\\|(?:leftClick|middleClick|rightClick|leftDoubleClick|middleDoubleClick|rightDoubleClick|scrollUp|scrollDown))*$",
                "description": "Code unique ou combinaison de codes du même type séparés par |; les espaces autour de chaque membre sont supprimés à l’import."
              },
              "value": {
                "type": "boolean"
              }
            },
            "required": [
              "type",
              "code"
            ]
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "const": "mouseMotionOut"
              },
              "code": {
                "type": "string",
                "enum": [
                  "mouseMotionOut",
                  "mouseLeft",
                  "mouseRight",
                  "mouseUp",
                  "mouseDown"
                ]
              },
              "value": {
                "type": "boolean"
              }
            },
            "required": [
              "type",
              "code"
            ]
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "const": "mousePosition"
              },
              "code": {
                "type": "string",
                "enum": [
                  "mousePosition"
                ]
              },
              "value": {
                "$ref": "#/components/schemas/Point"
              }
            },
            "required": [
              "type",
              "code"
            ]
          },
          {
            "type": "object",
            "properties": {
              "type": {
                "const": "setting"
              },
              "code": {
                "type": "string",
                "minLength": 1,
                "description": "Codes autorisés dans x-allowed-codes; combinaisons | autorisées sauf nextProfile/previousProfile/switchToProfile. Verrous de joystick: suffixe facultatif :index (entier positif recommandé).",
                "x-allowed-codes": [
                  "toggleOutput",
                  "toggleGamepad",
                  "toggleKeyboard",
                  "toggleInputBlocking",
                  "unlockInputMute",
                  "toggleMouse2Stick",
                  "toggleHeadStick",
                  "toggleDetectionSystem",
                  "toggleMouseOutput",
                  "toggleMouseMotionOutput",
                  "toggleHandMouseOutput",
                  "toggleHeadMouseOutput",
                  "toggleOpenTrackOutput",
                  "calibrateHeadStick",
                  "calibrateFaceBlendshapes",
                  "releaseAll",
                  "toggleMiniView",
                  "toggleMesh",
                  "toggleFaceLandmarker",
                  "toggleHandLandmarker",
                  "togglePoseLandmarker",
                  "toggleDebug",
                  "toggleWidget",
                  "toggleDarkTheme",
                  "toggleVideoShow",
                  "centerMouse",
                  "hideForPerformance",
                  "voiceActivation",
                  "toggleVoiceOutput",
                  "toggleFaceDetectionOutput",
                  "toggleOverlay",
                  "requireFacingCamera",
                  "ignoreOverlayMouseEvents",
                  "toggleOverlayJoystickLockLeft",
                  "toggleOverlayJoystickLockRight",
                  "toggleOverlayJoystickLockDpad",
                  "toggleWebsocket",
                  "allowWebsocketInput",
                  "allowWebsocketOutput",
                  "allowSerialOutput",
                  "toggleSerial",
                  "allowHIDOutput",
                  "toggleGamepadForwarding",
                  "nextProfile",
                  "previousProfile",
                  "switchToProfile"
                ]
              },
              "value": {
                "type": "boolean"
              }
            },
            "required": [
              "type",
              "code"
            ]
          }
        ],
        "description": "Sortie d’un mapping à importer. value est facultatif et prend false ou 0 par défaut; fournir la valeur du type correspondant. Utiliser les plages décrites dans les événements d’entrée."
      },
      "ImportMapping": {
        "type": "object",
        "properties": {
          "id": {
            "type": "number"
          },
          "description": {
            "type": "string"
          },
          "input": {
            "$ref": "#/components/schemas/MappingInput"
          },
          "output": {
            "$ref": "#/components/schemas/ImportMappingOutput"
          },
          "groupId": {
            "type": "string"
          },
          "groupOperator": {
            "type": "string",
            "enum": [
              "AND",
              "OR"
            ]
          },
          "groupSettings": {
            "type": "object",
            "additionalProperties": true
          },
          "position": {
            "type": "number"
          },
          "targetProfile": {
            "type": "string"
          },
          "mute": {
            "type": "boolean"
          }
        },
        "required": [
          "input",
          "output"
        ],
        "description": "À l’import: id par défaut = index, description limitée à 200 caractères, noms de profil nettoyés et limités à 50 caractères; les champs non pris en charge sont supprimés."
      },
      "Mapping": {
        "type": "object",
        "properties": {
          "id": {
            "type": "number"
          },
          "description": {
            "type": "string"
          },
          "input": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string"
              },
              "code": {
                "type": "string"
              },
              "settings": {
                "type": "object",
                "additionalProperties": true
              }
            },
            "required": [
              "type",
              "code"
            ]
          },
          "output": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string"
              },
              "code": {
                "type": "string"
              },
              "value": {}
            },
            "required": [
              "type",
              "code",
              "value"
            ]
          },
          "groupId": {
            "type": "string"
          },
          "groupOperator": {
            "type": "string",
            "enum": [
              "AND",
              "OR"
            ]
          },
          "groupSettings": {
            "type": "object",
            "additionalProperties": true
          },
          "position": {
            "type": "number"
          },
          "targetProfile": {
            "type": "string"
          },
          "mute": {
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "description",
          "input",
          "output"
        ],
        "description": "Mapping d’un profil existant. Utiliser ImportMapping pour construire un nouvel import."
      },
      "Mappings": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Mapping"
        }
      },
      "Settings": {
        "type": "object",
        "properties": {
          "toggleOutput": {
            "type": "boolean"
          },
          "toggleGamepad": {
            "type": "boolean"
          },
          "toggleKeyboard": {
            "type": "boolean"
          },
          "toggleMouse2Stick": {
            "type": "boolean"
          },
          "toggleHeadStick": {
            "type": "boolean"
          },
          "toggleDetectionSystem": {
            "type": "boolean"
          },
          "toggleGamepadForwarding": {
            "type": "boolean"
          },
          "toggleMouseOutput": {
            "type": "boolean"
          },
          "toggleMouseMotionOutput": {
            "type": "boolean"
          },
          "toggleHandMouseOutput": {
            "type": "boolean"
          },
          "toggleHeadMouseOutput": {
            "type": "boolean"
          },
          "toggleOpenTrackOutput": {
            "type": "boolean"
          },
          "calibrateHeadStick": {
            "type": "boolean"
          },
          "calibrateFaceBlendshapes": {
            "type": "boolean"
          },
          "releaseAll": {
            "type": "boolean"
          },
          "toggleMiniView": {
            "type": "boolean"
          },
          "toggleMesh": {
            "type": "boolean"
          },
          "toggleFaceLandmarker": {
            "type": "boolean"
          },
          "toggleHandLandmarker": {
            "type": "boolean"
          },
          "togglePoseLandmarker": {
            "type": "boolean"
          },
          "toggleDebug": {
            "type": "boolean"
          },
          "toggleWidget": {
            "type": "boolean"
          },
          "toggleDarkTheme": {
            "type": "boolean"
          },
          "autoStartFaceDetection": {
            "type": "boolean"
          },
          "autoStartMainOutput": {
            "type": "boolean"
          },
          "autoStartOverlay": {
            "type": "boolean"
          },
          "toggleOverlay": {
            "type": "boolean"
          },
          "toggleWebsocket": {
            "type": "boolean"
          },
          "allowWebsocketInput": {
            "type": "boolean"
          },
          "allowWebsocketOutput": {
            "type": "boolean"
          },
          "websocketFullSpeed": {
            "type": "boolean"
          },
          "toggleVideoShow": {
            "type": "boolean"
          },
          "enableExternalAI": {
            "type": "boolean"
          },
          "allowSerialOutput": {
            "type": "boolean"
          },
          "toggleSerial": {
            "type": "boolean"
          },
          "allowHIDOutput": {
            "type": "boolean"
          },
          "isKeyboardInAI": {
            "type": "boolean"
          },
          "autoStart": {
            "type": "boolean"
          },
          "centerMouse": {
            "type": "boolean"
          },
          "hideForPerformance": {
            "type": "boolean"
          },
          "voiceActivation": {
            "type": "boolean"
          },
          "toggleVoiceOutput": {
            "type": "boolean"
          },
          "toggleFaceDetectionOutput": {
            "type": "boolean"
          },
          "autoStartVoice": {
            "type": "boolean"
          },
          "autoStartOpenTrackOutput": {
            "type": "boolean"
          },
          "autoStartEyeTracker": {
            "type": "boolean"
          },
          "enableAnalytics": {
            "type": "boolean"
          },
          "enablePostHog": {
            "type": "boolean"
          },
          "enableCrisp": {
            "type": "boolean"
          },
          "requireFacingCamera": {
            "type": "boolean"
          },
          "ignoreOverlayMouseEvents": {
            "type": "boolean"
          },
          "toggleMcpServer": {
            "type": "boolean"
          },
          "requireWebsocketAuth": {
            "type": "boolean"
          },
          "autoStartWebRTC": {
            "type": "boolean"
          },
          "toggleInputBlocking": {
            "type": "boolean"
          },
          "unlockInputMute": {
            "type": "boolean"
          }
        },
        "required": [],
        "additionalProperties": {
          "type": "boolean"
        },
        "description": "Instantané de toutes les clés exposées. Champs optionnels pour rester compatible avec les versions de Desktop; utiliser les clés réellement reçues. La clé secrète n’est jamais renvoyée."
      },
      "SettingHints": {
        "type": "object",
        "additionalProperties": {
          "type": "string"
        },
        "description": "Textes anglais associés à certaines clés; notamment toggleOutput, autoStartMainOutput et releaseAll."
      },
      "SparseGamepadState": {
        "type": "object",
        "properties": {
          "buttons": {
            "type": "object",
            "additionalProperties": {
              "type": [
                "boolean",
                "number"
              ]
            }
          },
          "axes": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            }
          },
          "triggers": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            }
          }
        },
        "required": [
          "buttons",
          "axes",
          "triggers"
        ],
        "description": "Seulement les valeurs de manette non neutres. Une clé absente signifie false/0; ne contient ni clavier, ni souris."
      },
      "DecodedState": {
        "type": "object",
        "properties": {
          "type": {
            "const": "state"
          },
          "data": {
            "$ref": "#/components/schemas/SparseGamepadState"
          }
        },
        "required": [
          "type",
          "data"
        ]
      },
      "Display": {
        "type": "object",
        "properties": {
          "id": {
            "type": "number"
          },
          "bounds": {
            "$ref": "#/components/schemas/Bounds"
          },
          "workArea": {
            "$ref": "#/components/schemas/Bounds"
          },
          "scaleFactor": {
            "type": "number"
          },
          "scalePercent": {
            "type": "number"
          },
          "rotation": {
            "type": "number"
          },
          "isPrimary": {
            "type": "boolean"
          },
          "label": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "bounds",
          "workArea",
          "scaleFactor",
          "scalePercent",
          "rotation",
          "isPrimary",
          "label"
        ],
        "description": "Écran trié par bounds.y puis bounds.x. label est destiné à l’affichage; id peut changer entre systèmes ou sessions."
      },
      "ScreenshotData": {
        "type": "object",
        "properties": {
          "width": {
            "type": "number"
          },
          "height": {
            "type": "number"
          },
          "timestamp": {
            "type": "integer",
            "description": "Date.now(), millisecondes depuis Unix epoch."
          },
          "filePath": {
            "type": "string"
          },
          "buffer": {
            "type": "string",
            "description": "Octets encodés en base64 (sans préfixe)."
          },
          "dataUrl": {
            "type": "string",
            "description": "Préfixe data:image/jpeg;base64,. Un repli PNG est possible malgré le préfixe; vérifier les octets si le format exact est nécessaire."
          }
        },
        "required": [
          "width",
          "height",
          "timestamp"
        ],
        "description": "width/height sont les dimensions originales de capture, avant réduction. filePath est un chemin local au processus Desktop. Le fichier JPEG final reste dans le dossier temporaire; pas de nettoyage automatique garanti."
      },
      "WindowSelector": {
        "type": "object",
        "properties": {
          "activeWindowOnly": {
            "type": "boolean"
          },
          "windowTitle": {
            "type": "string"
          },
          "windowTitleMatch": {
            "type": "string",
            "enum": [
              "exact",
              "contains",
              "startsWith"
            ],
            "default": "contains"
          }
        },
        "required": [],
        "description": "Correspondance du titre sans tenir compte de la casse. activeWindowOnly=true a priorité. Pour WINDOW_*: fenêtre active par défaut sans titre; activeWindowOnly=false sans titre provoque une erreur. Pour TAKE_SCREENSHOT: écran complet par défaut, sans fenêtre active implicite."
      },
      "AUTHPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "AUTH"
          },
          "key": {
            "type": "string",
            "description": "Clé de l’application, trim() avant comparaison; obligatoire uniquement lorsque CONNECT.authRequired vaut true."
          }
        },
        "required": [
          "type"
        ],
        "not": {
          "required": [
            "events"
          ]
        }
      },
      "CONNECTPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "CONNECT"
          },
          "info": {
            "const": "WebSocket Server Started"
          },
          "authRequired": {
            "type": "boolean"
          }
        },
        "required": [
          "type",
          "info",
          "authRequired"
        ]
      },
      "AUTH_OKPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "AUTH_OK"
          }
        },
        "required": [
          "type"
        ]
      },
      "AUTH_ERRORPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "AUTH_ERROR"
          },
          "error": {
            "const": "Invalid authentication key"
          }
        },
        "required": [
          "type",
          "error"
        ]
      },
      "InputEventsPayload": {
        "type": "object",
        "properties": {
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InputEvent"
            }
          }
        },
        "required": [
          "events"
        ],
        "not": {
          "required": [
            "type"
          ]
        },
        "description": "Enveloppe sans type de premier niveau, distincte de la diffusion sortante EVENTS."
      },
      "getStatePayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "getState"
          }
        },
        "required": [
          "type"
        ],
        "not": {
          "required": [
            "events"
          ]
        }
      },
      "statePayload": {
        "type": "string",
        "description": "Une première désérialisation JSON retourne cette chaîne; la désérialiser une deuxième fois pour obtenir DecodedState.",
        "x-decoded-schema": {
          "$ref": "#/components/schemas/DecodedState"
        }
      },
      "EVENTSPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "EVENTS"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OutputEvent"
            },
            "minItems": 1
          }
        },
        "required": [
          "type",
          "events"
        ]
      },
      "GET_CURRENT_MAPPINGSPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "GET_CURRENT_MAPPINGS"
          }
        },
        "required": [
          "type"
        ],
        "not": {
          "required": [
            "events"
          ]
        }
      },
      "CURRENT_MAPPINGSPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "CURRENT_MAPPINGS"
          },
          "data": {
            "type": "object",
            "properties": {
              "profile": {
                "type": "string"
              },
              "profiles": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "mappings": {
                "$ref": "#/components/schemas/Mappings"
              }
            },
            "required": []
          }
        },
        "required": [
          "type",
          "data"
        ]
      },
      "GET_PROFILE_MAPPINGSPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "GET_PROFILE_MAPPINGS"
          },
          "profile": {
            "type": "string",
            "minLength": 1
          }
        },
        "required": [
          "type",
          "profile"
        ],
        "not": {
          "required": [
            "events"
          ]
        }
      },
      "PROFILE_MAPPINGSPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "PROFILE_MAPPINGS"
          },
          "data": {
            "type": "object",
            "properties": {
              "requestId": {
                "type": "string",
                "description": "À ignorer; ce champ ne correspond pas à un identifiant fourni par le client."
              },
              "profile": {
                "type": "string"
              },
              "mappings": {
                "$ref": "#/components/schemas/Mappings"
              },
              "error": {
                "type": "string"
              }
            },
            "required": []
          }
        },
        "required": [
          "type",
          "data"
        ]
      },
      "MCP_SWITCH_PROFILEPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "MCP_SWITCH_PROFILE"
          },
          "profile": {
            "type": "string",
            "minLength": 1
          }
        },
        "required": [
          "type",
          "profile"
        ],
        "not": {
          "required": [
            "events"
          ]
        }
      },
      "MCP_PROFILE_SWITCHEDPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "MCP_PROFILE_SWITCHED"
          },
          "data": {
            "type": "object",
            "properties": {
              "profile": {
                "type": "string"
              },
              "mappings": {
                "$ref": "#/components/schemas/Mappings"
              }
            },
            "required": [
              "profile",
              "mappings"
            ]
          }
        },
        "required": [
          "type",
          "data"
        ]
      },
      "GET_TEMPLATE_PROFILESPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "GET_TEMPLATE_PROFILES"
          }
        },
        "required": [
          "type"
        ],
        "not": {
          "required": [
            "events"
          ]
        }
      },
      "TEMPLATE_PROFILESPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "TEMPLATE_PROFILES"
          },
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "mappings": {
                  "$ref": "#/components/schemas/Mappings"
                },
                "outputTypes": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              },
              "required": [
                "name",
                "mappings"
              ]
            }
          }
        },
        "required": [
          "type",
          "data"
        ]
      },
      "MCP_IMPORT_MAPPINGSPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "MCP_IMPORT_MAPPINGS"
          },
          "mode": {
            "type": "string",
            "enum": [
              "new",
              "append"
            ]
          },
          "name": {
            "type": "string"
          },
          "mappings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ImportMapping"
            },
            "description": "Seuls les 500 premiers mappings sont validés et importés; les suivants sont ignorés, sans avertissement exposé dans la réponse."
          }
        },
        "required": [
          "type",
          "mode",
          "mappings"
        ],
        "not": {
          "required": [
            "events"
          ]
        },
        "allOf": [
          {
            "if": {
              "properties": {
                "mode": {
                  "const": "new"
                }
              },
              "required": [
                "mode"
              ]
            },
            "then": {
              "required": [
                "name"
              ],
              "properties": {
                "name": {
                  "type": "string",
                  "minLength": 1
                }
              }
            }
          }
        ]
      },
      "MAPPINGS_IMPORTEDPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "MAPPINGS_IMPORTED"
          },
          "data": {
            "type": "object",
            "properties": {
              "profile": {
                "type": "string"
              },
              "mappings": {
                "$ref": "#/components/schemas/Mappings"
              },
              "mode": {
                "type": "string",
                "enum": [
                  "new",
                  "append"
                ]
              }
            },
            "required": [
              "profile",
              "mappings",
              "mode"
            ]
          }
        },
        "required": [
          "type",
          "data"
        ]
      },
      "GET_SETTINGSPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "GET_SETTINGS"
          }
        },
        "required": [
          "type"
        ],
        "not": {
          "required": [
            "events"
          ]
        }
      },
      "SETTINGS_STATEPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "SETTINGS_STATE"
          },
          "data": {
            "type": "object",
            "properties": {
              "settings": {
                "$ref": "#/components/schemas/Settings"
              },
              "hints": {
                "$ref": "#/components/schemas/SettingHints"
              }
            },
            "required": [
              "settings",
              "hints"
            ]
          }
        },
        "required": [
          "type",
          "data"
        ]
      },
      "SET_SETTINGPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "SET_SETTING"
          },
          "key": {
            "$ref": "#/components/schemas/WritableSettingKey"
          },
          "value": {
            "type": "boolean"
          },
          "toggle": {
            "type": "boolean"
          }
        },
        "required": [
          "type",
          "key"
        ],
        "not": {
          "required": [
            "events"
          ]
        },
        "oneOf": [
          {
            "required": [
              "value"
            ],
            "properties": {
              "toggle": {
                "const": false
              }
            }
          },
          {
            "required": [
              "toggle"
            ],
            "properties": {
              "toggle": {
                "const": true
              }
            },
            "not": {
              "required": [
                "value"
              ]
            }
          }
        ]
      },
      "SETTING_APPLIEDPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "SETTING_APPLIED"
          },
          "data": {
            "type": "object",
            "properties": {
              "key": {
                "$ref": "#/components/schemas/WritableSettingKey"
              },
              "value": {
                "type": "boolean"
              }
            },
            "required": [
              "key",
              "value"
            ]
          }
        },
        "required": [
          "type",
          "data"
        ]
      },
      "NAVIGATE_TOPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "NAVIGATE_TO"
          },
          "page": {
            "type": "string",
            "minLength": 1
          },
          "tab": {
            "type": [
              "string",
              "null"
            ]
          },
          "editMappingId": {
            "type": [
              "number",
              "null"
            ]
          },
          "profile": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "type",
          "page"
        ],
        "not": {
          "required": [
            "events"
          ]
        }
      },
      "NAVIGATEDPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "NAVIGATED"
          },
          "data": {
            "type": "object",
            "properties": {
              "page": {
                "type": "string"
              },
              "tab": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "page",
              "tab"
            ]
          }
        },
        "required": [
          "type",
          "data"
        ]
      },
      "GET_CURRENT_PAGEPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "GET_CURRENT_PAGE"
          }
        },
        "required": [
          "type"
        ],
        "not": {
          "required": [
            "events"
          ]
        }
      },
      "CURRENT_PAGEPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "CURRENT_PAGE"
          },
          "data": {
            "type": "object",
            "properties": {
              "page": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "tab": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "pathname": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "page",
              "tab",
              "pathname"
            ]
          }
        },
        "required": [
          "type",
          "data"
        ]
      },
      "TAKE_SCREENSHOTPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "TAKE_SCREENSHOT"
          },
          "activeWindowOnly": {
            "type": "boolean"
          },
          "windowTitle": {
            "type": "string"
          },
          "windowTitleMatch": {
            "type": "string",
            "enum": [
              "exact",
              "contains",
              "startsWith"
            ],
            "default": "contains"
          },
          "region": {
            "type": "object",
            "properties": {
              "x": {
                "type": "number"
              },
              "y": {
                "type": "number"
              },
              "width": {
                "type": "number"
              },
              "height": {
                "type": "number"
              }
            },
            "required": []
          },
          "quality": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "default": 80
          },
          "format": {
            "type": "string",
            "enum": [
              "base64",
              "file",
              "both",
              "dataUrl"
            ],
            "default": "file"
          },
          "maxWidth": {
            "type": "integer",
            "minimum": 1,
            "description": "Limite carrée de réduction, prioritaire sur maxHeight. Sans les deux paramètres, 1024 est utilisé."
          },
          "maxHeight": {
            "type": "integer",
            "minimum": 1
          }
        },
        "required": [
          "type"
        ],
        "not": {
          "required": [
            "events"
          ]
        }
      },
      "SCREENSHOT_DATAPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "SCREENSHOT_DATA"
          },
          "data": {
            "$ref": "#/components/schemas/ScreenshotData"
          }
        },
        "required": [
          "type",
          "data"
        ]
      },
      "LIST_WINDOWSPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "LIST_WINDOWS"
          }
        },
        "required": [
          "type"
        ],
        "not": {
          "required": [
            "events"
          ]
        }
      },
      "WINDOWS_LISTPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "WINDOWS_LIST"
          },
          "data": {
            "type": "object",
            "properties": {
              "windows": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "name"
                  ]
                }
              }
            },
            "required": [
              "windows"
            ]
          }
        },
        "required": [
          "type",
          "data"
        ]
      },
      "GET_DISPLAYSPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "GET_DISPLAYS"
          }
        },
        "required": [
          "type"
        ],
        "not": {
          "required": [
            "events"
          ]
        }
      },
      "DISPLAYS_LISTPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "DISPLAYS_LIST"
          },
          "data": {
            "type": "object",
            "properties": {
              "displays": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Display"
                }
              }
            },
            "required": [
              "displays"
            ]
          }
        },
        "required": [
          "type",
          "data"
        ]
      },
      "WINDOW_GET_PROPERTIESPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "WINDOW_GET_PROPERTIES"
          },
          "activeWindowOnly": {
            "type": "boolean"
          },
          "windowTitle": {
            "type": "string"
          },
          "windowTitleMatch": {
            "type": "string",
            "enum": [
              "exact",
              "contains",
              "startsWith"
            ],
            "default": "contains"
          }
        },
        "required": [
          "type"
        ],
        "not": {
          "required": [
            "events"
          ]
        }
      },
      "WINDOW_PROPERTIESPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "WINDOW_PROPERTIES"
          },
          "data": {
            "type": "object",
            "properties": {
              "title": {
                "type": "string"
              },
              "region": {
                "$ref": "#/components/schemas/WindowRegion"
              }
            },
            "required": [
              "title",
              "region"
            ]
          }
        },
        "required": [
          "type",
          "data"
        ]
      },
      "WINDOW_MOVEPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "WINDOW_MOVE"
          },
          "activeWindowOnly": {
            "type": "boolean"
          },
          "windowTitle": {
            "type": "string"
          },
          "windowTitleMatch": {
            "type": "string",
            "enum": [
              "exact",
              "contains",
              "startsWith"
            ],
            "default": "contains"
          },
          "x": {
            "type": "number",
            "default": 0
          },
          "y": {
            "type": "number",
            "default": 0
          }
        },
        "required": [
          "type"
        ],
        "not": {
          "required": [
            "events"
          ]
        }
      },
      "WINDOW_MOVEDPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "WINDOW_MOVED"
          },
          "data": {
            "type": "object",
            "properties": {
              "success": {
                "const": true
              }
            },
            "required": [
              "success"
            ]
          }
        },
        "required": [
          "type",
          "data"
        ]
      },
      "WINDOW_RESIZEPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "WINDOW_RESIZE"
          },
          "activeWindowOnly": {
            "type": "boolean"
          },
          "windowTitle": {
            "type": "string"
          },
          "windowTitleMatch": {
            "type": "string",
            "enum": [
              "exact",
              "contains",
              "startsWith"
            ],
            "default": "contains"
          },
          "width": {
            "type": "number",
            "default": 800
          },
          "height": {
            "type": "number",
            "default": 600
          }
        },
        "required": [
          "type"
        ],
        "not": {
          "required": [
            "events"
          ]
        }
      },
      "WINDOW_RESIZEDPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "WINDOW_RESIZED"
          },
          "data": {
            "type": "object",
            "properties": {
              "success": {
                "const": true
              }
            },
            "required": [
              "success"
            ]
          }
        },
        "required": [
          "type",
          "data"
        ]
      },
      "WINDOW_FOCUSPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "WINDOW_FOCUS"
          },
          "activeWindowOnly": {
            "type": "boolean"
          },
          "windowTitle": {
            "type": "string"
          },
          "windowTitleMatch": {
            "type": "string",
            "enum": [
              "exact",
              "contains",
              "startsWith"
            ],
            "default": "contains"
          }
        },
        "required": [
          "type"
        ],
        "not": {
          "required": [
            "events"
          ]
        }
      },
      "WINDOW_FOCUSEDPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "WINDOW_FOCUSED"
          },
          "data": {
            "type": "object",
            "properties": {
              "success": {
                "const": true
              }
            },
            "required": [
              "success"
            ]
          }
        },
        "required": [
          "type",
          "data"
        ]
      },
      "GET_MOUSE_CURSOR_POSITIONPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "GET_MOUSE_CURSOR_POSITION"
          }
        },
        "required": [
          "type"
        ],
        "not": {
          "required": [
            "events"
          ]
        }
      },
      "MOUSE_POSITIONPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "MOUSE_POSITION"
          },
          "data": {
            "$ref": "#/components/schemas/Point"
          }
        },
        "required": [
          "type",
          "data"
        ]
      },
      "ERRORPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "ERROR"
          },
          "error": {
            "type": "string"
          }
        },
        "required": [
          "type",
          "error"
        ]
      },
      "SCREENSHOT_ERRORPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "SCREENSHOT_ERROR"
          },
          "error": {
            "type": "string"
          }
        },
        "required": [
          "type",
          "error"
        ]
      },
      "MOUSE_POSITION_ERRORPayload": {
        "type": "object",
        "properties": {
          "type": {
            "const": "MOUSE_POSITION_ERROR"
          },
          "error": {
            "type": "string"
          }
        },
        "required": [
          "type",
          "error"
        ]
      }
    },
    "messages": {
      "AUTH": {
        "name": "AUTH",
        "title": "AUTH",
        "summary": "Authentification au niveau message JSON, pas un en-tête HTTP",
        "description": "Authentification au niveau message JSON, pas un en-tête HTTP. Si l’authentification est désactivée, AUTH est accepté même sans clé. Clé incorrecte: AUTH_ERROR puis fermeture.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/AUTHPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "AUTH",
              "key": "YOUR_AUTHENTICATION_KEY"
            }
          }
        ]
      },
      "CONNECT": {
        "name": "CONNECT",
        "title": "CONNECT",
        "summary": "Accusé de connexion envoyé automatiquement",
        "description": "Accusé de connexion envoyé automatiquement. Auth requise uniquement si option activée ET clé configurée non vide.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/CONNECTPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "CONNECT",
              "info": "WebSocket Server Started",
              "authRequired": true
            }
          }
        ]
      },
      "AUTH_OK": {
        "name": "AUTH_OK",
        "title": "AUTH_OK",
        "summary": "Authentification acceptée pour cette connexion.",
        "description": "Authentification acceptée pour cette connexion.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/AUTH_OKPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "AUTH_OK"
            }
          }
        ]
      },
      "AUTH_ERROR": {
        "name": "AUTH_ERROR",
        "title": "AUTH_ERROR",
        "summary": "Clé incorrecte; le serveur ferme ensuite la connexion sans code personnalisé.",
        "description": "Clé incorrecte; le serveur ferme ensuite la connexion sans code personnalisé.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/AUTH_ERRORPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "AUTH_ERROR",
              "error": "Invalid authentication key"
            }
          }
        ]
      },
      "InputEvents": {
        "name": "InputEvents",
        "title": "InputEvents",
        "summary": "Applique les entrées dans l’ordre du tableau, sans accusé de traitement",
        "description": "Applique les entrées dans l’ordre du tableau, sans accusé de traitement. Envoyer uniquement events dans cette enveloppe. Le réglage allowWebsocketInput ne garantit pas le blocage de ces messages; utiliser une clé d’authentification et des clients de confiance.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/InputEventsPayload"
        },
        "examples": [
          {
            "name": "press-with-release-timer",
            "payload": {
              "events": [
                {
                  "type": "button",
                  "code": "A",
                  "value": true,
                  "delay": 100
                }
              ]
            }
          },
          {
            "name": "release",
            "payload": {
              "events": [
                {
                  "type": "button",
                  "code": "A",
                  "value": false
                }
              ]
            }
          },
          {
            "name": "stick-direction",
            "payload": {
              "events": [
                {
                  "type": "button",
                  "code": "LEFT_STICK_UP",
                  "value": 0.5
                }
              ]
            }
          },
          {
            "name": "axis",
            "payload": {
              "events": [
                {
                  "type": "axis",
                  "code": "leftX",
                  "value": 0.5
                }
              ]
            }
          },
          {
            "name": "trigger",
            "payload": {
              "events": [
                {
                  "type": "trigger",
                  "code": "leftTrigger",
                  "value": 1
                }
              ]
            }
          },
          {
            "name": "keyboard",
            "payload": {
              "events": [
                {
                  "type": "keyboard",
                  "code": "space",
                  "value": false
                }
              ]
            }
          },
          {
            "name": "mouse-button",
            "payload": {
              "events": [
                {
                  "type": "mouseButton",
                  "code": "leftClick",
                  "value": false
                }
              ]
            }
          },
          {
            "name": "scroll",
            "payload": {
              "events": [
                {
                  "type": "mouseScroll",
                  "code": "down",
                  "value": 120
                }
              ]
            }
          },
          {
            "name": "cursor",
            "payload": {
              "events": [
                {
                  "type": "mousePosition",
                  "code": "mousePosition",
                  "value": {
                    "x": 100,
                    "y": 200
                  }
                }
              ]
            }
          },
          {
            "name": "reset",
            "payload": {
              "events": [
                {
                  "type": "reset"
                }
              ]
            }
          },
          {
            "name": "legacy-mouse-motion",
            "payload": {
              "events": [
                {
                  "type": "mouseMotionOut",
                  "code": "mouseLeft",
                  "value": false
                }
              ]
            }
          },
          {
            "name": "legacy-setting",
            "payload": {
              "events": [
                {
                  "type": "setting",
                  "code": "toggleOutput",
                  "value": false
                }
              ]
            }
          }
        ]
      },
      "getState": {
        "name": "getState",
        "title": "getState",
        "summary": "Demande un état de manette limité aux valeurs non neutres",
        "description": "Demande un état de manette limité aux valeurs non neutres. Réponse state diffusée à tous les clients et encodée deux fois en JSON; indépendante de allowWebsocketOutput.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/getStatePayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "getState"
            }
          }
        ]
      },
      "state": {
        "name": "state",
        "title": "state",
        "summary": "État diffusé à tous les clients après getState, y compris avant authentification",
        "description": "État diffusé à tous les clients après getState, y compris avant authentification. La charge utile JSON est une chaîne contenant un objet JSON.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/statePayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": "{\"type\":\"state\",\"data\":{\"buttons\":{\"A\":true},\"axes\":{},\"triggers\":{}}}"
          }
        ],
        "x-delivery": "broadcast",
        "x-decoded-schema": {
          "$ref": "#/components/schemas/DecodedState"
        }
      },
      "EVENTS": {
        "name": "EVENTS",
        "title": "EVENTS",
        "summary": "Deltas diffusés si toggleWebsocket ET allowWebsocketOutput sont vrais",
        "description": "Deltas diffusés si toggleWebsocket ET allowWebsocketOutput sont vrais. Pas d’état initial; environ 33 ms de temporisation en mode normal, sans temporisation ajoutée en pleine vitesse. Les écarts numériques doivent dépasser 0.01 après arrondi à deux décimales. Diffusion vers tous les clients ouverts sans filtrage par authentification.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/EVENTSPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "EVENTS",
              "events": [
                {
                  "type": "button",
                  "code": "A",
                  "value": false
                }
              ]
            }
          }
        ],
        "x-delivery": "broadcast"
      },
      "GET_CURRENT_MAPPINGS": {
        "name": "GET_CURRENT_MAPPINGS",
        "title": "GET_CURRENT_MAPPINGS",
        "summary": "Lit le profil actif, la liste des noms de profils et ses mappings.",
        "description": "Lit le profil actif, la liste des noms de profils et ses mappings.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/GET_CURRENT_MAPPINGSPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "GET_CURRENT_MAPPINGS"
            }
          }
        ]
      },
      "CURRENT_MAPPINGS": {
        "name": "CURRENT_MAPPINGS",
        "title": "CURRENT_MAPPINGS",
        "summary": "Profil actif, noms des profils et mappings",
        "description": "Profil actif, noms des profils et mappings. Des champs peuvent manquer si le serveur s’arrête pendant la requête.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/CURRENT_MAPPINGSPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "CURRENT_MAPPINGS",
              "data": {
                "profile": "Default",
                "profiles": [
                  "Default"
                ],
                "mappings": []
              }
            }
          }
        ]
      },
      "GET_PROFILE_MAPPINGS": {
        "name": "GET_PROFILE_MAPPINGS",
        "title": "GET_PROFILE_MAPPINGS",
        "summary": "Lit les mappings du nom de profil résolu; un nom inconnu renvoie une liste vide.",
        "description": "Lit les mappings du nom de profil résolu; un nom inconnu renvoie une liste vide.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/GET_PROFILE_MAPPINGSPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "GET_PROFILE_MAPPINGS",
              "profile": "Default"
            }
          }
        ]
      },
      "PROFILE_MAPPINGS": {
        "name": "PROFILE_MAPPINGS",
        "title": "PROFILE_MAPPINGS",
        "summary": "Nom du profil et mappings",
        "description": "Nom du profil et mappings. Ignorer requestId: il ne permet pas d’associer la réponse à un identifiant envoyé par le client.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/PROFILE_MAPPINGSPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "PROFILE_MAPPINGS",
              "data": {
                "requestId": "ws-1792627200000-1",
                "profile": "Default",
                "mappings": []
              }
            }
          }
        ]
      },
      "MCP_SWITCH_PROFILE": {
        "name": "MCP_SWITCH_PROFILE",
        "title": "MCP_SWITCH_PROFILE",
        "summary": "Change le profil actif",
        "description": "Change le profil actif. Nom inconnu ou restriction d’abonnement: ERROR.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/MCP_SWITCH_PROFILEPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "MCP_SWITCH_PROFILE",
              "profile": "Default"
            }
          }
        ]
      },
      "MCP_PROFILE_SWITCHED": {
        "name": "MCP_PROFILE_SWITCHED",
        "title": "MCP_PROFILE_SWITCHED",
        "summary": "Profil actif après changement.",
        "description": "Profil actif après changement.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/MCP_PROFILE_SWITCHEDPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "MCP_PROFILE_SWITCHED",
              "data": {
                "profile": "Default",
                "mappings": []
              }
            }
          }
        ]
      },
      "GET_TEMPLATE_PROFILES": {
        "name": "GET_TEMPLATE_PROFILES",
        "title": "GET_TEMPLATE_PROFILES",
        "summary": "Lit les profils intégrés dont type vaut template.",
        "description": "Lit les profils intégrés dont type vaut template.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/GET_TEMPLATE_PROFILESPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "GET_TEMPLATE_PROFILES"
            }
          }
        ]
      },
      "TEMPLATE_PROFILES": {
        "name": "TEMPLATE_PROFILES",
        "title": "TEMPLATE_PROFILES",
        "summary": "Liste des modèles intégrés; outputTypes peut être absent.",
        "description": "Liste des modèles intégrés; outputTypes peut être absent.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/TEMPLATE_PROFILESPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "TEMPLATE_PROFILES",
              "data": []
            }
          }
        ]
      },
      "MCP_IMPORT_MAPPINGS": {
        "name": "MCP_IMPORT_MAPPINGS",
        "title": "MCP_IMPORT_MAPPINGS",
        "summary": "Importe des mappings",
        "description": "Importe des mappings. new exige name; append modifie le profil actif et remplace les conflits de même type/code de sortie. Sans abonnement, un dialogue demande à l’utilisateur de choisir l’action. Cette décision peut survenir après le délai de 15 s; ne pas réessayer automatiquement.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/MCP_IMPORT_MAPPINGSPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "MCP_IMPORT_MAPPINGS",
              "mode": "new",
              "name": "WebSocket Demo",
              "mappings": [
                {
                  "input": {
                    "type": "keyboard",
                    "code": "space"
                  },
                  "output": {
                    "type": "button",
                    "code": "A",
                    "value": false
                  },
                  "description": "Space to A"
                }
              ]
            }
          },
          {
            "name": "append",
            "payload": {
              "type": "MCP_IMPORT_MAPPINGS",
              "mode": "append",
              "mappings": [
                {
                  "input": {
                    "type": "keyboard",
                    "code": "space"
                  },
                  "output": {
                    "type": "button",
                    "code": "A",
                    "value": false
                  }
                }
              ]
            }
          }
        ]
      },
      "MAPPINGS_IMPORTED": {
        "name": "MAPPINGS_IMPORTED",
        "title": "MAPPINGS_IMPORTED",
        "summary": "Résultat effectif après import ou choix utilisateur.",
        "description": "Résultat effectif après import ou choix utilisateur.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/MAPPINGS_IMPORTEDPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "MAPPINGS_IMPORTED",
              "data": {
                "profile": "WebSocket Demo",
                "mappings": [
                  {
                    "id": 0,
                    "input": {
                      "type": "keyboard",
                      "code": "space"
                    },
                    "output": {
                      "type": "button",
                      "code": "A",
                      "value": false
                    },
                    "description": "Space to A"
                  }
                ],
                "mode": "new"
              }
            }
          }
        ]
      },
      "GET_SETTINGS": {
        "name": "GET_SETTINGS",
        "title": "GET_SETTINGS",
        "summary": "Lit les paramètres booléens exposés et leurs indications; ne retourne pas la clé d’authentification.",
        "description": "Lit les paramètres booléens exposés et leurs indications; ne retourne pas la clé d’authentification.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/GET_SETTINGSPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "GET_SETTINGS"
            }
          }
        ]
      },
      "SETTINGS_STATE": {
        "name": "SETTINGS_STATE",
        "title": "SETTINGS_STATE",
        "summary": "Paramètres exposés et indications en anglais",
        "description": "Paramètres exposés et indications en anglais. Certaines clés sont en lecture seule pour SET_SETTING.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/SETTINGS_STATEPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "SETTINGS_STATE",
              "data": {
                "settings": {
                  "toggleOutput": true,
                  "allowWebsocketInput": true,
                  "requireWebsocketAuth": true
                },
                "hints": {
                  "releaseAll": "One-shot action; returned value remains false."
                }
              }
            }
          }
        ]
      },
      "SET_SETTING": {
        "name": "SET_SETTING",
        "title": "SET_SETTING",
        "summary": "Définit value ou bascule avec toggle=true, exclusivement",
        "description": "Définit value ou bascule avec toggle=true, exclusivement. releaseAll=true déclenche une libération globale et retourne false. Désactiver le transport utilisé peut fermer la connexion avant la réponse. Activer toggleWebsocket, toggleMcpServer ou enableExternalAI désactive allowWebsocketOutput; activer MCP active aussi WebSocket et son option d’entrée. autoStartMainOutput=true active également toggleOutput.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/SET_SETTINGPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "SET_SETTING",
              "key": "releaseAll",
              "value": true
            }
          },
          {
            "name": "toggle",
            "payload": {
              "type": "SET_SETTING",
              "key": "toggleOutput",
              "toggle": true
            }
          }
        ]
      },
      "SETTING_APPLIED": {
        "name": "SETTING_APPLIED",
        "title": "SETTING_APPLIED",
        "summary": "Valeur réellement conservée après action.",
        "description": "Valeur réellement conservée après action.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/SETTING_APPLIEDPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "SETTING_APPLIED",
              "data": {
                "key": "releaseAll",
                "value": false
              }
            }
          }
        ]
      },
      "NAVIGATE_TO": {
        "name": "NAVIGATE_TO",
        "title": "NAVIGATE_TO",
        "summary": "Navigue vers une route de Desktop, avec / initial ajouté si absent",
        "description": "Navigue vers une route de Desktop, avec / initial ajouté si absent. /agent ouvre une fenêtre séparée. Sur /mapping ou /mapping-v2, editMappingId/profile prennent priorité sur tab. NAVIGATED confirme le lancement de navigation, pas la validité ou le chargement de la page.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/NAVIGATE_TOPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "NAVIGATE_TO",
              "page": "mapping",
              "profile": "Default"
            }
          }
        ]
      },
      "NAVIGATED": {
        "name": "NAVIGATED",
        "title": "NAVIGATED",
        "summary": "Route demandée normalisée avec / initial; tab reprend la demande même si recherche mapping prioritaire.",
        "description": "Route demandée normalisée avec / initial; tab reprend la demande même si recherche mapping prioritaire.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/NAVIGATEDPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "NAVIGATED",
              "data": {
                "page": "/mapping",
                "tab": null
              }
            }
          }
        ]
      },
      "GET_CURRENT_PAGE": {
        "name": "GET_CURRENT_PAGE",
        "title": "GET_CURRENT_PAGE",
        "summary": "Lit le premier segment de la route courante, son paramètre tab et le pathname.",
        "description": "Lit le premier segment de la route courante, son paramètre tab et le pathname.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/GET_CURRENT_PAGEPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "GET_CURRENT_PAGE"
            }
          }
        ]
      },
      "CURRENT_PAGE": {
        "name": "CURRENT_PAGE",
        "title": "CURRENT_PAGE",
        "summary": "page sans / initial; racine → dashboard, gameDiscovery → game-discovery",
        "description": "page sans / initial; racine → dashboard, gameDiscovery → game-discovery. Champs null si lecture impossible.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/CURRENT_PAGEPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "CURRENT_PAGE",
              "data": {
                "page": "mapping",
                "tab": null,
                "pathname": "/mapping"
              }
            }
          }
        ]
      },
      "TAKE_SCREENSHOT": {
        "name": "TAKE_SCREENSHOT",
        "title": "TAKE_SCREENSHOT",
        "summary": "Capture la fenêtre active demandée, sinon le titre sélectionné, la région fournie ou l’écran complet",
        "description": "Capture la fenêtre active demandée, sinon le titre sélectionné, la région fournie ou l’écran complet. Format JPEG habituel, qualité 80 par défaut. maxWidth, sinon maxHeight, sinon 1024 définit une seule limite pour les deux dimensions; le ratio est conservé. Vérifier le format des octets si nécessaire: un repli PNG est possible malgré le nom .jpg ou le préfixe JPEG.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/TAKE_SCREENSHOTPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "TAKE_SCREENSHOT",
              "format": "base64",
              "quality": 80,
              "maxWidth": 1024
            }
          }
        ]
      },
      "SCREENSHOT_DATA": {
        "name": "SCREENSHOT_DATA",
        "title": "SCREENSHOT_DATA",
        "summary": "Capture créée: file→filePath, base64→buffer, both→les deux, dataUrl→dataUrl",
        "description": "Capture créée: file→filePath, base64→buffer, both→les deux, dataUrl→dataUrl. Dimensions originales avant réduction.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/SCREENSHOT_DATAPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "SCREENSHOT_DATA",
              "data": {
                "width": 1920,
                "height": 1080,
                "timestamp": 1792627200000,
                "filePath": "/tmp/playability-screenshot-example.jpg"
              }
            }
          }
        ]
      },
      "LIST_WINDOWS": {
        "name": "LIST_WINDOWS",
        "title": "LIST_WINDOWS",
        "summary": "Liste les fenêtres avec leur titre et un index temporaire",
        "description": "Liste les fenêtres avec leur titre et un index temporaire. Nécessite les permissions système correspondantes.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/LIST_WINDOWSPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "LIST_WINDOWS"
            }
          }
        ]
      },
      "WINDOWS_LIST": {
        "name": "WINDOWS_LIST",
        "title": "WINDOWS_LIST",
        "summary": "id est l’index de l’énumération converti en chaîne; il ne peut pas sélectionner une fenêtre",
        "description": "id est l’index de l’énumération converti en chaîne; il ne peut pas sélectionner une fenêtre. Utiliser windowTitle.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/WINDOWS_LISTPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "WINDOWS_LIST",
              "data": {
                "windows": [
                  {
                    "id": "0",
                    "name": "Example window"
                  }
                ]
              }
            }
          }
        ]
      },
      "GET_DISPLAYS": {
        "name": "GET_DISPLAYS",
        "title": "GET_DISPLAYS",
        "summary": "Liste les écrans avec leur nom, leurs dimensions, leur position et leur facteur d’échelle.",
        "description": "Liste les écrans avec leur nom, leurs dimensions, leur position et leur facteur d’échelle.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/GET_DISPLAYSPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "GET_DISPLAYS"
            }
          }
        ]
      },
      "DISPLAYS_LIST": {
        "name": "DISPLAYS_LIST",
        "title": "DISPLAYS_LIST",
        "summary": "Écrans et géométrie.",
        "description": "Écrans et géométrie.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/DISPLAYS_LISTPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "DISPLAYS_LIST",
              "data": {
                "displays": [
                  {
                    "id": 1,
                    "bounds": {
                      "x": 0,
                      "y": 0,
                      "width": 1920,
                      "height": 1080
                    },
                    "workArea": {
                      "x": 0,
                      "y": 0,
                      "width": 1920,
                      "height": 1040
                    },
                    "scaleFactor": 1,
                    "scalePercent": 100,
                    "rotation": 0,
                    "isPrimary": true,
                    "label": "Primary Display (1920 × 1080, 100%)"
                  }
                ]
              }
            }
          }
        ]
      },
      "WINDOW_GET_PROPERTIES": {
        "name": "WINDOW_GET_PROPERTIES",
        "title": "WINDOW_GET_PROPERTIES",
        "summary": "Lit le titre et la région de la fenêtre sélectionnée",
        "description": "Lit le titre et la région de la fenêtre sélectionnée. Sans titre, fenêtre active par défaut; activeWindowOnly=false sans titre provoque ERROR. Le premier titre correspondant est choisi sans tenir compte de la casse. Nécessite les permissions système correspondantes.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/WINDOW_GET_PROPERTIESPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "WINDOW_GET_PROPERTIES",
              "windowTitle": "Example window"
            }
          }
        ]
      },
      "WINDOW_PROPERTIES": {
        "name": "WINDOW_PROPERTIES",
        "title": "WINDOW_PROPERTIES",
        "summary": "Titre et dimensions de la fenêtre.",
        "description": "Titre et dimensions de la fenêtre.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/WINDOW_PROPERTIESPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "WINDOW_PROPERTIES",
              "data": {
                "title": "Example window",
                "region": {
                  "left": 100,
                  "top": 100,
                  "width": 800,
                  "height": 600
                }
              }
            }
          }
        ]
      },
      "WINDOW_MOVE": {
        "name": "WINDOW_MOVE",
        "title": "WINDOW_MOVE",
        "summary": "Déplace la fenêtre; x/y absents valent 0",
        "description": "Déplace la fenêtre; x/y absents valent 0. Sans titre, fenêtre active par défaut; activeWindowOnly=false sans titre provoque ERROR. Le premier titre correspondant est choisi sans tenir compte de la casse. Nécessite les permissions système correspondantes.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/WINDOW_MOVEPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "WINDOW_MOVE",
              "windowTitle": "Example window",
              "x": 100,
              "y": 100
            }
          }
        ]
      },
      "WINDOW_MOVED": {
        "name": "WINDOW_MOVED",
        "title": "WINDOW_MOVED",
        "summary": "Opération effectuée.",
        "description": "Opération effectuée.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/WINDOW_MOVEDPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "WINDOW_MOVED",
              "data": {
                "success": true
              }
            }
          }
        ]
      },
      "WINDOW_RESIZE": {
        "name": "WINDOW_RESIZE",
        "title": "WINDOW_RESIZE",
        "summary": "Redimensionne la fenêtre; valeurs absentes 800×600, sinon les dimensions fournies",
        "description": "Redimensionne la fenêtre; valeurs absentes 800×600, sinon les dimensions fournies. Sans titre, fenêtre active par défaut; activeWindowOnly=false sans titre provoque ERROR. Le premier titre correspondant est choisi sans tenir compte de la casse. Nécessite les permissions système correspondantes.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/WINDOW_RESIZEPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "WINDOW_RESIZE",
              "windowTitle": "Example window",
              "width": 800,
              "height": 600
            }
          }
        ]
      },
      "WINDOW_RESIZED": {
        "name": "WINDOW_RESIZED",
        "title": "WINDOW_RESIZED",
        "summary": "Opération effectuée.",
        "description": "Opération effectuée.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/WINDOW_RESIZEDPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "WINDOW_RESIZED",
              "data": {
                "success": true
              }
            }
          }
        ]
      },
      "WINDOW_FOCUS": {
        "name": "WINDOW_FOCUS",
        "title": "WINDOW_FOCUS",
        "summary": "Active la fenêtre sélectionnée",
        "description": "Active la fenêtre sélectionnée. Sans titre, fenêtre active par défaut; activeWindowOnly=false sans titre provoque ERROR. Le premier titre correspondant est choisi sans tenir compte de la casse. Nécessite les permissions système correspondantes.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/WINDOW_FOCUSPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "WINDOW_FOCUS",
              "windowTitle": "Example window"
            }
          }
        ]
      },
      "WINDOW_FOCUSED": {
        "name": "WINDOW_FOCUSED",
        "title": "WINDOW_FOCUSED",
        "summary": "Opération effectuée.",
        "description": "Opération effectuée.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/WINDOW_FOCUSEDPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "WINDOW_FOCUSED",
              "data": {
                "success": true
              }
            }
          }
        ]
      },
      "GET_MOUSE_CURSOR_POSITION": {
        "name": "GET_MOUSE_CURSOR_POSITION",
        "title": "GET_MOUSE_CURSOR_POSITION",
        "summary": "Lit la position de la souris",
        "description": "Lit la position de la souris. Les coordonnées peuvent différer de l’espace logique utilisé par mousePosition.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/GET_MOUSE_CURSOR_POSITIONPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "GET_MOUSE_CURSOR_POSITION"
            }
          }
        ]
      },
      "MOUSE_POSITION": {
        "name": "MOUSE_POSITION",
        "title": "MOUSE_POSITION",
        "summary": "Coordonnées de la souris renvoyées par le système.",
        "description": "Coordonnées de la souris renvoyées par le système.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/MOUSE_POSITIONPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "MOUSE_POSITION",
              "data": {
                "x": 100,
                "y": 200
              }
            }
          }
        ]
      },
      "ERROR": {
        "name": "ERROR",
        "title": "ERROR",
        "summary": "Erreur de format, d’authentification, de validation ou d’exécution",
        "description": "Erreur de format, d’authentification, de validation ou d’exécution. Aucun identifiant de requête n’est associé. Une erreur Unauthorized ferme la connexion; une commande inconnue peut rester sans réponse.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/ERRORPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "ERROR",
              "error": "Profile name is required"
            }
          }
        ]
      },
      "SCREENSHOT_ERROR": {
        "name": "SCREENSHOT_ERROR",
        "title": "SCREENSHOT_ERROR",
        "summary": "La capture n’a pas pu être effectuée",
        "description": "La capture n’a pas pu être effectuée. Une erreur générale peut aussi être renvoyée avant le début de la capture.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/SCREENSHOT_ERRORPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "SCREENSHOT_ERROR",
              "error": "No window found matching \"Example window\". Use list_windows to see available windows."
            }
          }
        ]
      },
      "MOUSE_POSITION_ERROR": {
        "name": "MOUSE_POSITION_ERROR",
        "title": "MOUSE_POSITION_ERROR",
        "summary": "La position du curseur n’a pas pu être lue",
        "description": "La position du curseur n’a pas pu être lue. Une erreur générale peut aussi être renvoyée avant cette opération.",
        "contentType": "application/json",
        "payload": {
          "$ref": "#/components/schemas/MOUSE_POSITION_ERRORPayload"
        },
        "examples": [
          {
            "name": "example",
            "payload": {
              "type": "MOUSE_POSITION_ERROR",
              "error": "Mouse position unavailable"
            }
          }
        ]
      }
    }
  },
  "x-runtime-limits": {
    "inputReleaseDelayMaxMs": 5000,
    "importedMappingLimit": 500,
    "profileNameMaxLength": 50,
    "mappingDescriptionMaxLength": 200,
    "outputNormalDelayMs": 33,
    "outputNumericPrecision": 2,
    "outputNumericDeltaThreshold": 0.01,
    "requestTimeoutMs": 15000
  },
  "x-protocol-caveats": [
    "Envoyer une seule requête avec réponse à la fois: aucun identifiant de corrélation client n’est disponible.",
    "Les diffusions EVENTS et state peuvent parvenir aux clients connectés avant leur authentification; désactiver les sorties inutiles.",
    "Le réglage allowWebsocketInput ne garantit pas le blocage des entrées; réserver le service aux clients de confiance.",
    "Une commande inconnue peut rester sans réponse. Une déconnexion ne relâche pas automatiquement les commandes maintenues.",
    "Après un changement de clé ou de politique d’authentification, reconnecter et réauthentifier le client."
  ]
}
