Skip to content

Terminal Commands

Add custom terminal commands that players can run in the in-game terminal.

Basic Command

typescript
import { Command, RegisterCommand } from "@hotbunny/hackhub-content-sdk";

@RegisterCommand
export class PingCommand extends Command {

    CommandName = "myping";
    Description = "Custom ping tool";

    async Run(tools) {
        const args = tools.getArgs();

        if (args.length === 0) {
            tools.printError("Usage: myping <ip>");
            return;
        }

        tools.println(`Pinging ${args[0]}...`);
        await tools.sleep(1000);
        tools.printSuccess("Reply received.");
    }

}

Properties

PropertyTypeRequiredDescription
CommandNamestringYesThe command name players type
DescriptionstringYesHelp text shown in autocomplete
AutocompleteCommandAutoComplete[]NoAutocomplete hints (defaults to [])
PackageNamestringNoPackage name (if installable via apt-get install)

The Run Method

Run(tools) is called when the player executes the command. It can be async or synchronous.

CommandTools API

Output

MethodDescription
tools.println(content)Print a line. Accepts a string, a styled segment, or an array of strings/segments (see Colored output)
tools.printError(text)Print an error message (red)
tools.printWarning(text)Print a warning message (yellow)
tools.printSuccess(text)Print a success message (green)
tools.printInfo(text)Print an info message (cyan)
tools.printColor(text, color)Print text in an arbitrary color (named or hex)
tools.printTable(headers, rows)Print a formatted ASCII table
tools.newLine()Print an empty line
tools.clear()Clear the terminal

Colored output

println accepts more than a plain string. Pass a styled segment or an array of strings/segments to render colored, bold, italic, underlined or dimmed text — all on one line. This is sandbox-safe: you describe the styling, the engine renders it (no raw HTML/JSX).

typescript
// plain string (unchanged)
tools.println("Scanning...");

// a single styled segment
tools.println({ text: "Vulnerability found!", color: "red", bold: true });

// mix strings and segments on one line
tools.println([
    "Status: ",
    { text: "OPEN", color: "green", bold: true },
    "  •  ",
    { text: "high", color: "#ff8800", italic: true },
]);

// shorthand for a single color
tools.printColor("custom colored line", "purple");

A segment supports color (named color or hex), bold, italic, underline and dim. See PrintColor, TextSegment and Printable.

Available since SDK v0.16.0

Styled segments, arrays and printColor require @hotbunny/[email protected] or newer. On older versions println only accepts a plain string. Run npm install @hotbunny/hackhub-content-sdk@latest to update.

Input & Args

MethodReturnsDescription
tools.getArgs()string[]Get command arguments
tools.parseFlags()ParsedFlagsParse args into flags and positional arguments
tools.prompt(input)Promise<string>Show a prompt and wait for user input. Pass a string label, or { label, password?, color? } to mask the input (passwords)

Control Flow

MethodReturnsDescription
tools.sleep(ms)Promise<void>Wait for a duration
tools.exec(command)Promise<void>Execute another terminal command
tools.isLocked()booleanCheck if terminal is locked (waiting for command)
tools.lock()voidLock terminal input
tools.unlock()voidUnlock terminal input

Example: Using parseFlags

typescript
@RegisterCommand
export class ScanTool extends Command {
    CommandName = "modscan";
    Description = "Scan a target";

    Autocomplete = [
        { label: "modscan", type: "STRING" },
        { label: "<target>", type: "IP" },
    ];

    async Run(tools) {
        const { flags, positional } = tools.parseFlags();
        const target = positional[0];

        if (!target) {
            tools.printError("Usage: modscan <ip> [--deep] [--port=80]");
            return;
        }

        tools.println(`Scanning ${target}...`);
        if (flags.deep) tools.println("Deep scan enabled");
        if (flags.port) tools.println(`Targeting port ${flags.port}`);

        await tools.sleep(2000);
        tools.printTable(
            ["Port", "Service", "Status"],
            [
                ["22", "ssh", "OPEN"],
                ["80", "http", "OPEN"],
                ["443", "https", "CLOSED"],
            ]
        );
    }
}

Example: Interactive Prompt

typescript
async Run(tools) {
    const answer = await tools.prompt("Enter target IP:");
    tools.println(`You entered: ${answer}`);
}

Example: Masked Password Prompt

Pass an options object with password: true to hide the typed characters (shown as *), just like the built-in ssh / sudo prompts.

typescript
async Run(tools) {
    const password = await tools.prompt({ label: "Password >", password: true });
    // `password` holds the typed value, but it was never echoed in plaintext.
}

Available since SDK v0.21.0

The object form ({ label, password?, color? }) requires @hotbunny/[email protected] or newer. On older versions prompt only accepts a plain string label.

Autocomplete

Define autocomplete hints to help players with argument types:

typescript
Autocomplete = [
    { label: "modscan", type: "STRING" },
    { label: "<target>", type: "IP" },
    { label: "<port>", type: "PORT" },
];

Autocomplete Types

TypeDescription
STRINGGeneric text
IPIP address
PORTPort number
DOMAINDomain name
MACMAC address
FILEFile path

File Autocomplete Options

When using FILE type, you can add extra options:

typescript
Autocomplete = [
    { label: "mycommand", type: "STRING" },
    { label: "<file>", type: "FILE", extension: "txt" },
    { label: "<folder>", type: "FILE", isFolder: true },
];
PropertyTypeDescription
extensionstring?Filter by file extension
isFolderboolean?Only show folders

Installable Commands

If you set PackageName, the command must be installed via apt-get install before the player can use it:

typescript
@RegisterCommand
export class HackTool extends Command {

    CommandName = "hacktool";
    Description = "Advanced hacking toolkit";
    PackageName = "hacktool";

    async Run(tools) {
        tools.println("HackTool v1.0 loaded.");
    }

}

The player installs it with: apt-get install hacktool

Default (Always-Available) Commands

If you want your command to work like a built-in system command (cd, ls, cat) — available immediately, with no apt-get install step — register it with { default: true }:

typescript
@RegisterCommand({ default: true })
export class HackTool extends Command {

    CommandName = "hacktool";
    Description = "Advanced hacking toolkit";

    async Run(tools) {
        tools.println("HackTool v1.0 loaded.");
    }

}

Command availability

  • @RegisterCommand with a PackageName → installable; the player must run apt-get install <PackageName> (which requires an internet connection) before using it.
  • @RegisterCommand without a PackageName → always available.
  • @RegisterCommand({ default: true }) → always available, even if a PackageName is set. This is the explicit, self-documenting way to ship a built-in-style command.

Available since SDK v0.17.0

The @RegisterCommand({ default: true }) form requires @hotbunny/[email protected] or newer. Run npm install @hotbunny/hackhub-content-sdk@latest to update.

Execution Scope

By default a command runs everywhere — on the player's own machine and inside an active SSH session on a remote host. Use the scope option to restrict where a command may execute:

typescript
@RegisterCommand({ default: true, scope: "local" })
export class LocalOnlyTool extends Command {

    CommandName = "localtool";
    Description = "Only runs on the player's own machine";

    async Run(tools) {
        tools.println("Running locally.");
    }

}
ScopeWhere the command is available
"both"Local machine and remote SSH sessions (default)
"local"Only on the player's own machine — hidden while connected over SSH
"remote"Only inside an active SSH session on a remote host

This is useful when a tool only makes sense in one context — for example a command that manipulates the local filesystem should be "local", while a post-exploitation tool meant to run on a compromised box should be "remote".

When combined with the Files helpers, a "remote" command's filesystem calls resolve against the remote machine. Use Files.isRemoteSession() to branch behaviour explicitly.

Available since SDK v0.18.5

The scope option requires @hotbunny/[email protected] or newer. Run npm install @hotbunny/hackhub-content-sdk@latest to update.

HotBunny Interactive Entertainment Inc.