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:52789 - Namespace:
/plugin - Transport: WebSocket
- Authentication: Challenge-response model
- Environment Variables:
HYPERHQ_PLUGIN_ID- Your plugin's unique identifierHYPERHQ_AUTH_CHALLENGE- Authentication challenge tokenHYPERHQ_SOCKET_PORT- Socket server port (usually 52789)
Benefits:
- Real-time bidirectional communication
- Event-based architecture
- Progress updates and logging
- Request/response pattern
- Connection state management
See Socket.IO Connection Guide for complete implementation details.
HyperAI 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
});
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; // Array of SystemEntity
}
});
Returns: Array of system objects with properties:
id: string - Unique system identifiername: string - Display nameplatform: string - Platform namereferenceId: string - External reference IDromsCount: number - Number of ROMsallowedExtensions: string - Supported file extensions
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: {
systemId: 'steam',
games: [
{
name: 'Half-Life 2',
fileName: 'hl2.exe',
romPath: 'C:\\Steam\\HL2',
referenceId: 'steam_220',
developer: 'Valve',
publisher: 'Valve',
year: '2004',
genre: 'FPS',
rating: '9.5',
enabled: true
}
]
},
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; // Array of GameEntity
}
});
Returns: Array of game objects with properties:
id: string - Unique game identifiername: string - Game 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 publisheryear: string - Release yeargenre: string - Game genrerating: string - Game ratingenabled: boolean - Whether game is enabled
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 supported)
- ✅ Check event.type before processing event.data
- ✅ Validate event data structure before using
DON'T:
- ❌ Subscribe to all events "just in case"
- ❌ Perform heavy operations in event handlers
- ❌ Assume event data structure without checking
- ❌ Block the event handler with synchronous operations
Plugin Logging
Plugins should implement logging for debugging and monitoring. HyperHQ provides multiple ways to capture logs depending on your plugin type.
Logging Best Practices
Recommended Approach:
- Always log locally - Use console logging for debugging during development
- Send important logs to HyperHQ - Use Socket.IO to make logs visible in the plugin UI
- Use appropriate log levels - debug, info, warn, error
Plugin Logging Architecture
All plugins use Socket.IO for logging to HyperHQ:
All plugins (JavaScript, Go, Python, C#, etc.) use the same logging pattern via Socket.IO.
JavaScript Plugin Logging
JavaScript plugins can use Socket.IO logging directly or through convenience APIs:
Option 1: Direct Socket.IO (Recommended)
// Send log to HyperHQ via Socket.IO
socket.emit('plugin_log', {
level: 'info',
message: 'Plugin execution started',
timestamp: Date.now()
});
Option 2: Convenience API (if available)
// If your JavaScript plugin has the hyperai API available
hyperai.log.debug('Detailed debug information');
hyperai.log.info('General information');
hyperai.log.warn('Warning message');
hyperai.log.error('Error message');
Example usage
async function execute(data) {
// Log using Socket.IO
socket.emit('plugin_log', {
level: 'info',
message: 'Plugin execution started',
timestamp: Date.now()
});
try {
const result = await fetchData();
socket.emit('plugin_log', {
level: 'debug',
message: 'Data fetched successfully',
timestamp: Date.now(),
metadata: { resultCount: result.length }
});
return result;
} catch (error) {
socket.emit('plugin_log', {
level: 'error',
message: 'Failed to fetch data: ' + error.message,
timestamp: Date.now(),
metadata: { error: error.stack }
});
return { error: error.message };
}
}
Executable Plugin Logging (Socket.IO)
For executable plugins (Go, Python, C#, etc.), implement logging using Socket.IO events:
Logging Flow
Error Handling with Logging
Log Event Format
socket.emit('plugin_log', {
level: 'info', // debug | info | warn | error
message: 'Log message',
timestamp: Date.now(),
metadata: { // Optional additional context
action: 'sync_games',
itemsProcessed: 150
}
});
Full Example - Go Plugin with Logging
package main
import (
"fmt"
"log"
"time"
socketio "github.com/googollee/go-socket.io"
)
type Plugin struct {
Socket *socketio.Client
LogLevel string
}
// Log levels
const (
LogDebug = "debug"
LogInfo = "info"
LogWarn = "warn"
LogError = "error"
)
// Send log to HyperHQ via Socket.IO
func (p *Plugin) sendLog(level string, message string, metadata map[string]interface{}) {
// Always log locally first
log.Printf("[%s] %s", level, message)
// Send to HyperHQ if connected
if p.Socket != nil {
logData := map[string]interface{}{
"level": level,
"message": message,
"timestamp": time.Now().UnixMilli(),
}
if metadata != nil {
logData["metadata"] = metadata
}
p.Socket.Emit("plugin_log", logData)
}
}
// Convenience methods
func (p *Plugin) logDebug(format string, v ...interface{}) {
if p.LogLevel == "debug" {
message := fmt.Sprintf(format, v...)
p.sendLog(LogDebug, message, nil)
}
}
func (p *Plugin) logInfo(format string, v ...interface{}) {
message := fmt.Sprintf(format, v...)
p.sendLog(LogInfo, message, nil)
}
func (p *Plugin) logWarn(format string, v ...interface{}) {
message := fmt.Sprintf(format, v...)
p.sendLog(LogWarn, message, nil)
}
func (p *Plugin) logError(format string, v ...interface{}) {
message := fmt.Sprintf(format, v...)
p.sendLog(LogError, message, nil)
}
// Usage example
func (p *Plugin) Execute(data map[string]interface{}) interface{} {
p.logInfo("Starting game sync")
action := data["action"].(string)
p.logDebug("Action requested: %s", action)
switch action {
case "sync":
count, err := p.syncGames()
if err != nil {
p.logError("Sync failed: %v", err)
return map[string]string{"error": err.Error()}
}
p.logInfo("Sync completed: %d games synced", count)
return map[string]int{"gamesAdded": count}
default:
p.logWarn("Unknown action requested: %s", action)
return map[string]string{"error": "Unknown action"}
}
}
Python Plugin with Logging
import socketio
import time
import logging
class Plugin:
def __init__(self):
self.sio = socketio.Client()
self.log_level = "info"
# Also configure Python's built-in logging
logging.basicConfig(
level=logging.INFO,
format='[%(levelname)s] %(message)s'
)
def send_log(self, level, message, metadata=None):
"""Send log to HyperHQ and local console"""
# Log locally
if level == "debug":
logging.debug(message)
elif level == "info":
logging.info(message)
elif level == "warn":
logging.warning(message)
elif level == "error":
logging.error(message)
# Send to HyperHQ
if self.sio.connected:
log_data = {
"level": level,
"message": message,
"timestamp": int(time.time() * 1000)
}
if metadata:
log_data["metadata"] = metadata
self.sio.emit("plugin_log", log_data)
# Convenience methods
def log_debug(self, message, metadata=None):
if self.log_level == "debug":
self.send_log("debug", message, metadata)
def log_info(self, message, metadata=None):
self.send_log("info", message, metadata)
def log_warn(self, message, metadata=None):
self.send_log("warn", message, metadata)
def log_error(self, message, metadata=None):
self.send_log("error", message, metadata)
# Usage
def execute(self, data):
self.log_info("Plugin execution started")
try:
result = self.perform_action(data)
self.log_debug(f"Result: {result}")
return result
except Exception as e:
self.log_error(f"Execution failed: {str(e)}")
return {"error": str(e)}
C# Plugin with Logging
using SocketIOClient;
using System;
using System.Collections.Generic;
public class Plugin
{
private SocketIO socket;
private string logLevel = "info";
// Log levels
private const string LogDebug = "debug";
private const string LogInfo = "info";
private const string LogWarn = "warn";
private const string LogError = "error";
// Send log to HyperHQ via Socket.IO
private async void SendLog(string level, string message, Dictionary<string, object> metadata = null)
{
// Log locally first
Console.WriteLine($"[{level.ToUpper()}] {message}");
// Send to HyperHQ if connected
if (socket != null && socket.Connected)
{
var logData = new Dictionary<string, object>
{
["level"] = level,
["message"] = message,
["timestamp"] = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
};
if (metadata != null)
{
logData["metadata"] = metadata;
}
await socket.EmitAsync("plugin_log", logData);
}
}
// Convenience methods
public void LogDebug(string message, Dictionary<string, object> metadata = null)
{
if (logLevel == "debug")
SendLog(LogDebug, message, metadata);
}
public void LogInfo(string message, Dictionary<string, object> metadata = null)
{
SendLog(LogInfo, message, metadata);
}
public void LogWarn(string message, Dictionary<string, object> metadata = null)
{
SendLog(LogWarn, message, metadata);
}
public void LogError(string message, Dictionary<string, object> metadata = null)
{
SendLog(LogError, message, metadata);
}
// Usage example
public object Execute(Dictionary<string, object> data)
{
LogInfo("Starting plugin execution");
try
{
var result = PerformAction(data);
LogDebug($"Action completed successfully");
return result;
}
catch (Exception ex)
{
LogError($"Execution failed: {ex.Message}");
return new Dictionary<string, string> { ["error"] = ex.Message };
}
}
}
Log Levels Guide
| Level | Purpose | When to Use | Visible in Production |
|---|---|---|---|
| debug | Detailed diagnostic info | Development, troubleshooting | No (unless enabled) |
| info | General informational | Normal operations, milestones | Yes |
| warn | Warning conditions | Deprecated features, recoverable issues | Yes |
| error | Error conditions | Failures, exceptions, critical issues | Yes |
Logging Tips
DO:
- ✅ Log important milestones (connection established, sync started, etc.)
- ✅ Log errors with context (what failed, why, what was being attempted)
- ✅ Include relevant metadata (counts, IDs, timestamps)
- ✅ Use appropriate log levels
- ✅ Log locally AND send to HyperHQ
DON'T:
- ❌ Log sensitive data (passwords, API keys, personal info)
- ❌ Log excessively in loops (use counters instead)
- ❌ Log large data objects (summarize instead)
- ❌ Spam debug logs in production
Example: Comprehensive Logging Strategy
// JavaScript plugin with good logging practices
async function execute(data) {
// Log execution start
hyperai.log.info('Plugin execution started', { action: data.action });
const action = data.action || 'default';
try {
switch (action) {
case 'sync_games':
hyperai.log.info('Starting game sync');
// Get systems
hyperai.log.debug('Fetching systems from HyperHQ');
const systems = await hyperai.data.getSystems();
hyperai.log.debug(`Found ${systems.length} systems`);
// Sync each system
let totalGames = 0;
for (const system of systems) {
hyperai.log.info(`Syncing system: ${system.name}`);
try {
const games = await this.fetchGamesFromAPI(system.id);
await hyperai.data.addGames(system.id, games);
totalGames += games.length;
hyperai.log.info(`✓ Synced ${games.length} games for ${system.name}`);
} catch (error) {
hyperai.log.warn(`Failed to sync ${system.name}: ${error.message}`);
// Continue with next system
}
}
hyperai.log.info(`Sync complete: ${totalGames} total games synced`);
return { success: true, gamesAdded: totalGames };
default:
hyperai.log.warn(`Unknown action requested: ${action}`);
return { error: 'Unknown action' };
}
} catch (error) {
hyperai.log.error('Plugin execution failed', {
action: action,
error: error.message,
stack: error.stack
});
return { error: error.message };
}
}
Viewing Logs in HyperHQ
All logs sent via Socket.IO are:
- Stored in HyperHQ's log database
- Displayed in the plugin's "Logs" tab in the UI
- Filtered by log level
- Searchable by message content
- Downloadable for offline analysis
This helps you debug issues and monitor plugin behavior in production.
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",
"main": "index.js", // For JavaScript plugins
"capabilities": [
"socketio",
"gameScanning",
"mediaDownload"
],
"permissions": [
"filesystem:read",
"filesystem:write",
"network",
"system:read"
],
"ui": {
"icon": "icon.png",
"category": "utility"
},
"settings": [...],
"actions": [
{
"id": "scan_library",
"name": "Scan Game Library",
"description": "Scan for new games",
"icon": "scan"
}
],
"hyperaiVersion": ">=2.0.0",
"platforms": ["win32", "darwin", "linux"]
}
Manifest Field Reference
| Field | Type | Required | Description |
|---|---|---|---|
id | string | ✅ | Unique plugin identifier (kebab-case) |
name | string | ✅ | Display name |
version | string | ✅ | Semantic version (1.0.0) |
description | string | ✅ | Brief description |
author | string | ✅ | Author name/email |
type | string | ✅ | "javascript" or "executable" |
executable | string | For executable | Executable filename |
main | string | For JavaScript | Entry point file |
capabilities | string[] | ⚠️ | Plugin capabilities |
permissions | string[] | ⚠️ | Required permissions |
settings | object[] | ⚠️ | Plugin settings schema |
hyperaiVersion | string | ⚠️ | Minimum HyperAI version |
Permission Types
| Permission | Purpose | Example |
|---|---|---|
filesystem:read | Read files | Read ROM files |
filesystem:write | Write files | Download media |
network | Network access | API calls |
system:read | Read system info | Disk usage |
system:execute | Execute programs | Launch tools |
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