HyperHQ Plugin API Reference
Complete reference for developers building HyperHQ plugins.
Required Plugin Methods
Every plugin must implement these methods:
initialize(data)
Purpose: Receive settings and prepare plugin for use
Called: When plugin starts or settings change
Parameters: { settings: {}, pluginData: {} }
Must Return: "initialized" on success, {error: "message"} on failure
// Example
function initialize(data) {
this.settings = data.settings;
this.apiKey = this.settings.apiKey;
if (!this.apiKey) {
return { error: "API Key is required" };
}
return "initialized";
}
execute(data)
Purpose: Perform main plugin functionality
Called: When user activates plugin
Parameters: { action: "action_name", ...otherData }
Must Return: Any data or {error: "message"} on failure
// Example
function execute(data) {
const action = data.action || "default";
switch (action) {
case "scan_files":
return this.scanFiles(data.path);
case "get_status":
return { status: "running", files: 1234 };
default:
return { error: `Unknown action: ${action}` };
}
}
test(data)
Purpose: Health check for plugin
Called: When HyperHQ needs to verify plugin works
Parameters: {}
Must Return: true if healthy, false if not
// Example
function test(data) {
try {
// Test if we can perform basic operations
const testResult = this.performQuickTest();
return testResult.success;
} catch (error) {
return false;
}
}
shutdown(data) (Optional)
Purpose: Clean up before plugin exits
Called: When HyperHQ is closing
Parameters: {}
Must Return: "ok"
Communication Protocol
All HyperHQ plugins communicate via Socket.IO (WebSocket-based real-time bidirectional communication).
Connection Details:
- Server URL:
http://localhost:${HYPERHQ_SOCKET_PORT} - Namespace: default Socket.IO namespace (
/) - Transport: WebSocket, with polling fallback for standard Socket.IO clients
- Authentication: Challenge-response model
- Environment Variables:
HYPERHQ_PLUGIN_ID- Your plugin's unique identifierHYPERHQ_AUTH_CHALLENGE- Authentication challenge tokenHYPERHQ_SOCKET_PORT- Socket server port. HyperHQ tries 52789 first, then 52790-52818, then an OS-assigned port.HYPERHQ_PLUGIN_NAME- Your plugin display nameHYPERHQ_PLUGIN_VERSION- Your plugin version
Benefits:
- Real-time bidirectional communication
- Event-based architecture
- Progress updates and logging
- Request/response pattern
- Connection state management
Implemented Socket.IO event names:
| Direction | Event | Purpose |
|---|---|---|
| Plugin -> HyperHQ | authenticate | Send {pluginId, challenge} or reconnect with {pluginId, sessionToken} |
| HyperHQ -> Plugin | authenticated | Returns {success, pluginId, sessionToken, serverPort} |
| HyperHQ -> Plugin | request | Calls plugin methods such as initialize, execute, test, and shutdown |
| Plugin -> HyperHQ | response | Responds to a request message with {id, type: "response", data} |
| Plugin -> HyperHQ | requestData or request_data | Requests HyperHQ data/actions |
| HyperHQ -> Plugin | dataResponse and data_response | Response to requestData/request_data |
| Plugin -> HyperHQ | subscribeEvents | Subscribes to broadcast events |
| HyperHQ -> Plugin | eventsSubscribed | Confirms event subscriptions |
| HyperHQ -> Plugin | hyperHqEvent | Broadcasts subscribed events |
| Plugin -> HyperHQ | statusUpdate | Sends progress/status text and keeps active requests alive |
| Plugin -> HyperHQ | requestFile | Requests a file from the plugin's own install directory |
| HyperHQ -> Plugin | fileData | Returns base64 file data or an error |
See Socket.IO Connection Guide for complete implementation details.
HyperHQ Plugin API (JavaScript Plugins)
JavaScript plugins have access to the global hyperai object:
Data Access Methods
hyperai.data = {
// Get all ROMs, optionally filtered by system
getRoms: async (systemId?: string) => Promise<Rom[]>,
// Get all systems
getSystems: async () => Promise<System[]>,
// Get all games for a specific system
getGames: async (systemId: string) => Promise<Game[]>,
// Get media file for a game
getMedia: async (gameId: string, mediaType: string) => Promise<MediaFile>
}
Settings Management
hyperai.settings = {
// Get a single setting value
get: async (key: string) => Promise<any>,
// Set a single setting value
set: async (key: string, value: any) => Promise<void>,
// Get all plugin settings
getAll: async () => Promise<Record<string, any>>,
// Update multiple settings at once
setAll: async (settings: Record<string, any>) => Promise<void>
}
UI Interactions
hyperai.ui = {
// Show a notification to the user
showNotification: (message: string, type?: 'info' | 'success' | 'warning' | 'error') => void,
// Show a dialog and wait for user response
showDialog: async (options: DialogOptions) => Promise<any>,
// Update plugin status display
updateStatus: (status: string) => void
}
File System (Sandboxed)
hyperai.fs = {
// Read file contents as string
readFile: async (path: string) => Promise<string>,
// Write string content to file
writeFile: async (path: string, content: string) => Promise<void>,
// Check if file/directory exists
exists: async (path: string) => Promise<boolean>,
// List files in directory
list: async (path: string) => Promise<string[]>
}
Logging
hyperai.log = {
debug: (message: string, data?: any) => void,
info: (message: string, data?: any) => void,
warn: (message: string, data?: any) => void,
error: (message: string, data?: any) => void
}
Socket.IO Data Request Methods
Executable plugins using Socket.IO can request data from HyperHQ.
Quick Reference
Blocking Methods (await response):
getSystems- Get all game systemsgetMediaFolders- Get media folder paths for a systemgetGamesForSystem- Get all games for a specific system
Fire-and-Forget Methods (immediate acknowledgment):
createSystem- Create a new game systemcreateEmulator- Create a new emulator configurationaddGames- Add games/ROMs to a systemlaunchGame- Launch a game by ID
How Data Requests Work
Request Format
socket.emit('requestData', {
method: 'methodName',
params: { /* parameters */ },
requestId: 'unique-request-id',
sessionToken: sessionToken // From authentication
});
request_data is also accepted for compatibility. HyperHQ emits both dataResponse and data_response for each data request response; listen to one of them and de-duplicate by requestId if your client subscribes to both.
Available Data Methods
getSystems (Blocking - Awaits Response)
Retrieves all game systems.
Flow:
Code:
// Request
socket.emit('requestData', {
method: 'getSystems',
params: {},
requestId: 'get-systems-001',
sessionToken: sessionToken
});
// Response arrives via 'dataResponse' event
socket.on('dataResponse', (response) => {
if (response.requestId === 'get-systems-001') {
const systems = response.data.systems;
}
});
Returns: Array of system objects with properties:
id: string - Unique system identifiername: string - Display namedisplayName: string - Display name from HyperHQplatform: string - Platform namereferenceId: string - External reference IDgamesCount: number - Number of ROMs/games
getMediaFolders (Blocking - Awaits Response)
Retrieves media folder paths for a system.
Flow:
Code:
// Request
socket.emit('requestData', {
method: 'getMediaFolders',
params: {
systemReferenceId: 'steam',
mediaTypes: ['boxart', 'background', 'screenshot', 'logo']
},
requestId: 'get-folders-001',
sessionToken: sessionToken
});
// Response
{
requestId: 'get-folders-001',
success: true,
data: {
folders: {
boxart: 'C:\\HyperSpin\\Media\\Steam\\Images\\Artwork1',
background: 'C:\\HyperSpin\\Media\\Steam\\Images\\Background',
screenshot: 'C:\\HyperSpin\\Media\\Steam\\Images\\Screenshot',
logo: 'C:\\HyperSpin\\Media\\Steam\\Images\\Logo'
}
}
}
createSystem (Fire-and-Forget)
Creates a new game system.
Flow:
Code:
socket.emit('requestData', {
method: 'createSystem',
params: {
name: 'Steam',
description: 'Steam games library',
platform: 'PC',
referenceId: 'steam', // Plugin-provided identifier
allowedExtensions: '.exe',
enabled: true
},
requestId: 'create-system-001',
sessionToken: sessionToken
});
createEmulator (Fire-and-Forget)
Creates a new emulator configuration.
Flow:
Code:
socket.emit('requestData', {
method: 'createEmulator',
params: {
name: 'RetroArch',
type: 'RetroArch',
executable: 'C:\\RetroArch\\retroarch.exe',
commandLine: '-L cores/[core] [romPath]',
extensions: '.nes,.snes,.gb',
enabled: true
},
requestId: 'create-emu-001',
sessionToken: sessionToken
});
addGames (Fire-and-Forget)
Adds games/ROMs to a system.
Flow:
Code:
socket.emit('requestData', {
method: 'addGames',
params: {
systemName: 'Steam',
games: [
{
name: 'Half-Life 2',
fileName: 'hl2.exe',
romPath: 'C:\\Steam\\HL2',
gameReferenceId: 'steam_220',
developer: 'Valve',
publisher: 'Valve',
releaseYear: '2004',
genres: 'FPS'
}
]
},
requestId: 'add-games-001',
sessionToken: sessionToken
});
getGamesForSystem (Blocking - Awaits Response)
Retrieves all games for a specific system.
Flow:
Code:
// Request
socket.emit('requestData', {
method: 'getGamesForSystem',
params: {
systemId: 'steam'
},
requestId: 'get-games-001',
sessionToken: sessionToken
});
// Response arrives via 'dataResponse' event
socket.on('dataResponse', (response) => {
if (response.requestId === 'get-games-001') {
const games = response.data.games;
const systemId = response.data.systemId;
const systemName = response.data.systemName;
}
});
Returns: Array of game objects with properties:
id: string - Unique game identifiername: string - Game namedisplayName: string - Display namefileName: string - ROM filenameromPath: string - Full path to ROM filesystemId: string - Parent system IDreferenceId: string - External reference ID (optional)developer: string - Game developerpublisher: string - Game publisherreleaseYear: string - Release yearplays: number - Play counthidden: boolean - Whether game is hiddenfavourite: boolean - Whether game is marked as favoritesteamAppId: string - Steam App ID when availableisSteam: boolean - Whether game is a Steam game
launchGame (Fire-and-Forget)
Launches a game by its ID.
Flow:
Code:
socket.emit('requestData', {
method: 'launchGame',
params: {
gameId: '12345-abcde-67890'
},
requestId: 'launch-game-001',
sessionToken: sessionToken
});
Parameters:
gameId: string - The unique ID of the game to launch
Notes:
- This is an asynchronous operation that returns immediately
- Game launch happens in the background
- Listen for
gameLaunchedevents to know when the game actually starts - Errors during launch will be emitted as separate events
Response Format
All Socket.IO data responses follow this structure:
{
requestId: string; // Matches your request
success: boolean; // true if successful
data?: any; // Response data (if success)
error?: string; // Error message (if failed)
}
Event Subscription System
Plugins can subscribe to HyperHQ events to receive real-time notifications about game launches, system changes, and other activities.
How Event Subscription Works
Subscribing to Events
After authentication, subscribe to events you want to receive:
// Subscribe to multiple events
socket.emit('subscribeEvents', [
'gameLaunched',
'gameClosed',
'systemChanged',
'romSelected'
]);
// Confirmation
socket.on('eventsSubscribed', (data) => {
console.log('Subscribed to events:', data.events);
});
Listening for Events
Once subscribed, listen for the hyperHqEvent event:
socket.on('hyperHqEvent', (event) => {
console.log('Event type:', event.type);
console.log('Event data:', event.data);
console.log('Timestamp:', event.timestamp);
// Handle specific events
switch (event.type) {
case 'gameLaunched':
handleGameLaunched(event.data);
break;
case 'gameClosed':
handleGameClosed(event.data);
break;
case 'systemChanged':
handleSystemChanged(event.data);
break;
case 'romSelected':
handleRomSelected(event.data);
break;
}
});
Available Events
| Event | When Triggered | Data Format |
|---|---|---|
gameLaunched | When a game starts | {gameId: string, gameName: string, systemId: string, timestamp: number} |
gameClosed | When a game exits | {gameId: string, gameName: string, systemId: string, exitCode: number, timestamp: number} |
systemChanged | When active system changes | {systemId: string, systemName: string, timestamp: number} |
romSelected | When user selects a ROM | {gameId: string, gameName: string, systemId: string, timestamp: number} |
mediaProgress | Media download progress | {gameId: string, mediaType: string, progress: number, timestamp: number} |
mediaDownloadStart | Media download starts | {gameId: string, mediaType: string, timestamp: number} |
mediaDownloadFinish | Media download completes | {gameId: string, mediaType: string, success: boolean, timestamp: number} |
dbConnected | Database connection established | {timestamp: number} |
Complete Example
// After authentication
socket.on('authenticated', (response) => {
if (response.success) {
console.log('Authenticated. Session token:', response.sessionToken);
// Subscribe to game lifecycle events
socket.emit('subscribeEvents', [
'gameLaunched',
'gameClosed'
]);
}
});
// Handle subscription confirmation
socket.on('eventsSubscribed', (data) => {
console.log('Subscribed to:', data.events);
});
// Listen for events
socket.on('hyperHqEvent', (event) => {
if (event.type === 'gameLaunched') {
console.log(`Game launched: ${event.data.gameName}`);
console.log(` Game ID: ${event.data.gameId}`);
console.log(` System: ${event.data.systemId}`);
// Example: Track play time
startPlayTimeTracking(event.data.gameId);
}
if (event.type === 'gameClosed') {
console.log(`Game closed: ${event.data.gameName}`);
console.log(` Exit code: ${event.data.exitCode}`);
console.log(` Duration: ${event.data.playTime}ms`);
// Example: Save play session
savePlaySession(event.data);
}
});
// Example: Launch a game and wait for confirmation
socket.emit('requestData', {
method: 'launchGame',
params: { gameId: 'game-123' },
requestId: 'launch-001',
sessionToken: sessionToken
});
// The launchGame method returns immediately, but you can listen for the actual launch
socket.on('hyperHqEvent', (event) => {
if (event.type === 'gameLaunched' && event.data.gameId === 'game-123') {
console.log('Game actually started');
}
});
Event Flow for Game Launch
Best Practices
Do:
- Subscribe only to events you actually need.
- Handle events asynchronously to avoid blocking.
- Unsubscribe from events when no longer needed, if your client supports it.
- Check
event.typebefore processingevent.data. - Validate event data structure before using it.
Avoid:
- Subscribing to all events "just in case".
- Performing heavy operations directly in event handlers.
- Assuming every event has the exact same shape.
- Blocking the event handler with synchronous work.
Plugin Logging and Status
HyperHQ currently captures plugin process output through the launcher and accepts live status updates over Socket.IO. The Socket.IO server does not currently implement plugin_log, pluginLog, plugin:progress, or plugin:notify as documented events.
Use local logging for detailed diagnostics, and use statusUpdate for short user-visible progress or heartbeat messages while a HyperHQ-initiated request is running.
Diagnostics Flow
Status Update Event
statusUpdate accepts this payload:
{
status: string;
message?: string;
}
Example:
socket.emit('statusUpdate', {
status: 'syncing',
message: 'Imported 120 of 450 games'
});
When a plugin is handling a request from HyperHQ, sending statusUpdate also resets the active request timeout. This is the supported way to keep long operations such as scans, imports, downloads, and setup wizards alive.
Long-Running Request Flow
Error Handling Flow
For persistent diagnostic logs, write to stdout/stderr and/or a local plugin log file. Do not send secrets such as auth headers, cookies, API keys, OAuth tokens, or session tokens.
Plugin Settings System
Setting Types
| Type | Use For | Validation |
|---|---|---|
text | Strings | pattern, minLength, maxLength |
password | Sensitive strings | minLength, maxLength |
number | Numbers | min, max, step |
boolean | True/false | None |
select | Multiple choice | options array |
file | File paths | extensions filter |
directory | Folder paths | None |
Settings Definition
{
"settings": [
{
"key": "apiKey",
"type": "password",
"label": "API Key",
"defaultValue": "",
"description": "Your API key for external service",
"required": true,
"validation": {
"minLength": 16,
"maxLength": 64
}
},
{
"key": "timeout",
"type": "number",
"label": "Request Timeout",
"defaultValue": 30,
"description": "Timeout in seconds",
"validation": {
"min": 5,
"max": 300,
"step": 5
}
},
{
"key": "autoConnect",
"type": "boolean",
"label": "Auto Connect",
"defaultValue": true,
"description": "Automatically connect on startup"
},
{
"key": "logLevel",
"type": "select",
"label": "Log Level",
"defaultValue": "info",
"options": [
{"label": "Debug", "value": "debug"},
{"label": "Info", "value": "info"},
{"label": "Warning", "value": "warning"},
{"label": "Error", "value": "error"}
]
}
]
}
Plugin Manifest Schema
Complete Manifest Example
{
"id": "my-plugin",
"name": "My Plugin",
"version": "1.0.0",
"description": "Short description of what your plugin does",
"author": "Your Name <[email protected]>",
"homepage": "https://github.com/user/my-plugin",
"repository": "https://github.com/user/my-plugin",
"license": "MIT",
"keywords": ["utility", "automation", "sync"],
"type": "executable",
"executable": "plugin.exe",
"executableProviders": {
"windows": "plugin.exe",
"linux": "plugin",
"macos": "plugin"
},
"main": "index.js",
"communication": {
"preferred": "socketio",
"fallback": "stdio",
"socketio": {
"enabled": true,
"autoReconnect": true,
"heartbeat": 30000,
"events": ["gameLaunched", "gameClosed", "mediaProgress"],
"fileStreaming": true,
"dataRequests": true
}
},
"capabilities": [
{
"name": "game-import",
"description": "Import games into HyperHQ",
"required": true
},
{
"name": "media-download",
"description": "Download media assets",
"required": false
}
],
"permissions": [
{
"type": "file",
"scope": "plugin-directory",
"description": "Read files bundled with this plugin"
},
{
"type": "network",
"scope": "external-apis",
"description": "Call external service APIs"
}
],
"ui": {
"hasSettingsPanel": true,
"hasMainPanel": false,
"hasStatusIndicator": true
},
"settings": [...],
"actions": [
{
"id": "scan_library",
"label": "Scan Game Library",
"description": "Scan for new games",
"icon": "scan",
"type": "primary"
}
],
"hyperaiVersion": "1.2.0",
"platforms": ["windows", "macos", "linux"]
}
Manifest Field Reference
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Required | Unique plugin identifier using lowercase letters, numbers, hyphens, or underscores |
name | string | Required | Display name |
version | string | Required | Semantic version, such as 1.0.0 |
description | string | Required | Brief description |
author | string | Required | Author name/email |
type | string | Required | javascript or executable |
executable | string | For executable | Default executable filename |
executableProviders | object | Optional | Platform-specific executable names for windows, linux, and macos |
main | string | For JavaScript | Entry point file |
communication | object | Optional | Preferred/fallback communication and Socket.IO flags |
capabilities | object[] | Required | Capabilities shaped as {name, description, required} |
permissions | object[] | Optional | Permissions shaped as {type, scope, description} |
settings | object[] | Optional | Plugin settings schema |
actions | object[] | Optional | UI actions shaped as {id, label, description, icon, type} |
onboarding | object | Optional | Setup wizards and wizard steps |
hyperaiVersion | string | Optional | Compatible HyperAI/HyperHQ host version |
platforms | string[] | Optional | Supported platforms: windows, macos, linux |
Permission Types
| Permission | Purpose | Example |
|---|---|---|
file | File access | { "type": "file", "scope": "plugin-directory", "description": "Read bundled assets" } |
network | Network access | { "type": "network", "scope": "external-apis", "description": "Call provider APIs" } |
system | System operations | { "type": "system", "scope": "emulator-executable", "description": "Launch emulator tools" } |
database | HyperHQ data changes | { "type": "database", "scope": "roms", "description": "Add games to HyperHQ" } |
Error Handling
Common Error Patterns
// Validation errors
if (!requiredParam) {
return { error: "Missing required parameter: requiredParam" };
}
// Operation errors
try {
const result = await riskyOperation();
return result;
} catch (error) {
return { error: `Operation failed: ${error.message}` };
}
// Timeout errors
const timeout = setTimeout(() => {
return { error: "Operation timed out" };
}, 30000);
Error Response Format
{
id: "request-id",
type: "error",
error: "Human readable error message",
details: { // Optional additional info
code: "ERROR_CODE",
stack: "...",
context: {...}
}
}
Best Practices
Performance
- Use Socket.IO events for all communication
- Send progress events for long operations (>5 seconds)
- Cache expensive computations
- Use streaming for large data sets where applicable
- Batch data requests when possible
Security
- Validate all input parameters
- Don't trust external data
- Use minimal required permissions
- Sanitize file paths
- Never log sensitive data (API keys, passwords)
Reliability
- Handle all exceptions gracefully
- Test with invalid/missing data
- Implement proper timeouts (30s max)
- Provide meaningful error messages
- Implement graceful shutdown
User Experience
- Use clear, helpful setting descriptions
- Provide good default values
- Send progress updates for slow operations
- Include usage examples in README
- Test on clean systems
Environment Variables
HyperHQ provides these environment variables to plugins:
| Variable | Description | Example |
|---|---|---|
HYPERHQ_PLUGIN_ID | Your plugin's ID | "my-plugin" |
HYPERHQ_AUTH_CHALLENGE | Authentication challenge | "abc123..." |
HYPERHQ_SOCKET_PORT | Socket.IO server port | "52789" |
PLUGIN_DATA_DIR | Your plugin's data folder | "C:\ProgramData\..." |
PLUGIN_DEBUG | Debug mode flag | "true" / "false" |
Quick Test Commands
# Test initialize
echo '{"id":"1","type":"request","method":"initialize","data":{"settings":{}}}' | ./plugin.exe
# Test execute
echo '{"id":"2","type":"request","method":"execute","data":{"action":"test"}}' | ./plugin.exe
# Test health
echo '{"id":"3","type":"request","method":"test","data":{}}' | ./plugin.exe
# Test shutdown
echo '{"id":"4","type":"request","method":"shutdown","data":{}}' | ./plugin.exe
Language Templates
Download ready-to-use starter templates:
- Python Template - Recommended for beginners
- JavaScript Template - In-process execution
- Node.js Template - Good for web developers
- C# Template - High performance option
- Go Template - Small, fast executables
Each template includes:
- Complete working plugin example with Socket.IO support
- Authentication implementation
- Build scripts for your platform
- Testing utilities
- Comprehensive documentation
For complete tutorials and examples, see:
- Plugin Developer Guide - Step-by-step tutorials
- Socket.IO Connection Guide - Real-time communication
- System Architecture - How plugins integrate