Skip to main content

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 identifier
    • HYPERHQ_AUTH_CHALLENGE - Authentication challenge token
    • HYPERHQ_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):

Fire-and-Forget Methods (immediate acknowledgment):

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 identifier
  • name: string - Display name
  • platform: string - Platform name
  • referenceId: string - External reference ID
  • romsCount: number - Number of ROMs
  • allowedExtensions: 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 identifier
  • name: string - Game name
  • fileName: string - ROM filename
  • romPath: string - Full path to ROM file
  • systemId: string - Parent system ID
  • referenceId: string - External reference ID (optional)
  • developer: string - Game developer
  • publisher: string - Game publisher
  • year: string - Release year
  • genre: string - Game genre
  • rating: string - Game rating
  • enabled: 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 gameLaunched events 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

EventWhen TriggeredData Format
gameLaunchedWhen a game starts{gameId: string, gameName: string, systemId: string, timestamp: number}
gameClosedWhen a game exits{gameId: string, gameName: string, systemId: string, exitCode: number, timestamp: number}
systemChangedWhen active system changes{systemId: string, systemName: string, timestamp: number}
romSelectedWhen user selects a ROM{gameId: string, gameName: string, systemId: string, timestamp: number}
mediaProgressMedia download progress{gameId: string, mediaType: string, progress: number, timestamp: number}
mediaDownloadStartMedia download starts{gameId: string, mediaType: string, timestamp: number}
mediaDownloadFinishMedia download completes{gameId: string, mediaType: string, success: boolean, timestamp: number}
dbConnectedDatabase 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:

  1. Always log locally - Use console logging for debugging during development
  2. Send important logs to HyperHQ - Use Socket.IO to make logs visible in the plugin UI
  3. 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:

// 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

LevelPurposeWhen to UseVisible in Production
debugDetailed diagnostic infoDevelopment, troubleshootingNo (unless enabled)
infoGeneral informationalNormal operations, milestonesYes
warnWarning conditionsDeprecated features, recoverable issuesYes
errorError conditionsFailures, exceptions, critical issuesYes

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:

  1. Stored in HyperHQ's log database
  2. Displayed in the plugin's "Logs" tab in the UI
  3. Filtered by log level
  4. Searchable by message content
  5. Downloadable for offline analysis

This helps you debug issues and monitor plugin behavior in production.

Plugin Settings System

Setting Types

TypeUse ForValidation
textStringspattern, minLength, maxLength
passwordSensitive stringsminLength, maxLength
numberNumbersmin, max, step
booleanTrue/falseNone
selectMultiple choiceoptions array
fileFile pathsextensions filter
directoryFolder pathsNone

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

FieldTypeRequiredDescription
idstringUnique plugin identifier (kebab-case)
namestringDisplay name
versionstringSemantic version (1.0.0)
descriptionstringBrief description
authorstringAuthor name/email
typestring"javascript" or "executable"
executablestringFor executableExecutable filename
mainstringFor JavaScriptEntry point file
capabilitiesstring[]⚠️Plugin capabilities
permissionsstring[]⚠️Required permissions
settingsobject[]⚠️Plugin settings schema
hyperaiVersionstring⚠️Minimum HyperAI version

Permission Types

PermissionPurposeExample
filesystem:readRead filesRead ROM files
filesystem:writeWrite filesDownload media
networkNetwork accessAPI calls
system:readRead system infoDisk usage
system:executeExecute programsLaunch 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:

VariableDescriptionExample
HYPERHQ_PLUGIN_IDYour plugin's ID"my-plugin"
HYPERHQ_AUTH_CHALLENGEAuthentication challenge"abc123..."
HYPERHQ_SOCKET_PORTSocket.IO server port"52789"
PLUGIN_DATA_DIRYour plugin's data folder"C:\ProgramData\..."
PLUGIN_DEBUGDebug 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:

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: