omegga 1.11.0 → 1.12.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.
Files changed (122) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/brickadia/config.js +1 -1
  3. package/dist/brickadia/config.js.map +1 -1
  4. package/dist/brickadia/server.js +18 -18
  5. package/dist/brickadia/server.js.map +1 -1
  6. package/dist/cli/auth.js +1 -1
  7. package/dist/cli/auth.js.map +1 -1
  8. package/dist/cli/plugin.js +47 -22
  9. package/dist/cli/plugin.js.map +1 -1
  10. package/dist/cli/terminal.js +137 -28
  11. package/dist/cli/terminal.js.map +1 -1
  12. package/dist/config/index.js.map +1 -1
  13. package/dist/config/reader.js.map +1 -1
  14. package/dist/config/writer.js +1 -1
  15. package/dist/config/writer.js.map +1 -1
  16. package/dist/logger.js.map +1 -1
  17. package/dist/main.js +24 -21
  18. package/dist/main.js.map +1 -1
  19. package/dist/omegga/auth.js.map +1 -1
  20. package/dist/omegga/commandInjector.js +88 -77
  21. package/dist/omegga/commandInjector.js.map +1 -1
  22. package/dist/omegga/logWrangler.js +9 -10
  23. package/dist/omegga/logWrangler.js.map +1 -1
  24. package/dist/omegga/matchers/auth.js +3 -3
  25. package/dist/omegga/matchers/auth.js.map +1 -1
  26. package/dist/omegga/matchers/chat.js +4 -4
  27. package/dist/omegga/matchers/chat.js.map +1 -1
  28. package/dist/omegga/matchers/command.js +4 -4
  29. package/dist/omegga/matchers/command.js.map +1 -1
  30. package/dist/omegga/matchers/exit.js +1 -1
  31. package/dist/omegga/matchers/exit.js.map +1 -1
  32. package/dist/omegga/matchers/index.js.map +1 -1
  33. package/dist/omegga/matchers/init.js +1 -1
  34. package/dist/omegga/matchers/init.js.map +1 -1
  35. package/dist/omegga/matchers/interact.js +17 -21
  36. package/dist/omegga/matchers/interact.js.map +1 -1
  37. package/dist/omegga/matchers/join.js +7 -7
  38. package/dist/omegga/matchers/join.js.map +1 -1
  39. package/dist/omegga/matchers/leave.js +4 -3
  40. package/dist/omegga/matchers/leave.js.map +1 -1
  41. package/dist/omegga/matchers/mapChange.js +1 -1
  42. package/dist/omegga/matchers/mapChange.js.map +1 -1
  43. package/dist/omegga/matchers/minigameJoin.js +2 -2
  44. package/dist/omegga/matchers/minigameJoin.js.map +1 -1
  45. package/dist/omegga/matchers/version.js.map +1 -1
  46. package/dist/omegga/matchers/wire.js +2 -2
  47. package/dist/omegga/matchers/wire.js.map +1 -1
  48. package/dist/omegga/player.js +91 -88
  49. package/dist/omegga/player.js.map +1 -1
  50. package/dist/omegga/plugin/interface.js +19 -10
  51. package/dist/omegga/plugin/interface.js.map +1 -1
  52. package/dist/omegga/plugin/plugin_jsonrpc_stdio.js +88 -82
  53. package/dist/omegga/plugin/plugin_jsonrpc_stdio.js.map +1 -1
  54. package/dist/omegga/plugin/plugin_node_safe/proxyOmegga.js +75 -70
  55. package/dist/omegga/plugin/plugin_node_safe/proxyOmegga.js.map +1 -1
  56. package/dist/omegga/plugin/plugin_node_safe.js +30 -24
  57. package/dist/omegga/plugin/plugin_node_safe.js.map +1 -1
  58. package/dist/omegga/plugin/plugin_node_unsafe.js +9 -4
  59. package/dist/omegga/plugin/plugin_node_unsafe.js.map +1 -1
  60. package/dist/omegga/plugin.js +7 -7
  61. package/dist/omegga/plugin.js.map +1 -1
  62. package/dist/omegga/server.js +41 -38
  63. package/dist/omegga/server.js.map +1 -1
  64. package/dist/omegga/wrapper.js.map +1 -1
  65. package/dist/plugin.js.map +1 -1
  66. package/dist/updater/steam.js +4 -3
  67. package/dist/updater/steam.js.map +1 -1
  68. package/dist/util/brick/checkBounds.js.map +1 -1
  69. package/dist/util/brick/displayName.js +1 -1
  70. package/dist/util/brick/displayName.js.map +1 -1
  71. package/dist/util/brick/getBounds.js +4 -3
  72. package/dist/util/brick/getBounds.js.map +1 -1
  73. package/dist/util/brick/getBrickSize.js +2 -2
  74. package/dist/util/brick/getBrickSize.js.map +1 -1
  75. package/dist/util/brick/getScaleAxis.js +1 -1
  76. package/dist/util/brick/getScaleAxis.js.map +1 -1
  77. package/dist/util/brick/rotate.js.map +1 -1
  78. package/dist/util/brick/setOwnership.js.map +1 -1
  79. package/dist/util/color.js +31 -15
  80. package/dist/util/color.js.map +1 -1
  81. package/dist/util/index.js.map +1 -1
  82. package/dist/version.js.map +1 -1
  83. package/dist/webserver/backend/database.js +45 -3
  84. package/dist/webserver/backend/database.js.map +1 -1
  85. package/dist/webserver/backend/index.js +6 -4
  86. package/dist/webserver/backend/index.js.map +1 -1
  87. package/dist/webserver/backend/metrics.js +1 -0
  88. package/dist/webserver/backend/metrics.js.map +1 -1
  89. package/dist/webserver/backend/permissions.js.map +1 -1
  90. package/dist/webserver/backend/router/plugin.js +6 -6
  91. package/dist/webserver/backend/router/plugin.js.map +1 -1
  92. package/dist/webserver/backend/router/user.js +53 -0
  93. package/dist/webserver/backend/router/user.js.map +1 -1
  94. package/dist/webserver/backend/router/world.js +1 -1
  95. package/dist/webserver/backend/router/world.js.map +1 -1
  96. package/dist/webserver/backend/scopes.js +6 -0
  97. package/dist/webserver/backend/scopes.js.map +1 -1
  98. package/dist/worker.js +30 -28
  99. package/dist/worker.js.map +1 -1
  100. package/eslint.config.mjs +109 -38
  101. package/package.json +11 -9
  102. package/public/app.html +135 -5
  103. package/public/assets/IconWorldPlus-Bzt4-NTF.js +2 -0
  104. package/public/assets/IconWorldPlus-Bzt4-NTF.js.map +1 -0
  105. package/public/assets/app-kNLgsOPx.js +7 -0
  106. package/public/assets/app-kNLgsOPx.js.map +1 -0
  107. package/public/assets/{index-5EkjXNMi.js → index-QURAMkP4.js} +2 -2
  108. package/public/assets/{index-5EkjXNMi.js.map → index-QURAMkP4.js.map} +1 -1
  109. package/public/assets/shared/{@components-VkA0DIz6.js → @components--CUpATJw.js} +2 -2
  110. package/public/assets/shared/@components--CUpATJw.js.map +1 -0
  111. package/public/assets/shared/{@hooks-CiJaUL6_.js → @hooks-CBm94p6G.js} +2 -2
  112. package/public/assets/shared/{@hooks-CiJaUL6_.js.map → @hooks-CBm94p6G.js.map} +1 -1
  113. package/public/assets/shared/@tabler_icons-react-B98xAreb.js +3 -0
  114. package/public/assets/shared/{@tabler_icons-react-B4130bhe.js.map → @tabler_icons-react-B98xAreb.js.map} +1 -1
  115. package/public/auth.html +71 -1
  116. package/templates/safe-ts/omegga.d.ts +135 -48
  117. package/public/assets/IconWorldPlus-DKSZjbqh.js +0 -2
  118. package/public/assets/IconWorldPlus-DKSZjbqh.js.map +0 -1
  119. package/public/assets/app-DvPQUQnE.js +0 -7
  120. package/public/assets/app-DvPQUQnE.js.map +0 -1
  121. package/public/assets/shared/@components-VkA0DIz6.js.map +0 -1
  122. package/public/assets/shared/@tabler_icons-react-B4130bhe.js +0 -3
package/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  ## Latest
4
4
 
5
+ ## 1.12.0 - 2026-08-05
6
+
7
+ Web UI user management (ownership granting, self-rename, new `/user` terminal subcommands) and a typing/lint cleanup across the codebase.
8
+
9
+ - Web UI: Owners can grant ownership to another user from the user inspector (confirms with password, and a TOTP code when MFA is enabled)
10
+ - Web UI: Users can rename themselves from the account view with the new `user.rename` permission; new names are checked case-insensitively so users can't take another user's name
11
+ - Terminal: `/user` gains `rename`, `disable`, `enable`, `delete`, `grantowner`, and `revokeowner` subcommands; running `/user` alone lists usage like `/worlds`
12
+ - Terminal: Revoking ownership is console-only and refuses to demote the last remaining owner
13
+ - Fix double indentation of the player list in `/status`
14
+ - Fix type errors across the entire codebase; both the backend and frontend tsconfigs typecheck strict
15
+ - Set up eslint and fix all lints
16
+ - Build: Remove the legacy `build:old` (tspc) script and its `ts-patch`/`typescript-transform-paths` dependencies, and fix `npm run dts`
17
+ - CI: Run eslint and both typechecks on every push
18
+
5
19
  ## 1.11.0 - 2026-07-19
6
20
 
7
21
  - Add `/version` terminal command, warning when the binary on disk differs from the running build
@@ -22,7 +22,7 @@ ServerDescription=${config.server.description || ""}
22
22
  ServerPassword=${config.server.password || ""}
23
23
  MaxPlayers=${config.server.players || 20}
24
24
  bPubliclyListed=${config.server.publiclyListed ?? true ? "True" : "False"}
25
- WelcomeMessage="${config.server.welcomeMessage?.replace(/"/g, '"').replace(/\\/g, "\\\\") || ""}"`
25
+ WelcomeMessage="${config.server.welcomeMessage?.replace(/\\/g, "\\\\").replace(/"/g, '\\"') || ""}"`
26
26
  );
27
27
  }
28
28
  function read() {
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sources":["../../src/brickadia/config.ts"],"sourcesContent":["import { IConfig } from '@config/types';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport * as file from '../util/file';\n\nimport { CONFIG_SAVED_DIR } from '@/softconfig';\nconst CONFIG_PATH = 'Config/LinuxServer';\nconst SERVER_SETTINGS = 'GameUserSettings.ini';\n\n// Function that writes config\nexport function write(serverPath: string, config: IConfig) {\n const configPath = path.join(\n serverPath,\n config.server.savedDir ?? CONFIG_SAVED_DIR,\n CONFIG_PATH,\n );\n const settingsPath = path.join(configPath, SERVER_SETTINGS);\n\n file.mkdir(configPath);\n\n fs.writeFileSync(\n settingsPath,\n `[Server__BP_ServerSettings_General_C BP_ServerSettings_General_C]\nServerName=${config.server.name || ''}\nServerDescription=${config.server.description || ''}\nServerPassword=${config.server.password || ''}\nMaxPlayers=${config.server.players || 20}\nbPubliclyListed=${(config.server.publiclyListed ?? true) ? 'True' : 'False'}\nWelcomeMessage=\"${config.server.welcomeMessage?.replace(/\"/g, '\\\"').replace(/\\\\/g, '\\\\\\\\') || ''}\"`,\n );\n\n /*\n[Server__BP_ServerSettings_General_C BP_ServerSettings_General_C]\nServerTypeIndex2=1\nServerName=\nServerDescription=\nServerPassword=\nMaxPlayers=30\nbPubliclyListed=True\nWelcomeMessage=\"<color=\\\"0055ff\\\">Welcome to <b>{2}</>, {1}.</><>a\"\nbGlobalRulesetSelfDamage=True\nbGlobalRulesetPhysicsDamage=False\nbGlobalRulesetEnableCameraBlockedEffects=False\nUploadTimeout=5.000000\nMaxPrefabBricks=1000\nMaxPrefabComponents=20\nMaxPrefabSize=(X=1000,Y=1000,Z=1000)\nMaxPhysicsObjects=2000\nMaxPhysicsObjectsPerUser=50\nMaxPlacementBricks=2000\nMaxPlacementEntities=10\nbEnforceApplicatorLimits=False\nTemplatePlacementTimeout=2.000000\nMaxSelectionBoxSize=(X=1000,Y=1000,Z=1000)\nMaxSelectedBricks=1000\nSelectionTimeout=2.000000\n\n[Server__BP_ServerSettingsAutoSave_C BP_ServerSettingsAutoSave_C]\nbEnableAutoSave=True\nbAnnounceAutoSave=True\nAutoSaveInterval=300.000000\nbIncludeScreenshot=False\n */\n}\n\nexport function read() {\n // TODO: Implement server config reading\n}\n"],"names":["CONFIG_SAVED_DIR","file.mkdir"],"mappings":";;;;;;AAOA,MAAM,cAAc;AACpB,MAAM,kBAAkB;AAGjB,SAAS,MAAM,YAAoB,QAAiB;AACzD,QAAM,aAAa,KAAK;AAAA,IACtB;AAAA,IACA,OAAO,OAAO,YAAYA,WAAAA;AAAAA,IAC1B;AAAA,EAAA;AAEF,QAAM,eAAe,KAAK,KAAK,YAAY,eAAe;AAE1DC,OAAAA,MAAW,UAAU;AAErB,KAAG;AAAA,IACD;AAAA,IACA;AAAA,aACS,OAAO,OAAO,QAAQ,EAAE;AAAA,oBACjB,OAAO,OAAO,eAAe,EAAE;AAAA,iBAClC,OAAO,OAAO,YAAY,EAAE;AAAA,aAChC,OAAO,OAAO,WAAW,EAAE;AAAA,kBACrB,OAAO,OAAO,kBAAkB,OAAQ,SAAS,OAAO;AAAA,kBACzD,OAAO,OAAO,gBAAgB,QAAQ,MAAM,GAAI,EAAE,QAAQ,OAAO,MAAM,KAAK,EAAE;AAAA,EAAA;AAmChG;AAEO,SAAS,OAAO;AAEvB;;;"}
1
+ {"version":3,"file":"config.js","sources":["../../src/brickadia/config.ts"],"sourcesContent":["import { type IConfig } from '@config/types';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport * as file from '../util/file';\n\nimport { CONFIG_SAVED_DIR } from '@/softconfig';\nconst CONFIG_PATH = 'Config/LinuxServer';\nconst SERVER_SETTINGS = 'GameUserSettings.ini';\n\n// Function that writes config\nexport function write(serverPath: string, config: IConfig) {\n const configPath = path.join(\n serverPath,\n config.server.savedDir ?? CONFIG_SAVED_DIR,\n CONFIG_PATH,\n );\n const settingsPath = path.join(configPath, SERVER_SETTINGS);\n\n file.mkdir(configPath);\n\n fs.writeFileSync(\n settingsPath,\n `[Server__BP_ServerSettings_General_C BP_ServerSettings_General_C]\nServerName=${config.server.name || ''}\nServerDescription=${config.server.description || ''}\nServerPassword=${config.server.password || ''}\nMaxPlayers=${config.server.players || 20}\nbPubliclyListed=${(config.server.publiclyListed ?? true) ? 'True' : 'False'}\nWelcomeMessage=\"${\n config.server.welcomeMessage\n ?.replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"') || ''\n }\"`,\n );\n\n /*\n[Server__BP_ServerSettings_General_C BP_ServerSettings_General_C]\nServerTypeIndex2=1\nServerName=\nServerDescription=\nServerPassword=\nMaxPlayers=30\nbPubliclyListed=True\nWelcomeMessage=\"<color=\\\"0055ff\\\">Welcome to <b>{2}</>, {1}.</><>a\"\nbGlobalRulesetSelfDamage=True\nbGlobalRulesetPhysicsDamage=False\nbGlobalRulesetEnableCameraBlockedEffects=False\nUploadTimeout=5.000000\nMaxPrefabBricks=1000\nMaxPrefabComponents=20\nMaxPrefabSize=(X=1000,Y=1000,Z=1000)\nMaxPhysicsObjects=2000\nMaxPhysicsObjectsPerUser=50\nMaxPlacementBricks=2000\nMaxPlacementEntities=10\nbEnforceApplicatorLimits=False\nTemplatePlacementTimeout=2.000000\nMaxSelectionBoxSize=(X=1000,Y=1000,Z=1000)\nMaxSelectedBricks=1000\nSelectionTimeout=2.000000\n\n[Server__BP_ServerSettingsAutoSave_C BP_ServerSettingsAutoSave_C]\nbEnableAutoSave=True\nbAnnounceAutoSave=True\nAutoSaveInterval=300.000000\nbIncludeScreenshot=False\n */\n}\n\nexport function read() {\n // TODO: Implement server config reading\n}\n"],"names":["CONFIG_SAVED_DIR","file.mkdir"],"mappings":";;;;;;AAOA,MAAM,cAAc;AACpB,MAAM,kBAAkB;AAGjB,SAAS,MAAM,YAAoB,QAAiB;AACzD,QAAM,aAAa,KAAK;AAAA,IACtB;AAAA,IACA,OAAO,OAAO,YAAYA,WAAAA;AAAAA,IAC1B;AAAA,EAAA;AAEF,QAAM,eAAe,KAAK,KAAK,YAAY,eAAe;AAE1DC,OAAAA,MAAW,UAAU;AAErB,KAAG;AAAA,IACD;AAAA,IACA;AAAA,aACS,OAAO,OAAO,QAAQ,EAAE;AAAA,oBACjB,OAAO,OAAO,eAAe,EAAE;AAAA,iBAClC,OAAO,OAAO,YAAY,EAAE;AAAA,aAChC,OAAO,OAAO,WAAW,EAAE;AAAA,kBACrB,OAAO,OAAO,kBAAkB,OAAQ,SAAS,OAAO;AAAA,kBAErE,OAAO,OAAO,gBACV,QAAQ,OAAO,MAAM,EACtB,QAAQ,MAAM,KAAK,KAAK,EAC7B;AAAA,EAAA;AAmCJ;AAEO,SAAS,OAAO;AAEvB;;;"}
@@ -108,7 +108,9 @@ class BrickadiaServer extends EventEmitter {
108
108
  { source: "config", file: this.getConfigWorld() },
109
109
  { source: "env", file: this.getEnvWorld() }
110
110
  ];
111
- return candidates.find(({ file }) => file && this.worldExists(file)) ?? null;
111
+ return candidates.find(
112
+ (c) => !!c.file && this.worldExists(c.file)
113
+ ) ?? null;
112
114
  }
113
115
  /**
114
116
  * Resolve the path to the game server binary for steam/override installs.
@@ -224,23 +226,21 @@ class BrickadiaServer extends EventEmitter {
224
226
  // remove password argument if not provided or token is provided
225
227
  `-port="${this.config.server.port}"`,
226
228
  this.config.server.launchArgs
227
- ].filter(Boolean);
229
+ ].filter((arg) => typeof arg === "string").filter((arg) => arg.length > 0);
228
230
  logger.default.verbose(
229
231
  "Params for spawn",
230
232
  params.join(" ").replace(/-User=".*?"/, '-User="<hidden>"').replace(/-Password=".*?"/, '-Password="<hidden>"').replace(/-Token=".*?"/, '-Token="<hidden>"').replace(/-Cookie=".*?"/, '-Cookie="<hidden>"').replace(/-Cookie=\S+/, "-Cookie=<hidden>")
231
233
  );
232
- this.#child = node_child_process.spawn("stdbuf", params);
233
- logger.default.verbose(
234
- "Spawn process",
235
- this.#child ? this.#child.pid : "failed".red
236
- );
237
- this.#child.stdin.setDefaultEncoding("utf8");
234
+ const child = node_child_process.spawn("stdbuf", params);
235
+ this.#child = child;
236
+ logger.default.verbose("Spawn process", child.pid ?? "failed".red);
237
+ child.stdin.setDefaultEncoding("utf8");
238
238
  this.#outInterface = readline.createInterface({
239
- input: this.#child.stdout,
239
+ input: child.stdout,
240
240
  terminal: false
241
241
  });
242
242
  this.#errInterface = readline.createInterface({
243
- input: this.#child.stderr,
243
+ input: child.stderr,
244
244
  terminal: false
245
245
  });
246
246
  this.attachListeners();
@@ -292,18 +292,18 @@ class BrickadiaServer extends EventEmitter {
292
292
  }
293
293
  // attaches proxy event listeners
294
294
  attachListeners() {
295
- this.#outInterface.on("line", this.lineListener);
296
- this.#errInterface.on("line", this.errorListener);
297
- this.#child.on("exit", this.exitListener);
298
- this.#child.on("close", () => {
295
+ this.#outInterface?.on("line", this.lineListener);
296
+ this.#errInterface?.on("line", this.errorListener);
297
+ this.#child?.on("exit", this.exitListener);
298
+ this.#child?.on("close", () => {
299
299
  });
300
300
  }
301
301
  // removes previously attached proxy event listeners
302
302
  detachListeners() {
303
- this.#outInterface.off("line", this.lineListener);
304
- this.#errInterface.off("line", this.errorListener);
305
- this.#child.off("exit", this.exitListener);
306
- this.#child.removeAllListeners("close");
303
+ this.#outInterface?.off("line", this.lineListener);
304
+ this.#errInterface?.off("line", this.errorListener);
305
+ this.#child?.off("exit", this.exitListener);
306
+ this.#child?.removeAllListeners("close");
307
307
  }
308
308
  // -- listeners for basic events (line, err, exit)
309
309
  errorListener(line) {
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sources":["../../src/brickadia/server.ts"],"sourcesContent":["/*\n Brickadia Server Wrapper\n Manages IO with the game server\n*/\n\nimport Logger from '@/logger';\nimport {\n ACTIVE_WORLD_FILE,\n CONFIG_SAVED_DIR,\n GAME_BIN_PATH,\n getOverrideGameBinary,\n getSteamGameDir,\n getSteamInstallDir,\n} from '@/softconfig';\nimport { getGlobalToken } from '@cli/auth';\nimport { IConfig } from '@config/types';\nimport { checkWsl } from '@util/wsl';\nimport 'colors';\nimport { ChildProcessWithoutNullStreams, spawn } from 'node:child_process';\nimport EventEmitter from 'node:events';\nimport { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { env } from 'node:process';\nimport readline from 'readline';\nimport stripAnsi from 'strip-ansi';\n\n// list of errors that can be solved by yelling at the user\nconst knownErrors: {\n name: string;\n solution?: string;\n match: RegExp;\n message?: string;\n}[] = [\n {\n name: 'MISSING_LIBGL',\n solution: 'apt-get install libgl1-mesa-glx libglib2.0-0',\n match:\n /error while loading shared libraries: libGL\\.so\\.1: cannot open shared object file/,\n },\n {\n name: 'MISSING_GLIB',\n solution: 'apt-get install libgl1-mesa-glx libglib2.0-0',\n match:\n /error while loading shared libraries: libgthread-2\\.0\\.so\\.0: cannot open shared object file/,\n },\n];\n\n/** Start a brickadia server */\nexport default class BrickadiaServer extends EventEmitter {\n #child: ChildProcessWithoutNullStreams = null;\n #errInterface: readline.Interface = null;\n #outInterface: readline.Interface = null;\n\n config: IConfig;\n path: string;\n\n constructor(dataPath: string, config: IConfig) {\n super();\n\n this.config = config;\n // use the data path if it's absolute, otherwise build an absolute path\n this.path =\n path.isAbsolute(dataPath) || dataPath.startsWith('/')\n ? dataPath\n : path.join(process.cwd(), dataPath);\n\n this.lineListener = this.lineListener.bind(this);\n this.errorListener = this.errorListener.bind(this);\n this.exitListener = this.exitListener.bind(this);\n }\n\n getActiveWorldFile(): string {\n return path.join(this.path, ACTIVE_WORLD_FILE);\n }\n\n /** A world specified by the active world file */\n getActiveWorld(): string | null {\n const activeWorldFile = this.getActiveWorldFile();\n if (existsSync(activeWorldFile)) {\n try {\n return readFileSync(activeWorldFile, 'utf8').trim();\n } catch (err) {\n Logger.errorp(\n 'Failed to read active world file',\n activeWorldFile.yellow,\n err,\n );\n return null;\n }\n }\n return null;\n }\n\n /** Set the world to use next startup */\n setActiveWorld(world: string | null): boolean {\n const activeWorldFile = this.getActiveWorldFile();\n if (!world || world === null) {\n if (existsSync(activeWorldFile)) {\n Logger.verbose('Removing active world file', activeWorldFile.yellow);\n unlinkSync(activeWorldFile);\n }\n return true;\n }\n\n if (!this.worldExists(world)) {\n Logger.verbose(\n 'Cannot set active world to',\n world.yellow,\n 'as it does not exist',\n );\n return false;\n }\n\n Logger.verbose('Setting active world to', world.yellow);\n try {\n writeFileSync(activeWorldFile, world, 'utf8');\n return true;\n } catch (err) {\n Logger.errorp(\n 'Failed to write active world file',\n activeWorldFile.yellow,\n err,\n );\n return false;\n }\n }\n\n /** A world specified by the config */\n getConfigWorld(): string | null {\n return this.config?.server?.world ?? null;\n }\n\n /** A world specified by the BRICKADIA_WORLD env variable */\n getEnvWorld(): string | null {\n return env.BRICKADIA_WORLD || null;\n }\n\n /** Check if a world exists */\n worldExists(world: string): boolean {\n const savedDir = this.config?.server?.savedDir ?? CONFIG_SAVED_DIR;\n const worldPath = path.join(this.path, savedDir, 'Worlds', world + '.brdb');\n return existsSync(worldPath);\n }\n\n /** Get the world that will be used next startup */\n getNextWorld() {\n const candidates = [\n { source: 'file', file: this.getActiveWorld() },\n { source: 'config', file: this.getConfigWorld() },\n { source: 'env', file: this.getEnvWorld() },\n ];\n\n return (\n candidates.find(({ file }) => file && this.worldExists(file)) ?? null\n );\n }\n\n /**\n * Resolve the path to the game server binary for steam/override installs.\n * Returns null for launcher-managed installs, where the binary lives in a\n * branch directory the launcher owns and isn't known statically here.\n */\n getGameBinaryPath(): string | null {\n const overrideBinary = getOverrideGameBinary();\n if (overrideBinary) return overrideBinary;\n\n // a configured branch means the launcher manages the install, not steam\n const isSteam = !this.config.server.branch;\n if (!isSteam) return null;\n\n const steamBeta = this.config.server.steambeta ?? 'main';\n return path.join(\n getSteamInstallDir(), // steam install directory\n steamBeta, // steam beta branch (or main)\n getSteamGameDir(), // Brickadia\n GAME_BIN_PATH, // path to binary\n );\n }\n\n // start the server child process\n start() {\n const {\n email,\n password,\n token: confToken,\n }: { email?: string; password?: string; token?: string } = this.config\n .credentials || {};\n\n const token = confToken || process.env.BRICKADIA_TOKEN || getGlobalToken();\n\n if (token) {\n Logger.verbose('Starting server with hosting token');\n } else {\n Logger.verbose(\n 'Starting server',\n (!email && !password ? 'without' : 'with').yellow,\n 'credentials',\n );\n }\n\n const isSteam = !this.config.server.branch;\n const steamBeta = this.config.server.steambeta ?? 'main';\n const overrideBinary = getOverrideGameBinary();\n const steamBinary = path.join(\n getSteamInstallDir(), // steam install directory\n steamBeta, // steam beta branch (or main)\n getSteamGameDir(), // Brickadia\n GAME_BIN_PATH, // path to binary\n );\n\n let gameBinary = this.getGameBinaryPath() ?? steamBinary;\n\n if (overrideBinary) {\n if (!existsSync(overrideBinary)) {\n Logger.error(\n 'Override binary',\n overrideBinary.yellow,\n 'does not exist!',\n );\n throw new Error(`Override binary ${overrideBinary} does not exist!`);\n }\n\n Logger.verbose(\n 'Using override binary',\n overrideBinary.yellow,\n 'instead of',\n steamBinary.yellow,\n );\n gameBinary = overrideBinary;\n } else if (isSteam) {\n Logger.verbose('Using steam binary', steamBeta.yellow);\n } else {\n Logger.verbose(\n 'Running',\n (this.config.server.__LOCAL\n ? path.join(__dirname, '../../tools/brickadia.sh')\n : 'brickadia launcher'\n ).yellow,\n );\n if (typeof this.config.server.branch === 'string')\n Logger.verbose('Using branch', this.config.server.branch.yellow);\n }\n\n // handle local launcher support\n const launchArgs = isSteam\n ? [gameBinary]\n : [\n this.config.server.__LOCAL\n ? path.join(__dirname, '../../tools/brickadia.sh')\n : 'brickadia',\n this.config.server.branch && `--branch=${this.config.server.branch}`,\n '--server',\n '--',\n ];\n\n const world = this.getNextWorld();\n if (world) {\n Logger.verbose(\n 'Using world',\n world.file.yellow,\n 'from',\n world.source.yellow,\n );\n } else if (this.config.server.map) {\n Logger.verbose('Using map', this.config.server.map.yellow, 'from config');\n }\n\n const params = [\n '--output=L',\n '--',\n ...launchArgs,\n !world &&\n this.config.server.map &&\n `-Environment=\"${this.config.server.map}\"`,\n world && `-World=\"${world.file}\"`,\n '-NotInstalled',\n '-log',\n checkWsl() === 1 ? '-OneThread' : null,\n this.path ? `-UserDir=\"${this.path}\"` : null,\n token ? `-Token=\"${token}\"` : null, // remove token argument if not provided\n !token && email ? `-User=\"${email}\"` : null, // remove email argument if not provided or token is provided\n !token && password ? `-Password=\"${password}\"` : null, // remove password argument if not provided or token is provided\n `-port=\"${this.config.server.port}\"`,\n this.config.server.launchArgs,\n ].filter(Boolean); // remove unused arguments\n Logger.verbose(\n 'Params for spawn',\n params\n .join(' ')\n .replace(/-User=\".*?\"/, '-User=\"<hidden>\"')\n .replace(/-Password=\".*?\"/, '-Password=\"<hidden>\"')\n .replace(/-Token=\".*?\"/, '-Token=\"<hidden>\"')\n .replace(/-Cookie=\".*?\"/, '-Cookie=\"<hidden>\"')\n .replace(/-Cookie=\\S+/, '-Cookie=<hidden>'),\n );\n\n // Either unbuffer or stdbuf must be used because brickadia's output is buffered\n // this means that multiple lines can be bundled together if the output buffer is not full\n // unfortunately without stdbuf or unbuffer, the output would not happen immediately\n this.#child = spawn('stdbuf', params);\n\n Logger.verbose(\n 'Spawn process',\n this.#child ? this.#child.pid : 'failed'.red,\n );\n\n this.#child.stdin.setDefaultEncoding('utf8');\n this.#outInterface = readline.createInterface({\n input: this.#child.stdout,\n terminal: false,\n });\n this.#errInterface = readline.createInterface({\n input: this.#child.stderr,\n terminal: false,\n });\n this.attachListeners();\n Logger.verbose('Attached listeners');\n }\n\n // write a string to the child process\n write(line: string) {\n if (line.length >= 512) {\n // show a warning\n Logger.warn(\n 'WARNING'.yellow,\n 'The following line was called and is',\n 'longer than allowed limit'.red,\n );\n Logger.warn(line.replace(/\\n$/, ''));\n // throw a fake error to get the line number\n try {\n throw new Error('Console Line Too Long');\n } catch (err) {\n Logger.warn(err);\n }\n return;\n }\n if (this.#child) {\n Logger.verbose('WRITE'.green, line.replace(/\\n$/, ''));\n this.#child.stdin.write(line);\n }\n }\n\n // write a line to the child process\n writeln(line: string) {\n this.write(line + '\\n');\n }\n\n // forcibly kills the server\n stop() {\n if (!this.#child) {\n Logger.verbose('Cannot stop server as no subprocess exists');\n return;\n }\n\n Logger.verbose('Forcibly stopping server');\n // kill the process\n this.#child.kill('SIGINT');\n\n // ...kill it again just to make sure it's dead\n spawn('kill', ['-9', this.#child.pid + '']);\n }\n\n // detaches listeners\n cleanup() {\n if (!this.#child) return;\n\n Logger.verbose('Cleaning up brickadia server');\n\n // detach listener\n this.detachListeners();\n\n this.#child = null;\n this.#outInterface = null;\n this.#errInterface = null;\n }\n\n // attaches proxy event listeners\n attachListeners() {\n this.#outInterface.on('line', this.lineListener);\n this.#errInterface.on('line', this.errorListener);\n this.#child.on('exit', this.exitListener);\n this.#child.on('close', () => {});\n }\n\n // removes previously attached proxy event listeners\n detachListeners() {\n this.#outInterface.off('line', this.lineListener);\n this.#errInterface.off('line', this.errorListener);\n this.#child.off('exit', this.exitListener);\n this.#child.removeAllListeners('close');\n }\n\n // -- listeners for basic events (line, err, exit)\n errorListener(line: string) {\n Logger.verbose('ERROR'.red, line);\n this.emit('err', line);\n for (const { match, solution, name, message } of knownErrors) {\n if (line.match(match)) {\n Logger.error(\n `Encountered ${name.red}. ${\n solution ? 'Known fix:\\n ' + solution : message || 'Unknown error.'\n }`,\n );\n }\n }\n }\n\n exitListener(...args: any[]) {\n Logger.verbose('Exit listener fired');\n this.emit('closed', ...args);\n this.cleanup();\n }\n\n lineListener(line: string) {\n this.emit('line', stripAnsi(line));\n }\n}\n"],"names":["ACTIVE_WORLD_FILE","existsSync","readFileSync","Logger","unlinkSync","writeFileSync","env","CONFIG_SAVED_DIR","getOverrideGameBinary","getSteamInstallDir","getSteamGameDir","GAME_BIN_PATH","getGlobalToken","checkWsl","spawn"],"mappings":";;;;;;;;;;;;;;AA2BA,MAAM,cAKA;AAAA,EACJ;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,OACE;AAAA,EAAA;AAAA,EAEJ;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,OACE;AAAA,EAAA;AAEN;AAGA,MAAqB,wBAAwB,aAAa;AAAA,EACxD,SAAyC;AAAA,EACzC,gBAAoC;AAAA,EACpC,gBAAoC;AAAA,EAKpC,YAAY,UAAkB,QAAiB;AAC7C,UAAA;AAEA,SAAK,SAAS;AAEd,SAAK,OACH,KAAK,WAAW,QAAQ,KAAK,SAAS,WAAW,GAAG,IAChD,WACA,KAAK,KAAK,QAAQ,IAAA,GAAO,QAAQ;AAEvC,SAAK,eAAe,KAAK,aAAa,KAAK,IAAI;AAC/C,SAAK,gBAAgB,KAAK,cAAc,KAAK,IAAI;AACjD,SAAK,eAAe,KAAK,aAAa,KAAK,IAAI;AAAA,EACjD;AAAA,EAEA,qBAA6B;AAC3B,WAAO,KAAK,KAAK,KAAK,MAAMA,WAAAA,iBAAiB;AAAA,EAC/C;AAAA;AAAA,EAGA,iBAAgC;AAC9B,UAAM,kBAAkB,KAAK,mBAAA;AAC7B,QAAIC,GAAAA,WAAW,eAAe,GAAG;AAC/B,UAAI;AACF,eAAOC,gBAAa,iBAAiB,MAAM,EAAE,KAAA;AAAA,MAC/C,SAAS,KAAK;AACZC,eAAAA,QAAO;AAAA,UACL;AAAA,UACA,gBAAgB;AAAA,UAChB;AAAA,QAAA;AAEF,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,eAAe,OAA+B;AAC5C,UAAM,kBAAkB,KAAK,mBAAA;AAC7B,QAAI,CAAC,SAAS,UAAU,MAAM;AAC5B,UAAIF,GAAAA,WAAW,eAAe,GAAG;AAC/BE,eAAAA,QAAO,QAAQ,8BAA8B,gBAAgB,MAAM;AACnEC,WAAAA,WAAW,eAAe;AAAA,MAC5B;AACA,aAAO;AAAA,IACT;AAEA,QAAI,CAAC,KAAK,YAAY,KAAK,GAAG;AAC5BD,aAAAA,QAAO;AAAA,QACL;AAAA,QACA,MAAM;AAAA,QACN;AAAA,MAAA;AAEF,aAAO;AAAA,IACT;AAEAA,WAAAA,QAAO,QAAQ,2BAA2B,MAAM,MAAM;AACtD,QAAI;AACFE,uBAAc,iBAAiB,OAAO,MAAM;AAC5C,aAAO;AAAA,IACT,SAAS,KAAK;AACZF,aAAAA,QAAO;AAAA,QACL;AAAA,QACA,gBAAgB;AAAA,QAChB;AAAA,MAAA;AAEF,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA,EAGA,iBAAgC;AAC9B,WAAO,KAAK,QAAQ,QAAQ,SAAS;AAAA,EACvC;AAAA;AAAA,EAGA,cAA6B;AAC3B,WAAOG,aAAAA,IAAI,mBAAmB;AAAA,EAChC;AAAA;AAAA,EAGA,YAAY,OAAwB;AAClC,UAAM,WAAW,KAAK,QAAQ,QAAQ,YAAYC,WAAAA;AAClD,UAAM,YAAY,KAAK,KAAK,KAAK,MAAM,UAAU,UAAU,QAAQ,OAAO;AAC1E,WAAON,GAAAA,WAAW,SAAS;AAAA,EAC7B;AAAA;AAAA,EAGA,eAAe;AACb,UAAM,aAAa;AAAA,MACjB,EAAE,QAAQ,QAAQ,MAAM,KAAK,iBAAe;AAAA,MAC5C,EAAE,QAAQ,UAAU,MAAM,KAAK,iBAAe;AAAA,MAC9C,EAAE,QAAQ,OAAO,MAAM,KAAK,cAAY;AAAA,IAAE;AAG5C,WACE,WAAW,KAAK,CAAC,EAAE,KAAA,MAAW,QAAQ,KAAK,YAAY,IAAI,CAAC,KAAK;AAAA,EAErE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,oBAAmC;AACjC,UAAM,iBAAiBO,WAAAA,sBAAA;AACvB,QAAI,eAAgB,QAAO;AAG3B,UAAM,UAAU,CAAC,KAAK,OAAO,OAAO;AACpC,QAAI,CAAC,QAAS,QAAO;AAErB,UAAM,YAAY,KAAK,OAAO,OAAO,aAAa;AAClD,WAAO,KAAK;AAAA,MACVC,8BAAA;AAAA;AAAA,MACA;AAAA;AAAA,MACAC,2BAAA;AAAA;AAAA,MACAC,WAAAA;AAAAA;AAAAA,IAAA;AAAA,EAEJ;AAAA;AAAA,EAGA,QAAQ;AACN,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA,OAAO;AAAA,IAAA,IACkD,KAAK,OAC7D,eAAe,CAAA;AAElB,UAAM,QAAQ,aAAa,QAAQ,IAAI,mBAAmBC,KAAAA,eAAA;AAE1D,QAAI,OAAO;AACTT,aAAAA,QAAO,QAAQ,oCAAoC;AAAA,IACrD,OAAO;AACLA,aAAAA,QAAO;AAAA,QACL;AAAA,SACC,CAAC,SAAS,CAAC,WAAW,YAAY,QAAQ;AAAA,QAC3C;AAAA,MAAA;AAAA,IAEJ;AAEA,UAAM,UAAU,CAAC,KAAK,OAAO,OAAO;AACpC,UAAM,YAAY,KAAK,OAAO,OAAO,aAAa;AAClD,UAAM,iBAAiBK,WAAAA,sBAAA;AACvB,UAAM,cAAc,KAAK;AAAA,MACvBC,8BAAA;AAAA;AAAA,MACA;AAAA;AAAA,MACAC,2BAAA;AAAA;AAAA,MACAC,WAAAA;AAAAA;AAAAA,IAAA;AAGF,QAAI,aAAa,KAAK,kBAAA,KAAuB;AAE7C,QAAI,gBAAgB;AAClB,UAAI,CAACV,GAAAA,WAAW,cAAc,GAAG;AAC/BE,eAAAA,QAAO;AAAA,UACL;AAAA,UACA,eAAe;AAAA,UACf;AAAA,QAAA;AAEF,cAAM,IAAI,MAAM,mBAAmB,cAAc,kBAAkB;AAAA,MACrE;AAEAA,aAAAA,QAAO;AAAA,QACL;AAAA,QACA,eAAe;AAAA,QACf;AAAA,QACA,YAAY;AAAA,MAAA;AAEd,mBAAa;AAAA,IACf,WAAW,SAAS;AAClBA,aAAAA,QAAO,QAAQ,sBAAsB,UAAU,MAAM;AAAA,IACvD,OAAO;AACLA,aAAAA,QAAO;AAAA,QACL;AAAA,SACC,KAAK,OAAO,OAAO,UAChB,KAAK,KAAK,WAAW,0BAA0B,IAC/C,sBACF;AAAA,MAAA;AAEJ,UAAI,OAAO,KAAK,OAAO,OAAO,WAAW;AACvCA,eAAAA,QAAO,QAAQ,gBAAgB,KAAK,OAAO,OAAO,OAAO,MAAM;AAAA,IACnE;AAGA,UAAM,aAAa,UACf,CAAC,UAAU,IACX;AAAA,MACE,KAAK,OAAO,OAAO,UACf,KAAK,KAAK,WAAW,0BAA0B,IAC/C;AAAA,MACJ,KAAK,OAAO,OAAO,UAAU,YAAY,KAAK,OAAO,OAAO,MAAM;AAAA,MAClE;AAAA,MACA;AAAA,IAAA;AAGN,UAAM,QAAQ,KAAK,aAAA;AACnB,QAAI,OAAO;AACTA,aAAAA,QAAO;AAAA,QACL;AAAA,QACA,MAAM,KAAK;AAAA,QACX;AAAA,QACA,MAAM,OAAO;AAAA,MAAA;AAAA,IAEjB,WAAW,KAAK,OAAO,OAAO,KAAK;AACjCA,qBAAO,QAAQ,aAAa,KAAK,OAAO,OAAO,IAAI,QAAQ,aAAa;AAAA,IAC1E;AAEA,UAAM,SAAS;AAAA,MACb;AAAA,MACA;AAAA,MACA,GAAG;AAAA,MACH,CAAC,SACC,KAAK,OAAO,OAAO,OACnB,iBAAiB,KAAK,OAAO,OAAO,GAAG;AAAA,MACzC,SAAS,WAAW,MAAM,IAAI;AAAA,MAC9B;AAAA,MACA;AAAA,MACAU,aAAA,MAAe,IAAI,eAAe;AAAA,MAClC,KAAK,OAAO,aAAa,KAAK,IAAI,MAAM;AAAA,MACxC,QAAQ,WAAW,KAAK,MAAM;AAAA;AAAA,MAC9B,CAAC,SAAS,QAAQ,UAAU,KAAK,MAAM;AAAA;AAAA,MACvC,CAAC,SAAS,WAAW,cAAc,QAAQ,MAAM;AAAA;AAAA,MACjD,UAAU,KAAK,OAAO,OAAO,IAAI;AAAA,MACjC,KAAK,OAAO,OAAO;AAAA,IAAA,EACnB,OAAO,OAAO;AAChBV,WAAAA,QAAO;AAAA,MACL;AAAA,MACA,OACG,KAAK,GAAG,EACR,QAAQ,eAAe,kBAAkB,EACzC,QAAQ,mBAAmB,sBAAsB,EACjD,QAAQ,gBAAgB,mBAAmB,EAC3C,QAAQ,iBAAiB,oBAAoB,EAC7C,QAAQ,eAAe,kBAAkB;AAAA,IAAA;AAM9C,SAAK,SAASW,yBAAM,UAAU,MAAM;AAEpCX,WAAAA,QAAO;AAAA,MACL;AAAA,MACA,KAAK,SAAS,KAAK,OAAO,MAAM,SAAS;AAAA,IAAA;AAG3C,SAAK,OAAO,MAAM,mBAAmB,MAAM;AAC3C,SAAK,gBAAgB,SAAS,gBAAgB;AAAA,MAC5C,OAAO,KAAK,OAAO;AAAA,MACnB,UAAU;AAAA,IAAA,CACX;AACD,SAAK,gBAAgB,SAAS,gBAAgB;AAAA,MAC5C,OAAO,KAAK,OAAO;AAAA,MACnB,UAAU;AAAA,IAAA,CACX;AACD,SAAK,gBAAA;AACLA,WAAAA,QAAO,QAAQ,oBAAoB;AAAA,EACrC;AAAA;AAAA,EAGA,MAAM,MAAc;AAClB,QAAI,KAAK,UAAU,KAAK;AAEtBA,aAAAA,QAAO;AAAA,QACL,UAAU;AAAA,QACV;AAAA,QACA,4BAA4B;AAAA,MAAA;AAE9BA,aAAAA,QAAO,KAAK,KAAK,QAAQ,OAAO,EAAE,CAAC;AAEnC,UAAI;AACF,cAAM,IAAI,MAAM,uBAAuB;AAAA,MACzC,SAAS,KAAK;AACZA,eAAAA,QAAO,KAAK,GAAG;AAAA,MACjB;AACA;AAAA,IACF;AACA,QAAI,KAAK,QAAQ;AACfA,qBAAO,QAAQ,QAAQ,OAAO,KAAK,QAAQ,OAAO,EAAE,CAAC;AACrD,WAAK,OAAO,MAAM,MAAM,IAAI;AAAA,IAC9B;AAAA,EACF;AAAA;AAAA,EAGA,QAAQ,MAAc;AACpB,SAAK,MAAM,OAAO,IAAI;AAAA,EACxB;AAAA;AAAA,EAGA,OAAO;AACL,QAAI,CAAC,KAAK,QAAQ;AAChBA,aAAAA,QAAO,QAAQ,4CAA4C;AAC3D;AAAA,IACF;AAEAA,WAAAA,QAAO,QAAQ,0BAA0B;AAEzC,SAAK,OAAO,KAAK,QAAQ;AAGzBW,uBAAAA,MAAM,QAAQ,CAAC,MAAM,KAAK,OAAO,MAAM,EAAE,CAAC;AAAA,EAC5C;AAAA;AAAA,EAGA,UAAU;AACR,QAAI,CAAC,KAAK,OAAQ;AAElBX,WAAAA,QAAO,QAAQ,8BAA8B;AAG7C,SAAK,gBAAA;AAEL,SAAK,SAAS;AACd,SAAK,gBAAgB;AACrB,SAAK,gBAAgB;AAAA,EACvB;AAAA;AAAA,EAGA,kBAAkB;AAChB,SAAK,cAAc,GAAG,QAAQ,KAAK,YAAY;AAC/C,SAAK,cAAc,GAAG,QAAQ,KAAK,aAAa;AAChD,SAAK,OAAO,GAAG,QAAQ,KAAK,YAAY;AACxC,SAAK,OAAO,GAAG,SAAS,MAAM;AAAA,IAAC,CAAC;AAAA,EAClC;AAAA;AAAA,EAGA,kBAAkB;AAChB,SAAK,cAAc,IAAI,QAAQ,KAAK,YAAY;AAChD,SAAK,cAAc,IAAI,QAAQ,KAAK,aAAa;AACjD,SAAK,OAAO,IAAI,QAAQ,KAAK,YAAY;AACzC,SAAK,OAAO,mBAAmB,OAAO;AAAA,EACxC;AAAA;AAAA,EAGA,cAAc,MAAc;AAC1BA,WAAAA,QAAO,QAAQ,QAAQ,KAAK,IAAI;AAChC,SAAK,KAAK,OAAO,IAAI;AACrB,eAAW,EAAE,OAAO,UAAU,MAAM,QAAA,KAAa,aAAa;AAC5D,UAAI,KAAK,MAAM,KAAK,GAAG;AACrBA,eAAAA,QAAO;AAAA,UACL,eAAe,KAAK,GAAG,KACrB,WAAW,mBAAmB,WAAW,WAAW,gBACtD;AAAA,QAAA;AAAA,MAEJ;AAAA,IACF;AAAA,EACF;AAAA,EAEA,gBAAgB,MAAa;AAC3BA,WAAAA,QAAO,QAAQ,qBAAqB;AACpC,SAAK,KAAK,UAAU,GAAG,IAAI;AAC3B,SAAK,QAAA;AAAA,EACP;AAAA,EAEA,aAAa,MAAc;AACzB,SAAK,KAAK,QAAQ,UAAU,IAAI,CAAC;AAAA,EACnC;AACF;;"}
1
+ {"version":3,"file":"server.js","sources":["../../src/brickadia/server.ts"],"sourcesContent":["/*\n Brickadia Server Wrapper\n Manages IO with the game server\n*/\n\nimport Logger from '@/logger';\nimport {\n ACTIVE_WORLD_FILE,\n CONFIG_SAVED_DIR,\n GAME_BIN_PATH,\n getOverrideGameBinary,\n getSteamGameDir,\n getSteamInstallDir,\n} from '@/softconfig';\nimport { getGlobalToken } from '@cli/auth';\nimport { type IConfig } from '@config/types';\nimport { checkWsl } from '@util/wsl';\nimport 'colors';\nimport { type ChildProcessWithoutNullStreams, spawn } from 'node:child_process';\nimport EventEmitter from 'node:events';\nimport { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { env } from 'node:process';\nimport readline from 'readline';\nimport stripAnsi from 'strip-ansi';\n\n// list of errors that can be solved by yelling at the user\nconst knownErrors: {\n name: string;\n solution?: string;\n match: RegExp;\n message?: string;\n}[] = [\n {\n name: 'MISSING_LIBGL',\n solution: 'apt-get install libgl1-mesa-glx libglib2.0-0',\n match:\n /error while loading shared libraries: libGL\\.so\\.1: cannot open shared object file/,\n },\n {\n name: 'MISSING_GLIB',\n solution: 'apt-get install libgl1-mesa-glx libglib2.0-0',\n match:\n /error while loading shared libraries: libgthread-2\\.0\\.so\\.0: cannot open shared object file/,\n },\n];\n\n/** Start a brickadia server */\nexport default class BrickadiaServer extends EventEmitter {\n #child: ChildProcessWithoutNullStreams | null = null;\n #errInterface: readline.Interface | null = null;\n #outInterface: readline.Interface | null = null;\n\n config: IConfig;\n path: string;\n\n constructor(dataPath: string, config: IConfig) {\n super();\n\n this.config = config;\n // use the data path if it's absolute, otherwise build an absolute path\n this.path =\n path.isAbsolute(dataPath) || dataPath.startsWith('/')\n ? dataPath\n : path.join(process.cwd(), dataPath);\n\n this.lineListener = this.lineListener.bind(this);\n this.errorListener = this.errorListener.bind(this);\n this.exitListener = this.exitListener.bind(this);\n }\n\n getActiveWorldFile(): string {\n return path.join(this.path, ACTIVE_WORLD_FILE);\n }\n\n /** A world specified by the active world file */\n getActiveWorld(): string | null {\n const activeWorldFile = this.getActiveWorldFile();\n if (existsSync(activeWorldFile)) {\n try {\n return readFileSync(activeWorldFile, 'utf8').trim();\n } catch (err) {\n Logger.errorp(\n 'Failed to read active world file',\n activeWorldFile.yellow,\n err,\n );\n return null;\n }\n }\n return null;\n }\n\n /** Set the world to use next startup */\n setActiveWorld(world: string | null): boolean {\n const activeWorldFile = this.getActiveWorldFile();\n if (!world || world === null) {\n if (existsSync(activeWorldFile)) {\n Logger.verbose('Removing active world file', activeWorldFile.yellow);\n unlinkSync(activeWorldFile);\n }\n return true;\n }\n\n if (!this.worldExists(world)) {\n Logger.verbose(\n 'Cannot set active world to',\n world.yellow,\n 'as it does not exist',\n );\n return false;\n }\n\n Logger.verbose('Setting active world to', world.yellow);\n try {\n writeFileSync(activeWorldFile, world, 'utf8');\n return true;\n } catch (err) {\n Logger.errorp(\n 'Failed to write active world file',\n activeWorldFile.yellow,\n err,\n );\n return false;\n }\n }\n\n /** A world specified by the config */\n getConfigWorld(): string | null {\n return this.config?.server?.world ?? null;\n }\n\n /** A world specified by the BRICKADIA_WORLD env variable */\n getEnvWorld(): string | null {\n return env.BRICKADIA_WORLD || null;\n }\n\n /** Check if a world exists */\n worldExists(world: string): boolean {\n const savedDir = this.config?.server?.savedDir ?? CONFIG_SAVED_DIR;\n const worldPath = path.join(this.path, savedDir, 'Worlds', world + '.brdb');\n return existsSync(worldPath);\n }\n\n /** Get the world that will be used next startup */\n getNextWorld(): { source: string; file: string } | null {\n const candidates = [\n { source: 'file', file: this.getActiveWorld() },\n { source: 'config', file: this.getConfigWorld() },\n { source: 'env', file: this.getEnvWorld() },\n ];\n\n return (\n candidates.find(\n (c): c is { source: string; file: string } =>\n !!c.file && this.worldExists(c.file),\n ) ?? null\n );\n }\n\n /**\n * Resolve the path to the game server binary for steam/override installs.\n * Returns null for launcher-managed installs, where the binary lives in a\n * branch directory the launcher owns and isn't known statically here.\n */\n getGameBinaryPath(): string | null {\n const overrideBinary = getOverrideGameBinary();\n if (overrideBinary) return overrideBinary;\n\n // a configured branch means the launcher manages the install, not steam\n const isSteam = !this.config.server.branch;\n if (!isSteam) return null;\n\n const steamBeta = this.config.server.steambeta ?? 'main';\n return path.join(\n getSteamInstallDir(), // steam install directory\n steamBeta, // steam beta branch (or main)\n getSteamGameDir(), // Brickadia\n GAME_BIN_PATH, // path to binary\n );\n }\n\n // start the server child process\n start() {\n const {\n email,\n password,\n token: confToken,\n }: { email?: string; password?: string; token?: string } = this.config\n .credentials || {};\n\n const token = confToken || process.env.BRICKADIA_TOKEN || getGlobalToken();\n\n if (token) {\n Logger.verbose('Starting server with hosting token');\n } else {\n Logger.verbose(\n 'Starting server',\n (!email && !password ? 'without' : 'with').yellow,\n 'credentials',\n );\n }\n\n const isSteam = !this.config.server.branch;\n const steamBeta = this.config.server.steambeta ?? 'main';\n const overrideBinary = getOverrideGameBinary();\n const steamBinary = path.join(\n getSteamInstallDir(), // steam install directory\n steamBeta, // steam beta branch (or main)\n getSteamGameDir(), // Brickadia\n GAME_BIN_PATH, // path to binary\n );\n\n let gameBinary = this.getGameBinaryPath() ?? steamBinary;\n\n if (overrideBinary) {\n if (!existsSync(overrideBinary)) {\n Logger.error(\n 'Override binary',\n overrideBinary.yellow,\n 'does not exist!',\n );\n throw new Error(`Override binary ${overrideBinary} does not exist!`);\n }\n\n Logger.verbose(\n 'Using override binary',\n overrideBinary.yellow,\n 'instead of',\n steamBinary.yellow,\n );\n gameBinary = overrideBinary;\n } else if (isSteam) {\n Logger.verbose('Using steam binary', steamBeta.yellow);\n } else {\n Logger.verbose(\n 'Running',\n (this.config.server.__LOCAL\n ? path.join(__dirname, '../../tools/brickadia.sh')\n : 'brickadia launcher'\n ).yellow,\n );\n if (typeof this.config.server.branch === 'string')\n Logger.verbose('Using branch', this.config.server.branch.yellow);\n }\n\n // handle local launcher support\n const launchArgs = isSteam\n ? [gameBinary]\n : [\n this.config.server.__LOCAL\n ? path.join(__dirname, '../../tools/brickadia.sh')\n : 'brickadia',\n this.config.server.branch && `--branch=${this.config.server.branch}`,\n '--server',\n '--',\n ];\n\n const world = this.getNextWorld();\n if (world) {\n Logger.verbose(\n 'Using world',\n world.file.yellow,\n 'from',\n world.source.yellow,\n );\n } else if (this.config.server.map) {\n Logger.verbose('Using map', this.config.server.map.yellow, 'from config');\n }\n\n const params = [\n '--output=L',\n '--',\n ...launchArgs,\n !world &&\n this.config.server.map &&\n `-Environment=\"${this.config.server.map}\"`,\n world && `-World=\"${world.file}\"`,\n '-NotInstalled',\n '-log',\n checkWsl() === 1 ? '-OneThread' : null,\n this.path ? `-UserDir=\"${this.path}\"` : null,\n token ? `-Token=\"${token}\"` : null, // remove token argument if not provided\n !token && email ? `-User=\"${email}\"` : null, // remove email argument if not provided or token is provided\n !token && password ? `-Password=\"${password}\"` : null, // remove password argument if not provided or token is provided\n `-port=\"${this.config.server.port}\"`,\n this.config.server.launchArgs,\n ]\n .filter(arg => typeof arg === 'string')\n .filter(arg => arg.length > 0); // remove unused/empty arguments\n Logger.verbose(\n 'Params for spawn',\n params\n .join(' ')\n .replace(/-User=\".*?\"/, '-User=\"<hidden>\"')\n .replace(/-Password=\".*?\"/, '-Password=\"<hidden>\"')\n .replace(/-Token=\".*?\"/, '-Token=\"<hidden>\"')\n .replace(/-Cookie=\".*?\"/, '-Cookie=\"<hidden>\"')\n .replace(/-Cookie=\\S+/, '-Cookie=<hidden>'),\n );\n\n // Either unbuffer or stdbuf must be used because brickadia's output is buffered\n // this means that multiple lines can be bundled together if the output buffer is not full\n // unfortunately without stdbuf or unbuffer, the output would not happen immediately\n const child = spawn('stdbuf', params);\n this.#child = child;\n\n Logger.verbose('Spawn process', child.pid ?? 'failed'.red);\n\n child.stdin.setDefaultEncoding('utf8');\n this.#outInterface = readline.createInterface({\n input: child.stdout,\n terminal: false,\n });\n this.#errInterface = readline.createInterface({\n input: child.stderr,\n terminal: false,\n });\n this.attachListeners();\n Logger.verbose('Attached listeners');\n }\n\n // write a string to the child process\n write(line: string) {\n if (line.length >= 512) {\n // show a warning\n Logger.warn(\n 'WARNING'.yellow,\n 'The following line was called and is',\n 'longer than allowed limit'.red,\n );\n Logger.warn(line.replace(/\\n$/, ''));\n // throw a fake error to get the line number\n try {\n throw new Error('Console Line Too Long');\n } catch (err) {\n Logger.warn(err);\n }\n return;\n }\n if (this.#child) {\n Logger.verbose('WRITE'.green, line.replace(/\\n$/, ''));\n this.#child.stdin.write(line);\n }\n }\n\n // write a line to the child process\n writeln(line: string) {\n this.write(line + '\\n');\n }\n\n // forcibly kills the server\n stop() {\n if (!this.#child) {\n Logger.verbose('Cannot stop server as no subprocess exists');\n return;\n }\n\n Logger.verbose('Forcibly stopping server');\n // kill the process\n this.#child.kill('SIGINT');\n\n // ...kill it again just to make sure it's dead\n spawn('kill', ['-9', this.#child.pid + '']);\n }\n\n // detaches listeners\n cleanup() {\n if (!this.#child) return;\n\n Logger.verbose('Cleaning up brickadia server');\n\n // detach listener\n this.detachListeners();\n\n this.#child = null;\n this.#outInterface = null;\n this.#errInterface = null;\n }\n\n // attaches proxy event listeners\n attachListeners() {\n this.#outInterface?.on('line', this.lineListener);\n this.#errInterface?.on('line', this.errorListener);\n this.#child?.on('exit', this.exitListener);\n this.#child?.on('close', () => {});\n }\n\n // removes previously attached proxy event listeners\n detachListeners() {\n this.#outInterface?.off('line', this.lineListener);\n this.#errInterface?.off('line', this.errorListener);\n this.#child?.off('exit', this.exitListener);\n this.#child?.removeAllListeners('close');\n }\n\n // -- listeners for basic events (line, err, exit)\n errorListener(line: string) {\n Logger.verbose('ERROR'.red, line);\n this.emit('err', line);\n for (const { match, solution, name, message } of knownErrors) {\n if (line.match(match)) {\n Logger.error(\n `Encountered ${name.red}. ${\n solution ? 'Known fix:\\n ' + solution : message || 'Unknown error.'\n }`,\n );\n }\n }\n }\n\n exitListener(...args: any[]) {\n Logger.verbose('Exit listener fired');\n this.emit('closed', ...args);\n this.cleanup();\n }\n\n lineListener(line: string) {\n this.emit('line', stripAnsi(line));\n }\n}\n"],"names":["ACTIVE_WORLD_FILE","existsSync","readFileSync","Logger","unlinkSync","writeFileSync","env","CONFIG_SAVED_DIR","getOverrideGameBinary","getSteamInstallDir","getSteamGameDir","GAME_BIN_PATH","getGlobalToken","checkWsl","spawn"],"mappings":"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"}
package/dist/cli/auth.js CHANGED
@@ -120,7 +120,7 @@ async function authFromPrompt({
120
120
  console.log(">>".green, "Storing auth tokens...");
121
121
  const authPath = path.join(
122
122
  softconfig.default.CONFIG_HOME,
123
- savedDir !== softconfig.default.CONFIG_SAVED_DIR ? savedDir : "",
123
+ savedDir && savedDir !== softconfig.default.CONFIG_SAVED_DIR ? savedDir : "",
124
124
  authDir ?? softconfig.default.CONFIG_AUTH_DIR
125
125
  );
126
126
  file.mkdir(authPath);
@@ -1 +1 @@
1
- {"version":3,"file":"auth.js","sources":["../../src/cli/auth.ts"],"sourcesContent":["import soft from '@/softconfig';\nimport { genAuthFiles, writeAuthFiles } from '@omegga/auth';\nimport * as file from '@util/file';\nimport 'colors';\nimport fs, { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport prompts from 'prompts';\n\nexport const AUTH_PATH = path.join(soft.CONFIG_HOME, soft.CONFIG_AUTH_DIR);\n\n// async function to prompt for credentials\nasync function credentialPrompt() {\n console.log(\n '>>'.green,\n 'Enter',\n 'Brickadia'.green.underline,\n 'credentials (not stored)',\n );\n\n const response = await prompts([\n {\n type: 'text',\n name: 'email',\n message: 'Email',\n },\n {\n type: 'password',\n name: 'password',\n message: 'Password',\n },\n ]);\n\n return [response.email, response.password];\n}\n\nasync function authFromPrompt({\n email,\n password,\n debug = false,\n isSteam,\n branch,\n authDir,\n savedDir,\n launchArgs,\n}: {\n email?: string | null;\n password?: string | null;\n debug?: boolean;\n isSteam?: boolean;\n branch?: string;\n authDir?: string;\n savedDir?: string;\n launchArgs?: string;\n}) {\n let files: Record<string, Buffer>;\n\n if (isSteam || !email || !password) {\n // Prompt user to pick to select username/password or Auth Token\n const { authType } = await prompts({\n type: 'select',\n name: 'authType',\n message: 'Select authentication method',\n choices: [\n { title: 'Hosting Token', value: 'token' },\n { title: 'Username/Password', value: 'credentials' },\n ],\n });\n\n if (!authType) {\n console.error('!>'.red, 'No authentication method selected');\n return false;\n }\n\n if (authType === 'token') {\n // Prompt for hosting token\n const { token } = await prompts({\n type: 'password',\n name: 'token',\n message:\n 'Paste your server hosting token (from https://brickadia.com/account)',\n validate: value => (value ? true : 'Token is required'),\n });\n\n if (!token) {\n console.error('!>'.red, 'Hosting token is required');\n return false;\n }\n\n // Write to global token file\n try {\n console.log('>>'.green, 'Storing hosting token...');\n fs.writeFileSync(soft.GLOBAL_TOKEN, token.trim());\n } catch (err) {\n console.error('!>'.red, 'Error writing hosting token to config\\n', err);\n return false;\n }\n\n return true;\n }\n\n if (isSteam) {\n console.error(\n '!>'.red,\n 'Launching with steam requires a hosting token right now.',\n );\n return false;\n }\n\n // Non-steam password auth is handled below\n }\n\n if (!email || !password || !isSteam) {\n // prompt for user credentials\n try {\n [email, password] = await credentialPrompt();\n } catch (err) {\n console.error('!>'.red, 'Error prompting credentials\\n', err);\n return false;\n }\n }\n\n if (!email || !password) {\n console.error('!>'.red, 'Email and password are required');\n return false;\n }\n\n // generate auth tokens\n console.log('>>'.green, 'Generating auth tokens...');\n const timeout = setTimeout(() => {\n console.log('>>'.green, 'Probably also installing the game...');\n }, 10000);\n try {\n files = await genAuthFiles(email, password, {\n debug,\n branch,\n authDir,\n savedDir,\n launchArgs,\n });\n clearTimeout(timeout);\n } catch (err) {\n clearTimeout(timeout);\n console.error('!>'.red, 'Error generating tokens:', err);\n return false;\n }\n\n if (!files) {\n console.error('!>'.red, 'Authentication Failed');\n return;\n }\n\n // save the tokens to the config path (will be copied when omegga starts)\n try {\n console.log('>>'.green, 'Storing auth tokens...');\n const authPath = path.join(\n soft.CONFIG_HOME,\n savedDir !== soft.CONFIG_SAVED_DIR ? savedDir : '',\n authDir ?? soft.CONFIG_AUTH_DIR,\n );\n file.mkdir(authPath);\n writeAuthFiles(authPath, files);\n } catch (err) {\n console.error('!>'.red, 'Error writing tokens to config\\n', err);\n return false;\n }\n\n console.log('>>'.green, 'Auth tokens successfully generated!');\n return true;\n}\n\n// check if auth files exist\nfunction authExists(dir?: string) {\n return soft.BRICKADIA_AUTH_FILES.every(f =>\n fs.existsSync(path.join(dir ?? AUTH_PATH, f)),\n );\n}\n\n// delete auth files stored in config\n// delete auth files stored in config\nfunction deleteAuthFiles(dir?: string) {\n const isConfigHome = dir && dir.startsWith(soft.CONFIG_HOME);\n // will not delete files outside of the config home\n if (dir && !isConfigHome) return;\n file.rmdir(dir ?? AUTH_PATH);\n\n // Remove the global auth token\n if (existsSync(soft.GLOBAL_TOKEN)) {\n fs.unlinkSync(soft.GLOBAL_TOKEN);\n }\n}\n\nexport function getGlobalToken() {\n if (!fs.existsSync(soft.GLOBAL_TOKEN)) return null;\n return fs.readFileSync(soft.GLOBAL_TOKEN, 'utf-8').trim();\n}\n\nexport const prompt = authFromPrompt;\nexport const exists = authExists;\nexport const clean = deleteAuthFiles;\n\nexport default {\n AUTH_PATH,\n prompt,\n exists,\n clean,\n getGlobalToken,\n};\n"],"names":["soft","genAuthFiles","file.mkdir","writeAuthFiles","file.rmdir","existsSync"],"mappings":"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"}
1
+ {"version":3,"file":"auth.js","sources":["../../src/cli/auth.ts"],"sourcesContent":["import soft from '@/softconfig';\nimport { genAuthFiles, writeAuthFiles } from '@omegga/auth';\nimport * as file from '@util/file';\nimport 'colors';\nimport fs, { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport prompts from 'prompts';\n\nexport const AUTH_PATH = path.join(soft.CONFIG_HOME, soft.CONFIG_AUTH_DIR);\n\n// async function to prompt for credentials\nasync function credentialPrompt() {\n console.log(\n '>>'.green,\n 'Enter',\n 'Brickadia'.green.underline,\n 'credentials (not stored)',\n );\n\n const response = await prompts([\n {\n type: 'text',\n name: 'email',\n message: 'Email',\n },\n {\n type: 'password',\n name: 'password',\n message: 'Password',\n },\n ]);\n\n return [response.email, response.password];\n}\n\nasync function authFromPrompt({\n email,\n password,\n debug = false,\n isSteam,\n branch,\n authDir,\n savedDir,\n launchArgs,\n}: {\n email?: string | null;\n password?: string | null;\n debug?: boolean;\n isSteam?: boolean;\n branch?: string;\n authDir?: string;\n savedDir?: string;\n launchArgs?: string;\n}) {\n let files: Record<string, Buffer> | null;\n\n if (isSteam || !email || !password) {\n // Prompt user to pick to select username/password or Auth Token\n const { authType } = await prompts({\n type: 'select',\n name: 'authType',\n message: 'Select authentication method',\n choices: [\n { title: 'Hosting Token', value: 'token' },\n { title: 'Username/Password', value: 'credentials' },\n ],\n });\n\n if (!authType) {\n console.error('!>'.red, 'No authentication method selected');\n return false;\n }\n\n if (authType === 'token') {\n // Prompt for hosting token\n const { token } = await prompts({\n type: 'password',\n name: 'token',\n message:\n 'Paste your server hosting token (from https://brickadia.com/account)',\n validate: value => (value ? true : 'Token is required'),\n });\n\n if (!token) {\n console.error('!>'.red, 'Hosting token is required');\n return false;\n }\n\n // Write to global token file\n try {\n console.log('>>'.green, 'Storing hosting token...');\n fs.writeFileSync(soft.GLOBAL_TOKEN, token.trim());\n } catch (err) {\n console.error('!>'.red, 'Error writing hosting token to config\\n', err);\n return false;\n }\n\n return true;\n }\n\n if (isSteam) {\n console.error(\n '!>'.red,\n 'Launching with steam requires a hosting token right now.',\n );\n return false;\n }\n\n // Non-steam password auth is handled below\n }\n\n if (!email || !password || !isSteam) {\n // prompt for user credentials\n try {\n [email, password] = await credentialPrompt();\n } catch (err) {\n console.error('!>'.red, 'Error prompting credentials\\n', err);\n return false;\n }\n }\n\n if (!email || !password) {\n console.error('!>'.red, 'Email and password are required');\n return false;\n }\n\n // generate auth tokens\n console.log('>>'.green, 'Generating auth tokens...');\n const timeout = setTimeout(() => {\n console.log('>>'.green, 'Probably also installing the game...');\n }, 10000);\n try {\n files = await genAuthFiles(email, password, {\n debug,\n branch,\n authDir,\n savedDir,\n launchArgs,\n });\n clearTimeout(timeout);\n } catch (err) {\n clearTimeout(timeout);\n console.error('!>'.red, 'Error generating tokens:', err);\n return false;\n }\n\n if (!files) {\n console.error('!>'.red, 'Authentication Failed');\n return;\n }\n\n // save the tokens to the config path (will be copied when omegga starts)\n try {\n console.log('>>'.green, 'Storing auth tokens...');\n const authPath = path.join(\n soft.CONFIG_HOME,\n savedDir && savedDir !== soft.CONFIG_SAVED_DIR ? savedDir : '',\n authDir ?? soft.CONFIG_AUTH_DIR,\n );\n file.mkdir(authPath);\n writeAuthFiles(authPath, files);\n } catch (err) {\n console.error('!>'.red, 'Error writing tokens to config\\n', err);\n return false;\n }\n\n console.log('>>'.green, 'Auth tokens successfully generated!');\n return true;\n}\n\n// check if auth files exist\nfunction authExists(dir?: string) {\n return soft.BRICKADIA_AUTH_FILES.every(f =>\n fs.existsSync(path.join(dir ?? AUTH_PATH, f)),\n );\n}\n\n// delete auth files stored in config\n// delete auth files stored in config\nfunction deleteAuthFiles(dir?: string) {\n const isConfigHome = dir && dir.startsWith(soft.CONFIG_HOME);\n // will not delete files outside of the config home\n if (dir && !isConfigHome) return;\n file.rmdir(dir ?? AUTH_PATH);\n\n // Remove the global auth token\n if (existsSync(soft.GLOBAL_TOKEN)) {\n fs.unlinkSync(soft.GLOBAL_TOKEN);\n }\n}\n\nexport function getGlobalToken() {\n if (!fs.existsSync(soft.GLOBAL_TOKEN)) return null;\n return fs.readFileSync(soft.GLOBAL_TOKEN, 'utf-8').trim();\n}\n\nexport const prompt = authFromPrompt;\nexport const exists = authExists;\nexport const clean = deleteAuthFiles;\n\nexport default {\n AUTH_PATH,\n prompt,\n exists,\n clean,\n getGlobalToken,\n};\n"],"names":["soft","genAuthFiles","file.mkdir","writeAuthFiles","file.rmdir","existsSync"],"mappings":";;;;;;;;;AAQO,MAAM,YAAY,KAAK,KAAKA,WAAAA,QAAK,aAAaA,WAAAA,QAAK,eAAe;AAGzE,eAAe,mBAAmB;AAChC,UAAQ;AAAA,IACN,KAAK;AAAA,IACL;AAAA,IACA,YAAY,MAAM;AAAA,IAClB;AAAA,EAAA;AAGF,QAAM,WAAW,MAAM,QAAQ;AAAA,IAC7B;AAAA,MACE,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,IAAA;AAAA,IAEX;AAAA,MACE,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,IAAA;AAAA,EACX,CACD;AAED,SAAO,CAAC,SAAS,OAAO,SAAS,QAAQ;AAC3C;AAEA,eAAe,eAAe;AAAA,EAC5B;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GASG;AACD,MAAI;AAEJ,MAAI,WAAW,CAAC,SAAS,CAAC,UAAU;AAElC,UAAM,EAAE,aAAa,MAAM,QAAQ;AAAA,MACjC,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS;AAAA,QACP,EAAE,OAAO,iBAAiB,OAAO,QAAA;AAAA,QACjC,EAAE,OAAO,qBAAqB,OAAO,cAAA;AAAA,MAAc;AAAA,IACrD,CACD;AAED,QAAI,CAAC,UAAU;AACb,cAAQ,MAAM,KAAK,KAAK,mCAAmC;AAC3D,aAAO;AAAA,IACT;AAEA,QAAI,aAAa,SAAS;AAExB,YAAM,EAAE,UAAU,MAAM,QAAQ;AAAA,QAC9B,MAAM;AAAA,QACN,MAAM;AAAA,QACN,SACE;AAAA,QACF,UAAU,CAAA,UAAU,QAAQ,OAAO;AAAA,MAAA,CACpC;AAED,UAAI,CAAC,OAAO;AACV,gBAAQ,MAAM,KAAK,KAAK,2BAA2B;AACnD,eAAO;AAAA,MACT;AAGA,UAAI;AACF,gBAAQ,IAAI,KAAK,OAAO,0BAA0B;AAClD,WAAG,cAAcA,WAAAA,QAAK,cAAc,MAAM,MAAM;AAAA,MAClD,SAAS,KAAK;AACZ,gBAAQ,MAAM,KAAK,KAAK,2CAA2C,GAAG;AACtE,eAAO;AAAA,MACT;AAEA,aAAO;AAAA,IACT;AAEA,QAAI,SAAS;AACX,cAAQ;AAAA,QACN,KAAK;AAAA,QACL;AAAA,MAAA;AAEF,aAAO;AAAA,IACT;AAAA,EAGF;AAEA,MAAI,CAAC,SAAS,CAAC,YAAY,CAAC,SAAS;AAEnC,QAAI;AACF,OAAC,OAAO,QAAQ,IAAI,MAAM,iBAAA;AAAA,IAC5B,SAAS,KAAK;AACZ,cAAQ,MAAM,KAAK,KAAK,iCAAiC,GAAG;AAC5D,aAAO;AAAA,IACT;AAAA,EACF;AAEA,MAAI,CAAC,SAAS,CAAC,UAAU;AACvB,YAAQ,MAAM,KAAK,KAAK,iCAAiC;AACzD,WAAO;AAAA,EACT;AAGA,UAAQ,IAAI,KAAK,OAAO,2BAA2B;AACnD,QAAM,UAAU,WAAW,MAAM;AAC/B,YAAQ,IAAI,KAAK,OAAO,sCAAsC;AAAA,EAChE,GAAG,GAAK;AACR,MAAI;AACF,YAAQ,MAAMC,KAAAA,aAAa,OAAO,UAAU;AAAA,MAC1C;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA,CACD;AACD,iBAAa,OAAO;AAAA,EACtB,SAAS,KAAK;AACZ,iBAAa,OAAO;AACpB,YAAQ,MAAM,KAAK,KAAK,4BAA4B,GAAG;AACvD,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,OAAO;AACV,YAAQ,MAAM,KAAK,KAAK,uBAAuB;AAC/C;AAAA,EACF;AAGA,MAAI;AACF,YAAQ,IAAI,KAAK,OAAO,wBAAwB;AAChD,UAAM,WAAW,KAAK;AAAA,MACpBD,WAAAA,QAAK;AAAA,MACL,YAAY,aAAaA,WAAAA,QAAK,mBAAmB,WAAW;AAAA,MAC5D,WAAWA,WAAAA,QAAK;AAAA,IAAA;AAElBE,SAAAA,MAAW,QAAQ;AACnBC,SAAAA,eAAe,UAAU,KAAK;AAAA,EAChC,SAAS,KAAK;AACZ,YAAQ,MAAM,KAAK,KAAK,oCAAoC,GAAG;AAC/D,WAAO;AAAA,EACT;AAEA,UAAQ,IAAI,KAAK,OAAO,qCAAqC;AAC7D,SAAO;AACT;AAGA,SAAS,WAAW,KAAc;AAChC,SAAOH,WAAAA,QAAK,qBAAqB;AAAA,IAAM,CAAA,MACrC,GAAG,WAAW,KAAK,KAAK,OAAO,WAAW,CAAC,CAAC;AAAA,EAAA;AAEhD;AAIA,SAAS,gBAAgB,KAAc;AACrC,QAAM,eAAe,OAAO,IAAI,WAAWA,WAAAA,QAAK,WAAW;AAE3D,MAAI,OAAO,CAAC,aAAc;AAC1BI,OAAAA,MAAW,OAAO,SAAS;AAG3B,MAAIC,GAAAA,WAAWL,mBAAK,YAAY,GAAG;AACjC,OAAG,WAAWA,WAAAA,QAAK,YAAY;AAAA,EACjC;AACF;AAEO,SAAS,iBAAiB;AAC/B,MAAI,CAAC,GAAG,WAAWA,WAAAA,QAAK,YAAY,EAAG,QAAO;AAC9C,SAAO,GAAG,aAAaA,WAAAA,QAAK,cAAc,OAAO,EAAE,KAAA;AACrD;AAEO,MAAM,SAAS;AACf,MAAM,SAAS;AACf,MAAM,QAAQ;AAErB,MAAA,QAAe;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;;;;;"}
@@ -45,9 +45,11 @@ const transformers = [
45
45
  }
46
46
  ];
47
47
  const transformUrl = (url) => {
48
- const found = transformers.find((t) => url.match(t.pattern));
49
- if (!found) return { type: "raw", url };
50
- return found.fn(url.match(found.pattern).groups);
48
+ for (const t of transformers) {
49
+ const match = url.match(t.pattern);
50
+ if (match?.groups) return t.fn(match.groups);
51
+ }
52
+ return { type: "raw", url };
51
53
  };
52
54
  let needsNL = false;
53
55
  const rewriteLine = (...args) => {
@@ -65,7 +67,12 @@ const plg = (plugin2, ...args) => {
65
67
  };
66
68
  const plgLog = (plugin2, ...args) => {
67
69
  if (logger.default.VERBOSE) plg(plugin2, ...args);
68
- else rewriteLine(plugin2.name, ">>".green, ...args);
70
+ else
71
+ rewriteLine(
72
+ plugin2.name ?? ("url" in plugin2 ? plugin2.url : ""),
73
+ ">>".green,
74
+ ...args
75
+ );
69
76
  };
70
77
  const plgWarn = (plugin2, ...args) => {
71
78
  if (needsNL) {
@@ -162,16 +169,17 @@ async function install(plugins, _options) {
162
169
  );
163
170
  log("Attempting to install", (plugins.length + "").yellow, "plugins...");
164
171
  for (const pluginUrl of plugins) {
165
- const plugin2 = transformUrl(pluginUrl);
166
- if (!("name" in plugin2)) {
172
+ const transformed = transformUrl(pluginUrl);
173
+ let name = transformed.name;
174
+ if (!name) {
167
175
  try {
168
- const { name } = path.parse(plugin2.url);
169
- plugin2.name = name.replace(/^omegga-/, "");
176
+ name = path.parse(transformed.url).name.replace(/^omegga-/, "");
170
177
  } catch (e) {
171
- console.error("!>".red, "Error parsing name from url", plugin2.url);
178
+ console.error("!>".red, "Error parsing name from url", transformed.url);
172
179
  break;
173
180
  }
174
181
  }
182
+ const plugin2 = { ...transformed, name };
175
183
  log(
176
184
  "Installing plugin",
177
185
  plugin2.name.yellow,
@@ -210,7 +218,7 @@ async function install(plugins, _options) {
210
218
  verboseLog("Changing permission of", postInstallPath);
211
219
  fs.chmodSync(postInstallPath, "0755");
212
220
  verboseLog("Executing bash file");
213
- let { stdout, stderr } = await exec(postInstallPath, {
221
+ const { stdout, stderr } = await exec(postInstallPath, {
214
222
  cwd: pluginPath,
215
223
  shell: "bash"
216
224
  });
@@ -242,7 +250,7 @@ async function update(pluginsNames, _options) {
242
250
  return;
243
251
  }
244
252
  log("Checking", (plugins.length + "").yellow, "plugins for updates...");
245
- let pluginsToUpdate = [];
253
+ const pluginsToUpdate = [];
246
254
  for (const plugin2 of plugins) {
247
255
  if (needsNL) {
248
256
  console.log();
@@ -262,6 +270,7 @@ async function update(pluginsNames, _options) {
262
270
  status = await git.status();
263
271
  } catch (e) {
264
272
  plgErr(plugin2, "Error getting status", e);
273
+ continue;
265
274
  }
266
275
  try {
267
276
  if (status.files.some((f) => f.working_dir !== "?")) {
@@ -317,7 +326,7 @@ async function update(pluginsNames, _options) {
317
326
  continue;
318
327
  }
319
328
  plgLog(plugin2, "Update available");
320
- pluginsToUpdate.push(plugin2);
329
+ pluginsToUpdate.push({ ...plugin2, path: pluginPath, git });
321
330
  } catch (e) {
322
331
  plgErr(plugin2, "Error", e);
323
332
  }
@@ -359,7 +368,7 @@ async function update(pluginsNames, _options) {
359
368
  plgLog(plugin2, "Running post install script...");
360
369
  try {
361
370
  fs.chmodSync(postInstallPath, "0755");
362
- let { stdout, stderr } = await exec(postInstallPath, {
371
+ const { stdout, stderr } = await exec(postInstallPath, {
363
372
  cwd: plugin2.path,
364
373
  shell: "bash"
365
374
  });
@@ -545,6 +554,10 @@ async function init() {
545
554
  }
546
555
  function pluginLoaderFactory() {
547
556
  const workDir = getWorkDir();
557
+ if (!workDir) {
558
+ err("Not an omegga directory, run ", "omegga init".yellow, "to setup one.");
559
+ process.exit(1);
560
+ }
548
561
  return new plugin.PluginLoader(workDir);
549
562
  }
550
563
  async function loadPlugin(pluginName) {
@@ -559,12 +572,24 @@ async function loadPlugin(pluginName) {
559
572
  const plugin2 = await pluginLoader.scanPlugin(
560
573
  path.join(pluginLoader.path, foundPluginDirectory)
561
574
  );
575
+ if (!plugin2) {
576
+ err(`Plugin ${pluginName} failed to load!`);
577
+ process.exit(1);
578
+ }
562
579
  return plugin2;
563
580
  }
581
+ function getConfigDoc(plugin2) {
582
+ const configDoc = plugin2.getDocumentation()?.config;
583
+ if (!configDoc) {
584
+ err("Plugin", plugin2.getName().cyan, "has no config documentation");
585
+ process.exit(1);
586
+ }
587
+ return configDoc;
588
+ }
564
589
  async function listConfig(pluginName, json = false) {
565
590
  const plugin2 = await loadPlugin(pluginName);
566
- const configDoc = plugin2.getDocumentation().config;
567
- const config2 = await plugin2.storage.getConfig();
591
+ const configDoc = plugin2.getDocumentation()?.config ?? {};
592
+ const config2 = await plugin2.storage.getConfig() ?? {};
568
593
  if (json) {
569
594
  console.log(JSON.stringify(config2, null, 2));
570
595
  } else {
@@ -574,7 +599,7 @@ async function listConfig(pluginName, json = false) {
574
599
  log(
575
600
  key.cyan,
576
601
  "=",
577
- ["string", "number", "boolean"].includes(typeof value) ? value.toString().yellow : JSON.stringify(value).yellow
602
+ typeof value === "string" || typeof value === "number" || typeof value === "boolean" ? value.toString().yellow : JSON.stringify(value).yellow
578
603
  );
579
604
  }
580
605
  }
@@ -582,12 +607,12 @@ async function listConfig(pluginName, json = false) {
582
607
  }
583
608
  async function getConfig(pluginName, configName, json = false) {
584
609
  const plugin2 = await loadPlugin(pluginName);
585
- const configDoc = plugin2.getDocumentation().config[configName];
610
+ const configDoc = getConfigDoc(plugin2)[configName];
586
611
  if (configDoc === void 0) {
587
612
  err("Config", configName.cyan, "not found");
588
613
  process.exit(1);
589
614
  }
590
- const config2 = await plugin2.storage.getConfig();
615
+ const config2 = await plugin2.storage.getConfig() ?? {};
591
616
  if (json) {
592
617
  console.log(JSON.stringify(config2[configName], null, 2));
593
618
  } else {
@@ -597,7 +622,7 @@ async function getConfig(pluginName, configName, json = false) {
597
622
  }
598
623
  async function setConfig(pluginName, configName, valueString) {
599
624
  const plugin2 = await loadPlugin(pluginName);
600
- const configDoc = plugin2.getDocumentation().config[configName];
625
+ const configDoc = getConfigDoc(plugin2)[configName];
601
626
  if (configDoc === void 0) {
602
627
  err("Config", configName.cyan, "not found");
603
628
  process.exit(1);
@@ -633,7 +658,7 @@ async function setConfig(pluginName, configName, valueString) {
633
658
  err("set-context does not support config type", configDoc.type.cyan);
634
659
  process.exit(1);
635
660
  }
636
- const pluginConfig = await plugin2.storage.getConfig();
661
+ const pluginConfig = await plugin2.storage.getConfig() ?? {};
637
662
  pluginConfig[configName] = parsed;
638
663
  await plugin2.storage.setConfig(pluginConfig);
639
664
  log(
@@ -669,12 +694,12 @@ async function resetAllConfigs(pluginName, force) {
669
694
  }
670
695
  async function resetConfig(pluginName, configName) {
671
696
  const plugin2 = await loadPlugin(pluginName);
672
- const configDoc = plugin2.getDocumentation().config[configName];
697
+ const configDoc = getConfigDoc(plugin2)[configName];
673
698
  if (configDoc === void 0) {
674
699
  err("Config", configName.cyan, "not found");
675
700
  process.exit(1);
676
701
  }
677
- const pluginConfig = await plugin2.storage.getConfig();
702
+ const pluginConfig = await plugin2.storage.getConfig() ?? {};
678
703
  pluginConfig[configName] = plugin2.storage.getDefaultConfig()[configName];
679
704
  await plugin2.storage.setConfig(pluginConfig);
680
705
  log(