Terminal Commands
Add custom terminal commands that players can run in the in-game terminal.
Basic Command
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
| Property | Type | Required | Description |
|---|---|---|---|
CommandName | string | Yes | The command name players type |
Description | string | Yes | Help text shown in autocomplete |
Autocomplete | CommandAutoComplete[] | No | Autocomplete hints (defaults to []) |
PackageName | string | No | Package 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
| Method | Description |
|---|---|
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).
// 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
| Method | Returns | Description |
|---|---|---|
tools.getArgs() | string[] | Get command arguments |
tools.parseFlags() | ParsedFlags | Parse 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
| Method | Returns | Description |
|---|---|---|
tools.sleep(ms) | Promise<void> | Wait for a duration |
tools.exec(command) | Promise<void> | Execute another terminal command |
tools.isLocked() | boolean | Check if terminal is locked (waiting for command) |
tools.lock() | void | Lock terminal input |
tools.unlock() | void | Unlock terminal input |
Example: Using parseFlags
@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
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.
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:
Autocomplete = [
{ label: "modscan", type: "STRING" },
{ label: "<target>", type: "IP" },
{ label: "<port>", type: "PORT" },
];Autocomplete Types
| Type | Description |
|---|---|
STRING | Generic text |
IP | IP address |
PORT | Port number |
DOMAIN | Domain name |
MAC | MAC address |
FILE | File path |
File Autocomplete Options
When using FILE type, you can add extra options:
Autocomplete = [
{ label: "mycommand", type: "STRING" },
{ label: "<file>", type: "FILE", extension: "txt" },
{ label: "<folder>", type: "FILE", isFolder: true },
];| Property | Type | Description |
|---|---|---|
extension | string? | Filter by file extension |
isFolder | boolean? | Only show folders |
Installable Commands
If you set PackageName, the command must be installed via apt-get install before the player can use it:
@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 }:
@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
@RegisterCommandwith aPackageName→ installable; the player must runapt-get install <PackageName>(which requires an internet connection) before using it.@RegisterCommandwithout aPackageName→ always available.@RegisterCommand({ default: true })→ always available, even if aPackageNameis 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:
@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.");
}
}| Scope | Where 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.
