omegga 1.18.0 → 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -3
- package/dist/brickadia/server.js +8 -2
- package/dist/brickadia/server.js.map +1 -1
- package/dist/cli/auth.js +18 -27
- package/dist/cli/auth.js.map +1 -1
- package/dist/cli/plugin.js +7 -7
- package/dist/cli/plugin.js.map +1 -1
- package/dist/cli/terminal.js +15 -0
- package/dist/cli/terminal.js.map +1 -1
- package/dist/config/index.js +40 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/types.js +23 -0
- package/dist/config/types.js.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/logger.js +59 -3
- package/dist/logger.js.map +1 -1
- package/dist/main.js +8 -0
- package/dist/main.js.map +1 -1
- package/dist/omegga/plugin/plugin_jsonrpc_stdio.js +9 -1
- package/dist/omegga/plugin/plugin_jsonrpc_stdio.js.map +1 -1
- package/dist/omegga/plugin/plugin_node_safe.js +3 -0
- package/dist/omegga/plugin/plugin_node_safe.js.map +1 -1
- package/dist/softconfig.js +14 -0
- package/dist/softconfig.js.map +1 -1
- package/dist/util/distro.js +39 -0
- package/dist/util/distro.js.map +1 -0
- package/dist/util/logfile.js +188 -0
- package/dist/util/logfile.js.map +1 -0
- package/package.json +3 -2
- package/tools/install_launcher.sh +6 -2
- package/tools/install_steamcmd.sh +58 -14
package/dist/cli/terminal.js
CHANGED
|
@@ -948,6 +948,18 @@ class Terminal {
|
|
|
948
948
|
`${p.displayName.underline} (${p.name.underline}) left.`.brightBlue
|
|
949
949
|
)
|
|
950
950
|
);
|
|
951
|
+
omegga.on(
|
|
952
|
+
"kick",
|
|
953
|
+
(name, kicker, reason) => this.log(
|
|
954
|
+
`${name.underline} was kicked by ${kicker.underline} (${reason})`.brightRed
|
|
955
|
+
)
|
|
956
|
+
);
|
|
957
|
+
omegga.on(
|
|
958
|
+
"ban",
|
|
959
|
+
(name, kicker, reason, duration) => this.log(
|
|
960
|
+
`${name.underline} was banned by ${kicker.underline} (${reason})${duration ? `, expires in ${duration}` : ""}`.brightRed
|
|
961
|
+
)
|
|
962
|
+
);
|
|
951
963
|
omegga.on("chat", (name, message) => {
|
|
952
964
|
const player = omegga.getPlayer(name);
|
|
953
965
|
this.log(
|
|
@@ -1024,9 +1036,11 @@ class Terminal {
|
|
|
1024
1036
|
err("Error stopping server:", e);
|
|
1025
1037
|
}
|
|
1026
1038
|
this.rl.close();
|
|
1039
|
+
logger.default.record("log", [">>".green, "Goodbye"]);
|
|
1027
1040
|
process.exit();
|
|
1028
1041
|
}
|
|
1029
1042
|
async handleLine(line) {
|
|
1043
|
+
if (line.trim()) logger.default.record("log", ["$".grey, line]);
|
|
1030
1044
|
if (line.startsWith("/")) {
|
|
1031
1045
|
const [cmd, ...args] = line.slice(1).split(" ");
|
|
1032
1046
|
const c = COMMANDS.find((c2) => c2.aliases.includes(cmd));
|
|
@@ -1069,6 +1083,7 @@ class Terminal {
|
|
|
1069
1083
|
}
|
|
1070
1084
|
// let readline render a log without interrupting user input
|
|
1071
1085
|
#write(method, args) {
|
|
1086
|
+
logger.default.record(method, args);
|
|
1072
1087
|
if (process.stdout.isTTY) {
|
|
1073
1088
|
process.stdout.clearLine(0);
|
|
1074
1089
|
process.stdout.cursorTo(0);
|
package/dist/cli/terminal.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal.js","sources":["../../src/cli/terminal.ts"],"sourcesContent":["import Logger from '@/logger';\nimport { type OmeggaPlayer } from '@/plugin';\nimport { steamcmdCheckUpdate, steamcmdDownloadGame } from '@/updater';\nimport { readBinaryVersion } from '@omegga/matchers/version';\nimport Omegga from '@omegga/server';\nimport { type IOmeggaOptions } from '@omegga/types';\nimport { sanitize } from '@util/chat';\nimport { checkWsl } from '@util/wsl';\nimport { serverEvents } from '@webserver/backend/events';\nimport readline from 'readline';\nimport { install, update as updatePlugins } from './plugin';\n\ndeclare global {\n interface String {\n brightRed: string;\n brightGreen: string;\n brightYellow: string;\n brightBlue: string;\n brightMagenta: string;\n brightCyan: string;\n brightWhite: string;\n }\n}\n\nlet log: (...args: unknown[]) => void,\n err: (...args: unknown[]) => void,\n warn: (...args: unknown[]) => void,\n info: (...args: unknown[]) => void;\n\ntype TerminalCommand = {\n aliases: string[];\n desc: string;\n descLines?: () => string[];\n fn(this: Terminal, ...args: string[]): void | Promise<void>;\n};\n\nfunction gameCommand(\n aliases: string[],\n desc: string,\n usage: string,\n command: string,\n quoteArgs?: boolean,\n): TerminalCommand {\n return {\n aliases,\n desc,\n fn(...args) {\n if (!this.omegga.started) {\n err('The server is not running');\n return;\n }\n if (!args.length) {\n err('usage:', ('/' + aliases[0] + ' ' + usage).yellow);\n return;\n }\n this.omegga.writeln(\n `${command} ${quoteArgs ? '\"' : ''}${args.join(' ')}${\n quoteArgs ? '\"' : ''\n }`,\n );\n this.rl.prompt(true);\n },\n };\n}\n\n// subcommands of /user, rendered in /help and in the /user usage list\nconst USER_COMMANDS = [\n {\n command: '/user passwd <username>',\n desc: \"reset a user's password\",\n },\n {\n command: '/user rename <username> <newname>',\n desc: 'rename a user',\n },\n {\n command: '/user resetmfa <username>',\n desc: 'clear TOTP, passkeys, and recovery codes',\n },\n {\n command: '/user disable <username>',\n desc: 'prevent a user from signing in',\n },\n {\n command: '/user enable <username>',\n desc: 're-enable a disabled user',\n },\n {\n command: '/user delete <username>',\n desc: 'permanently delete a user',\n },\n {\n command: '/user grantowner <username>',\n desc: 'make a user an owner',\n },\n {\n command: '/user revokeowner <username>',\n desc: 'remove ownership from a user',\n },\n];\n\nconst COMMANDS: TerminalCommand[] = [\n // Brickadia commands\n gameCommand(\n ['kick'],\n 'kick a player',\n '<name|id> [reason]',\n 'Chat.Command /Kick',\n ),\n gameCommand(\n ['ban', 'banish'],\n 'ban a player',\n '<name|id> [time] [reason]',\n 'Ban',\n ),\n gameCommand(\n ['unban', 'pardon'],\n 'unban a player',\n '<name|id>',\n 'Chat.Command /Unban',\n ),\n gameCommand(\n ['grantrole', 'giverole'],\n 'grant a role to a player',\n '<role> <name|id>',\n 'Chat.Command /GrantRole',\n ),\n gameCommand(\n ['revokerole', 'takerole'],\n 'revoke a role from a player',\n '<role> <name|id>',\n 'Chat.Command /RevokeRole',\n ),\n gameCommand(\n ['clearbricks'],\n \"clear a player's bricks\",\n '<name|id>',\n 'Bricks.Clear',\n true,\n ),\n gameCommand(['clearallbricks'], 'clear all bricks', '', 'Bricks.ClearAll'),\n\n // Help command\n {\n aliases: ['help', 'cmds', 'commands'],\n desc: 'list supported commands and their descriptions',\n fn() {\n const maxLen = Math.max(...COMMANDS.map(c => c.aliases[0].length));\n log('Omegga Help Text:\\n');\n log(\n ' Console input not starting with / will be sent in chat from a \"SERVER\" user',\n );\n log(\n ' Console input starting with / will be treated as one of the following commands\\n',\n );\n log(\n '-- Available Omegga commands (type',\n '/command'.yellow.underline,\n 'to run)',\n );\n\n for (const command of COMMANDS.sort((a, b) =>\n a.aliases[0].localeCompare(b.aliases[0]),\n )) {\n const name = command.aliases[0];\n this.log(\n ' ',\n ('/' + name).yellow.underline,\n '-'.padStart(maxLen - name.length + 1),\n command.desc,\n );\n if (command.descLines)\n for (const descLine of command.descLines())\n this.log(' ', ' '.repeat(maxLen + 1), descLine);\n }\n },\n },\n\n // Server controls\n {\n aliases: ['stop', 'exit', 'quit', 'close'],\n desc: 'stop the server and close Omegga',\n fn() {\n return this.shutdown();\n },\n },\n {\n aliases: ['restart'],\n desc: 'trigger a server restart (without announcement or saving)',\n async fn() {\n log('Restarting server...');\n await this.omegga.restartServer();\n },\n },\n {\n aliases: ['version', 'ver'],\n desc: 'print the game version',\n fn() {\n // the running version, and what's actually on disk right now - a steam\n // update can replace the binary underneath a running server\n const running = this.omegga.version;\n const binary = readBinaryVersion(this.omegga.getGameBinaryPath());\n\n if (running > 0) log('Brickadia Server', ('CL' + running).green);\n else warn('Game version is not known yet.');\n\n if (binary == null) return;\n\n if (running > 0 && binary !== running) {\n warn(\n 'Server binary on disk is',\n ('CL' + binary).yellow + '.',\n 'Restart to run the updated build.',\n );\n } else if (!(running > 0)) {\n log('Server binary on disk is', ('CL' + binary).green);\n }\n },\n },\n {\n aliases: ['update'],\n desc: 'try to update the server and restart if started (without announcement or saving',\n async fn() {\n if (!this.omegga.config.__STEAM) {\n err(\n 'This command is only available when the server is installed via SteamCMD',\n );\n return;\n }\n if (this.omegga.stopping || this.omegga.starting) {\n err(\n 'The server is currently starting or stopping. Please wait a moment',\n );\n return;\n }\n const wasStarted = this.omegga.started;\n if (wasStarted) {\n log('Stopping server to update...');\n await this.omegga.stop();\n }\n log('Updating server...');\n try {\n await steamcmdDownloadGame({\n steambeta: this.omegga.config.server?.steambeta,\n steambetaPassword: this.omegga.config.server?.steambetaPassword,\n });\n log('Server updated successfully.');\n } catch (e) {\n err('Error updating server:', e);\n }\n if (wasStarted) {\n log('Starting server...');\n await this.omegga.start();\n }\n },\n },\n {\n aliases: ['updatecheck', 'uc', 'check'],\n desc: 'check for updates. /uc show for details, /uc on|off to toggle auto-update',\n async fn(subcommand?: string) {\n if (!this.omegga.config.__STEAM) {\n err(\n 'This command is only available when the server is installed via SteamCMD',\n );\n return;\n }\n\n const db = this.omegga.webserver?.database;\n\n if (subcommand === 'on' || subcommand === 'off') {\n if (!db) {\n err('Database not available.');\n return;\n }\n const config = await db.getAutoRestartConfig();\n config.autoUpdateEnabled = subcommand === 'on';\n await db.setAutoRestartConfig(config);\n log(\n subcommand === 'on'\n ? 'Auto-update enabled.'.green\n : 'Auto-update disabled.'.yellow,\n );\n return;\n }\n\n log('Checking for updates...');\n const result = await steamcmdCheckUpdate(\n this.omegga.config.server?.steambeta,\n );\n if (!result) {\n err('Failed to check for updates.');\n return;\n }\n const { local, remote, hasUpdate } = result;\n\n if (hasUpdate === true) log('Update available!'.green);\n else if (hasUpdate === false) log('Up to date.'.green);\n else log('Could not determine update status.'.yellow);\n\n if (subcommand === 'show') {\n const branch =\n local.betaKey && local.betaKey !== 'main' ? local.betaKey : 'public';\n const remoteBuild = remote?.branches?.[branch];\n const remotePart = remoteBuild\n ? `BuildID ${remoteBuild.buildid}` +\n (remoteBuild.timeupdated\n ? ` (${new Date(Number(remoteBuild.timeupdated) * 1000).toLocaleString()})`\n : '')\n : 'unknown (private branch?)';\n log(\n [\n ` Local: BuildID ${local.buildId} (${local.installState})`,\n ` Remote: ${remotePart}`,\n ` Branch: ${local.betaKey ?? 'public'}`,\n ].join('\\n'),\n );\n }\n\n if (db) {\n const config = await db.getAutoRestartConfig();\n log(\n config.autoUpdateEnabled\n ? `Auto-update: enabled (every ${config.autoUpdateIntervalMins}m)`\n : 'Auto-update: disabled',\n );\n }\n },\n },\n {\n aliases: ['kill'],\n desc: 'forcefully kill brickadia server process without closing omegga',\n async fn() {\n log('Stopping server...');\n await this.omegga.stop();\n },\n },\n {\n aliases: ['start'],\n desc: 'start the server if it is stopped',\n fn() {\n if (\n !this.omegga ||\n (this.omegga && (this.omegga.starting || this.omegga.started))\n ) {\n err('The server is already running');\n return;\n }\n\n log('Starting server...');\n this.omegga.start();\n },\n },\n {\n aliases: ['cmd'],\n desc: 'run a console command on the Brickadia server. requires /debug for log to show',\n fn(...args) {\n if (!this.omegga.started) {\n err('The server is not running');\n return;\n }\n this.omegga.writeln(args.join(' '));\n },\n },\n {\n aliases: ['debug'],\n desc: 'toggle visibility of brickadia console logs',\n fn() {\n this.options.debug = !this.options.debug;\n log(\n 'Brickadia logs now',\n this.options.debug ? 'visible'.green : 'hidden'.red,\n );\n },\n },\n {\n aliases: ['status'],\n desc: 'display server status information. brick count, online players, etc',\n async fn() {\n if (!this.omegga.started) {\n err('Omegga is not running');\n return;\n }\n const msToTime = (ms: number) => new Date(ms).toISOString().slice(11, 19);\n try {\n const status = await this.omegga.getServerStatus();\n\n log('Server Status');\n this.log(`\n${status.serverName.yellow}\nBricks: ${(status.bricks + '').yellow}\nComponents: ${(status.components + '').yellow}\nUptime: ${msToTime(status.time).yellow}\nPlayers: ${status.players.length === 0 ? 'none'.grey : ''}\n ${status.players\n .map(p => `[${msToTime(p.time).grey}] ${p.name.brightYellow}`)\n .join('\\n ')}\n`);\n } catch {\n err('An error occurred while getting server status');\n }\n },\n },\n\n // plugin stuff\n {\n aliases: ['reload'],\n desc: 'shorthand for /plugins reload',\n async fn() {\n await COMMANDS.find(c => c.aliases.includes('plugins'))?.fn.bind(this)(\n 'reload',\n );\n },\n },\n {\n aliases: ['plugins', 'plugin', 'p'],\n desc: 'manage Omegga plugins',\n descLines: () => [\n '/plugins install <...plugin names>'.yellow + ' installs plugins',\n '/plugins update [...plugin names]'.yellow +\n ' updates all plugins, or those specified',\n '/plugins load <...plugin names>'.yellow +\n ' loads plugins, and enables if they were disabled',\n '/plugins unload <...plugin names>'.yellow + ' unloads plugins',\n '/plugins reload [...plugin names]'.yellow +\n ' reloads all plugins, or those specified',\n '/plugins enable <...plugin names>'.yellow + ' enables plugins',\n '/plugins disable <...plugin names>'.yellow +\n ' disables plugins, and unloads if they were loaded',\n ],\n async fn(subcommand: string, ...args: string[]) {\n if (!this.omegga.pluginLoader) {\n err('Omegga is not using plugins');\n return;\n }\n\n if (!subcommand) {\n const plugins = this.omegga.pluginLoader.plugins;\n if (plugins.length === 0) {\n log('No plugins are installed');\n log(' Install some with', '/plugins install'.yellow.underline);\n } else {\n log('Installed plugins'.yellow.underline);\n for (const plugin of plugins) {\n const name = plugin.getName();\n const docs = plugin.getDocumentation();\n if (plugin.isLoaded()) log(name.cyan.underline + ' (loaded)');\n else if (plugin.isEnabled())\n log(name.green.underline + ' (enabled, not loaded)');\n else log(name.red.underline + ' (disabled)');\n\n log(' ' + (docs?.description ?? 'no description'));\n log((' Author: ' + (docs?.author ?? 'unknown')).gray);\n }\n }\n } else if (subcommand === 'install') {\n // install plugins\n await install(args, { verbose: true });\n log();\n log(\n 'Use',\n '/plugins reload'.yellow,\n 'to reload all plugins and use the installed ones',\n );\n } else if (subcommand === 'update') {\n // update plugins\n await updatePlugins(args, { verbose: true });\n log();\n log(\n 'Use',\n '/plugins reload'.yellow,\n 'to reload all plugins and use the updated ones',\n );\n } else if (subcommand === 'load') {\n if (args.length === 0) {\n log('Please specify at least one plugin to load');\n return;\n }\n\n for (const pluginName of args) {\n const plugin = this.omegga.pluginLoader.plugins.find(\n p => p.getName().toLowerCase() === pluginName.toLowerCase(),\n );\n\n if (!plugin) {\n warn('No plugin by the name', pluginName.cyan);\n continue;\n }\n\n const name = plugin.getName();\n if (plugin.isLoaded()) {\n warn(\n name.cyan,\n 'is already loaded, reload with',\n ('/plugins reload ' + name).yellow,\n );\n return;\n }\n\n if (!plugin.isEnabled()) {\n log('Enabling and loading', name.cyan.underline);\n plugin.setEnabled(true);\n await plugin.load();\n } else {\n log('Loading', name.cyan.underline);\n await plugin.load();\n }\n }\n } else if (subcommand === 'unload') {\n if (args.length === 0) {\n log('Please specify at least one plugin to unload');\n return;\n }\n\n for (const pluginName of args) {\n const plugin = this.omegga.pluginLoader.plugins.find(\n p => p.getName().toLowerCase() === pluginName.toLowerCase(),\n );\n\n if (!plugin) {\n warn('No plugin by the name', pluginName.cyan);\n continue;\n }\n\n const name = plugin.getName();\n if (!plugin.isLoaded()) {\n warn(\n name.cyan,\n 'is already unloaded, load with',\n ('/plugins load ' + name).yellow,\n );\n return;\n }\n\n log('Unloading', name.cyan.underline);\n await plugin.unload();\n }\n } else if (subcommand === 'reload') {\n let plugins = (\n args.length === 0\n ? this.omegga.pluginLoader.plugins\n : this.omegga.pluginLoader.plugins.filter(p =>\n args\n .map(a => a.toLowerCase())\n .includes(p.getName().toLowerCase()),\n )\n ).filter(p => p.isEnabled());\n\n const loaded = plugins.filter(p => p.isLoaded());\n log('Unloading', loaded.length, 'plugins');\n for (const plugin of loaded) await plugin.unload();\n\n if (args.length === 0) {\n log('Scanning for new plugins');\n if (!(await this.omegga.pluginLoader.scan())) {\n err('Could not scan for plugins. All plugins are unloaded');\n return;\n }\n }\n\n plugins = (\n args.length === 0\n ? this.omegga.pluginLoader.plugins\n : this.omegga.pluginLoader.plugins.filter(p =>\n args\n .map(a => a.toLowerCase())\n .includes(p.getName().toLowerCase()),\n )\n ).filter(p => p.isEnabled());\n log('Loading', plugins.length, 'plugins');\n for (const plugin of plugins) await plugin.load();\n\n log('Reloaded', plugins.length, 'plugins');\n } else if (subcommand === 'enable') {\n if (args.length === 0) {\n log('Please specify at least one plugin to enable');\n return;\n }\n\n for (const pluginName of args) {\n const plugin = this.omegga.pluginLoader.plugins.find(\n p => p.getName().toLowerCase() === pluginName.toLowerCase(),\n );\n\n if (!plugin) {\n warn('No plugin by the name', pluginName.cyan);\n continue;\n }\n\n const name = plugin.getName();\n if (plugin.isEnabled()) {\n warn(\n name.cyan,\n 'is already enabled, disable with',\n ('/plugins disable ' + name).yellow,\n );\n return;\n }\n\n log(\n 'Enabling',\n name.cyan.underline,\n '(load with',\n ('/plugins load ' + name).yellow + ')',\n );\n plugin.setEnabled(true);\n }\n } else if (subcommand === 'disable') {\n if (args.length === 0) {\n log('Please specify at least one plugin to disable');\n return;\n }\n\n for (const pluginName of args) {\n const plugin = this.omegga.pluginLoader.plugins.find(\n p => p.getName().toLowerCase() === pluginName.toLowerCase(),\n );\n\n if (!plugin) {\n warn('No plugin by the name', pluginName.cyan);\n continue;\n }\n\n const name = plugin.getName();\n if (!plugin.isEnabled()) {\n warn(\n name.cyan,\n 'is already disabled, enable with',\n ('/plugins enable ' + name).yellow,\n );\n return;\n }\n\n if (plugin.isLoaded()) {\n log('Unloading and disabling', name.cyan.underline);\n await plugin.unload();\n plugin.setEnabled(false);\n } else {\n log('Disabling', name.cyan.underline);\n plugin.setEnabled(false);\n }\n }\n }\n },\n },\n {\n aliases: ['world', 'worlds', 'w'],\n desc: 'Manage worlds on the server',\n descLines: () => ['Type /worlds load a world'],\n async fn(subcommand: string, ...args: string[]) {\n switch (subcommand) {\n default: {\n const commands = [\n {\n command: '/worlds use [world]',\n desc: 'set or remove a world as the default world for startup',\n short: '/w u [world]',\n },\n {\n command: '/worlds load <world> [rev]',\n desc: 'load a world',\n short: '/w load <world> [rev]',\n },\n {\n command: '/worlds saveas <world>',\n desc: 'save the current world as a new world',\n short: '/w sa <world>',\n },\n {\n command: '/worlds save',\n desc: 'save the current world',\n short: '/w s',\n },\n {\n command: '/worlds revisions <world>',\n desc: 'list revisions of a world',\n short: '/w r <world>',\n },\n {\n command: '/worlds new',\n desc: 'create a new world',\n short: '/w new <name> [Plate|Space|Studio|Peaks]',\n },\n ];\n log(\n 'Default world is',\n this.omegga.getNextWorld()?.file?.yellow || 'none'.grey,\n );\n log('Available world commands:');\n for (const { command, desc, short } of commands) {\n this.log(' ', command.yellow, '-', desc, `(${short.yellow})`);\n }\n return;\n }\n\n case 'use':\n case 'u': {\n const world = args[0]?.replace(/\\.brdb$/, '');\n if (world && !this.omegga.worldExists(world)) {\n err(`World \"${world}\" does not exist`);\n return;\n }\n if (this.omegga.setActiveWorld(world || null)) {\n if (world) {\n log(\n `Default world set to ${world.yellow}. It will be loaded next startup.`,\n );\n } else {\n log(\n 'Default world cleared. No world will be loaded on next startup.',\n );\n }\n } else {\n err(`Failed to set default world to ${world.yellow}.`);\n }\n return;\n }\n\n case 'list':\n case 'ls': {\n // list worlds\n const worlds = this.omegga.getWorlds();\n if (worlds.length === 0) {\n log(\n 'No worlds found. Create one with',\n '/worlds saveas <world>'.yellow,\n );\n } else {\n log('Available worlds:');\n const prefix = this.omegga.worldPath + '/';\n for (const world of worlds) {\n this.log(\n ' ',\n world.replace(prefix, '').replace(/\\.brdb$/, '').yellow,\n );\n }\n }\n return;\n }\n case 'load':\n case 'l': {\n if (!this.omegga.started) {\n err('The server is not running');\n return;\n }\n\n // load a world\n if (args.length === 0) {\n err('Please specify a world to load');\n return;\n }\n const worldName = args[0].replace(/\\.brdb$/, '');\n if (!this.omegga.getWorldPath(worldName)) {\n err(`World \"${worldName}\" does not exist`);\n return;\n }\n let revision: number | undefined;\n if (args.length > 1) {\n revision = parseInt(args[1], 10);\n if (isNaN(revision) || revision < 1) {\n err(`Invalid revision number: ${args[1]}`);\n return;\n }\n }\n let loadRes = false;\n if (revision) {\n log(`Loading world ${worldName.yellow} at revision ${revision}...`);\n loadRes = await this.omegga.loadWorldRevision(worldName, revision);\n } else {\n log(`Loading world ${worldName.yellow}...`);\n loadRes = await this.omegga.loadWorld(worldName);\n }\n if (loadRes) {\n log(`World ${worldName.yellow} loaded successfully.`);\n } else {\n err(`Failed to load world ${worldName.yellow}`);\n }\n\n return;\n }\n case 'saveas':\n case 'sa': {\n if (!this.omegga.started) {\n err('The server is not running');\n return;\n }\n\n // save the current world as a new world\n if (args.length === 0) {\n err('Please specify a world to save as');\n return;\n }\n const saveWorldName = args[0].replace(/\\.brdb$/, '');\n if (this.omegga.getWorldPath(saveWorldName)) {\n err(`World \"${saveWorldName}\" already exists`);\n return;\n }\n log(`Saving current world as ${saveWorldName.yellow}...`);\n const saveAsRes = await this.omegga.saveWorldAs(saveWorldName);\n if (saveAsRes) {\n log(`Current world saved as ${saveWorldName.yellow} successfully.`);\n } else {\n err(`Failed to save current world as ${saveWorldName.yellow}`);\n }\n return;\n }\n\n case 'save':\n case 's': {\n if (!this.omegga.started) {\n err('The server is not running');\n return;\n }\n\n // save the current world\n log('Saving current world...');\n const res = await this.omegga.saveWorld();\n if (res) {\n log('Current world saved successfully.');\n } else {\n err('Failed to save the current world');\n }\n return;\n }\n\n case 'revisions':\n case 'r': {\n if (!this.omegga.started) {\n err('The server is not running');\n return;\n }\n\n // list revisions of a world\n if (args.length === 0) {\n err('Please specify a world to list revisions for');\n return;\n }\n const revisionsWorldName = args[0].replace(/\\.brdb$/, '');\n if (!this.omegga.getWorldPath(revisionsWorldName)) {\n err(`World \"${revisionsWorldName}\" does not exist`);\n return;\n }\n log(`Listing revisions for world ${revisionsWorldName.yellow}...`);\n try {\n const revisions =\n await this.omegga.getWorldRevisions(revisionsWorldName);\n if (revisions.length === 0) {\n log('No revisions found for world', revisionsWorldName.yellow);\n } else {\n log('Revisions for world', revisionsWorldName.yellow + ':');\n for (const rev of revisions) {\n this.log(\n ' ',\n `Revision ${rev.index.toString().yellow} - ${\n rev.date\n .toISOString()\n .replace('T', ' ')\n .replace('.000Z', '').blue\n }`,\n `(${rev.note})`.grey,\n );\n }\n }\n } catch (e) {\n err('An error occurred while getting world revisions:', String(e));\n }\n return;\n }\n\n case 'new':\n case 'n': {\n if (!this.omegga.started) {\n err('The server is not running');\n return;\n }\n\n // create a new world\n if (args.length === 0) {\n err('Please specify a name for the new world');\n return;\n }\n const newWorldName = args[0].replace(/\\.brdb$/, '');\n if (this.omegga.getWorldPath(newWorldName)) {\n err(`World \"${newWorldName}\" already exists`);\n return;\n }\n const map = (args[1] ? args[1] : 'Plate') as\n | 'Plate'\n | 'Space'\n | 'Studio'\n | 'Peaks';\n if (!['Plate', 'Space', 'Studio', 'Peaks'].includes(map)) {\n err(`Invalid preset \"${map}\". Valid map are: Plate, Space, Studio`);\n return;\n }\n log(`Creating new world ${newWorldName.yellow}...`);\n this.omegga.createEmptyWorld(newWorldName, map);\n }\n }\n },\n },\n {\n aliases: ['user'],\n desc: 'manage web UI users',\n descLines: () =>\n USER_COMMANDS.map(({ command, desc }) => command.yellow + ' - ' + desc),\n async fn(subcommand?: string, username?: string, newName?: string) {\n const db = this.omegga.webserver?.database;\n if (!db) {\n err('Database not available.');\n return;\n }\n const known = USER_COMMANDS.map(c => c.command.split(' ')[1]);\n if (!subcommand || !known.includes(subcommand)) {\n if (subcommand) err(`Unknown subcommand \"${subcommand}\".`);\n log('Available user commands:');\n for (const { command, desc } of USER_COMMANDS) {\n this.log(' ', command.yellow, '-', desc);\n }\n return;\n }\n if (!username) {\n err(\n `Usage: /user ${subcommand} <username>${\n subcommand === 'rename' ? ' <newname>' : ''\n }`,\n );\n return;\n }\n if (!(await db.userExists(username))) {\n err(`User \"${username}\" does not exist.`);\n return;\n }\n switch (subcommand) {\n case 'passwd': {\n const password = await new Promise<string>(resolve =>\n this.rl.question('New password: ', resolve),\n );\n if (!password) {\n err('Password cannot be empty.');\n return;\n }\n await db.userPasswd(username, password);\n log(`Password updated for \"${username.yellow}\".`);\n break;\n }\n case 'resetmfa': {\n await db.resetUserMfa(username);\n log(\n `MFA cleared for \"${username.yellow}\" (TOTP, passkeys, recovery codes).`,\n );\n break;\n }\n case 'rename': {\n if (!newName) {\n err('Usage: /user rename <username> <newname>');\n return;\n }\n const user = await db.findUserByUsername(username);\n if (!user) {\n err(`User \"${username}\" does not exist.`);\n return;\n }\n try {\n await db.renameUser(user.id, newName);\n } catch (e) {\n err(e instanceof Error ? e.message : 'Error renaming user.');\n return;\n }\n log(`Renamed \"${username.yellow}\" to \"${newName.yellow}\".`);\n break;\n }\n case 'disable': {\n await db.banUser(username, true);\n log(`Disabled \"${username.yellow}\". They can no longer sign in.`);\n break;\n }\n case 'enable': {\n await db.banUser(username, false);\n log(`Enabled \"${username.yellow}\". They can sign in again.`);\n break;\n }\n case 'delete': {\n const confirm = await new Promise<string>(resolve =>\n this.rl.question(\n `Permanently delete \"${username}\"? This cannot be undone. (y/N): `,\n resolve,\n ),\n );\n if (!/^y(es)?$/i.test(confirm.trim())) {\n log('Cancelled.');\n return;\n }\n await db.deleteUser(username);\n log(`Deleted user \"${username.yellow}\".`);\n break;\n }\n case 'grantowner': {\n try {\n await db.grantOwner(username);\n } catch (e) {\n err(e instanceof Error ? e.message : 'Error granting ownership.');\n return;\n }\n log(`Granted ownership to \"${username.yellow}\".`);\n break;\n }\n case 'revokeowner': {\n try {\n await db.revokeOwner(username);\n } catch (e) {\n err(e instanceof Error ? e.message : 'Error revoking ownership.');\n return;\n }\n log(`Revoked ownership from \"${username.yellow}\".`);\n break;\n }\n }\n },\n },\n];\n\n// the terminal wraps omegga and displays console output and handles console input\nexport default class Terminal {\n options: IOmeggaOptions;\n omegga: Omegga;\n rl: readline.Interface;\n // readline closes when stdin ends (ctrl+c in a tty, a detached container) -\n // using it after that throws ERR_USE_AFTER_CLOSE\n #rlClosed = false;\n #shuttingDown = false;\n constructor(omegga: Omegga, options: IOmeggaOptions = {}) {\n this.options = options;\n this.omegga = omegga;\n\n // terminal interface\n this.rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n terminal: true,\n });\n this.rl.setPrompt('> '.brightGreen);\n\n // shortand fns\n log = (...args) => this.log('>>'.green, ...args);\n err = (...args) => this.error('!>'.red, ...args);\n info = (...args) => this.log('?>'.blue, ...args);\n warn = (...args) => this.warn('W>'.yellow, ...args);\n\n // print log line if debug is enabled\n let launcherDone = false;\n let lastDoneMessage = '';\n omegga.on('line', l => {\n // Redact the hosting token only in what we print to the debug log.\n // Brickadia echoes its full command line (including -Token=\"...\") via a\n // LogInit line on stdout; restrict the redaction to engine log lines\n // (LogInit/etc.) so we don't mangle other output (e.g. chat) that might\n // legitimately contain a -Token= string. The raw `l` is still delivered\n // to matchers/plugins elsewhere, so this must not mutate that value.\n if (options.debug)\n this.log(\n '::'.blue,\n /^Log\\w+:/.test(l)\n ? l.replace(/-Token=\"[^\"]*\"/, '-Token=\"REDACTED\"')\n : l,\n );\n else if (!launcherDone) {\n if (l.match(/Update Failed/)) {\n err(l);\n info(\n 'This might be resolved by deleting brickadia and reinstalling:',\n );\n info(' rm -rf ~/.local/share/brickadia-launcher'.grey);\n }\n if (l.startsWith('DOWNLOADING')) {\n // TODO: if someone is proactive enough in the future, you could turn these steps into a legitimate progress bar\n /* Here's some launcher log lines that might be helpful\n Grabbing manifest and checking game files...\n DOWNLOADING MANIFEST | Hold on...\n DOWNLOADING MANIFEST (100%) | Hold on...\n DOWNLOADING MANIFEST (100%) | 0 B/s - 13 KiB / 13 KiB (Unknown time remaining...)\n DOWNLOADING MANIFEST (100%) | Done!\n Checking launcher and downloading version info...\n Installing patches and booting...\n DOWNLOADING BRICKADIA (0%) | Done!\n DOWNLOADING BRICKADIA (100%) | 110 MiB/s - 584 MiB / 584 MiB (1 second remaining...)\n DOWNLOADING BRICKADIA (100%) | Nearly done...\n DOWNLOADING BRICKADIA (100%), INSTALLING BRICKADIA (86%) | Nearly done...\n */\n const isDone = Boolean(l.match(/Done!/)) && l !== lastDoneMessage;\n if (process.stdout.isTTY) {\n process.stdout.clearLine(0);\n process.stdout.cursorTo(0);\n process.stdout.write(l);\n // add a newline on \"Done!\" but prevent newlines on repeat done messages\n if (isDone) process.stdout.write('\\n');\n } else if (isDone) {\n // without a tty there is no cursor to rewind, so print the\n // milestones on their own lines instead of every progress update\n process.stdout.write(l + '\\n');\n }\n if (isDone) lastDoneMessage = l;\n } else if (l.match(/Disabling core dumps./)) {\n launcherDone = true;\n }\n }\n\n // [2025.07.07-20.45.40:310][845]LogBRWorldManager: World successfully loaded.\n if (\n l.match(\n /^\\[[^\\]]+\\]\\[[^\\]]+\\]LogBRWorldManager: World successfully loaded\\.$/,\n )\n ) {\n info('World successfully loaded.');\n }\n });\n\n // print debug events regardless of debug status\n omegga.on('debug', l => this.log('?>'.magenta, l));\n\n // print chat events as players join/leave the server\n omegga.on('join', (p: OmeggaPlayer) =>\n this.log(\n `${p.displayName.underline} (${p.name.underline}) joined.`.brightBlue,\n ),\n );\n omegga.on('leave', (p: OmeggaPlayer) =>\n this.log(\n `${p.displayName.underline} (${p.name.underline}) left.`.brightBlue,\n ),\n );\n omegga.on('chat', (name, message) => {\n const player = omegga.getPlayer(name);\n this.log(\n `${(player?.displayName ?? player?.name ?? name).brightYellow.underline}: ${message}`,\n );\n });\n omegga.on('start', () => {\n const wsl = checkWsl();\n log(\n `Server has started${\n wsl === 1 ? ' in single thread mode due to WSL1' : ''\n }. Type ${'/help'.yellow} for more commands`,\n );\n if (!this.omegga.getNextWorld()) {\n info(\n 'No',\n 'default world'.yellow,\n 'is configured. Run',\n '/worlds use <world>'.yellow,\n 'to load one on startup.',\n );\n }\n\n // check if this is WSL2 and wsl2binds is installed\n if (\n wsl === 2 &&\n this.omegga.pluginLoader &&\n !this.omegga.pluginLoader.plugins.some(p => p.getName() === 'wsl2binds')\n ) {\n warn(\n '####'.yellow,\n `You are running ${'WSL2'.yellow} and you don't have the ${\n 'wsl2binds'.yellow\n } plugin.`,\n );\n warn(\n '####'.yellow,\n `The wsl2binds plugin works as a ${\n 'UDP proxy'.underline\n } between Windows and the WSL2 VM.`,\n );\n warn('####'.yellow, 'Install it by:');\n warn('####'.yellow, ' 1. Closing omegga with', '/stop'.green);\n warn(\n '####'.yellow,\n ' 2. Installing wsl2binds with',\n 'omegga install gh:Meshiest/wsl2binds'.green,\n );\n warn('####'.yellow, ' 3. Restarting', 'omegga'.green);\n }\n });\n omegga.on('mapchange', ({ map }) =>\n log('Map changed to', (map.charAt(0).toUpperCase() + map.slice(1)).green),\n );\n omegga.on('unauthorized', () => {\n err('Server failed authentication check');\n info('You can clear auth tokens with', 'omegga auth -gl'.green);\n info(\n 'This will require you to paste a new hosting token or sign-in again',\n );\n process.exit();\n });\n omegga.on('error', e => err('Server caught unhandled exception:\\n' + e));\n omegga.on('server:stopped', () =>\n log('Server has closed. Type', '/stop'.yellow, 'to close omegga'),\n );\n\n this.rl.on('line', this.handleLine.bind(this));\n this.rl.on('close', () => (this.#rlClosed = true));\n\n // Gracefully shut down server on Ctrl+C and SIGTERM. Listening for SIGINT on\n // the interface stops readline from closing itself on ctrl+c in a tty -\n // every log after that close would throw while trying to redraw the prompt\n this.rl.on('SIGINT', () => this.shutdown());\n process.on('SIGINT', () => this.shutdown());\n process.on('SIGTERM', () => this.shutdown());\n }\n\n // stop the server and close omegga - a second request force closes\n async shutdown() {\n if (this.#shuttingDown) {\n err('Force closing Omegga...');\n process.exit(1);\n }\n this.#shuttingDown = true;\n log('Stopping server...');\n try {\n await this.omegga.stop();\n } catch (e) {\n err('Error stopping server:', e);\n }\n this.rl.close();\n process.exit();\n }\n\n async handleLine(line: string) {\n if (line.startsWith('/')) {\n const [cmd, ...args] = line.slice(1).split(' ');\n const c = COMMANDS.find(c => c.aliases.includes(cmd));\n if (!c) {\n err(\n `unrecognized command /${cmd.underline}. Type /help for more info`\n .red,\n );\n } else {\n try {\n const res = c.fn.bind(this)(...args);\n if (res instanceof Promise) await res;\n } catch (e) {\n err('unhandled terminal error', e);\n }\n }\n } else if (line.trim().length > 0) {\n if (this.omegga.started) {\n // broadcast when the chat does not start with a command\n this.omegga.broadcast(\n `\"[<b><color=\\\\\"ff00ff\\\\\">SERVER</></>]: ${sanitize(line)}\"`,\n );\n if (process.stdout.isTTY) process.stdout.clearLine(0);\n this.log(`[${'SERVER'.brightMagenta.underline}]: ${line}`);\n\n // if omegga is running a webserver - send this message in the chat log\n if (this.omegga.webserver) {\n const user = { name: 'SERVER', id: '', web: true, color: 'ff00ff' };\n // create database entry, send to web ui\n const chatLog = await this.omegga.webserver.database.addChatLog(\n 'msg',\n user,\n line,\n );\n serverEvents.emit('chat', chatLog);\n }\n } else {\n err(\n 'Server is not started yet. type'.red,\n '/help'.yellow,\n 'for more info'.red,\n );\n }\n }\n }\n\n // let readline render a log without interrupting user input\n #write(method: 'log' | 'debug' | 'warn' | 'error', args: unknown[]) {\n // stdout has no cursor controls when omegga is piped or run in a container\n if (process.stdout.isTTY) {\n process.stdout.clearLine(0);\n process.stdout.cursorTo(0);\n }\n console[method](...Logger.timestamped(args));\n // there is no prompt to redraw without a terminal, and its escape codes\n // would just be noise in a piped log\n if (process.stdout.isTTY && !this.#rlClosed) this.rl.prompt(true);\n }\n\n log(...args: unknown[]) {\n this.#write('log', args);\n }\n\n // let readline render a debug log without interrupting user input\n debug(...args: unknown[]) {\n this.#write('debug', args);\n }\n\n // let readline render a warning log without interrupting user input\n warn(...args: unknown[]) {\n this.#write('warn', args);\n }\n\n // let readline render an error log without interrupting user input\n error(...args: unknown[]) {\n this.#write('error', args);\n }\n}\n"],"names":["readBinaryVersion","steamcmdDownloadGame","steamcmdCheckUpdate","plugin","install","updatePlugins","wsl","checkWsl","c","sanitize","serverEvents","Logger"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"terminal.js","sources":["../../src/cli/terminal.ts"],"sourcesContent":["import Logger from '@/logger';\nimport { type OmeggaPlayer } from '@/plugin';\nimport { steamcmdCheckUpdate, steamcmdDownloadGame } from '@/updater';\nimport { readBinaryVersion } from '@omegga/matchers/version';\nimport Omegga from '@omegga/server';\nimport { type IOmeggaOptions } from '@omegga/types';\nimport { sanitize } from '@util/chat';\nimport { checkWsl } from '@util/wsl';\nimport { serverEvents } from '@webserver/backend/events';\nimport readline from 'readline';\nimport { install, update as updatePlugins } from './plugin';\n\ndeclare global {\n interface String {\n brightRed: string;\n brightGreen: string;\n brightYellow: string;\n brightBlue: string;\n brightMagenta: string;\n brightCyan: string;\n brightWhite: string;\n }\n}\n\nlet log: (...args: unknown[]) => void,\n err: (...args: unknown[]) => void,\n warn: (...args: unknown[]) => void,\n info: (...args: unknown[]) => void;\n\ntype TerminalCommand = {\n aliases: string[];\n desc: string;\n descLines?: () => string[];\n fn(this: Terminal, ...args: string[]): void | Promise<void>;\n};\n\nfunction gameCommand(\n aliases: string[],\n desc: string,\n usage: string,\n command: string,\n quoteArgs?: boolean,\n): TerminalCommand {\n return {\n aliases,\n desc,\n fn(...args) {\n if (!this.omegga.started) {\n err('The server is not running');\n return;\n }\n if (!args.length) {\n err('usage:', ('/' + aliases[0] + ' ' + usage).yellow);\n return;\n }\n this.omegga.writeln(\n `${command} ${quoteArgs ? '\"' : ''}${args.join(' ')}${\n quoteArgs ? '\"' : ''\n }`,\n );\n this.rl.prompt(true);\n },\n };\n}\n\n// subcommands of /user, rendered in /help and in the /user usage list\nconst USER_COMMANDS = [\n {\n command: '/user passwd <username>',\n desc: \"reset a user's password\",\n },\n {\n command: '/user rename <username> <newname>',\n desc: 'rename a user',\n },\n {\n command: '/user resetmfa <username>',\n desc: 'clear TOTP, passkeys, and recovery codes',\n },\n {\n command: '/user disable <username>',\n desc: 'prevent a user from signing in',\n },\n {\n command: '/user enable <username>',\n desc: 're-enable a disabled user',\n },\n {\n command: '/user delete <username>',\n desc: 'permanently delete a user',\n },\n {\n command: '/user grantowner <username>',\n desc: 'make a user an owner',\n },\n {\n command: '/user revokeowner <username>',\n desc: 'remove ownership from a user',\n },\n];\n\nconst COMMANDS: TerminalCommand[] = [\n // Brickadia commands\n gameCommand(\n ['kick'],\n 'kick a player',\n '<name|id> [reason]',\n 'Chat.Command /Kick',\n ),\n gameCommand(\n ['ban', 'banish'],\n 'ban a player',\n '<name|id> [time] [reason]',\n 'Ban',\n ),\n gameCommand(\n ['unban', 'pardon'],\n 'unban a player',\n '<name|id>',\n 'Chat.Command /Unban',\n ),\n gameCommand(\n ['grantrole', 'giverole'],\n 'grant a role to a player',\n '<role> <name|id>',\n 'Chat.Command /GrantRole',\n ),\n gameCommand(\n ['revokerole', 'takerole'],\n 'revoke a role from a player',\n '<role> <name|id>',\n 'Chat.Command /RevokeRole',\n ),\n gameCommand(\n ['clearbricks'],\n \"clear a player's bricks\",\n '<name|id>',\n 'Bricks.Clear',\n true,\n ),\n gameCommand(['clearallbricks'], 'clear all bricks', '', 'Bricks.ClearAll'),\n\n // Help command\n {\n aliases: ['help', 'cmds', 'commands'],\n desc: 'list supported commands and their descriptions',\n fn() {\n const maxLen = Math.max(...COMMANDS.map(c => c.aliases[0].length));\n log('Omegga Help Text:\\n');\n log(\n ' Console input not starting with / will be sent in chat from a \"SERVER\" user',\n );\n log(\n ' Console input starting with / will be treated as one of the following commands\\n',\n );\n log(\n '-- Available Omegga commands (type',\n '/command'.yellow.underline,\n 'to run)',\n );\n\n for (const command of COMMANDS.sort((a, b) =>\n a.aliases[0].localeCompare(b.aliases[0]),\n )) {\n const name = command.aliases[0];\n this.log(\n ' ',\n ('/' + name).yellow.underline,\n '-'.padStart(maxLen - name.length + 1),\n command.desc,\n );\n if (command.descLines)\n for (const descLine of command.descLines())\n this.log(' ', ' '.repeat(maxLen + 1), descLine);\n }\n },\n },\n\n // Server controls\n {\n aliases: ['stop', 'exit', 'quit', 'close'],\n desc: 'stop the server and close Omegga',\n fn() {\n return this.shutdown();\n },\n },\n {\n aliases: ['restart'],\n desc: 'trigger a server restart (without announcement or saving)',\n async fn() {\n log('Restarting server...');\n await this.omegga.restartServer();\n },\n },\n {\n aliases: ['version', 'ver'],\n desc: 'print the game version',\n fn() {\n // the running version, and what's actually on disk right now - a steam\n // update can replace the binary underneath a running server\n const running = this.omegga.version;\n const binary = readBinaryVersion(this.omegga.getGameBinaryPath());\n\n if (running > 0) log('Brickadia Server', ('CL' + running).green);\n else warn('Game version is not known yet.');\n\n if (binary == null) return;\n\n if (running > 0 && binary !== running) {\n warn(\n 'Server binary on disk is',\n ('CL' + binary).yellow + '.',\n 'Restart to run the updated build.',\n );\n } else if (!(running > 0)) {\n log('Server binary on disk is', ('CL' + binary).green);\n }\n },\n },\n {\n aliases: ['update'],\n desc: 'try to update the server and restart if started (without announcement or saving',\n async fn() {\n if (!this.omegga.config.__STEAM) {\n err(\n 'This command is only available when the server is installed via SteamCMD',\n );\n return;\n }\n if (this.omegga.stopping || this.omegga.starting) {\n err(\n 'The server is currently starting or stopping. Please wait a moment',\n );\n return;\n }\n const wasStarted = this.omegga.started;\n if (wasStarted) {\n log('Stopping server to update...');\n await this.omegga.stop();\n }\n log('Updating server...');\n try {\n await steamcmdDownloadGame({\n steambeta: this.omegga.config.server?.steambeta,\n steambetaPassword: this.omegga.config.server?.steambetaPassword,\n });\n log('Server updated successfully.');\n } catch (e) {\n err('Error updating server:', e);\n }\n if (wasStarted) {\n log('Starting server...');\n await this.omegga.start();\n }\n },\n },\n {\n aliases: ['updatecheck', 'uc', 'check'],\n desc: 'check for updates. /uc show for details, /uc on|off to toggle auto-update',\n async fn(subcommand?: string) {\n if (!this.omegga.config.__STEAM) {\n err(\n 'This command is only available when the server is installed via SteamCMD',\n );\n return;\n }\n\n const db = this.omegga.webserver?.database;\n\n if (subcommand === 'on' || subcommand === 'off') {\n if (!db) {\n err('Database not available.');\n return;\n }\n const config = await db.getAutoRestartConfig();\n config.autoUpdateEnabled = subcommand === 'on';\n await db.setAutoRestartConfig(config);\n log(\n subcommand === 'on'\n ? 'Auto-update enabled.'.green\n : 'Auto-update disabled.'.yellow,\n );\n return;\n }\n\n log('Checking for updates...');\n const result = await steamcmdCheckUpdate(\n this.omegga.config.server?.steambeta,\n );\n if (!result) {\n err('Failed to check for updates.');\n return;\n }\n const { local, remote, hasUpdate } = result;\n\n if (hasUpdate === true) log('Update available!'.green);\n else if (hasUpdate === false) log('Up to date.'.green);\n else log('Could not determine update status.'.yellow);\n\n if (subcommand === 'show') {\n const branch =\n local.betaKey && local.betaKey !== 'main' ? local.betaKey : 'public';\n const remoteBuild = remote?.branches?.[branch];\n const remotePart = remoteBuild\n ? `BuildID ${remoteBuild.buildid}` +\n (remoteBuild.timeupdated\n ? ` (${new Date(Number(remoteBuild.timeupdated) * 1000).toLocaleString()})`\n : '')\n : 'unknown (private branch?)';\n log(\n [\n ` Local: BuildID ${local.buildId} (${local.installState})`,\n ` Remote: ${remotePart}`,\n ` Branch: ${local.betaKey ?? 'public'}`,\n ].join('\\n'),\n );\n }\n\n if (db) {\n const config = await db.getAutoRestartConfig();\n log(\n config.autoUpdateEnabled\n ? `Auto-update: enabled (every ${config.autoUpdateIntervalMins}m)`\n : 'Auto-update: disabled',\n );\n }\n },\n },\n {\n aliases: ['kill'],\n desc: 'forcefully kill brickadia server process without closing omegga',\n async fn() {\n log('Stopping server...');\n await this.omegga.stop();\n },\n },\n {\n aliases: ['start'],\n desc: 'start the server if it is stopped',\n fn() {\n if (\n !this.omegga ||\n (this.omegga && (this.omegga.starting || this.omegga.started))\n ) {\n err('The server is already running');\n return;\n }\n\n log('Starting server...');\n this.omegga.start();\n },\n },\n {\n aliases: ['cmd'],\n desc: 'run a console command on the Brickadia server. requires /debug for log to show',\n fn(...args) {\n if (!this.omegga.started) {\n err('The server is not running');\n return;\n }\n this.omegga.writeln(args.join(' '));\n },\n },\n {\n aliases: ['debug'],\n desc: 'toggle visibility of brickadia console logs',\n fn() {\n this.options.debug = !this.options.debug;\n log(\n 'Brickadia logs now',\n this.options.debug ? 'visible'.green : 'hidden'.red,\n );\n },\n },\n {\n aliases: ['status'],\n desc: 'display server status information. brick count, online players, etc',\n async fn() {\n if (!this.omegga.started) {\n err('Omegga is not running');\n return;\n }\n const msToTime = (ms: number) => new Date(ms).toISOString().slice(11, 19);\n try {\n const status = await this.omegga.getServerStatus();\n\n log('Server Status');\n this.log(`\n${status.serverName.yellow}\nBricks: ${(status.bricks + '').yellow}\nComponents: ${(status.components + '').yellow}\nUptime: ${msToTime(status.time).yellow}\nPlayers: ${status.players.length === 0 ? 'none'.grey : ''}\n ${status.players\n .map(p => `[${msToTime(p.time).grey}] ${p.name.brightYellow}`)\n .join('\\n ')}\n`);\n } catch {\n err('An error occurred while getting server status');\n }\n },\n },\n\n // plugin stuff\n {\n aliases: ['reload'],\n desc: 'shorthand for /plugins reload',\n async fn() {\n await COMMANDS.find(c => c.aliases.includes('plugins'))?.fn.bind(this)(\n 'reload',\n );\n },\n },\n {\n aliases: ['plugins', 'plugin', 'p'],\n desc: 'manage Omegga plugins',\n descLines: () => [\n '/plugins install <...plugin names>'.yellow + ' installs plugins',\n '/plugins update [...plugin names]'.yellow +\n ' updates all plugins, or those specified',\n '/plugins load <...plugin names>'.yellow +\n ' loads plugins, and enables if they were disabled',\n '/plugins unload <...plugin names>'.yellow + ' unloads plugins',\n '/plugins reload [...plugin names]'.yellow +\n ' reloads all plugins, or those specified',\n '/plugins enable <...plugin names>'.yellow + ' enables plugins',\n '/plugins disable <...plugin names>'.yellow +\n ' disables plugins, and unloads if they were loaded',\n ],\n async fn(subcommand: string, ...args: string[]) {\n if (!this.omegga.pluginLoader) {\n err('Omegga is not using plugins');\n return;\n }\n\n if (!subcommand) {\n const plugins = this.omegga.pluginLoader.plugins;\n if (plugins.length === 0) {\n log('No plugins are installed');\n log(' Install some with', '/plugins install'.yellow.underline);\n } else {\n log('Installed plugins'.yellow.underline);\n for (const plugin of plugins) {\n const name = plugin.getName();\n const docs = plugin.getDocumentation();\n if (plugin.isLoaded()) log(name.cyan.underline + ' (loaded)');\n else if (plugin.isEnabled())\n log(name.green.underline + ' (enabled, not loaded)');\n else log(name.red.underline + ' (disabled)');\n\n log(' ' + (docs?.description ?? 'no description'));\n log((' Author: ' + (docs?.author ?? 'unknown')).gray);\n }\n }\n } else if (subcommand === 'install') {\n // install plugins\n await install(args, { verbose: true });\n log();\n log(\n 'Use',\n '/plugins reload'.yellow,\n 'to reload all plugins and use the installed ones',\n );\n } else if (subcommand === 'update') {\n // update plugins\n await updatePlugins(args, { verbose: true });\n log();\n log(\n 'Use',\n '/plugins reload'.yellow,\n 'to reload all plugins and use the updated ones',\n );\n } else if (subcommand === 'load') {\n if (args.length === 0) {\n log('Please specify at least one plugin to load');\n return;\n }\n\n for (const pluginName of args) {\n const plugin = this.omegga.pluginLoader.plugins.find(\n p => p.getName().toLowerCase() === pluginName.toLowerCase(),\n );\n\n if (!plugin) {\n warn('No plugin by the name', pluginName.cyan);\n continue;\n }\n\n const name = plugin.getName();\n if (plugin.isLoaded()) {\n warn(\n name.cyan,\n 'is already loaded, reload with',\n ('/plugins reload ' + name).yellow,\n );\n return;\n }\n\n if (!plugin.isEnabled()) {\n log('Enabling and loading', name.cyan.underline);\n plugin.setEnabled(true);\n await plugin.load();\n } else {\n log('Loading', name.cyan.underline);\n await plugin.load();\n }\n }\n } else if (subcommand === 'unload') {\n if (args.length === 0) {\n log('Please specify at least one plugin to unload');\n return;\n }\n\n for (const pluginName of args) {\n const plugin = this.omegga.pluginLoader.plugins.find(\n p => p.getName().toLowerCase() === pluginName.toLowerCase(),\n );\n\n if (!plugin) {\n warn('No plugin by the name', pluginName.cyan);\n continue;\n }\n\n const name = plugin.getName();\n if (!plugin.isLoaded()) {\n warn(\n name.cyan,\n 'is already unloaded, load with',\n ('/plugins load ' + name).yellow,\n );\n return;\n }\n\n log('Unloading', name.cyan.underline);\n await plugin.unload();\n }\n } else if (subcommand === 'reload') {\n let plugins = (\n args.length === 0\n ? this.omegga.pluginLoader.plugins\n : this.omegga.pluginLoader.plugins.filter(p =>\n args\n .map(a => a.toLowerCase())\n .includes(p.getName().toLowerCase()),\n )\n ).filter(p => p.isEnabled());\n\n const loaded = plugins.filter(p => p.isLoaded());\n log('Unloading', loaded.length, 'plugins');\n for (const plugin of loaded) await plugin.unload();\n\n if (args.length === 0) {\n log('Scanning for new plugins');\n if (!(await this.omegga.pluginLoader.scan())) {\n err('Could not scan for plugins. All plugins are unloaded');\n return;\n }\n }\n\n plugins = (\n args.length === 0\n ? this.omegga.pluginLoader.plugins\n : this.omegga.pluginLoader.plugins.filter(p =>\n args\n .map(a => a.toLowerCase())\n .includes(p.getName().toLowerCase()),\n )\n ).filter(p => p.isEnabled());\n log('Loading', plugins.length, 'plugins');\n for (const plugin of plugins) await plugin.load();\n\n log('Reloaded', plugins.length, 'plugins');\n } else if (subcommand === 'enable') {\n if (args.length === 0) {\n log('Please specify at least one plugin to enable');\n return;\n }\n\n for (const pluginName of args) {\n const plugin = this.omegga.pluginLoader.plugins.find(\n p => p.getName().toLowerCase() === pluginName.toLowerCase(),\n );\n\n if (!plugin) {\n warn('No plugin by the name', pluginName.cyan);\n continue;\n }\n\n const name = plugin.getName();\n if (plugin.isEnabled()) {\n warn(\n name.cyan,\n 'is already enabled, disable with',\n ('/plugins disable ' + name).yellow,\n );\n return;\n }\n\n log(\n 'Enabling',\n name.cyan.underline,\n '(load with',\n ('/plugins load ' + name).yellow + ')',\n );\n plugin.setEnabled(true);\n }\n } else if (subcommand === 'disable') {\n if (args.length === 0) {\n log('Please specify at least one plugin to disable');\n return;\n }\n\n for (const pluginName of args) {\n const plugin = this.omegga.pluginLoader.plugins.find(\n p => p.getName().toLowerCase() === pluginName.toLowerCase(),\n );\n\n if (!plugin) {\n warn('No plugin by the name', pluginName.cyan);\n continue;\n }\n\n const name = plugin.getName();\n if (!plugin.isEnabled()) {\n warn(\n name.cyan,\n 'is already disabled, enable with',\n ('/plugins enable ' + name).yellow,\n );\n return;\n }\n\n if (plugin.isLoaded()) {\n log('Unloading and disabling', name.cyan.underline);\n await plugin.unload();\n plugin.setEnabled(false);\n } else {\n log('Disabling', name.cyan.underline);\n plugin.setEnabled(false);\n }\n }\n }\n },\n },\n {\n aliases: ['world', 'worlds', 'w'],\n desc: 'Manage worlds on the server',\n descLines: () => ['Type /worlds load a world'],\n async fn(subcommand: string, ...args: string[]) {\n switch (subcommand) {\n default: {\n const commands = [\n {\n command: '/worlds use [world]',\n desc: 'set or remove a world as the default world for startup',\n short: '/w u [world]',\n },\n {\n command: '/worlds load <world> [rev]',\n desc: 'load a world',\n short: '/w load <world> [rev]',\n },\n {\n command: '/worlds saveas <world>',\n desc: 'save the current world as a new world',\n short: '/w sa <world>',\n },\n {\n command: '/worlds save',\n desc: 'save the current world',\n short: '/w s',\n },\n {\n command: '/worlds revisions <world>',\n desc: 'list revisions of a world',\n short: '/w r <world>',\n },\n {\n command: '/worlds new',\n desc: 'create a new world',\n short: '/w new <name> [Plate|Space|Studio|Peaks]',\n },\n ];\n log(\n 'Default world is',\n this.omegga.getNextWorld()?.file?.yellow || 'none'.grey,\n );\n log('Available world commands:');\n for (const { command, desc, short } of commands) {\n this.log(' ', command.yellow, '-', desc, `(${short.yellow})`);\n }\n return;\n }\n\n case 'use':\n case 'u': {\n const world = args[0]?.replace(/\\.brdb$/, '');\n if (world && !this.omegga.worldExists(world)) {\n err(`World \"${world}\" does not exist`);\n return;\n }\n if (this.omegga.setActiveWorld(world || null)) {\n if (world) {\n log(\n `Default world set to ${world.yellow}. It will be loaded next startup.`,\n );\n } else {\n log(\n 'Default world cleared. No world will be loaded on next startup.',\n );\n }\n } else {\n err(`Failed to set default world to ${world.yellow}.`);\n }\n return;\n }\n\n case 'list':\n case 'ls': {\n // list worlds\n const worlds = this.omegga.getWorlds();\n if (worlds.length === 0) {\n log(\n 'No worlds found. Create one with',\n '/worlds saveas <world>'.yellow,\n );\n } else {\n log('Available worlds:');\n const prefix = this.omegga.worldPath + '/';\n for (const world of worlds) {\n this.log(\n ' ',\n world.replace(prefix, '').replace(/\\.brdb$/, '').yellow,\n );\n }\n }\n return;\n }\n case 'load':\n case 'l': {\n if (!this.omegga.started) {\n err('The server is not running');\n return;\n }\n\n // load a world\n if (args.length === 0) {\n err('Please specify a world to load');\n return;\n }\n const worldName = args[0].replace(/\\.brdb$/, '');\n if (!this.omegga.getWorldPath(worldName)) {\n err(`World \"${worldName}\" does not exist`);\n return;\n }\n let revision: number | undefined;\n if (args.length > 1) {\n revision = parseInt(args[1], 10);\n if (isNaN(revision) || revision < 1) {\n err(`Invalid revision number: ${args[1]}`);\n return;\n }\n }\n let loadRes = false;\n if (revision) {\n log(`Loading world ${worldName.yellow} at revision ${revision}...`);\n loadRes = await this.omegga.loadWorldRevision(worldName, revision);\n } else {\n log(`Loading world ${worldName.yellow}...`);\n loadRes = await this.omegga.loadWorld(worldName);\n }\n if (loadRes) {\n log(`World ${worldName.yellow} loaded successfully.`);\n } else {\n err(`Failed to load world ${worldName.yellow}`);\n }\n\n return;\n }\n case 'saveas':\n case 'sa': {\n if (!this.omegga.started) {\n err('The server is not running');\n return;\n }\n\n // save the current world as a new world\n if (args.length === 0) {\n err('Please specify a world to save as');\n return;\n }\n const saveWorldName = args[0].replace(/\\.brdb$/, '');\n if (this.omegga.getWorldPath(saveWorldName)) {\n err(`World \"${saveWorldName}\" already exists`);\n return;\n }\n log(`Saving current world as ${saveWorldName.yellow}...`);\n const saveAsRes = await this.omegga.saveWorldAs(saveWorldName);\n if (saveAsRes) {\n log(`Current world saved as ${saveWorldName.yellow} successfully.`);\n } else {\n err(`Failed to save current world as ${saveWorldName.yellow}`);\n }\n return;\n }\n\n case 'save':\n case 's': {\n if (!this.omegga.started) {\n err('The server is not running');\n return;\n }\n\n // save the current world\n log('Saving current world...');\n const res = await this.omegga.saveWorld();\n if (res) {\n log('Current world saved successfully.');\n } else {\n err('Failed to save the current world');\n }\n return;\n }\n\n case 'revisions':\n case 'r': {\n if (!this.omegga.started) {\n err('The server is not running');\n return;\n }\n\n // list revisions of a world\n if (args.length === 0) {\n err('Please specify a world to list revisions for');\n return;\n }\n const revisionsWorldName = args[0].replace(/\\.brdb$/, '');\n if (!this.omegga.getWorldPath(revisionsWorldName)) {\n err(`World \"${revisionsWorldName}\" does not exist`);\n return;\n }\n log(`Listing revisions for world ${revisionsWorldName.yellow}...`);\n try {\n const revisions =\n await this.omegga.getWorldRevisions(revisionsWorldName);\n if (revisions.length === 0) {\n log('No revisions found for world', revisionsWorldName.yellow);\n } else {\n log('Revisions for world', revisionsWorldName.yellow + ':');\n for (const rev of revisions) {\n this.log(\n ' ',\n `Revision ${rev.index.toString().yellow} - ${\n rev.date\n .toISOString()\n .replace('T', ' ')\n .replace('.000Z', '').blue\n }`,\n `(${rev.note})`.grey,\n );\n }\n }\n } catch (e) {\n err('An error occurred while getting world revisions:', String(e));\n }\n return;\n }\n\n case 'new':\n case 'n': {\n if (!this.omegga.started) {\n err('The server is not running');\n return;\n }\n\n // create a new world\n if (args.length === 0) {\n err('Please specify a name for the new world');\n return;\n }\n const newWorldName = args[0].replace(/\\.brdb$/, '');\n if (this.omegga.getWorldPath(newWorldName)) {\n err(`World \"${newWorldName}\" already exists`);\n return;\n }\n const map = (args[1] ? args[1] : 'Plate') as\n | 'Plate'\n | 'Space'\n | 'Studio'\n | 'Peaks';\n if (!['Plate', 'Space', 'Studio', 'Peaks'].includes(map)) {\n err(`Invalid preset \"${map}\". Valid map are: Plate, Space, Studio`);\n return;\n }\n log(`Creating new world ${newWorldName.yellow}...`);\n this.omegga.createEmptyWorld(newWorldName, map);\n }\n }\n },\n },\n {\n aliases: ['user'],\n desc: 'manage web UI users',\n descLines: () =>\n USER_COMMANDS.map(({ command, desc }) => command.yellow + ' - ' + desc),\n async fn(subcommand?: string, username?: string, newName?: string) {\n const db = this.omegga.webserver?.database;\n if (!db) {\n err('Database not available.');\n return;\n }\n const known = USER_COMMANDS.map(c => c.command.split(' ')[1]);\n if (!subcommand || !known.includes(subcommand)) {\n if (subcommand) err(`Unknown subcommand \"${subcommand}\".`);\n log('Available user commands:');\n for (const { command, desc } of USER_COMMANDS) {\n this.log(' ', command.yellow, '-', desc);\n }\n return;\n }\n if (!username) {\n err(\n `Usage: /user ${subcommand} <username>${\n subcommand === 'rename' ? ' <newname>' : ''\n }`,\n );\n return;\n }\n if (!(await db.userExists(username))) {\n err(`User \"${username}\" does not exist.`);\n return;\n }\n switch (subcommand) {\n case 'passwd': {\n const password = await new Promise<string>(resolve =>\n this.rl.question('New password: ', resolve),\n );\n if (!password) {\n err('Password cannot be empty.');\n return;\n }\n await db.userPasswd(username, password);\n log(`Password updated for \"${username.yellow}\".`);\n break;\n }\n case 'resetmfa': {\n await db.resetUserMfa(username);\n log(\n `MFA cleared for \"${username.yellow}\" (TOTP, passkeys, recovery codes).`,\n );\n break;\n }\n case 'rename': {\n if (!newName) {\n err('Usage: /user rename <username> <newname>');\n return;\n }\n const user = await db.findUserByUsername(username);\n if (!user) {\n err(`User \"${username}\" does not exist.`);\n return;\n }\n try {\n await db.renameUser(user.id, newName);\n } catch (e) {\n err(e instanceof Error ? e.message : 'Error renaming user.');\n return;\n }\n log(`Renamed \"${username.yellow}\" to \"${newName.yellow}\".`);\n break;\n }\n case 'disable': {\n await db.banUser(username, true);\n log(`Disabled \"${username.yellow}\". They can no longer sign in.`);\n break;\n }\n case 'enable': {\n await db.banUser(username, false);\n log(`Enabled \"${username.yellow}\". They can sign in again.`);\n break;\n }\n case 'delete': {\n const confirm = await new Promise<string>(resolve =>\n this.rl.question(\n `Permanently delete \"${username}\"? This cannot be undone. (y/N): `,\n resolve,\n ),\n );\n if (!/^y(es)?$/i.test(confirm.trim())) {\n log('Cancelled.');\n return;\n }\n await db.deleteUser(username);\n log(`Deleted user \"${username.yellow}\".`);\n break;\n }\n case 'grantowner': {\n try {\n await db.grantOwner(username);\n } catch (e) {\n err(e instanceof Error ? e.message : 'Error granting ownership.');\n return;\n }\n log(`Granted ownership to \"${username.yellow}\".`);\n break;\n }\n case 'revokeowner': {\n try {\n await db.revokeOwner(username);\n } catch (e) {\n err(e instanceof Error ? e.message : 'Error revoking ownership.');\n return;\n }\n log(`Revoked ownership from \"${username.yellow}\".`);\n break;\n }\n }\n },\n },\n];\n\n// the terminal wraps omegga and displays console output and handles console input\nexport default class Terminal {\n options: IOmeggaOptions;\n omegga: Omegga;\n rl: readline.Interface;\n // readline closes when stdin ends (ctrl+c in a tty, a detached container) -\n // using it after that throws ERR_USE_AFTER_CLOSE\n #rlClosed = false;\n #shuttingDown = false;\n constructor(omegga: Omegga, options: IOmeggaOptions = {}) {\n this.options = options;\n this.omegga = omegga;\n\n // terminal interface\n this.rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n terminal: true,\n });\n this.rl.setPrompt('> '.brightGreen);\n\n // shortand fns\n log = (...args) => this.log('>>'.green, ...args);\n err = (...args) => this.error('!>'.red, ...args);\n info = (...args) => this.log('?>'.blue, ...args);\n warn = (...args) => this.warn('W>'.yellow, ...args);\n\n // print log line if debug is enabled\n let launcherDone = false;\n let lastDoneMessage = '';\n omegga.on('line', l => {\n // Redact the hosting token only in what we print to the debug log.\n // Brickadia echoes its full command line (including -Token=\"...\") via a\n // LogInit line on stdout; restrict the redaction to engine log lines\n // (LogInit/etc.) so we don't mangle other output (e.g. chat) that might\n // legitimately contain a -Token= string. The raw `l` is still delivered\n // to matchers/plugins elsewhere, so this must not mutate that value.\n if (options.debug)\n this.log(\n '::'.blue,\n /^Log\\w+:/.test(l)\n ? l.replace(/-Token=\"[^\"]*\"/, '-Token=\"REDACTED\"')\n : l,\n );\n else if (!launcherDone) {\n if (l.match(/Update Failed/)) {\n err(l);\n info(\n 'This might be resolved by deleting brickadia and reinstalling:',\n );\n info(' rm -rf ~/.local/share/brickadia-launcher'.grey);\n }\n if (l.startsWith('DOWNLOADING')) {\n // TODO: if someone is proactive enough in the future, you could turn these steps into a legitimate progress bar\n /* Here's some launcher log lines that might be helpful\n Grabbing manifest and checking game files...\n DOWNLOADING MANIFEST | Hold on...\n DOWNLOADING MANIFEST (100%) | Hold on...\n DOWNLOADING MANIFEST (100%) | 0 B/s - 13 KiB / 13 KiB (Unknown time remaining...)\n DOWNLOADING MANIFEST (100%) | Done!\n Checking launcher and downloading version info...\n Installing patches and booting...\n DOWNLOADING BRICKADIA (0%) | Done!\n DOWNLOADING BRICKADIA (100%) | 110 MiB/s - 584 MiB / 584 MiB (1 second remaining...)\n DOWNLOADING BRICKADIA (100%) | Nearly done...\n DOWNLOADING BRICKADIA (100%), INSTALLING BRICKADIA (86%) | Nearly done...\n */\n const isDone = Boolean(l.match(/Done!/)) && l !== lastDoneMessage;\n if (process.stdout.isTTY) {\n process.stdout.clearLine(0);\n process.stdout.cursorTo(0);\n process.stdout.write(l);\n // add a newline on \"Done!\" but prevent newlines on repeat done messages\n if (isDone) process.stdout.write('\\n');\n } else if (isDone) {\n // without a tty there is no cursor to rewind, so print the\n // milestones on their own lines instead of every progress update\n process.stdout.write(l + '\\n');\n }\n if (isDone) lastDoneMessage = l;\n } else if (l.match(/Disabling core dumps./)) {\n launcherDone = true;\n }\n }\n\n // [2025.07.07-20.45.40:310][845]LogBRWorldManager: World successfully loaded.\n if (\n l.match(\n /^\\[[^\\]]+\\]\\[[^\\]]+\\]LogBRWorldManager: World successfully loaded\\.$/,\n )\n ) {\n info('World successfully loaded.');\n }\n });\n\n // print debug events regardless of debug status\n omegga.on('debug', l => this.log('?>'.magenta, l));\n\n // print chat events as players join/leave the server\n omegga.on('join', (p: OmeggaPlayer) =>\n this.log(\n `${p.displayName.underline} (${p.name.underline}) joined.`.brightBlue,\n ),\n );\n omegga.on('leave', (p: OmeggaPlayer) =>\n this.log(\n `${p.displayName.underline} (${p.name.underline}) left.`.brightBlue,\n ),\n );\n // admin actions belong in the console, which is also what logs them\n omegga.on('kick', (name: string, kicker: string, reason: string) =>\n this.log(\n `${name.underline} was kicked by ${kicker.underline} (${reason})`\n .brightRed,\n ),\n );\n omegga.on(\n 'ban',\n (name: string, kicker: string, reason: string, duration?: string) =>\n this.log(\n `${name.underline} was banned by ${kicker.underline} (${reason})${\n duration ? `, expires in ${duration}` : ''\n }`.brightRed,\n ),\n );\n omegga.on('chat', (name, message) => {\n const player = omegga.getPlayer(name);\n this.log(\n `${(player?.displayName ?? player?.name ?? name).brightYellow.underline}: ${message}`,\n );\n });\n omegga.on('start', () => {\n const wsl = checkWsl();\n log(\n `Server has started${\n wsl === 1 ? ' in single thread mode due to WSL1' : ''\n }. Type ${'/help'.yellow} for more commands`,\n );\n if (!this.omegga.getNextWorld()) {\n info(\n 'No',\n 'default world'.yellow,\n 'is configured. Run',\n '/worlds use <world>'.yellow,\n 'to load one on startup.',\n );\n }\n\n // check if this is WSL2 and wsl2binds is installed\n if (\n wsl === 2 &&\n this.omegga.pluginLoader &&\n !this.omegga.pluginLoader.plugins.some(p => p.getName() === 'wsl2binds')\n ) {\n warn(\n '####'.yellow,\n `You are running ${'WSL2'.yellow} and you don't have the ${\n 'wsl2binds'.yellow\n } plugin.`,\n );\n warn(\n '####'.yellow,\n `The wsl2binds plugin works as a ${\n 'UDP proxy'.underline\n } between Windows and the WSL2 VM.`,\n );\n warn('####'.yellow, 'Install it by:');\n warn('####'.yellow, ' 1. Closing omegga with', '/stop'.green);\n warn(\n '####'.yellow,\n ' 2. Installing wsl2binds with',\n 'omegga install gh:Meshiest/wsl2binds'.green,\n );\n warn('####'.yellow, ' 3. Restarting', 'omegga'.green);\n }\n });\n omegga.on('mapchange', ({ map }) =>\n log('Map changed to', (map.charAt(0).toUpperCase() + map.slice(1)).green),\n );\n omegga.on('unauthorized', () => {\n err('Server failed authentication check');\n info('You can clear auth tokens with', 'omegga auth -gl'.green);\n info(\n 'This will require you to paste a new hosting token or sign-in again',\n );\n process.exit();\n });\n omegga.on('error', e => err('Server caught unhandled exception:\\n' + e));\n omegga.on('server:stopped', () =>\n log('Server has closed. Type', '/stop'.yellow, 'to close omegga'),\n );\n\n this.rl.on('line', this.handleLine.bind(this));\n this.rl.on('close', () => (this.#rlClosed = true));\n\n // Gracefully shut down server on Ctrl+C and SIGTERM. Listening for SIGINT on\n // the interface stops readline from closing itself on ctrl+c in a tty -\n // every log after that close would throw while trying to redraw the prompt\n this.rl.on('SIGINT', () => this.shutdown());\n process.on('SIGINT', () => this.shutdown());\n process.on('SIGTERM', () => this.shutdown());\n }\n\n // stop the server and close omegga - a second request force closes\n async shutdown() {\n if (this.#shuttingDown) {\n err('Force closing Omegga...');\n process.exit(1);\n }\n this.#shuttingDown = true;\n log('Stopping server...');\n try {\n await this.omegga.stop();\n } catch (e) {\n err('Error stopping server:', e);\n }\n this.rl.close();\n // file only, pairing the `Hello!` main.ts records at startup\n Logger.record('log', ['>>'.green, 'Goodbye']);\n process.exit();\n }\n\n async handleLine(line: string) {\n // file only: readline echoed it already, and output with no sign of the\n // input that caused it is hard to read\n if (line.trim()) Logger.record('log', ['$'.grey, line]);\n\n if (line.startsWith('/')) {\n const [cmd, ...args] = line.slice(1).split(' ');\n const c = COMMANDS.find(c => c.aliases.includes(cmd));\n if (!c) {\n err(\n `unrecognized command /${cmd.underline}. Type /help for more info`\n .red,\n );\n } else {\n try {\n const res = c.fn.bind(this)(...args);\n if (res instanceof Promise) await res;\n } catch (e) {\n err('unhandled terminal error', e);\n }\n }\n } else if (line.trim().length > 0) {\n if (this.omegga.started) {\n // broadcast when the chat does not start with a command\n this.omegga.broadcast(\n `\"[<b><color=\\\\\"ff00ff\\\\\">SERVER</></>]: ${sanitize(line)}\"`,\n );\n if (process.stdout.isTTY) process.stdout.clearLine(0);\n this.log(`[${'SERVER'.brightMagenta.underline}]: ${line}`);\n\n // if omegga is running a webserver - send this message in the chat log\n if (this.omegga.webserver) {\n const user = { name: 'SERVER', id: '', web: true, color: 'ff00ff' };\n // create database entry, send to web ui\n const chatLog = await this.omegga.webserver.database.addChatLog(\n 'msg',\n user,\n line,\n );\n serverEvents.emit('chat', chatLog);\n }\n } else {\n err(\n 'Server is not started yet. type'.red,\n '/help'.yellow,\n 'for more info'.red,\n );\n }\n }\n }\n\n // let readline render a log without interrupting user input\n #write(method: 'log' | 'debug' | 'warn' | 'error', args: unknown[]) {\n // the log file's only sink for terminal output, including the constructor\n // events that never pass through Logger. recorded before stdout so a closed\n // pipe cannot cost the line, and from raw args because the file times it\n Logger.record(method, args);\n // stdout has no cursor controls when omegga is piped or run in a container\n if (process.stdout.isTTY) {\n process.stdout.clearLine(0);\n process.stdout.cursorTo(0);\n }\n console[method](...Logger.timestamped(args));\n // there is no prompt to redraw without a terminal, and its escape codes\n // would just be noise in a piped log\n if (process.stdout.isTTY && !this.#rlClosed) this.rl.prompt(true);\n }\n\n log(...args: unknown[]) {\n this.#write('log', args);\n }\n\n // let readline render a debug log without interrupting user input\n debug(...args: unknown[]) {\n this.#write('debug', args);\n }\n\n // let readline render a warning log without interrupting user input\n warn(...args: unknown[]) {\n this.#write('warn', args);\n }\n\n // let readline render an error log without interrupting user input\n error(...args: unknown[]) {\n this.#write('error', args);\n }\n}\n"],"names":["readBinaryVersion","steamcmdDownloadGame","steamcmdCheckUpdate","plugin","install","updatePlugins","wsl","checkWsl","Logger","c","sanitize","serverEvents"],"mappings":"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"}
|
package/dist/config/index.js
CHANGED
|
@@ -33,6 +33,11 @@ const defaultConfig = {
|
|
|
33
33
|
port: process.env.BRICKADIA_PORT ? Number(process.env.BRICKADIA_PORT) : 7777,
|
|
34
34
|
map: "Plate"
|
|
35
35
|
},
|
|
36
|
+
logs: {
|
|
37
|
+
enabled: true,
|
|
38
|
+
verbose: false,
|
|
39
|
+
timestamp: softconfig.LOGS_DEFAULTS.timestamp
|
|
40
|
+
},
|
|
36
41
|
metrics: {
|
|
37
42
|
enabled: false,
|
|
38
43
|
bind: softconfig.METRICS_DEFAULTS.bind,
|
|
@@ -55,6 +60,14 @@ function parseEnvSeconds(name) {
|
|
|
55
60
|
logger.default.warnp(`Ignoring ${name.yellow}: ${raw.yellow} is not a duration`);
|
|
56
61
|
return void 0;
|
|
57
62
|
}
|
|
63
|
+
function parseEnvPositive(name, noun) {
|
|
64
|
+
const raw = process.env[name];
|
|
65
|
+
if (!raw) return void 0;
|
|
66
|
+
const value = Number(raw);
|
|
67
|
+
if (Number.isFinite(value) && value > 0) return value;
|
|
68
|
+
logger.default.warnp(`Ignoring ${name.yellow}: ${raw.yellow} is not ${noun}`);
|
|
69
|
+
return void 0;
|
|
70
|
+
}
|
|
58
71
|
function parseEnvUrl(name) {
|
|
59
72
|
const raw = process.env[name]?.trim();
|
|
60
73
|
if (!raw) return void 0;
|
|
@@ -103,6 +116,29 @@ function applyMetricsOverrides(conf) {
|
|
|
103
116
|
if (touched) conf.metrics = { ...conf.metrics, prometheus };
|
|
104
117
|
return conf;
|
|
105
118
|
}
|
|
119
|
+
function applyLogsOverrides(conf) {
|
|
120
|
+
const enabled = env.parseEnvBool("LOGS_ENABLED");
|
|
121
|
+
if (enabled != null) conf.logs = { ...conf.logs, enabled };
|
|
122
|
+
const dir = process.env.LOGS_DIR?.trim();
|
|
123
|
+
if (dir) conf.logs = { ...conf.logs, dir };
|
|
124
|
+
const timestamp = process.env.LOGS_TIMESTAMP?.trim();
|
|
125
|
+
if (timestamp) conf.logs = { ...conf.logs, timestamp };
|
|
126
|
+
const verbose = env.parseEnvBool("LOGS_VERBOSE");
|
|
127
|
+
if (verbose != null) conf.logs = { ...conf.logs, verbose };
|
|
128
|
+
const maxSizeMB = parseEnvPositive("LOGS_MAX_SIZE_MB", "a size in megabytes");
|
|
129
|
+
if (maxSizeMB) conf.logs = { ...conf.logs, maxSizeMB };
|
|
130
|
+
const rawKeep = process.env.LOGS_KEEP_DAYS?.trim();
|
|
131
|
+
if (rawKeep) {
|
|
132
|
+
const keepDays = Number(rawKeep);
|
|
133
|
+
if (Number.isFinite(keepDays) && keepDays >= 0)
|
|
134
|
+
conf.logs = { ...conf.logs, keepDays };
|
|
135
|
+
else
|
|
136
|
+
logger.default.warnp(
|
|
137
|
+
`Ignoring ${"LOGS_KEEP_DAYS".yellow}: ${rawKeep.yellow} is not a number of days`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
return conf;
|
|
141
|
+
}
|
|
106
142
|
const write = writer.default(formats);
|
|
107
143
|
const read = reader.default(formats);
|
|
108
144
|
const find = (dir = ".") => {
|
|
@@ -122,15 +158,18 @@ const index = {
|
|
|
122
158
|
defaultConfig,
|
|
123
159
|
formats,
|
|
124
160
|
applyPortOverrides,
|
|
125
|
-
applyMetricsOverrides
|
|
161
|
+
applyMetricsOverrides,
|
|
162
|
+
applyLogsOverrides
|
|
126
163
|
};
|
|
127
164
|
exports.BrickadiaConfigSchema = types.BrickadiaConfigSchema;
|
|
128
165
|
exports.ConfigSchema = types.ConfigSchema;
|
|
129
166
|
exports.CredentialsSchema = types.CredentialsSchema;
|
|
167
|
+
exports.LogsConfigSchema = types.LogsConfigSchema;
|
|
130
168
|
exports.MetricsConfigSchema = types.MetricsConfigSchema;
|
|
131
169
|
exports.PrometheusConfigSchema = types.PrometheusConfigSchema;
|
|
132
170
|
exports.ServerConfigSchema = types.ServerConfigSchema;
|
|
133
171
|
exports.TerminalConfigSchema = types.TerminalConfigSchema;
|
|
172
|
+
exports.applyLogsOverrides = applyLogsOverrides;
|
|
134
173
|
exports.applyMetricsOverrides = applyMetricsOverrides;
|
|
135
174
|
exports.applyPortOverrides = applyPortOverrides;
|
|
136
175
|
exports.default = index;
|
package/dist/config/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/config/index.ts"],"sourcesContent":["import Logger from '@/logger';\nimport soft, { METRICS_DEFAULTS } from '@/softconfig';\nimport { parseEnvBool } from '@util/env';\nimport 'colors';\nimport Configstore from 'configstore';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport format_js from './formats/format_js';\nimport format_yaml from './formats/format_yml';\nimport reader from './reader';\nimport { type IConfig, type IConfigFormat } from './types';\nimport writer from './writer';\nexport * from './types';\n\nexport const store = new Configstore(\n soft.PROJECT_NAME,\n {\n defaultOmegga: '.',\n legacyBin: '',\n },\n {\n globalConfigPath: true,\n },\n);\n\n// find all format_EXT.js files in the formats path\nconst formats: IConfigFormat[] = [format_js, format_yaml];\n\n// create read/write funcs for the provided formats\nexport const defaultConfig: IConfig = {\n omegga: {\n webui: true,\n port: process.env.OMEGGA_PORT\n ? Number(process.env.OMEGGA_PORT)\n : soft.DEFAULT_PORT,\n https: true,\n },\n server: {\n port: process.env.BRICKADIA_PORT\n ? Number(process.env.BRICKADIA_PORT)\n : 7777,\n map: 'Plate',\n },\n metrics: {\n enabled: false,\n bind: METRICS_DEFAULTS.bind,\n port: METRICS_DEFAULTS.port,\n },\n};\n\n/** parse a port from the environment, warning (and ignoring) when it isn't one */\nfunction parseEnvPort(name: string) {\n const raw = process.env[name];\n if (!raw) return undefined;\n const port = Number(raw);\n if (Number.isInteger(port) && port > 0 && port < 65536) return port;\n Logger.warnp(`Ignoring ${name.yellow}: ${raw.yellow} is not a port`);\n return undefined;\n}\n\n/** parse a positive number of seconds, warning (and ignoring) when it isn't one */\nfunction parseEnvSeconds(name: string) {\n const raw = process.env[name];\n if (!raw) return undefined;\n const seconds = Number(raw);\n if (Number.isFinite(seconds) && seconds > 0) return seconds;\n Logger.warnp(`Ignoring ${name.yellow}: ${raw.yellow} is not a duration`);\n return undefined;\n}\n\n/** parse an http(s) URL, warning (and ignoring) when it isn't one */\nfunction parseEnvUrl(name: string) {\n const raw = process.env[name]?.trim();\n if (!raw) return undefined;\n try {\n const url = new URL(raw);\n if (url.protocol === 'http:' || url.protocol === 'https:') return raw;\n } catch {\n // fall through to the warning\n }\n Logger.warnp(`Ignoring ${name.yellow}: ${raw.yellow} is not an http URL`);\n return undefined;\n}\n\n/**\n * Parse a prometheus label value. The same restriction the config schema\n * applies, repeated here because environment overrides land after the schema\n * has already validated the file.\n */\nfunction parseEnvLabel(name: string) {\n const raw = process.env[name]?.trim();\n if (!raw) return undefined;\n if (/^[A-Za-z0-9_.:-]+$/.test(raw)) return raw;\n Logger.warnp(\n `Ignoring ${name.yellow}: ${raw.yellow} is not a valid label value`,\n );\n return undefined;\n}\n\n/**\n * Apply `OMEGGA_PORT` and `BRICKADIA_PORT` on top of a loaded config. A\n * container's config file lives in a mounted volume that can't be templated,\n * so the environment has to be able to win.\n */\nexport function applyPortOverrides(conf: IConfig): IConfig {\n const omeggaPort = parseEnvPort('OMEGGA_PORT');\n if (omeggaPort) conf.omegga = { ...conf.omegga, port: omeggaPort };\n\n const serverPort = parseEnvPort('BRICKADIA_PORT');\n if (serverPort) conf.server = { ...conf.server, port: serverPort };\n\n return conf;\n}\n\n/**\n * Apply `METRICS_ENABLED`, `METRICS_BIND`, and `METRICS_PORT` on top of a\n * loaded config, for the same reason `applyPortOverrides` exists: a container\n * can't template the config file in its mounted volume.\n */\nexport function applyMetricsOverrides(conf: IConfig): IConfig {\n const enabled = parseEnvBool('METRICS_ENABLED');\n if (enabled != null) conf.metrics = { ...conf.metrics, enabled };\n\n const bind = process.env.METRICS_BIND?.trim();\n if (bind) conf.metrics = { ...conf.metrics, bind };\n\n const port = parseEnvPort('METRICS_PORT');\n if (port) conf.metrics = { ...conf.metrics, port };\n\n const prometheus = { ...conf.metrics?.prometheus };\n let touched = false;\n const set = <K extends keyof typeof prometheus>(\n key: K,\n value: (typeof prometheus)[K] | undefined,\n ) => {\n if (value == null) return;\n prometheus[key] = value;\n touched = true;\n };\n\n set('enabled', parseEnvBool('METRICS_PROMETHEUS_ENABLED'));\n set('url', parseEnvUrl('METRICS_PROMETHEUS_URL'));\n set('instance', parseEnvLabel('METRICS_PROMETHEUS_INSTANCE'));\n set('timeout', parseEnvSeconds('METRICS_PROMETHEUS_TIMEOUT'));\n\n if (touched) conf.metrics = { ...conf.metrics, prometheus };\n\n return conf;\n}\n\n// Writes save data to a file\nexport const write = writer(formats);\n\n// reads save data from a file\nexport const read = reader(formats);\n\n// open config at a specified path\nexport const find = (dir = '.') => {\n // find the first config file matching these config file names\n for (const f of soft.CONFIG_FILENAMES) {\n for (const { extension } of formats) {\n const file = path.join(dir, f + '.' + extension);\n if (fs.existsSync(file)) return file;\n }\n }\n\n return undefined;\n};\n\nexport * from './types';\n\nexport default {\n store,\n write,\n read,\n find,\n defaultConfig,\n formats,\n applyPortOverrides,\n applyMetricsOverrides,\n};\n"],"names":["soft","format_js","format_yaml","METRICS_DEFAULTS","Logger","parseEnvBool","writer","reader"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/config/index.ts"],"sourcesContent":["import Logger from '@/logger';\nimport soft, { LOGS_DEFAULTS, METRICS_DEFAULTS } from '@/softconfig';\nimport { parseEnvBool } from '@util/env';\nimport 'colors';\nimport Configstore from 'configstore';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport format_js from './formats/format_js';\nimport format_yaml from './formats/format_yml';\nimport reader from './reader';\nimport { type IConfig, type IConfigFormat } from './types';\nimport writer from './writer';\nexport * from './types';\n\nexport const store = new Configstore(\n soft.PROJECT_NAME,\n {\n defaultOmegga: '.',\n legacyBin: '',\n },\n {\n globalConfigPath: true,\n },\n);\n\n// find all format_EXT.js files in the formats path\nconst formats: IConfigFormat[] = [format_js, format_yaml];\n\n// create read/write funcs for the provided formats\nexport const defaultConfig: IConfig = {\n omegga: {\n webui: true,\n port: process.env.OMEGGA_PORT\n ? Number(process.env.OMEGGA_PORT)\n : soft.DEFAULT_PORT,\n https: true,\n },\n server: {\n port: process.env.BRICKADIA_PORT\n ? Number(process.env.BRICKADIA_PORT)\n : 7777,\n map: 'Plate',\n },\n logs: {\n enabled: true,\n verbose: false,\n timestamp: LOGS_DEFAULTS.timestamp,\n },\n metrics: {\n enabled: false,\n bind: METRICS_DEFAULTS.bind,\n port: METRICS_DEFAULTS.port,\n },\n};\n\n/** parse a port from the environment, warning (and ignoring) when it isn't one */\nfunction parseEnvPort(name: string) {\n const raw = process.env[name];\n if (!raw) return undefined;\n const port = Number(raw);\n if (Number.isInteger(port) && port > 0 && port < 65536) return port;\n Logger.warnp(`Ignoring ${name.yellow}: ${raw.yellow} is not a port`);\n return undefined;\n}\n\n/** parse a positive number of seconds, warning (and ignoring) when it isn't one */\nfunction parseEnvSeconds(name: string) {\n const raw = process.env[name];\n if (!raw) return undefined;\n const seconds = Number(raw);\n if (Number.isFinite(seconds) && seconds > 0) return seconds;\n Logger.warnp(`Ignoring ${name.yellow}: ${raw.yellow} is not a duration`);\n return undefined;\n}\n\n/** parse a positive number, warning (and ignoring) when it isn't one */\nfunction parseEnvPositive(name: string, noun: string) {\n const raw = process.env[name];\n if (!raw) return undefined;\n const value = Number(raw);\n if (Number.isFinite(value) && value > 0) return value;\n Logger.warnp(`Ignoring ${name.yellow}: ${raw.yellow} is not ${noun}`);\n return undefined;\n}\n\n/** parse an http(s) URL, warning (and ignoring) when it isn't one */\nfunction parseEnvUrl(name: string) {\n const raw = process.env[name]?.trim();\n if (!raw) return undefined;\n try {\n const url = new URL(raw);\n if (url.protocol === 'http:' || url.protocol === 'https:') return raw;\n } catch {\n // fall through to the warning\n }\n Logger.warnp(`Ignoring ${name.yellow}: ${raw.yellow} is not an http URL`);\n return undefined;\n}\n\n/**\n * Parse a prometheus label value. The same restriction the config schema\n * applies, repeated here because environment overrides land after the schema\n * has already validated the file.\n */\nfunction parseEnvLabel(name: string) {\n const raw = process.env[name]?.trim();\n if (!raw) return undefined;\n if (/^[A-Za-z0-9_.:-]+$/.test(raw)) return raw;\n Logger.warnp(\n `Ignoring ${name.yellow}: ${raw.yellow} is not a valid label value`,\n );\n return undefined;\n}\n\n/**\n * Apply `OMEGGA_PORT` and `BRICKADIA_PORT` on top of a loaded config. A\n * container's config file lives in a mounted volume that can't be templated,\n * so the environment has to be able to win.\n */\nexport function applyPortOverrides(conf: IConfig): IConfig {\n const omeggaPort = parseEnvPort('OMEGGA_PORT');\n if (omeggaPort) conf.omegga = { ...conf.omegga, port: omeggaPort };\n\n const serverPort = parseEnvPort('BRICKADIA_PORT');\n if (serverPort) conf.server = { ...conf.server, port: serverPort };\n\n return conf;\n}\n\n/**\n * Apply `METRICS_ENABLED`, `METRICS_BIND`, and `METRICS_PORT` on top of a\n * loaded config, for the same reason `applyPortOverrides` exists: a container\n * can't template the config file in its mounted volume.\n */\nexport function applyMetricsOverrides(conf: IConfig): IConfig {\n const enabled = parseEnvBool('METRICS_ENABLED');\n if (enabled != null) conf.metrics = { ...conf.metrics, enabled };\n\n const bind = process.env.METRICS_BIND?.trim();\n if (bind) conf.metrics = { ...conf.metrics, bind };\n\n const port = parseEnvPort('METRICS_PORT');\n if (port) conf.metrics = { ...conf.metrics, port };\n\n const prometheus = { ...conf.metrics?.prometheus };\n let touched = false;\n const set = <K extends keyof typeof prometheus>(\n key: K,\n value: (typeof prometheus)[K] | undefined,\n ) => {\n if (value == null) return;\n prometheus[key] = value;\n touched = true;\n };\n\n set('enabled', parseEnvBool('METRICS_PROMETHEUS_ENABLED'));\n set('url', parseEnvUrl('METRICS_PROMETHEUS_URL'));\n set('instance', parseEnvLabel('METRICS_PROMETHEUS_INSTANCE'));\n set('timeout', parseEnvSeconds('METRICS_PROMETHEUS_TIMEOUT'));\n\n if (touched) conf.metrics = { ...conf.metrics, prometheus };\n\n return conf;\n}\n\n/**\n * Apply `LOGS_*` on top of a loaded config, for the same reason\n * `applyPortOverrides` exists: a container can't template the config file in\n * its mounted volume, and a panel that tails a fixed path needs `LOGS_DIR`.\n */\nexport function applyLogsOverrides(conf: IConfig): IConfig {\n const enabled = parseEnvBool('LOGS_ENABLED');\n if (enabled != null) conf.logs = { ...conf.logs, enabled };\n\n const dir = process.env.LOGS_DIR?.trim();\n if (dir) conf.logs = { ...conf.logs, dir };\n\n const timestamp = process.env.LOGS_TIMESTAMP?.trim();\n if (timestamp) conf.logs = { ...conf.logs, timestamp };\n\n const verbose = parseEnvBool('LOGS_VERBOSE');\n if (verbose != null) conf.logs = { ...conf.logs, verbose };\n\n const maxSizeMB = parseEnvPositive('LOGS_MAX_SIZE_MB', 'a size in megabytes');\n if (maxSizeMB) conf.logs = { ...conf.logs, maxSizeMB };\n\n // 0 is a valid retention, and parseEnvPositive reads falsy as \"not set\"\n const rawKeep = process.env.LOGS_KEEP_DAYS?.trim();\n if (rawKeep) {\n const keepDays = Number(rawKeep);\n if (Number.isFinite(keepDays) && keepDays >= 0)\n conf.logs = { ...conf.logs, keepDays };\n else\n Logger.warnp(\n `Ignoring ${'LOGS_KEEP_DAYS'.yellow}: ${rawKeep.yellow} is not a number of days`,\n );\n }\n\n return conf;\n}\n\n// Writes save data to a file\nexport const write = writer(formats);\n\n// reads save data from a file\nexport const read = reader(formats);\n\n// open config at a specified path\nexport const find = (dir = '.') => {\n // find the first config file matching these config file names\n for (const f of soft.CONFIG_FILENAMES) {\n for (const { extension } of formats) {\n const file = path.join(dir, f + '.' + extension);\n if (fs.existsSync(file)) return file;\n }\n }\n\n return undefined;\n};\n\nexport * from './types';\n\nexport default {\n store,\n write,\n read,\n find,\n defaultConfig,\n formats,\n applyPortOverrides,\n applyMetricsOverrides,\n applyLogsOverrides,\n};\n"],"names":["soft","format_js","format_yaml","LOGS_DEFAULTS","METRICS_DEFAULTS","Logger","parseEnvBool","writer","reader"],"mappings":"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"}
|
package/dist/config/types.js
CHANGED
|
@@ -32,6 +32,27 @@ const BrickadiaConfigSchema = zod.z.object({
|
|
|
32
32
|
const TerminalConfigSchema = zod.z.object({
|
|
33
33
|
timestamp: zod.z.string().optional()
|
|
34
34
|
});
|
|
35
|
+
const LogsConfigSchema = zod.z.object({
|
|
36
|
+
/** write console output to disk */
|
|
37
|
+
enabled: zod.z.boolean().optional(),
|
|
38
|
+
/** where the files go, relative to the omegga working directory */
|
|
39
|
+
dir: zod.z.string().optional(),
|
|
40
|
+
/** megabytes one day may reach before rolling to a numbered part */
|
|
41
|
+
maxSizeMB: zod.z.number().positive().optional(),
|
|
42
|
+
/** days to keep, counting today. 0 keeps them forever */
|
|
43
|
+
keepDays: zod.z.number().nonnegative().optional(),
|
|
44
|
+
/**
|
|
45
|
+
* keep `--verbose` detail in the file while leaving the console quiet.
|
|
46
|
+
* `--verbose` itself still writes to both.
|
|
47
|
+
*/
|
|
48
|
+
verbose: zod.z.boolean().optional(),
|
|
49
|
+
/**
|
|
50
|
+
* dateformat pattern prefixed to every line, always applied: a file outlives
|
|
51
|
+
* the day it was written. Unlike `terminal.timestamp`, which is off unless
|
|
52
|
+
* it is configured.
|
|
53
|
+
*/
|
|
54
|
+
timestamp: zod.z.string().optional()
|
|
55
|
+
});
|
|
35
56
|
const PrometheusConfigSchema = zod.z.object({
|
|
36
57
|
/** show the metrics dashboards in the web UI */
|
|
37
58
|
enabled: zod.z.boolean().optional(),
|
|
@@ -81,6 +102,7 @@ const ConfigSchema = zod.z.object({
|
|
|
81
102
|
omegga: ServerConfigSchema.optional(),
|
|
82
103
|
server: BrickadiaConfigSchema,
|
|
83
104
|
terminal: TerminalConfigSchema.optional(),
|
|
105
|
+
logs: LogsConfigSchema.optional(),
|
|
84
106
|
metrics: MetricsConfigSchema.optional(),
|
|
85
107
|
credentials: CredentialsSchema.optional(),
|
|
86
108
|
__STEAM: zod.z.boolean().optional()
|
|
@@ -88,6 +110,7 @@ const ConfigSchema = zod.z.object({
|
|
|
88
110
|
exports.BrickadiaConfigSchema = BrickadiaConfigSchema;
|
|
89
111
|
exports.ConfigSchema = ConfigSchema;
|
|
90
112
|
exports.CredentialsSchema = CredentialsSchema;
|
|
113
|
+
exports.LogsConfigSchema = LogsConfigSchema;
|
|
91
114
|
exports.MetricsConfigSchema = MetricsConfigSchema;
|
|
92
115
|
exports.PrometheusConfigSchema = PrometheusConfigSchema;
|
|
93
116
|
exports.ServerConfigSchema = ServerConfigSchema;
|
package/dist/config/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sources":["../../src/config/types.ts"],"sourcesContent":["import { z } from 'zod';\n\nexport const ServerConfigSchema = z.object({\n webui: z.boolean().optional(),\n port: z.number().optional(),\n https: z.boolean().optional(),\n plugins: z.boolean().optional(),\n singleUser: z.boolean().optional(),\n debug: z.boolean().optional(),\n});\n\nexport const BrickadiaConfigSchema = z.object({\n port: z.number(),\n map: z.string().optional(),\n world: z.string().optional(),\n branch: z.string().optional(),\n steambeta: z.string().optional(),\n steambetaPassword: z.string().optional(),\n name: z.string().optional(),\n description: z.string().optional(),\n password: z.string().optional(),\n players: z.number().optional(),\n publiclyListed: z.boolean().optional(),\n welcomeMessage: z.string().optional(),\n authDir: z.string().optional(),\n savedDir: z.string().optional(),\n launchArgs: z.string().optional(),\n remoteFiles: z.boolean().optional(),\n __LOCAL: z.boolean().optional(),\n __LEGACY: z.string().optional(),\n});\n\nexport const TerminalConfigSchema = z.object({\n timestamp: z.string().optional(),\n});\n\n/**\n * Reading metrics back out of a prometheus that scrapes omegga, to render the\n * web UI's metrics dashboards. Independent of the endpoint omegga serves: the\n * scraper may live elsewhere, and the endpoint is useful without a dashboard.\n */\nexport const PrometheusConfigSchema = z.object({\n /** show the metrics dashboards in the web UI */\n enabled: z.boolean().optional(),\n /** base URL of the prometheus HTTP API */\n url: z.string().optional(),\n /**\n * value of the `instance` label identifying this omegga's series. Queries go\n * unfiltered without it, which renders every scraped server's numbers at\n * once. Restricted to label-safe characters because it is interpolated into\n * PromQL, and a prometheus generally scrapes far more than one omegga.\n */\n instance: z\n .string()\n .regex(\n /^[A-Za-z0-9_.:-]+$/,\n 'must contain only letters, numbers, and _ . : -',\n )\n .optional(),\n /** seconds before a query is abandoned */\n timeout: z.number().positive().optional(),\n /** seconds a dashboard's results are reused between requests */\n cacheSeconds: z.number().nonnegative().optional(),\n /** how many days back the range picker may reach */\n retentionDays: z.number().positive().optional(),\n});\n\nexport const MetricsConfigSchema = z.object({\n /** serve a prometheus scrape endpoint */\n enabled: z.boolean().optional(),\n /** address to bind; defaults to loopback since the endpoint is unauthenticated */\n bind: z.string().optional(),\n port: z.number().optional(),\n path: z.string().optional(),\n /** when set, scrapes must send `Authorization: Bearer <token>` */\n token: z.string().optional(),\n /** export the standard `process_` and `nodejs_` metrics for omegga itself */\n defaultMetrics: z.boolean().optional(),\n /** seconds a cached server status may age before a scrape refreshes it */\n statusMaxAge: z.number().optional(),\n /** allow plugins to register their own metrics */\n plugins: z.boolean().optional(),\n /** query a prometheus to power the web UI's metrics dashboards */\n prometheus: PrometheusConfigSchema.optional(),\n});\n\nexport const CredentialsSchema = z.object({\n email: z.string().optional(),\n password: z.string().optional(),\n token: z.string().optional(),\n});\n\nexport const ConfigSchema = z.object({\n omegga: ServerConfigSchema.optional(),\n server: BrickadiaConfigSchema,\n terminal: TerminalConfigSchema.optional(),\n metrics: MetricsConfigSchema.optional(),\n credentials: CredentialsSchema.optional(),\n __STEAM: z.boolean().optional(),\n});\n\nexport type IServerConfig = z.infer<typeof ServerConfigSchema>;\nexport type IBrickadiaConfig = z.infer<typeof BrickadiaConfigSchema>;\nexport type IMetricsConfig = z.infer<typeof MetricsConfigSchema>;\nexport type IPrometheusConfig = z.infer<typeof PrometheusConfigSchema>;\nexport type IConfig = z.infer<typeof ConfigSchema>;\n\nexport type IConfigFormat = {\n extension: string;\n writer: (blob: IConfig) => string;\n} & (\n | {\n encoding: 'string';\n reader: (str: string) => IConfig;\n }\n | {\n encoding: 'buffer';\n reader: (str: Buffer) => IConfig;\n }\n);\n"],"names":["z"],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.js","sources":["../../src/config/types.ts"],"sourcesContent":["import { z } from 'zod';\n\nexport const ServerConfigSchema = z.object({\n webui: z.boolean().optional(),\n port: z.number().optional(),\n https: z.boolean().optional(),\n plugins: z.boolean().optional(),\n singleUser: z.boolean().optional(),\n debug: z.boolean().optional(),\n});\n\nexport const BrickadiaConfigSchema = z.object({\n port: z.number(),\n map: z.string().optional(),\n world: z.string().optional(),\n branch: z.string().optional(),\n steambeta: z.string().optional(),\n steambetaPassword: z.string().optional(),\n name: z.string().optional(),\n description: z.string().optional(),\n password: z.string().optional(),\n players: z.number().optional(),\n publiclyListed: z.boolean().optional(),\n welcomeMessage: z.string().optional(),\n authDir: z.string().optional(),\n savedDir: z.string().optional(),\n launchArgs: z.string().optional(),\n remoteFiles: z.boolean().optional(),\n __LOCAL: z.boolean().optional(),\n __LEGACY: z.string().optional(),\n});\n\nexport const TerminalConfigSchema = z.object({\n timestamp: z.string().optional(),\n});\n\n/**\n * Omegga's console output is mirrored to dated files under the working\n * directory, so a crash that scrolled off screen is still readable. Only what\n * omegga prints is captured; Brickadia writes its own log, and `--debug` is\n * what puts that here too.\n */\nexport const LogsConfigSchema = z.object({\n /** write console output to disk */\n enabled: z.boolean().optional(),\n /** where the files go, relative to the omegga working directory */\n dir: z.string().optional(),\n /** megabytes one day may reach before rolling to a numbered part */\n maxSizeMB: z.number().positive().optional(),\n /** days to keep, counting today. 0 keeps them forever */\n keepDays: z.number().nonnegative().optional(),\n /**\n * keep `--verbose` detail in the file while leaving the console quiet.\n * `--verbose` itself still writes to both.\n */\n verbose: z.boolean().optional(),\n /**\n * dateformat pattern prefixed to every line, always applied: a file outlives\n * the day it was written. Unlike `terminal.timestamp`, which is off unless\n * it is configured.\n */\n timestamp: z.string().optional(),\n});\n\n/**\n * Reading metrics back out of a prometheus that scrapes omegga, to render the\n * web UI's metrics dashboards. Independent of the endpoint omegga serves: the\n * scraper may live elsewhere, and the endpoint is useful without a dashboard.\n */\nexport const PrometheusConfigSchema = z.object({\n /** show the metrics dashboards in the web UI */\n enabled: z.boolean().optional(),\n /** base URL of the prometheus HTTP API */\n url: z.string().optional(),\n /**\n * value of the `instance` label identifying this omegga's series. Queries go\n * unfiltered without it, which renders every scraped server's numbers at\n * once. Restricted to label-safe characters because it is interpolated into\n * PromQL, and a prometheus generally scrapes far more than one omegga.\n */\n instance: z\n .string()\n .regex(\n /^[A-Za-z0-9_.:-]+$/,\n 'must contain only letters, numbers, and _ . : -',\n )\n .optional(),\n /** seconds before a query is abandoned */\n timeout: z.number().positive().optional(),\n /** seconds a dashboard's results are reused between requests */\n cacheSeconds: z.number().nonnegative().optional(),\n /** how many days back the range picker may reach */\n retentionDays: z.number().positive().optional(),\n});\n\nexport const MetricsConfigSchema = z.object({\n /** serve a prometheus scrape endpoint */\n enabled: z.boolean().optional(),\n /** address to bind; defaults to loopback since the endpoint is unauthenticated */\n bind: z.string().optional(),\n port: z.number().optional(),\n path: z.string().optional(),\n /** when set, scrapes must send `Authorization: Bearer <token>` */\n token: z.string().optional(),\n /** export the standard `process_` and `nodejs_` metrics for omegga itself */\n defaultMetrics: z.boolean().optional(),\n /** seconds a cached server status may age before a scrape refreshes it */\n statusMaxAge: z.number().optional(),\n /** allow plugins to register their own metrics */\n plugins: z.boolean().optional(),\n /** query a prometheus to power the web UI's metrics dashboards */\n prometheus: PrometheusConfigSchema.optional(),\n});\n\nexport const CredentialsSchema = z.object({\n email: z.string().optional(),\n password: z.string().optional(),\n token: z.string().optional(),\n});\n\nexport const ConfigSchema = z.object({\n omegga: ServerConfigSchema.optional(),\n server: BrickadiaConfigSchema,\n terminal: TerminalConfigSchema.optional(),\n logs: LogsConfigSchema.optional(),\n metrics: MetricsConfigSchema.optional(),\n credentials: CredentialsSchema.optional(),\n __STEAM: z.boolean().optional(),\n});\n\nexport type IServerConfig = z.infer<typeof ServerConfigSchema>;\nexport type IBrickadiaConfig = z.infer<typeof BrickadiaConfigSchema>;\nexport type ILogsConfig = z.infer<typeof LogsConfigSchema>;\nexport type IMetricsConfig = z.infer<typeof MetricsConfigSchema>;\nexport type IPrometheusConfig = z.infer<typeof PrometheusConfigSchema>;\nexport type IConfig = z.infer<typeof ConfigSchema>;\n\nexport type IConfigFormat = {\n extension: string;\n writer: (blob: IConfig) => string;\n} & (\n | {\n encoding: 'string';\n reader: (str: string) => IConfig;\n }\n | {\n encoding: 'buffer';\n reader: (str: Buffer) => IConfig;\n }\n);\n"],"names":["z"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ const brickadia = { Server: server$1, config };
|
|
|
12
12
|
exports.BrickadiaConfigSchema = types.BrickadiaConfigSchema;
|
|
13
13
|
exports.ConfigSchema = types.ConfigSchema;
|
|
14
14
|
exports.CredentialsSchema = types.CredentialsSchema;
|
|
15
|
+
exports.LogsConfigSchema = types.LogsConfigSchema;
|
|
15
16
|
exports.MetricsConfigSchema = types.MetricsConfigSchema;
|
|
16
17
|
exports.PrometheusConfigSchema = types.PrometheusConfigSchema;
|
|
17
18
|
exports.ServerConfigSchema = types.ServerConfigSchema;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["/*\n Library module, includes all available resources\n*/\n\nexport * from '@omegga/types';\nexport * from '@config/types';\nexport * from '@brickadia/types';\n\nexport * from './plugin';\n\n/** BrickadiaServer - manages the server child process */\nimport * as Server from '@brickadia/server';\n/** config writer for brickadia */\nimport * as brConfig from '@brickadia/config';\n\nexport const brickadia = { Server, config: brConfig };\n\n/** the actual object can be required too */\nimport Omegga from '@omegga/server';\nexport default Omegga;\nexport { Omegga };\n\n/** brickadia server wrapper, log parser, and plugin runner */\nexport * as omegga from './omegga';\n\n/** utility functions */\nexport * as util from '@util';\n\n/** config */\nexport * as config from '@config';\n"],"names":["Server"],"mappings":";;;;;;;;;;AAeO,MAAM,YAAY,EAAA,QAAEA,UAAQ,OAAQ
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["/*\n Library module, includes all available resources\n*/\n\nexport * from '@omegga/types';\nexport * from '@config/types';\nexport * from '@brickadia/types';\n\nexport * from './plugin';\n\n/** BrickadiaServer - manages the server child process */\nimport * as Server from '@brickadia/server';\n/** config writer for brickadia */\nimport * as brConfig from '@brickadia/config';\n\nexport const brickadia = { Server, config: brConfig };\n\n/** the actual object can be required too */\nimport Omegga from '@omegga/server';\nexport default Omegga;\nexport { Omegga };\n\n/** brickadia server wrapper, log parser, and plugin runner */\nexport * as omegga from './omegga';\n\n/** utility functions */\nexport * as util from '@util';\n\n/** config */\nexport * as config from '@config';\n"],"names":["Server"],"mappings":";;;;;;;;;;AAeO,MAAM,YAAY,EAAA,QAAEA,UAAQ,OAAQ;;;;;;;;;;;;;;;;;"}
|