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 CHANGED
@@ -42,6 +42,17 @@ Omegga plugins can do things like:
42
42
 
43
43
  Omegga runs on linux, including the [Windows Subsystem for Linux](docs/install/wsl.md). **Do not install or run it as root**; if `whoami` prints "root", [create a new user](docs/install/linux.md#creating-a-new-user) first.
44
44
 
45
+ Get Omegga on Debian, Ubuntu, Fedora, or Arch with:
46
+
47
+ ```sh
48
+ curl -fsSL https://omegga.brickadia.dev/install.sh | bash
49
+ ```
50
+
51
+ [Omegga `install.sh` Docs](docs/install/linux.md#quick-setup) | [Omegga `install.sh` Source](tools/install.sh)
52
+
53
+ <details>
54
+ <summary>Manual Install for Debian or Ubuntu</summary>
55
+
45
56
  ```sh
46
57
  # update what's installed, then install what omegga needs
47
58
  sudo apt update && sudo apt upgrade
@@ -60,6 +71,8 @@ nvm install 24
60
71
  npm i -g omegga
61
72
  ```
62
73
 
74
+ </details>
75
+
63
76
  Then make a folder for your server and start it:
64
77
 
65
78
  ```sh
@@ -127,7 +140,7 @@ pages under `docs/api` are generated from the JSDoc in `src/`; run
127
140
  1. **Bump**: Bump `version` in `package.json` and sync the lockfile with
128
141
  `npm i --package-lock-only`.
129
142
  2. **Changelog**: Move the `## Latest` entries in `CHANGELOG.md` under a
130
- `## <version> - <date>` heading.
143
+ `## <version> - <date>` heading. Preview with `just notes`
131
144
  3. **Pterodactyl**: Point the pinned image in `docker/pterodactyl/egg-omegga.json` at the new
132
145
  `<major>.<minor>`. Releases only move `latest`, `<major>`, and
133
146
  `<major>.<minor>`, so a pin left on the previous minor is frozen on an
@@ -135,5 +148,11 @@ pages under `docs/api` are generated from the JSDoc in `src/`; run
135
148
  panel.
136
149
  4. **Linters**: `just check`
137
150
  5. **Commit**: Ensure everything is pushed
138
- 6. **Deploy Container**: `just tag` to push tag the image to ghcr.
139
- 7. **Deploy NPM**: `just publish` to build `dist/` and publish the package to npm.
151
+ 6. **Tag**: `just tag` pushes `v<version>` and builds the ghcr image and npm releases
152
+ 7. **Approve the npm release**: A developer with npm access must do the following:
153
+
154
+ ```sh
155
+ npm stage list omegga # find the stage-id
156
+ npm stage download <id> # optional, inspect the tarball before approving
157
+ npm stage approve <id> # prompts for an OTP, then publishes
158
+ ```
@@ -10,17 +10,23 @@ const EventEmitter = require("node:events");
10
10
  const fs = require("node:fs");
11
11
  const path = require("node:path");
12
12
  const node_process = require("node:process");
13
+ const distro = require("../util/distro.js");
13
14
  const readline = require("readline");
14
15
  const stripAnsi = require("strip-ansi");
16
+ const MISSING_LIBRARY_FIX = distro.installHint({
17
+ debian: "libgl1 libglib2.0-0",
18
+ fedora: "mesa-libGL glib2",
19
+ arch: "libglvnd glib2"
20
+ });
15
21
  const knownErrors = [
16
22
  {
17
23
  name: "MISSING_LIBGL",
18
- solution: "apt-get install libgl1-mesa-glx libglib2.0-0",
24
+ solution: MISSING_LIBRARY_FIX,
19
25
  match: /error while loading shared libraries: libGL\.so\.1: cannot open shared object file/
20
26
  },
21
27
  {
22
28
  name: "MISSING_GLIB",
23
- solution: "apt-get install libgl1-mesa-glx libglib2.0-0",
29
+ solution: MISSING_LIBRARY_FIX,
24
30
  match: /error while loading shared libraries: libgthread-2\.0\.so\.0: cannot open shared object file/
25
31
  }
26
32
  ];
@@ -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 { 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 /**\n * Resolve the directory the game server binary lives in. Null whenever\n * {@link getGameBinaryPath} can't resolve a binary.\n */\n getGameBinaryDir(): string | null {\n const binary = this.getGameBinaryPath();\n return binary && path.dirname(binary);\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 // remote file access is on unless explicitly disabled in the config\n this.config.server.remoteFiles === false ? '-NoRemoteFileAccess' : null,\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 /**\n * pid of the running game process, or null when it isn't running.\n *\n * The child is spawned through `stdbuf`, which execs the game in place, so\n * this is the game's own pid and /proc entry.\n */\n get pid(): number | null {\n return this.#child?.pid ?? null;\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. this signals the pid\n // directly rather than shelling out: slim container images have no\n // `kill` binary, only the shell builtin, and spawn cannot use that.\n this.#child.kill('SIGKILL');\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(code: number | null, signal: NodeJS.Signals | null) {\n Logger.verbose('Exit listener fired');\n this.emit('closed', code, signal);\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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"}
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 { installHint } from '@util/distro';\nimport readline from 'readline';\nimport stripAnsi from 'strip-ansi';\n\n/**\n * libgl1-mesa-glx was dropped after Debian 11 and Ubuntu 22.04; libgl1 is what\n * provides libGL.so.1 there now.\n */\nconst MISSING_LIBRARY_FIX = installHint({\n debian: 'libgl1 libglib2.0-0',\n fedora: 'mesa-libGL glib2',\n arch: 'libglvnd glib2',\n});\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: MISSING_LIBRARY_FIX,\n match:\n /error while loading shared libraries: libGL\\.so\\.1: cannot open shared object file/,\n },\n {\n name: 'MISSING_GLIB',\n solution: MISSING_LIBRARY_FIX,\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 /**\n * Resolve the directory the game server binary lives in. Null whenever\n * {@link getGameBinaryPath} can't resolve a binary.\n */\n getGameBinaryDir(): string | null {\n const binary = this.getGameBinaryPath();\n return binary && path.dirname(binary);\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 // remote file access is on unless explicitly disabled in the config\n this.config.server.remoteFiles === false ? '-NoRemoteFileAccess' : null,\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 /**\n * pid of the running game process, or null when it isn't running.\n *\n * The child is spawned through `stdbuf`, which execs the game in place, so\n * this is the game's own pid and /proc entry.\n */\n get pid(): number | null {\n return this.#child?.pid ?? null;\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. this signals the pid\n // directly rather than shelling out: slim container images have no\n // `kill` binary, only the shell builtin, and spawn cannot use that.\n this.#child.kill('SIGKILL');\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(code: number | null, signal: NodeJS.Signals | null) {\n Logger.verbose('Exit listener fired');\n this.emit('closed', code, signal);\n this.cleanup();\n }\n\n lineListener(line: string) {\n this.emit('line', stripAnsi(line));\n }\n}\n"],"names":["installHint","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
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
3
+ const logger = require("../logger.js");
3
4
  const softconfig = require("../softconfig.js");
4
5
  const auth = require("../omegga/auth.js");
5
6
  const env = require("../util/env.js");
@@ -10,12 +11,10 @@ const path = require("node:path");
10
11
  const prompts = require("prompts");
11
12
  const AUTH_PATH = path.join(softconfig.default.CONFIG_HOME, softconfig.default.CONFIG_AUTH_DIR);
12
13
  function failNonInteractive() {
13
- console.error(
14
- "!>".red,
14
+ logger.default.errorp(
15
15
  "No stored auth files and no hosting token, and there is no terminal to prompt on"
16
16
  );
17
- console.error(
18
- "!>".red,
17
+ logger.default.errorp(
19
18
  "Generate a hosting token at https://brickadia.com/account/tokens, then set",
20
19
  "BRICKADIA_TOKEN".yellow,
21
20
  "or",
@@ -25,12 +24,7 @@ function failNonInteractive() {
25
24
  return false;
26
25
  }
27
26
  async function credentialPrompt() {
28
- console.log(
29
- ">>".green,
30
- "Enter",
31
- "Brickadia".green.underline,
32
- "credentials (not stored)"
33
- );
27
+ logger.default.logp("Enter", "Brickadia".green.underline, "credentials (not stored)");
34
28
  const response = await prompts([
35
29
  {
36
30
  type: "text",
@@ -68,7 +62,7 @@ async function authFromPrompt({
68
62
  ]
69
63
  });
70
64
  if (!authType) {
71
- console.error("!>".red, "No authentication method selected");
65
+ logger.default.errorp("No authentication method selected");
72
66
  return false;
73
67
  }
74
68
  if (authType === "token") {
@@ -79,23 +73,20 @@ async function authFromPrompt({
79
73
  validate: (value) => value ? true : "Token is required"
80
74
  });
81
75
  if (!token) {
82
- console.error("!>".red, "Hosting token is required");
76
+ logger.default.errorp("Hosting token is required");
83
77
  return false;
84
78
  }
85
79
  try {
86
- console.log(">>".green, "Storing hosting token...");
80
+ logger.default.logp("Storing hosting token...");
87
81
  fs.writeFileSync(softconfig.default.GLOBAL_TOKEN, token.trim());
88
82
  } catch (err) {
89
- console.error("!>".red, "Error writing hosting token to config\n", err);
83
+ logger.default.errorp("Error writing hosting token to config\n", err);
90
84
  return false;
91
85
  }
92
86
  return true;
93
87
  }
94
88
  if (isSteam) {
95
- console.error(
96
- "!>".red,
97
- "Launching with steam requires a hosting token right now."
98
- );
89
+ logger.default.errorp("Launching with steam requires a hosting token right now.");
99
90
  return false;
100
91
  }
101
92
  }
@@ -104,17 +95,17 @@ async function authFromPrompt({
104
95
  try {
105
96
  [email, password] = await credentialPrompt();
106
97
  } catch (err) {
107
- console.error("!>".red, "Error prompting credentials\n", err);
98
+ logger.default.errorp("Error prompting credentials\n", err);
108
99
  return false;
109
100
  }
110
101
  }
111
102
  if (!email || !password) {
112
- console.error("!>".red, "Email and password are required");
103
+ logger.default.errorp("Email and password are required");
113
104
  return false;
114
105
  }
115
- console.log(">>".green, "Generating auth tokens...");
106
+ logger.default.logp("Generating auth tokens...");
116
107
  const timeout = setTimeout(() => {
117
- console.log(">>".green, "Probably also installing the game...");
108
+ logger.default.logp("Probably also installing the game...");
118
109
  }, 1e4);
119
110
  try {
120
111
  files = await auth.genAuthFiles(email, password, {
@@ -127,15 +118,15 @@ async function authFromPrompt({
127
118
  clearTimeout(timeout);
128
119
  } catch (err) {
129
120
  clearTimeout(timeout);
130
- console.error("!>".red, "Error generating tokens:", err);
121
+ logger.default.errorp("Error generating tokens:", err);
131
122
  return false;
132
123
  }
133
124
  if (!files) {
134
- console.error("!>".red, "Authentication Failed");
125
+ logger.default.errorp("Authentication Failed");
135
126
  return;
136
127
  }
137
128
  try {
138
- console.log(">>".green, "Storing auth tokens...");
129
+ logger.default.logp("Storing auth tokens...");
139
130
  const authPath = path.join(
140
131
  softconfig.default.CONFIG_HOME,
141
132
  savedDir && savedDir !== softconfig.default.CONFIG_SAVED_DIR ? savedDir : "",
@@ -144,10 +135,10 @@ async function authFromPrompt({
144
135
  file.mkdir(authPath);
145
136
  auth.writeAuthFiles(authPath, files);
146
137
  } catch (err) {
147
- console.error("!>".red, "Error writing tokens to config\n", err);
138
+ logger.default.errorp("Error writing tokens to config\n", err);
148
139
  return false;
149
140
  }
150
- console.log(">>".green, "Auth tokens successfully generated!");
141
+ logger.default.logp("Auth tokens successfully generated!");
151
142
  return true;
152
143
  }
153
144
  function authExists(dir) {
@@ -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 { isNonInteractive } from '@util/env';\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/**\n * Refuse to authenticate rather than opening a prompt nothing can answer.\n *\n * Reaching a prompt on such a host is worse than failing: omegga blocks\n * before it logs anything a supervisor recognises as a start, so the failure\n * shows up as a startup timeout with no cause in the log.\n */\nfunction failNonInteractive() {\n console.error(\n '!>'.red,\n 'No stored auth files and no hosting token, and there is no terminal to prompt on',\n );\n console.error(\n '!>'.red,\n 'Generate a hosting token at https://brickadia.com/account/tokens, then set',\n 'BRICKADIA_TOKEN'.yellow,\n 'or',\n 'credentials.token'.yellow,\n 'in omegga-config.yml',\n );\n return false;\n}\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 if (isNonInteractive()) return failNonInteractive();\n\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 (generate one at https://brickadia.com/account/tokens)',\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 if (isNonInteractive()) return failNonInteractive();\n\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","isNonInteractive","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 Logger from '@/logger';\nimport soft from '@/softconfig';\nimport { genAuthFiles, writeAuthFiles } from '@omegga/auth';\nimport { isNonInteractive } from '@util/env';\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/**\n * Refuse to authenticate rather than opening a prompt nothing can answer.\n *\n * Reaching a prompt on such a host is worse than failing: omegga blocks\n * before it logs anything a supervisor recognises as a start, so the failure\n * shows up as a startup timeout with no cause in the log.\n */\nfunction failNonInteractive() {\n Logger.errorp(\n 'No stored auth files and no hosting token, and there is no terminal to prompt on',\n );\n Logger.errorp(\n 'Generate a hosting token at https://brickadia.com/account/tokens, then set',\n 'BRICKADIA_TOKEN'.yellow,\n 'or',\n 'credentials.token'.yellow,\n 'in omegga-config.yml',\n );\n return false;\n}\n\n// async function to prompt for credentials\nasync function credentialPrompt() {\n Logger.logp('Enter', 'Brickadia'.green.underline, 'credentials (not stored)');\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 if (isNonInteractive()) return failNonInteractive();\n\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 Logger.errorp('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 (generate one at https://brickadia.com/account/tokens)',\n validate: value => (value ? true : 'Token is required'),\n });\n\n if (!token) {\n Logger.errorp('Hosting token is required');\n return false;\n }\n\n // Write to global token file\n try {\n Logger.logp('Storing hosting token...');\n fs.writeFileSync(soft.GLOBAL_TOKEN, token.trim());\n } catch (err) {\n Logger.errorp('Error writing hosting token to config\\n', err);\n return false;\n }\n\n return true;\n }\n\n if (isSteam) {\n Logger.errorp('Launching with steam requires a hosting token right now.');\n return false;\n }\n\n // Non-steam password auth is handled below\n }\n\n if (!email || !password || !isSteam) {\n if (isNonInteractive()) return failNonInteractive();\n\n // prompt for user credentials\n try {\n [email, password] = await credentialPrompt();\n } catch (err) {\n Logger.errorp('Error prompting credentials\\n', err);\n return false;\n }\n }\n\n if (!email || !password) {\n Logger.errorp('Email and password are required');\n return false;\n }\n\n // generate auth tokens\n Logger.logp('Generating auth tokens...');\n const timeout = setTimeout(() => {\n Logger.logp('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 Logger.errorp('Error generating tokens:', err);\n return false;\n }\n\n if (!files) {\n Logger.errorp('Authentication Failed');\n return;\n }\n\n // save the tokens to the config path (will be copied when omegga starts)\n try {\n Logger.logp('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 Logger.errorp('Error writing tokens to config\\n', err);\n return false;\n }\n\n Logger.logp('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","Logger","isNonInteractive","genAuthFiles","file.mkdir","writeAuthFiles","file.rmdir","existsSync"],"mappings":"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"}
@@ -64,7 +64,7 @@ const plg = (plugin2, ...args) => {
64
64
  needsNL = false;
65
65
  console.log();
66
66
  }
67
- console.log(plugin2.name, ">>".green, ...args);
67
+ logger.default.log(plugin2.name, ">>".green, ...args);
68
68
  };
69
69
  const plgLog = (plugin2, ...args) => {
70
70
  if (logger.default.VERBOSE) plg(plugin2, ...args);
@@ -80,28 +80,28 @@ const plgWarn = (plugin2, ...args) => {
80
80
  needsNL = false;
81
81
  console.warn();
82
82
  }
83
- console.warn(plugin2.name, "W>".yellow, ...args);
83
+ logger.default.warn(plugin2.name, "W>".yellow, ...args);
84
84
  };
85
85
  const plgErr = (plugin2, ...args) => {
86
86
  if (needsNL) {
87
87
  needsNL = false;
88
88
  console.error();
89
89
  }
90
- console.error(plugin2.name, "!>".red, ...args);
90
+ logger.default.error(plugin2.name, "!>".red, ...args);
91
91
  };
92
92
  const err = (...args) => {
93
93
  if (needsNL) {
94
94
  needsNL = false;
95
95
  console.error();
96
96
  }
97
- console.error("!>".red, ...args);
97
+ logger.default.errorp(...args);
98
98
  };
99
99
  const log = (...args) => {
100
100
  if (needsNL) {
101
101
  needsNL = false;
102
102
  console.log();
103
103
  }
104
- console.log(">>".green, ...args);
104
+ logger.default.logp(...args);
105
105
  };
106
106
  const verboseLog = (...args) => {
107
107
  if (!logger.default.VERBOSE) return;
@@ -109,7 +109,7 @@ const verboseLog = (...args) => {
109
109
  needsNL = false;
110
110
  console.log();
111
111
  }
112
- console.log("V>".magenta, ...args);
112
+ logger.default.verbose(...args);
113
113
  };
114
114
  function checkPlugin(omeggaPath, plugin2) {
115
115
  const pluginPath = path.join(omeggaPath, softconfig.default.PLUGIN_PATH, plugin2.name);
@@ -176,7 +176,7 @@ async function install(plugins, _options) {
176
176
  try {
177
177
  name = path.parse(transformed.url).name.replace(/^omegga-/, "");
178
178
  } catch {
179
- console.error("!>".red, "Error parsing name from url", transformed.url);
179
+ err("Error parsing name from url", transformed.url);
180
180
  break;
181
181
  }
182
182
  }
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sources":["../../src/cli/plugin.ts"],"sourcesContent":["import Logger from '@/logger';\nimport soft from '@/softconfig';\nimport * as config from '@config';\nimport { PluginLoader } from '@omegga/plugin';\nimport { getPluginGit } from '@cli/plugin/git';\nimport { exec as execNonPromise } from 'node:child_process';\nimport 'colors';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport prompts from 'prompts';\nimport semver from 'semver';\nimport { promisify } from 'node:util';\nimport { VERSION } from '@/version';\n\nconst exec = promisify(execNonPromise);\n\nconst MAIN_BRANCHES = ['master', 'main'];\n\n// get the working directory for omegga\nfunction getWorkDir() {\n // default working directory is the one specified in config\n let workDir = config.store.get('defaultOmegga');\n verboseLog('Using working directory', workDir.yellow);\n\n // if there's a config in the current directory, use that one instead\n if (config.find('.')) workDir = '.';\n\n const confFile = config.find(workDir);\n\n return confFile\n ? path.resolve(process.cwd(), path.dirname(confFile))\n : undefined;\n}\n\ninterface IPlugin {\n name?: string;\n type: string | 'raw';\n url: string;\n}\n\ninterface IInstalledPlugin {\n name: string;\n path?: string;\n}\n\ninterface ITransformer {\n pattern: RegExp;\n fn(match: Record<string, string>): IPlugin;\n}\n\nconst PLUGIN_TYPES = ['safe', 'safe-ts', 'unsafe', 'rust', 'rpc'];\ntype PluginType = (typeof PLUGIN_TYPES)[number];\n\n// plugin url transformers\nconst transformers: ITransformer[] = [\n {\n // github transformer\n pattern: /^gh:(?<owner>[^/]+)\\/(?<repo>[^/]+)$/,\n fn: ({ owner, repo }) => ({\n type: 'short',\n name: repo,\n url: `https://github.com/${owner}/omegga-${repo}`,\n }),\n },\n {\n // gitlab transformer\n pattern: /^gl:(?<owner>[^/]+)\\/(?<repo>[^/]+)$/,\n fn: ({ owner, repo }) => ({\n type: 'short',\n name: repo,\n url: `https://gitlab.com/${owner}/omegga-${repo}`,\n }),\n },\n];\n\n// convert a shortened url into a full length one\nconst transformUrl = (url: string): IPlugin => {\n for (const t of transformers) {\n const match = url.match(t.pattern);\n if (match?.groups) return t.fn(match.groups);\n }\n return { type: 'raw', url };\n};\n\nlet needsNL = false;\n\n// rewrite a console line\nconst rewriteLine = (...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 process.stdout.write(args.join(' '));\n needsNL = true;\n};\n\n// logging helper functions\nconst plg = (plugin: IPlugin | IInstalledPlugin, ...args: unknown[]) => {\n if (needsNL) {\n needsNL = false;\n console.log();\n }\n console.log(plugin.name, '>>'.green, ...args);\n};\nconst plgLog = (plugin: IPlugin | IInstalledPlugin, ...args: unknown[]) => {\n if (Logger.VERBOSE) plg(plugin, ...args);\n else\n rewriteLine(\n plugin.name ?? ('url' in plugin ? plugin.url : ''),\n '>>'.green,\n ...args,\n );\n};\nconst plgWarn = (plugin: IPlugin | IInstalledPlugin, ...args: unknown[]) => {\n if (needsNL) {\n needsNL = false;\n console.warn();\n }\n console.warn(plugin.name, 'W>'.yellow, ...args);\n};\nconst plgErr = (plugin: IPlugin | IInstalledPlugin, ...args: unknown[]) => {\n if (needsNL) {\n needsNL = false;\n console.error();\n }\n console.error(plugin.name, '!>'.red, ...args);\n};\n\nconst err = (...args: unknown[]) => {\n if (needsNL) {\n needsNL = false;\n console.error();\n }\n console.error('!>'.red, ...args);\n};\nconst log = (...args: unknown[]) => {\n if (needsNL) {\n needsNL = false;\n console.log();\n }\n console.log('>>'.green, ...args);\n};\nconst verboseLog = (...args: unknown[]) => {\n if (!Logger.VERBOSE) return;\n if (needsNL) {\n needsNL = false;\n console.log();\n }\n console.log('V>'.magenta, ...args);\n};\n\nfunction checkPlugin(\n omeggaPath: string,\n plugin: (IPlugin & { name: string }) | IInstalledPlugin,\n) {\n const pluginPath = path.join(omeggaPath, soft.PLUGIN_PATH, plugin.name);\n\n // check for the plugin file, whatever it's called\n if (fs.existsSync(path.join(pluginPath, soft.PLUGIN_FILE))) {\n if (needsNL) {\n console.log();\n needsNL = false;\n }\n plgLog(plugin, 'Checking plugin file...');\n let data;\n try {\n data = JSON.parse(\n fs.readFileSync(path.join(pluginPath, soft.PLUGIN_FILE), 'utf8'),\n );\n } catch (e) {\n plgErr(plugin, 'Error reading plugin file', e);\n return false;\n }\n\n // make sure this plugin version is okay.\n // in the future, this may include deprecation warnings and format changes\n if (data.formatVersion !== 1) {\n plgWarn(\n plugin,\n 'WARNING - Plugin file has invalid',\n 'formatVersion'.yellow + '. Expected',\n '1'.yellow,\n );\n return;\n }\n\n // check the omeggaVersion field of the plugin file\n if (!data.omeggaVersion || !semver.validRange(data.omeggaVersion)) {\n plgWarn(\n plugin,\n 'WARNING - Plugin file has invalid',\n 'omeggaVersion'.yellow + '. Expected semver expression',\n );\n return false;\n } else if (!semver.satisfies(VERSION, data.omeggaVersion)) {\n plgWarn(\n plugin,\n `WARNING - Plugin is not made for this version of omegga (${VERSION.yellow} vs ${data.omeggaVersion.yellow})`,\n );\n return false;\n }\n\n plgLog(plugin, 'Plugin file', 'OK'.green);\n return true;\n }\n\n // no plugin file, no problem!\n plgWarn(\n plugin,\n `WARNING - Plugin is missing plugin file (${soft.PLUGIN_FILE}), this may be a problem in future versions`,\n );\n return true;\n}\n\n// install plugins from a list of plugins\nexport async function install(plugins: string[], _options: unknown) {\n const omeggaPath = getWorkDir();\n if (!omeggaPath)\n return err(\n 'Not an omegga directory, run ',\n 'omegga init'.yellow,\n 'to setup one.',\n );\n\n log('Attempting to install', (plugins.length + '').yellow, 'plugins...');\n\n for (const pluginUrl of plugins) {\n const transformed = transformUrl(pluginUrl);\n\n // if the plugin wasn't transformed, try to extract its name from the git url\n let name = transformed.name;\n if (!name) {\n try {\n name = path.parse(transformed.url).name.replace(/^omegga-/, '');\n } catch {\n console.error('!>'.red, 'Error parsing name from url', transformed.url);\n break;\n }\n }\n const plugin = { ...transformed, name };\n\n log(\n 'Installing plugin',\n plugin.name.yellow,\n 'from',\n plugin.url.yellow + '...',\n );\n\n // plugin absolute path\n const pluginPath = path.join(omeggaPath, soft.PLUGIN_PATH, plugin.name);\n\n // check if plugin already exists\n // TODO: if force is passed in, remove the old directory\n if (\n fs.existsSync(pluginPath) &&\n fs.existsSync(path.join(pluginPath, 'doc.json'))\n ) {\n plgErr(\n plugin,\n 'Directory already exists! Try',\n ('omegga update ' + plugin.name).yellow,\n 'or check for plugin name collisions',\n );\n continue;\n }\n\n if (!fs.existsSync(pluginPath)) {\n // create plugin local directory\n try {\n fs.mkdirSync(pluginPath, { recursive: true });\n } catch (e) {\n plgErr(plugin, 'Error creating plugin directory', e);\n }\n }\n\n // clone the plugin from git\n try {\n const git = getPluginGit();\n plgLog(plugin, 'Cloning...');\n await git.clone(plugin.url, pluginPath);\n } catch (e) {\n plgErr(plugin, 'Error cloning plugin', e);\n }\n\n // check for the plugin file for issues\n // TODO: if force is passed in, ignore the plugin check\n if (!checkPlugin(omeggaPath, plugin)) continue;\n\n const postInstallPath = path.join(pluginPath, soft.PLUGIN_POSTINSTALL);\n if (fs.existsSync(postInstallPath)) {\n plgLog(plugin, 'Running post install script...');\n try {\n verboseLog('Changing permission of', postInstallPath);\n fs.chmodSync(postInstallPath, '0755');\n verboseLog('Executing bash file');\n const { stdout, stderr } = await exec(postInstallPath, {\n cwd: pluginPath,\n shell: 'bash',\n });\n\n if (stderr.length) plgErr(plugin, stderr);\n\n verboseLog(stdout);\n } catch (e) {\n plgErr(plugin, 'Error running post install script:', e);\n }\n }\n }\n}\nexport async function update(pluginsNames: string[], _options: unknown) {\n const omeggaPath = getWorkDir();\n if (!omeggaPath)\n return err(\n 'Not an omegga directory, run ',\n 'omegga init'.yellow,\n 'to setup one.',\n );\n\n const git = getPluginGit();\n const pluginFolder = path.join(omeggaPath, soft.PLUGIN_PATH);\n\n // if no plugins are passed in, use every plugin in the plugins folder\n if (pluginsNames.length === 0) {\n pluginsNames = fs.readdirSync(pluginFolder);\n }\n\n const plugins: (IInstalledPlugin & { path: string })[] = pluginsNames\n .map(dir => path.join(pluginFolder, dir))\n // every plugin must be a directory git can update\n .filter(\n dir =>\n fs.existsSync(dir) &&\n fs.lstatSync(dir).isDirectory() &&\n fs.existsSync(path.join(dir, '.git')),\n )\n .map(dir => ({ name: path.basename(dir), path: dir }));\n\n if (plugins.length === 0) {\n log('Found no plugins that can be updated');\n return;\n }\n\n log('Checking', (plugins.length + '').yellow, 'plugins for updates...');\n\n // list of plugins that will be updated\n const pluginsToUpdate: (IInstalledPlugin & { path: string })[] = [];\n\n for (const plugin of plugins) {\n if (needsNL) {\n console.log();\n needsNL = false;\n }\n plgLog(plugin, 'Checking...');\n\n try {\n // local edits are someone's work in progress, and an update would eat them\n if (await git.isDirty(plugin.path)) {\n plgErr(plugin, 'Detected uncommitted changes - ignoring');\n continue;\n }\n\n const branch = await git.currentBranch(plugin.path);\n if (!branch || !MAIN_BRANCHES.includes(branch)) {\n plgErr(plugin, 'Not on a main branch - ignoring');\n continue;\n }\n\n plgLog(plugin, 'Fetching...');\n await git.fetch(plugin.path);\n\n const upstream = await git.compareUpstream(plugin.path);\n if (!upstream) {\n plgErr(plugin, 'No upstream branch - ignoring');\n continue;\n }\n\n // local developers, publish your code please!!! :)\n if (upstream.ahead) {\n plgErr(plugin, 'Detected plugin is ahead - ignoring');\n continue;\n }\n\n if (!upstream.behind) {\n plgLog(plugin, 'Already up-to-date!'.green);\n continue;\n }\n\n plgLog(plugin, 'Update available');\n pluginsToUpdate.push(plugin);\n } catch (e) {\n plgErr(plugin, 'Error', e);\n }\n }\n\n if (pluginsToUpdate.length === 0) {\n log('All plugins are currently up-to-date');\n return;\n }\n\n log('Updating', (pluginsToUpdate.length + '').yellow, 'plugins...');\n let updates = 0;\n\n for (const plugin of pluginsToUpdate) {\n if (needsNL) {\n console.log();\n needsNL = false;\n }\n\n // the commit to fall back to when the new code turns out to be unusable\n let previous: string;\n try {\n previous = await git.headSha(plugin.path);\n } catch (e) {\n plgErr(plugin, 'Error reading current commit', e);\n continue;\n }\n\n try {\n plgLog(plugin, 'Pulling update...');\n await git.fastForward(plugin.path);\n\n plgLog(plugin, 'Checking plugin versions...');\n if (!checkPlugin(omeggaPath, plugin)) {\n throw 'Incompatible';\n }\n\n const postInstallPath = path.join(plugin.path, soft.PLUGIN_POSTINSTALL);\n if (fs.existsSync(postInstallPath)) {\n plgLog(plugin, 'Running post install script...');\n try {\n fs.chmodSync(postInstallPath, '0755');\n const { stdout, stderr } = await exec(postInstallPath, {\n cwd: plugin.path,\n shell: 'bash',\n });\n\n if (stderr.length) plgErr(plugin, stderr);\n\n verboseLog(stdout);\n } catch (e) {\n plgErr(plugin, 'Error running post install script:', e);\n }\n }\n\n plgLog(plugin, 'Updated!'.green);\n updates++;\n } catch (e) {\n plgErr(plugin, 'Error updating plugin', e);\n try {\n plgWarn(plugin, 'Rolling back to', previous.slice(0, 8).yellow);\n await git.resetTo(plugin.path, previous);\n } catch (rollback) {\n plgErr(plugin, 'Error rolling back', rollback);\n }\n }\n }\n\n log('Updated', (updates + '').yellow, 'plugins!');\n}\n\nexport async function check(pluginNames: string[], _options: unknown) {\n const omeggaPath = getWorkDir();\n if (!omeggaPath)\n return err(\n 'Not an omegga directory, run ',\n 'omegga init'.yellow,\n 'to setup one.',\n );\n\n const pluginFolder = path.join(omeggaPath, soft.PLUGIN_PATH);\n\n // if no plugins are passed in, use every plugin in the plugins folder\n if (pluginNames.length === 0) {\n pluginNames = fs.readdirSync(pluginFolder);\n }\n\n const plugins: IInstalledPlugin[] = pluginNames\n .map(dir => path.join(pluginFolder, dir))\n // every plugin must be in a directory\n .filter(\n dir =>\n fs.existsSync(dir) &&\n fs.lstatSync(dir).isDirectory() &&\n fs.existsSync(path.join(dir, '.git')),\n )\n .map(dir => ({ name: path.basename(dir) }));\n\n if (plugins.length === 0) {\n log('Found no plugins that can be checked');\n return;\n }\n\n log(\n 'Checking',\n (plugins.length + '').yellow,\n 'plugins for valid plugin files',\n );\n\n for (const plugin of plugins) {\n // a plugin copied in by hand is still a plugin worth checking, so this\n // does not care whether the directory came from git\n plugin.path = path.join(pluginFolder, plugin.name);\n checkPlugin(omeggaPath, plugin);\n }\n console.log();\n}\n\nconst EXECUTABLES = ['setup.sh', 'omegga_plugin'];\n\nasync function init() {\n const { author, ...response } = await prompts([\n {\n type: 'text',\n name: 'name',\n message: 'What would you like to ' + 'name'.yellow + ' your plugin?',\n validate: value =>\n value.match(/^[\\w-_]+$/) ? true : 'Invalid plugin name!',\n },\n {\n type: 'select',\n name: 'type',\n message:\n 'What ' + 'type'.yellow + ' of plugin would you like to initialize?',\n choices: [\n { title: 'safe ' + '(default)'.italic, value: 'safe' },\n { title: 'unsafe', value: 'unsafe' },\n { title: 'rust', value: 'rust' },\n { title: 'rpc', value: 'rpc' },\n ],\n },\n {\n type: prev => (prev === 'safe' ? 'confirm' : null),\n name: 'ts',\n message:\n 'Would you like to use ' + 'TypeScript'.yellow + ' in your plugin?',\n initial: false,\n },\n {\n type: 'text',\n name: 'author',\n message: 'Who is the ' + 'author'.yellow + ' of this plugin?',\n },\n ]);\n\n const name = response.name;\n const type: PluginType =\n response.type === 'safe' && response.ts ? 'safe-ts' : response.type;\n\n if (!PLUGIN_TYPES.includes(type)) {\n err('Invalid plugin type', type.red, '!');\n process.exit(1);\n }\n\n let dest: string;\n if (!config.find('.')) {\n log(\n 'Warning:'.yellow,\n 'This is not an omegga installation, initializing here instead...',\n );\n dest = `./${name}`;\n } else {\n dest = `./plugins/${name}`;\n if (!fs.existsSync('./plugins')) fs.mkdirSync('./plugins');\n }\n\n if (fs.existsSync(dest)) {\n err('A directory already exists at the desired plugin location.');\n process.exit(1);\n }\n\n log('Initializing new', type.yellow, 'plugin', name.cyan, '...');\n\n const templateData: Record<string, string> = {\n name,\n author: author ?? 'AUTHOR',\n omeggaVersion: VERSION,\n };\n\n const copyAndRender = async (src: string, dest: string) => {\n const stats = fs.statSync(src);\n // remove .rename suffix from files\n dest = dest.replace(/\\.rename$/, '');\n if (stats.isDirectory()) {\n await fs.promises.mkdir(dest);\n const contents = await fs.promises.readdir(src);\n for (const child of contents)\n await copyAndRender(path.join(src, child), path.join(dest, child));\n } else {\n // copy and render the file\n const data = (await fs.promises.readFile(src)).toString();\n const mode = EXECUTABLES.includes(path.basename(dest)) ? 0o755 : 0o644;\n\n await fs.promises.writeFile(\n dest,\n data.replace(/{{(\\w+)}}/g, (_, p) => templateData[p] ?? `{{${p}}}`),\n { mode },\n );\n }\n };\n\n const src = path.join(__dirname, `../../templates/${type}`);\n\n verboseLog('Copying and rendering template...');\n await copyAndRender(src, dest);\n\n verboseLog('Starting a repo in the new plugin directory ...');\n try {\n await getPluginGit().init(dest);\n } catch (e) {\n log('Warning: could not start a git repo here.'.yellow, e);\n }\n\n if (fs.existsSync(path.join(src, 'package.json'))) {\n verboseLog('Running', 'npm i'.yellow, 'in the new plugin directory ...');\n try {\n await exec('npm i', { cwd: dest });\n } catch {\n log('Warning: npm i'.yellow, 'failed to execute. Proceeding anyway...');\n }\n }\n\n log('Initialized', type.yellow, 'plugin', `${name}`.cyan, 'successfully!');\n}\n\nfunction pluginLoaderFactory() {\n const workDir = getWorkDir();\n if (!workDir) {\n err('Not an omegga directory, run ', 'omegga init'.yellow, 'to setup one.');\n process.exit(1);\n }\n return new PluginLoader(workDir);\n}\n\n/** Loads in a plugin and it's documentation. */\nasync function loadPlugin(pluginName: string) {\n const pluginLoader = pluginLoaderFactory();\n const foundPluginDirectory = fs\n .readdirSync(pluginLoader.path)\n .find(dir => dir.toLowerCase() === pluginName.toLowerCase());\n if (!foundPluginDirectory) {\n err(\n `Plugin ${pluginName} not found! Make sure to use the plugin's directory name.`,\n );\n process.exit(1);\n }\n const plugin = await pluginLoader.scanPlugin(\n path.join(pluginLoader.path, foundPluginDirectory),\n );\n if (!plugin) {\n err(`Plugin ${pluginName} failed to load!`);\n process.exit(1);\n }\n return plugin;\n}\n\n/** Get a loaded plugin's config documentation or exit. */\nfunction getConfigDoc(plugin: Awaited<ReturnType<typeof loadPlugin>>) {\n const configDoc = plugin.getDocumentation()?.config;\n if (!configDoc) {\n err('Plugin', plugin.getName().cyan, 'has no config documentation');\n process.exit(1);\n }\n return configDoc;\n}\n\nasync function listConfig(pluginName: string, json = false) {\n const plugin = await loadPlugin(pluginName);\n // plugins without a config section are valid; list only skips unknown keys\n const configDoc = plugin.getDocumentation()?.config ?? {};\n const config = (await plugin.storage.getConfig()) ?? {};\n if (json) {\n console.log(JSON.stringify(config, null, 2));\n } else {\n for (const key in config) {\n if (!configDoc[key]) continue; // Skip unknown config keys.\n const value = config[key];\n log(\n key.cyan,\n '=',\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ? value.toString().yellow\n : JSON.stringify(value).yellow,\n );\n }\n }\n process.exit();\n}\n\nasync function getConfig(pluginName: string, configName: string, json = false) {\n const plugin = await loadPlugin(pluginName);\n const configDoc = getConfigDoc(plugin)[configName];\n if (configDoc === undefined) {\n err('Config', configName.cyan, 'not found');\n process.exit(1);\n }\n const config = (await plugin.storage.getConfig()) ?? {};\n if (json) {\n console.log(JSON.stringify(config[configName], null, 2));\n } else {\n log(config[configName]);\n }\n process.exit();\n}\n\nasync function setConfig(\n pluginName: string,\n configName: string,\n valueString: string,\n) {\n const plugin = await loadPlugin(pluginName);\n const configDoc = getConfigDoc(plugin)[configName];\n if (configDoc === undefined) {\n err('Config', configName.cyan, 'not found');\n process.exit(1);\n }\n\n // Parse the value string into a value of the correct type for the config.\n let parsed;\n switch (configDoc.type) {\n case 'boolean':\n parsed = valueString === 'true';\n break;\n case 'number':\n parsed = Number(valueString);\n if (isNaN(parsed)) {\n err('Invalid number value', valueString.yellow);\n process.exit(1);\n }\n break;\n case 'string':\n parsed = valueString;\n break;\n case 'enum':\n if (!configDoc.options.includes(valueString)) {\n err(\n 'Config',\n configName.cyan,\n 'must be one of',\n configDoc.options.join(', ').yellow,\n );\n process.exit(1);\n }\n parsed = valueString;\n break;\n default:\n err('set-context does not support config type', configDoc.type.cyan);\n process.exit(1);\n }\n\n const pluginConfig = (await plugin.storage.getConfig()) ?? {};\n pluginConfig[configName] = parsed;\n await plugin.storage.setConfig(pluginConfig);\n log(\n 'Set config',\n configName.cyan,\n 'of plugin',\n plugin.getName().cyan,\n 'to',\n parsed.toString().yellow,\n );\n process.exit();\n}\n\nasync function resetAllConfigs(pluginName: string, force: boolean) {\n const plugin = await loadPlugin(pluginName);\n if (!force) {\n const { answer } = await prompts([\n {\n type: 'confirm',\n name: 'answer',\n message: 'Are you sure you want to reset all configs?',\n initial: false,\n },\n ]);\n if (!answer) {\n log('Reset aborted.');\n process.exit(1);\n }\n }\n await plugin.storage.wipeConfig();\n await plugin.storage.init();\n log('Reset all configs of plugin', plugin.getName().cyan);\n process.exit();\n}\n\nasync function resetConfig(pluginName: string, configName: string) {\n const plugin = await loadPlugin(pluginName);\n const configDoc = getConfigDoc(plugin)[configName];\n if (configDoc === undefined) {\n err('Config', configName.cyan, 'not found');\n process.exit(1);\n }\n const pluginConfig = (await plugin.storage.getConfig()) ?? {};\n pluginConfig[configName] = plugin.storage.getDefaultConfig()[configName];\n await plugin.storage.setConfig(pluginConfig);\n log(\n 'Reset config',\n configName.cyan,\n 'of plugin',\n plugin.getName().cyan,\n 'to',\n pluginConfig[configName],\n );\n process.exit();\n}\n\nexport default {\n install,\n update,\n check,\n init,\n listConfig,\n getConfig,\n setConfig,\n resetAllConfigs,\n resetConfig,\n};\n"],"names":["promisify","execNonPromise","config.store","config.find","plugin","Logger","soft","VERSION","git","getPluginGit","src","dest","PluginLoader","config"],"mappings":"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"}
1
+ {"version":3,"file":"plugin.js","sources":["../../src/cli/plugin.ts"],"sourcesContent":["import Logger from '@/logger';\nimport soft from '@/softconfig';\nimport * as config from '@config';\nimport { PluginLoader } from '@omegga/plugin';\nimport { getPluginGit } from '@cli/plugin/git';\nimport { exec as execNonPromise } from 'node:child_process';\nimport 'colors';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport prompts from 'prompts';\nimport semver from 'semver';\nimport { promisify } from 'node:util';\nimport { VERSION } from '@/version';\n\nconst exec = promisify(execNonPromise);\n\nconst MAIN_BRANCHES = ['master', 'main'];\n\n// get the working directory for omegga\nfunction getWorkDir() {\n // default working directory is the one specified in config\n let workDir = config.store.get('defaultOmegga');\n verboseLog('Using working directory', workDir.yellow);\n\n // if there's a config in the current directory, use that one instead\n if (config.find('.')) workDir = '.';\n\n const confFile = config.find(workDir);\n\n return confFile\n ? path.resolve(process.cwd(), path.dirname(confFile))\n : undefined;\n}\n\ninterface IPlugin {\n name?: string;\n type: string | 'raw';\n url: string;\n}\n\ninterface IInstalledPlugin {\n name: string;\n path?: string;\n}\n\ninterface ITransformer {\n pattern: RegExp;\n fn(match: Record<string, string>): IPlugin;\n}\n\nconst PLUGIN_TYPES = ['safe', 'safe-ts', 'unsafe', 'rust', 'rpc'];\ntype PluginType = (typeof PLUGIN_TYPES)[number];\n\n// plugin url transformers\nconst transformers: ITransformer[] = [\n {\n // github transformer\n pattern: /^gh:(?<owner>[^/]+)\\/(?<repo>[^/]+)$/,\n fn: ({ owner, repo }) => ({\n type: 'short',\n name: repo,\n url: `https://github.com/${owner}/omegga-${repo}`,\n }),\n },\n {\n // gitlab transformer\n pattern: /^gl:(?<owner>[^/]+)\\/(?<repo>[^/]+)$/,\n fn: ({ owner, repo }) => ({\n type: 'short',\n name: repo,\n url: `https://gitlab.com/${owner}/omegga-${repo}`,\n }),\n },\n];\n\n// convert a shortened url into a full length one\nconst transformUrl = (url: string): IPlugin => {\n for (const t of transformers) {\n const match = url.match(t.pattern);\n if (match?.groups) return t.fn(match.groups);\n }\n return { type: 'raw', url };\n};\n\nlet needsNL = false;\n\n// rewrite a console line. stays on raw stdout rather than going through\n// Logger: it rewinds the cursor for progress, and a partial line is noise in\n// the log file. the `needsNL` newline below is that same bookkeeping\nconst rewriteLine = (...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 process.stdout.write(args.join(' '));\n needsNL = true;\n};\n\n// logging helper functions\nconst plg = (plugin: IPlugin | IInstalledPlugin, ...args: unknown[]) => {\n if (needsNL) {\n needsNL = false;\n console.log();\n }\n Logger.log(plugin.name, '>>'.green, ...args);\n};\nconst plgLog = (plugin: IPlugin | IInstalledPlugin, ...args: unknown[]) => {\n if (Logger.VERBOSE) plg(plugin, ...args);\n else\n rewriteLine(\n plugin.name ?? ('url' in plugin ? plugin.url : ''),\n '>>'.green,\n ...args,\n );\n};\nconst plgWarn = (plugin: IPlugin | IInstalledPlugin, ...args: unknown[]) => {\n if (needsNL) {\n needsNL = false;\n console.warn();\n }\n Logger.warn(plugin.name, 'W>'.yellow, ...args);\n};\nconst plgErr = (plugin: IPlugin | IInstalledPlugin, ...args: unknown[]) => {\n if (needsNL) {\n needsNL = false;\n console.error();\n }\n Logger.error(plugin.name, '!>'.red, ...args);\n};\n\nconst err = (...args: unknown[]) => {\n if (needsNL) {\n needsNL = false;\n console.error();\n }\n Logger.errorp(...args);\n};\nconst log = (...args: unknown[]) => {\n if (needsNL) {\n needsNL = false;\n console.log();\n }\n Logger.logp(...args);\n};\nconst verboseLog = (...args: unknown[]) => {\n if (!Logger.VERBOSE) return;\n if (needsNL) {\n needsNL = false;\n console.log();\n }\n Logger.verbose(...args);\n};\n\nfunction checkPlugin(\n omeggaPath: string,\n plugin: (IPlugin & { name: string }) | IInstalledPlugin,\n) {\n const pluginPath = path.join(omeggaPath, soft.PLUGIN_PATH, plugin.name);\n\n // check for the plugin file, whatever it's called\n if (fs.existsSync(path.join(pluginPath, soft.PLUGIN_FILE))) {\n if (needsNL) {\n console.log();\n needsNL = false;\n }\n plgLog(plugin, 'Checking plugin file...');\n let data;\n try {\n data = JSON.parse(\n fs.readFileSync(path.join(pluginPath, soft.PLUGIN_FILE), 'utf8'),\n );\n } catch (e) {\n plgErr(plugin, 'Error reading plugin file', e);\n return false;\n }\n\n // make sure this plugin version is okay.\n // in the future, this may include deprecation warnings and format changes\n if (data.formatVersion !== 1) {\n plgWarn(\n plugin,\n 'WARNING - Plugin file has invalid',\n 'formatVersion'.yellow + '. Expected',\n '1'.yellow,\n );\n return;\n }\n\n // check the omeggaVersion field of the plugin file\n if (!data.omeggaVersion || !semver.validRange(data.omeggaVersion)) {\n plgWarn(\n plugin,\n 'WARNING - Plugin file has invalid',\n 'omeggaVersion'.yellow + '. Expected semver expression',\n );\n return false;\n } else if (!semver.satisfies(VERSION, data.omeggaVersion)) {\n plgWarn(\n plugin,\n `WARNING - Plugin is not made for this version of omegga (${VERSION.yellow} vs ${data.omeggaVersion.yellow})`,\n );\n return false;\n }\n\n plgLog(plugin, 'Plugin file', 'OK'.green);\n return true;\n }\n\n // no plugin file, no problem!\n plgWarn(\n plugin,\n `WARNING - Plugin is missing plugin file (${soft.PLUGIN_FILE}), this may be a problem in future versions`,\n );\n return true;\n}\n\n// install plugins from a list of plugins\nexport async function install(plugins: string[], _options: unknown) {\n const omeggaPath = getWorkDir();\n if (!omeggaPath)\n return err(\n 'Not an omegga directory, run ',\n 'omegga init'.yellow,\n 'to setup one.',\n );\n\n log('Attempting to install', (plugins.length + '').yellow, 'plugins...');\n\n for (const pluginUrl of plugins) {\n const transformed = transformUrl(pluginUrl);\n\n // if the plugin wasn't transformed, try to extract its name from the git url\n let name = transformed.name;\n if (!name) {\n try {\n name = path.parse(transformed.url).name.replace(/^omegga-/, '');\n } catch {\n err('Error parsing name from url', transformed.url);\n break;\n }\n }\n const plugin = { ...transformed, name };\n\n log(\n 'Installing plugin',\n plugin.name.yellow,\n 'from',\n plugin.url.yellow + '...',\n );\n\n // plugin absolute path\n const pluginPath = path.join(omeggaPath, soft.PLUGIN_PATH, plugin.name);\n\n // check if plugin already exists\n // TODO: if force is passed in, remove the old directory\n if (\n fs.existsSync(pluginPath) &&\n fs.existsSync(path.join(pluginPath, 'doc.json'))\n ) {\n plgErr(\n plugin,\n 'Directory already exists! Try',\n ('omegga update ' + plugin.name).yellow,\n 'or check for plugin name collisions',\n );\n continue;\n }\n\n if (!fs.existsSync(pluginPath)) {\n // create plugin local directory\n try {\n fs.mkdirSync(pluginPath, { recursive: true });\n } catch (e) {\n plgErr(plugin, 'Error creating plugin directory', e);\n }\n }\n\n // clone the plugin from git\n try {\n const git = getPluginGit();\n plgLog(plugin, 'Cloning...');\n await git.clone(plugin.url, pluginPath);\n } catch (e) {\n plgErr(plugin, 'Error cloning plugin', e);\n }\n\n // check for the plugin file for issues\n // TODO: if force is passed in, ignore the plugin check\n if (!checkPlugin(omeggaPath, plugin)) continue;\n\n const postInstallPath = path.join(pluginPath, soft.PLUGIN_POSTINSTALL);\n if (fs.existsSync(postInstallPath)) {\n plgLog(plugin, 'Running post install script...');\n try {\n verboseLog('Changing permission of', postInstallPath);\n fs.chmodSync(postInstallPath, '0755');\n verboseLog('Executing bash file');\n const { stdout, stderr } = await exec(postInstallPath, {\n cwd: pluginPath,\n shell: 'bash',\n });\n\n if (stderr.length) plgErr(plugin, stderr);\n\n verboseLog(stdout);\n } catch (e) {\n plgErr(plugin, 'Error running post install script:', e);\n }\n }\n }\n}\nexport async function update(pluginsNames: string[], _options: unknown) {\n const omeggaPath = getWorkDir();\n if (!omeggaPath)\n return err(\n 'Not an omegga directory, run ',\n 'omegga init'.yellow,\n 'to setup one.',\n );\n\n const git = getPluginGit();\n const pluginFolder = path.join(omeggaPath, soft.PLUGIN_PATH);\n\n // if no plugins are passed in, use every plugin in the plugins folder\n if (pluginsNames.length === 0) {\n pluginsNames = fs.readdirSync(pluginFolder);\n }\n\n const plugins: (IInstalledPlugin & { path: string })[] = pluginsNames\n .map(dir => path.join(pluginFolder, dir))\n // every plugin must be a directory git can update\n .filter(\n dir =>\n fs.existsSync(dir) &&\n fs.lstatSync(dir).isDirectory() &&\n fs.existsSync(path.join(dir, '.git')),\n )\n .map(dir => ({ name: path.basename(dir), path: dir }));\n\n if (plugins.length === 0) {\n log('Found no plugins that can be updated');\n return;\n }\n\n log('Checking', (plugins.length + '').yellow, 'plugins for updates...');\n\n // list of plugins that will be updated\n const pluginsToUpdate: (IInstalledPlugin & { path: string })[] = [];\n\n for (const plugin of plugins) {\n if (needsNL) {\n console.log();\n needsNL = false;\n }\n plgLog(plugin, 'Checking...');\n\n try {\n // local edits are someone's work in progress, and an update would eat them\n if (await git.isDirty(plugin.path)) {\n plgErr(plugin, 'Detected uncommitted changes - ignoring');\n continue;\n }\n\n const branch = await git.currentBranch(plugin.path);\n if (!branch || !MAIN_BRANCHES.includes(branch)) {\n plgErr(plugin, 'Not on a main branch - ignoring');\n continue;\n }\n\n plgLog(plugin, 'Fetching...');\n await git.fetch(plugin.path);\n\n const upstream = await git.compareUpstream(plugin.path);\n if (!upstream) {\n plgErr(plugin, 'No upstream branch - ignoring');\n continue;\n }\n\n // local developers, publish your code please!!! :)\n if (upstream.ahead) {\n plgErr(plugin, 'Detected plugin is ahead - ignoring');\n continue;\n }\n\n if (!upstream.behind) {\n plgLog(plugin, 'Already up-to-date!'.green);\n continue;\n }\n\n plgLog(plugin, 'Update available');\n pluginsToUpdate.push(plugin);\n } catch (e) {\n plgErr(plugin, 'Error', e);\n }\n }\n\n if (pluginsToUpdate.length === 0) {\n log('All plugins are currently up-to-date');\n return;\n }\n\n log('Updating', (pluginsToUpdate.length + '').yellow, 'plugins...');\n let updates = 0;\n\n for (const plugin of pluginsToUpdate) {\n if (needsNL) {\n console.log();\n needsNL = false;\n }\n\n // the commit to fall back to when the new code turns out to be unusable\n let previous: string;\n try {\n previous = await git.headSha(plugin.path);\n } catch (e) {\n plgErr(plugin, 'Error reading current commit', e);\n continue;\n }\n\n try {\n plgLog(plugin, 'Pulling update...');\n await git.fastForward(plugin.path);\n\n plgLog(plugin, 'Checking plugin versions...');\n if (!checkPlugin(omeggaPath, plugin)) {\n throw 'Incompatible';\n }\n\n const postInstallPath = path.join(plugin.path, soft.PLUGIN_POSTINSTALL);\n if (fs.existsSync(postInstallPath)) {\n plgLog(plugin, 'Running post install script...');\n try {\n fs.chmodSync(postInstallPath, '0755');\n const { stdout, stderr } = await exec(postInstallPath, {\n cwd: plugin.path,\n shell: 'bash',\n });\n\n if (stderr.length) plgErr(plugin, stderr);\n\n verboseLog(stdout);\n } catch (e) {\n plgErr(plugin, 'Error running post install script:', e);\n }\n }\n\n plgLog(plugin, 'Updated!'.green);\n updates++;\n } catch (e) {\n plgErr(plugin, 'Error updating plugin', e);\n try {\n plgWarn(plugin, 'Rolling back to', previous.slice(0, 8).yellow);\n await git.resetTo(plugin.path, previous);\n } catch (rollback) {\n plgErr(plugin, 'Error rolling back', rollback);\n }\n }\n }\n\n log('Updated', (updates + '').yellow, 'plugins!');\n}\n\nexport async function check(pluginNames: string[], _options: unknown) {\n const omeggaPath = getWorkDir();\n if (!omeggaPath)\n return err(\n 'Not an omegga directory, run ',\n 'omegga init'.yellow,\n 'to setup one.',\n );\n\n const pluginFolder = path.join(omeggaPath, soft.PLUGIN_PATH);\n\n // if no plugins are passed in, use every plugin in the plugins folder\n if (pluginNames.length === 0) {\n pluginNames = fs.readdirSync(pluginFolder);\n }\n\n const plugins: IInstalledPlugin[] = pluginNames\n .map(dir => path.join(pluginFolder, dir))\n // every plugin must be in a directory\n .filter(\n dir =>\n fs.existsSync(dir) &&\n fs.lstatSync(dir).isDirectory() &&\n fs.existsSync(path.join(dir, '.git')),\n )\n .map(dir => ({ name: path.basename(dir) }));\n\n if (plugins.length === 0) {\n log('Found no plugins that can be checked');\n return;\n }\n\n log(\n 'Checking',\n (plugins.length + '').yellow,\n 'plugins for valid plugin files',\n );\n\n for (const plugin of plugins) {\n // a plugin copied in by hand is still a plugin worth checking, so this\n // does not care whether the directory came from git\n plugin.path = path.join(pluginFolder, plugin.name);\n checkPlugin(omeggaPath, plugin);\n }\n console.log();\n}\n\nconst EXECUTABLES = ['setup.sh', 'omegga_plugin'];\n\nasync function init() {\n const { author, ...response } = await prompts([\n {\n type: 'text',\n name: 'name',\n message: 'What would you like to ' + 'name'.yellow + ' your plugin?',\n validate: value =>\n value.match(/^[\\w-_]+$/) ? true : 'Invalid plugin name!',\n },\n {\n type: 'select',\n name: 'type',\n message:\n 'What ' + 'type'.yellow + ' of plugin would you like to initialize?',\n choices: [\n { title: 'safe ' + '(default)'.italic, value: 'safe' },\n { title: 'unsafe', value: 'unsafe' },\n { title: 'rust', value: 'rust' },\n { title: 'rpc', value: 'rpc' },\n ],\n },\n {\n type: prev => (prev === 'safe' ? 'confirm' : null),\n name: 'ts',\n message:\n 'Would you like to use ' + 'TypeScript'.yellow + ' in your plugin?',\n initial: false,\n },\n {\n type: 'text',\n name: 'author',\n message: 'Who is the ' + 'author'.yellow + ' of this plugin?',\n },\n ]);\n\n const name = response.name;\n const type: PluginType =\n response.type === 'safe' && response.ts ? 'safe-ts' : response.type;\n\n if (!PLUGIN_TYPES.includes(type)) {\n err('Invalid plugin type', type.red, '!');\n process.exit(1);\n }\n\n let dest: string;\n if (!config.find('.')) {\n log(\n 'Warning:'.yellow,\n 'This is not an omegga installation, initializing here instead...',\n );\n dest = `./${name}`;\n } else {\n dest = `./plugins/${name}`;\n if (!fs.existsSync('./plugins')) fs.mkdirSync('./plugins');\n }\n\n if (fs.existsSync(dest)) {\n err('A directory already exists at the desired plugin location.');\n process.exit(1);\n }\n\n log('Initializing new', type.yellow, 'plugin', name.cyan, '...');\n\n const templateData: Record<string, string> = {\n name,\n author: author ?? 'AUTHOR',\n omeggaVersion: VERSION,\n };\n\n const copyAndRender = async (src: string, dest: string) => {\n const stats = fs.statSync(src);\n // remove .rename suffix from files\n dest = dest.replace(/\\.rename$/, '');\n if (stats.isDirectory()) {\n await fs.promises.mkdir(dest);\n const contents = await fs.promises.readdir(src);\n for (const child of contents)\n await copyAndRender(path.join(src, child), path.join(dest, child));\n } else {\n // copy and render the file\n const data = (await fs.promises.readFile(src)).toString();\n const mode = EXECUTABLES.includes(path.basename(dest)) ? 0o755 : 0o644;\n\n await fs.promises.writeFile(\n dest,\n data.replace(/{{(\\w+)}}/g, (_, p) => templateData[p] ?? `{{${p}}}`),\n { mode },\n );\n }\n };\n\n const src = path.join(__dirname, `../../templates/${type}`);\n\n verboseLog('Copying and rendering template...');\n await copyAndRender(src, dest);\n\n verboseLog('Starting a repo in the new plugin directory ...');\n try {\n await getPluginGit().init(dest);\n } catch (e) {\n log('Warning: could not start a git repo here.'.yellow, e);\n }\n\n if (fs.existsSync(path.join(src, 'package.json'))) {\n verboseLog('Running', 'npm i'.yellow, 'in the new plugin directory ...');\n try {\n await exec('npm i', { cwd: dest });\n } catch {\n log('Warning: npm i'.yellow, 'failed to execute. Proceeding anyway...');\n }\n }\n\n log('Initialized', type.yellow, 'plugin', `${name}`.cyan, 'successfully!');\n}\n\nfunction pluginLoaderFactory() {\n const workDir = getWorkDir();\n if (!workDir) {\n err('Not an omegga directory, run ', 'omegga init'.yellow, 'to setup one.');\n process.exit(1);\n }\n return new PluginLoader(workDir);\n}\n\n/** Loads in a plugin and it's documentation. */\nasync function loadPlugin(pluginName: string) {\n const pluginLoader = pluginLoaderFactory();\n const foundPluginDirectory = fs\n .readdirSync(pluginLoader.path)\n .find(dir => dir.toLowerCase() === pluginName.toLowerCase());\n if (!foundPluginDirectory) {\n err(\n `Plugin ${pluginName} not found! Make sure to use the plugin's directory name.`,\n );\n process.exit(1);\n }\n const plugin = await pluginLoader.scanPlugin(\n path.join(pluginLoader.path, foundPluginDirectory),\n );\n if (!plugin) {\n err(`Plugin ${pluginName} failed to load!`);\n process.exit(1);\n }\n return plugin;\n}\n\n/** Get a loaded plugin's config documentation or exit. */\nfunction getConfigDoc(plugin: Awaited<ReturnType<typeof loadPlugin>>) {\n const configDoc = plugin.getDocumentation()?.config;\n if (!configDoc) {\n err('Plugin', plugin.getName().cyan, 'has no config documentation');\n process.exit(1);\n }\n return configDoc;\n}\n\nasync function listConfig(pluginName: string, json = false) {\n const plugin = await loadPlugin(pluginName);\n // plugins without a config section are valid; list only skips unknown keys\n const configDoc = plugin.getDocumentation()?.config ?? {};\n const config = (await plugin.storage.getConfig()) ?? {};\n if (json) {\n console.log(JSON.stringify(config, null, 2));\n } else {\n for (const key in config) {\n if (!configDoc[key]) continue; // Skip unknown config keys.\n const value = config[key];\n log(\n key.cyan,\n '=',\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'boolean'\n ? value.toString().yellow\n : JSON.stringify(value).yellow,\n );\n }\n }\n process.exit();\n}\n\nasync function getConfig(pluginName: string, configName: string, json = false) {\n const plugin = await loadPlugin(pluginName);\n const configDoc = getConfigDoc(plugin)[configName];\n if (configDoc === undefined) {\n err('Config', configName.cyan, 'not found');\n process.exit(1);\n }\n const config = (await plugin.storage.getConfig()) ?? {};\n if (json) {\n console.log(JSON.stringify(config[configName], null, 2));\n } else {\n log(config[configName]);\n }\n process.exit();\n}\n\nasync function setConfig(\n pluginName: string,\n configName: string,\n valueString: string,\n) {\n const plugin = await loadPlugin(pluginName);\n const configDoc = getConfigDoc(plugin)[configName];\n if (configDoc === undefined) {\n err('Config', configName.cyan, 'not found');\n process.exit(1);\n }\n\n // Parse the value string into a value of the correct type for the config.\n let parsed;\n switch (configDoc.type) {\n case 'boolean':\n parsed = valueString === 'true';\n break;\n case 'number':\n parsed = Number(valueString);\n if (isNaN(parsed)) {\n err('Invalid number value', valueString.yellow);\n process.exit(1);\n }\n break;\n case 'string':\n parsed = valueString;\n break;\n case 'enum':\n if (!configDoc.options.includes(valueString)) {\n err(\n 'Config',\n configName.cyan,\n 'must be one of',\n configDoc.options.join(', ').yellow,\n );\n process.exit(1);\n }\n parsed = valueString;\n break;\n default:\n err('set-context does not support config type', configDoc.type.cyan);\n process.exit(1);\n }\n\n const pluginConfig = (await plugin.storage.getConfig()) ?? {};\n pluginConfig[configName] = parsed;\n await plugin.storage.setConfig(pluginConfig);\n log(\n 'Set config',\n configName.cyan,\n 'of plugin',\n plugin.getName().cyan,\n 'to',\n parsed.toString().yellow,\n );\n process.exit();\n}\n\nasync function resetAllConfigs(pluginName: string, force: boolean) {\n const plugin = await loadPlugin(pluginName);\n if (!force) {\n const { answer } = await prompts([\n {\n type: 'confirm',\n name: 'answer',\n message: 'Are you sure you want to reset all configs?',\n initial: false,\n },\n ]);\n if (!answer) {\n log('Reset aborted.');\n process.exit(1);\n }\n }\n await plugin.storage.wipeConfig();\n await plugin.storage.init();\n log('Reset all configs of plugin', plugin.getName().cyan);\n process.exit();\n}\n\nasync function resetConfig(pluginName: string, configName: string) {\n const plugin = await loadPlugin(pluginName);\n const configDoc = getConfigDoc(plugin)[configName];\n if (configDoc === undefined) {\n err('Config', configName.cyan, 'not found');\n process.exit(1);\n }\n const pluginConfig = (await plugin.storage.getConfig()) ?? {};\n pluginConfig[configName] = plugin.storage.getDefaultConfig()[configName];\n await plugin.storage.setConfig(pluginConfig);\n log(\n 'Reset config',\n configName.cyan,\n 'of plugin',\n plugin.getName().cyan,\n 'to',\n pluginConfig[configName],\n );\n process.exit();\n}\n\nexport default {\n install,\n update,\n check,\n init,\n listConfig,\n getConfig,\n setConfig,\n resetAllConfigs,\n resetConfig,\n};\n"],"names":["promisify","execNonPromise","config.store","config.find","plugin","Logger","soft","VERSION","git","getPluginGit","src","dest","PluginLoader","config"],"mappings":";;;;;;;;;;;;;;;AAcA,MAAM,OAAOA,UAAAA,UAAUC,uBAAc;AAErC,MAAM,gBAAgB,CAAC,UAAU,MAAM;AAGvC,SAAS,aAAa;AAEpB,MAAI,UAAUC,MAAAA,MAAa,IAAI,eAAe;AAC9C,aAAW,2BAA2B,QAAQ,MAAM;AAGpD,MAAIC,WAAY,GAAG,EAAG,WAAU;AAEhC,QAAM,WAAWA,MAAAA,KAAY,OAAO;AAEpC,SAAO,WACH,KAAK,QAAQ,QAAQ,OAAO,KAAK,QAAQ,QAAQ,CAAC,IAClD;AACN;AAkBA,MAAM,eAAe,CAAC,QAAQ,WAAW,UAAU,QAAQ,KAAK;AAIhE,MAAM,eAA+B;AAAA,EACnC;AAAA;AAAA,IAEE,SAAS;AAAA,IACT,IAAI,CAAC,EAAE,OAAO,YAAY;AAAA,MACxB,MAAM;AAAA,MACN,MAAM;AAAA,MACN,KAAK,sBAAsB,KAAK,WAAW,IAAI;AAAA,IAAA;AAAA,EACjD;AAAA,EAEF;AAAA;AAAA,IAEE,SAAS;AAAA,IACT,IAAI,CAAC,EAAE,OAAO,YAAY;AAAA,MACxB,MAAM;AAAA,MACN,MAAM;AAAA,MACN,KAAK,sBAAsB,KAAK,WAAW,IAAI;AAAA,IAAA;AAAA,EACjD;AAEJ;AAGA,MAAM,eAAe,CAAC,QAAyB;AAC7C,aAAW,KAAK,cAAc;AAC5B,UAAM,QAAQ,IAAI,MAAM,EAAE,OAAO;AACjC,QAAI,OAAO,OAAQ,QAAO,EAAE,GAAG,MAAM,MAAM;AAAA,EAC7C;AACA,SAAO,EAAE,MAAM,OAAO,IAAA;AACxB;AAEA,IAAI,UAAU;AAKd,MAAM,cAAc,IAAI,SAAoB;AAE1C,MAAI,QAAQ,OAAO,OAAO;AACxB,YAAQ,OAAO,UAAU,CAAC;AAC1B,YAAQ,OAAO,SAAS,CAAC;AAAA,EAC3B;AACA,UAAQ,OAAO,MAAM,KAAK,KAAK,GAAG,CAAC;AACnC,YAAU;AACZ;AAGA,MAAM,MAAM,CAACC,YAAuC,SAAoB;AACtE,MAAI,SAAS;AACX,cAAU;AACV,YAAQ,IAAA;AAAA,EACV;AACAC,SAAAA,QAAO,IAAID,QAAO,MAAM,KAAK,OAAO,GAAG,IAAI;AAC7C;AACA,MAAM,SAAS,CAACA,YAAuC,SAAoB;AACzE,MAAIC,OAAAA,QAAO,QAAS,KAAID,SAAQ,GAAG,IAAI;AAAA;AAErC;AAAA,MACEA,QAAO,SAAS,SAASA,UAASA,QAAO,MAAM;AAAA,MAC/C,KAAK;AAAA,MACL,GAAG;AAAA,IAAA;AAET;AACA,MAAM,UAAU,CAACA,YAAuC,SAAoB;AAC1E,MAAI,SAAS;AACX,cAAU;AACV,YAAQ,KAAA;AAAA,EACV;AACAC,SAAAA,QAAO,KAAKD,QAAO,MAAM,KAAK,QAAQ,GAAG,IAAI;AAC/C;AACA,MAAM,SAAS,CAACA,YAAuC,SAAoB;AACzE,MAAI,SAAS;AACX,cAAU;AACV,YAAQ,MAAA;AAAA,EACV;AACAC,SAAAA,QAAO,MAAMD,QAAO,MAAM,KAAK,KAAK,GAAG,IAAI;AAC7C;AAEA,MAAM,MAAM,IAAI,SAAoB;AAClC,MAAI,SAAS;AACX,cAAU;AACV,YAAQ,MAAA;AAAA,EACV;AACAC,iBAAO,OAAO,GAAG,IAAI;AACvB;AACA,MAAM,MAAM,IAAI,SAAoB;AAClC,MAAI,SAAS;AACX,cAAU;AACV,YAAQ,IAAA;AAAA,EACV;AACAA,iBAAO,KAAK,GAAG,IAAI;AACrB;AACA,MAAM,aAAa,IAAI,SAAoB;AACzC,MAAI,CAACA,OAAAA,QAAO,QAAS;AACrB,MAAI,SAAS;AACX,cAAU;AACV,YAAQ,IAAA;AAAA,EACV;AACAA,iBAAO,QAAQ,GAAG,IAAI;AACxB;AAEA,SAAS,YACP,YACAD,SACA;AACA,QAAM,aAAa,KAAK,KAAK,YAAYE,WAAAA,QAAK,aAAaF,QAAO,IAAI;AAGtE,MAAI,GAAG,WAAW,KAAK,KAAK,YAAYE,WAAAA,QAAK,WAAW,CAAC,GAAG;AAC1D,QAAI,SAAS;AACX,cAAQ,IAAA;AACR,gBAAU;AAAA,IACZ;AACA,WAAOF,SAAQ,yBAAyB;AACxC,QAAI;AACJ,QAAI;AACF,aAAO,KAAK;AAAA,QACV,GAAG,aAAa,KAAK,KAAK,YAAYE,WAAAA,QAAK,WAAW,GAAG,MAAM;AAAA,MAAA;AAAA,IAEnE,SAAS,GAAG;AACV,aAAOF,SAAQ,6BAA6B,CAAC;AAC7C,aAAO;AAAA,IACT;AAIA,QAAI,KAAK,kBAAkB,GAAG;AAC5B;AAAA,QACEA;AAAA,QACA;AAAA,QACA,gBAAgB,SAAS;AAAA,QACzB,IAAI;AAAA,MAAA;AAEN;AAAA,IACF;AAGA,QAAI,CAAC,KAAK,iBAAiB,CAAC,OAAO,WAAW,KAAK,aAAa,GAAG;AACjE;AAAA,QACEA;AAAA,QACA;AAAA,QACA,gBAAgB,SAAS;AAAA,MAAA;AAE3B,aAAO;AAAA,IACT,WAAW,CAAC,OAAO,UAAUG,QAAAA,SAAS,KAAK,aAAa,GAAG;AACzD;AAAA,QACEH;AAAA,QACA,4DAA4DG,QAAAA,QAAQ,MAAM,OAAO,KAAK,cAAc,MAAM;AAAA,MAAA;AAE5G,aAAO;AAAA,IACT;AAEA,WAAOH,SAAQ,eAAe,KAAK,KAAK;AACxC,WAAO;AAAA,EACT;AAGA;AAAA,IACEA;AAAA,IACA,4CAA4CE,mBAAK,WAAW;AAAA,EAAA;AAE9D,SAAO;AACT;AAGA,eAAsB,QAAQ,SAAmB,UAAmB;AAClE,QAAM,aAAa,WAAA;AACnB,MAAI,CAAC;AACH,WAAO;AAAA,MACL;AAAA,MACA,cAAc;AAAA,MACd;AAAA,IAAA;AAGJ,MAAI,0BAA0B,QAAQ,SAAS,IAAI,QAAQ,YAAY;AAEvE,aAAW,aAAa,SAAS;AAC/B,UAAM,cAAc,aAAa,SAAS;AAG1C,QAAI,OAAO,YAAY;AACvB,QAAI,CAAC,MAAM;AACT,UAAI;AACF,eAAO,KAAK,MAAM,YAAY,GAAG,EAAE,KAAK,QAAQ,YAAY,EAAE;AAAA,MAChE,QAAQ;AACN,YAAI,+BAA+B,YAAY,GAAG;AAClD;AAAA,MACF;AAAA,IACF;AACA,UAAMF,UAAS,EAAE,GAAG,aAAa,KAAA;AAEjC;AAAA,MACE;AAAA,MACAA,QAAO,KAAK;AAAA,MACZ;AAAA,MACAA,QAAO,IAAI,SAAS;AAAA,IAAA;AAItB,UAAM,aAAa,KAAK,KAAK,YAAYE,WAAAA,QAAK,aAAaF,QAAO,IAAI;AAItE,QACE,GAAG,WAAW,UAAU,KACxB,GAAG,WAAW,KAAK,KAAK,YAAY,UAAU,CAAC,GAC/C;AACA;AAAA,QACEA;AAAA,QACA;AAAA,SACC,mBAAmBA,QAAO,MAAM;AAAA,QACjC;AAAA,MAAA;AAEF;AAAA,IACF;AAEA,QAAI,CAAC,GAAG,WAAW,UAAU,GAAG;AAE9B,UAAI;AACF,WAAG,UAAU,YAAY,EAAE,WAAW,MAAM;AAAA,MAC9C,SAAS,GAAG;AACV,eAAOA,SAAQ,mCAAmC,CAAC;AAAA,MACrD;AAAA,IACF;AAGA,QAAI;AACF,YAAMI,QAAMC,IAAAA,aAAA;AACZ,aAAOL,SAAQ,YAAY;AAC3B,YAAMI,MAAI,MAAMJ,QAAO,KAAK,UAAU;AAAA,IACxC,SAAS,GAAG;AACV,aAAOA,SAAQ,wBAAwB,CAAC;AAAA,IAC1C;AAIA,QAAI,CAAC,YAAY,YAAYA,OAAM,EAAG;AAEtC,UAAM,kBAAkB,KAAK,KAAK,YAAYE,WAAAA,QAAK,kBAAkB;AACrE,QAAI,GAAG,WAAW,eAAe,GAAG;AAClC,aAAOF,SAAQ,gCAAgC;AAC/C,UAAI;AACF,mBAAW,0BAA0B,eAAe;AACpD,WAAG,UAAU,iBAAiB,MAAM;AACpC,mBAAW,qBAAqB;AAChC,cAAM,EAAE,QAAQ,OAAA,IAAW,MAAM,KAAK,iBAAiB;AAAA,UACrD,KAAK;AAAA,UACL,OAAO;AAAA,QAAA,CACR;AAED,YAAI,OAAO,OAAQ,QAAOA,SAAQ,MAAM;AAExC,mBAAW,MAAM;AAAA,MACnB,SAAS,GAAG;AACV,eAAOA,SAAQ,sCAAsC,CAAC;AAAA,MACxD;AAAA,IACF;AAAA,EACF;AACF;AACA,eAAsB,OAAO,cAAwB,UAAmB;AACtE,QAAM,aAAa,WAAA;AACnB,MAAI,CAAC;AACH,WAAO;AAAA,MACL;AAAA,MACA,cAAc;AAAA,MACd;AAAA,IAAA;AAGJ,QAAMI,QAAMC,IAAAA,aAAA;AACZ,QAAM,eAAe,KAAK,KAAK,YAAYH,WAAAA,QAAK,WAAW;AAG3D,MAAI,aAAa,WAAW,GAAG;AAC7B,mBAAe,GAAG,YAAY,YAAY;AAAA,EAC5C;AAEA,QAAM,UAAmD,aACtD,IAAI,CAAA,QAAO,KAAK,KAAK,cAAc,GAAG,CAAC,EAEvC;AAAA,IACC,SACE,GAAG,WAAW,GAAG,KACjB,GAAG,UAAU,GAAG,EAAE,YAAA,KAClB,GAAG,WAAW,KAAK,KAAK,KAAK,MAAM,CAAC;AAAA,EAAA,EAEvC,IAAI,CAAA,SAAQ,EAAE,MAAM,KAAK,SAAS,GAAG,GAAG,MAAM,IAAA,EAAM;AAEvD,MAAI,QAAQ,WAAW,GAAG;AACxB,QAAI,sCAAsC;AAC1C;AAAA,EACF;AAEA,MAAI,aAAa,QAAQ,SAAS,IAAI,QAAQ,wBAAwB;AAGtE,QAAM,kBAA2D,CAAA;AAEjE,aAAWF,WAAU,SAAS;AAC5B,QAAI,SAAS;AACX,cAAQ,IAAA;AACR,gBAAU;AAAA,IACZ;AACA,WAAOA,SAAQ,aAAa;AAE5B,QAAI;AAEF,UAAI,MAAMI,MAAI,QAAQJ,QAAO,IAAI,GAAG;AAClC,eAAOA,SAAQ,yCAAyC;AACxD;AAAA,MACF;AAEA,YAAM,SAAS,MAAMI,MAAI,cAAcJ,QAAO,IAAI;AAClD,UAAI,CAAC,UAAU,CAAC,cAAc,SAAS,MAAM,GAAG;AAC9C,eAAOA,SAAQ,iCAAiC;AAChD;AAAA,MACF;AAEA,aAAOA,SAAQ,aAAa;AAC5B,YAAMI,MAAI,MAAMJ,QAAO,IAAI;AAE3B,YAAM,WAAW,MAAMI,MAAI,gBAAgBJ,QAAO,IAAI;AACtD,UAAI,CAAC,UAAU;AACb,eAAOA,SAAQ,+BAA+B;AAC9C;AAAA,MACF;AAGA,UAAI,SAAS,OAAO;AAClB,eAAOA,SAAQ,qCAAqC;AACpD;AAAA,MACF;AAEA,UAAI,CAAC,SAAS,QAAQ;AACpB,eAAOA,SAAQ,sBAAsB,KAAK;AAC1C;AAAA,MACF;AAEA,aAAOA,SAAQ,kBAAkB;AACjC,sBAAgB,KAAKA,OAAM;AAAA,IAC7B,SAAS,GAAG;AACV,aAAOA,SAAQ,SAAS,CAAC;AAAA,IAC3B;AAAA,EACF;AAEA,MAAI,gBAAgB,WAAW,GAAG;AAChC,QAAI,sCAAsC;AAC1C;AAAA,EACF;AAEA,MAAI,aAAa,gBAAgB,SAAS,IAAI,QAAQ,YAAY;AAClE,MAAI,UAAU;AAEd,aAAWA,WAAU,iBAAiB;AACpC,QAAI,SAAS;AACX,cAAQ,IAAA;AACR,gBAAU;AAAA,IACZ;AAGA,QAAI;AACJ,QAAI;AACF,iBAAW,MAAMI,MAAI,QAAQJ,QAAO,IAAI;AAAA,IAC1C,SAAS,GAAG;AACV,aAAOA,SAAQ,gCAAgC,CAAC;AAChD;AAAA,IACF;AAEA,QAAI;AACF,aAAOA,SAAQ,mBAAmB;AAClC,YAAMI,MAAI,YAAYJ,QAAO,IAAI;AAEjC,aAAOA,SAAQ,6BAA6B;AAC5C,UAAI,CAAC,YAAY,YAAYA,OAAM,GAAG;AACpC,cAAM;AAAA,MACR;AAEA,YAAM,kBAAkB,KAAK,KAAKA,QAAO,MAAME,WAAAA,QAAK,kBAAkB;AACtE,UAAI,GAAG,WAAW,eAAe,GAAG;AAClC,eAAOF,SAAQ,gCAAgC;AAC/C,YAAI;AACF,aAAG,UAAU,iBAAiB,MAAM;AACpC,gBAAM,EAAE,QAAQ,OAAA,IAAW,MAAM,KAAK,iBAAiB;AAAA,YACrD,KAAKA,QAAO;AAAA,YACZ,OAAO;AAAA,UAAA,CACR;AAED,cAAI,OAAO,OAAQ,QAAOA,SAAQ,MAAM;AAExC,qBAAW,MAAM;AAAA,QACnB,SAAS,GAAG;AACV,iBAAOA,SAAQ,sCAAsC,CAAC;AAAA,QACxD;AAAA,MACF;AAEA,aAAOA,SAAQ,WAAW,KAAK;AAC/B;AAAA,IACF,SAAS,GAAG;AACV,aAAOA,SAAQ,yBAAyB,CAAC;AACzC,UAAI;AACF,gBAAQA,SAAQ,mBAAmB,SAAS,MAAM,GAAG,CAAC,EAAE,MAAM;AAC9D,cAAMI,MAAI,QAAQJ,QAAO,MAAM,QAAQ;AAAA,MACzC,SAAS,UAAU;AACjB,eAAOA,SAAQ,sBAAsB,QAAQ;AAAA,MAC/C;AAAA,IACF;AAAA,EACF;AAEA,MAAI,YAAY,UAAU,IAAI,QAAQ,UAAU;AAClD;AAEA,eAAsB,MAAM,aAAuB,UAAmB;AACpE,QAAM,aAAa,WAAA;AACnB,MAAI,CAAC;AACH,WAAO;AAAA,MACL;AAAA,MACA,cAAc;AAAA,MACd;AAAA,IAAA;AAGJ,QAAM,eAAe,KAAK,KAAK,YAAYE,WAAAA,QAAK,WAAW;AAG3D,MAAI,YAAY,WAAW,GAAG;AAC5B,kBAAc,GAAG,YAAY,YAAY;AAAA,EAC3C;AAEA,QAAM,UAA8B,YACjC,IAAI,CAAA,QAAO,KAAK,KAAK,cAAc,GAAG,CAAC,EAEvC;AAAA,IACC,SACE,GAAG,WAAW,GAAG,KACjB,GAAG,UAAU,GAAG,EAAE,YAAA,KAClB,GAAG,WAAW,KAAK,KAAK,KAAK,MAAM,CAAC;AAAA,EAAA,EAEvC,IAAI,CAAA,SAAQ,EAAE,MAAM,KAAK,SAAS,GAAG,EAAA,EAAI;AAE5C,MAAI,QAAQ,WAAW,GAAG;AACxB,QAAI,sCAAsC;AAC1C;AAAA,EACF;AAEA;AAAA,IACE;AAAA,KACC,QAAQ,SAAS,IAAI;AAAA,IACtB;AAAA,EAAA;AAGF,aAAWF,WAAU,SAAS;AAG5B,IAAAA,QAAO,OAAO,KAAK,KAAK,cAAcA,QAAO,IAAI;AACjD,gBAAY,YAAYA,OAAM;AAAA,EAChC;AACA,UAAQ,IAAA;AACV;AAEA,MAAM,cAAc,CAAC,YAAY,eAAe;AAEhD,eAAe,OAAO;AACpB,QAAM,EAAE,QAAQ,GAAG,SAAA,IAAa,MAAM,QAAQ;AAAA,IAC5C;AAAA,MACE,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS,4BAA4B,OAAO,SAAS;AAAA,MACrD,UAAU,CAAA,UACR,MAAM,MAAM,WAAW,IAAI,OAAO;AAAA,IAAA;AAAA,IAEtC;AAAA,MACE,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SACE,UAAU,OAAO,SAAS;AAAA,MAC5B,SAAS;AAAA,QACP,EAAE,OAAO,UAAU,YAAY,QAAQ,OAAO,OAAA;AAAA,QAC9C,EAAE,OAAO,UAAU,OAAO,SAAA;AAAA,QAC1B,EAAE,OAAO,QAAQ,OAAO,OAAA;AAAA,QACxB,EAAE,OAAO,OAAO,OAAO,MAAA;AAAA,MAAM;AAAA,IAC/B;AAAA,IAEF;AAAA,MACE,MAAM,CAAA,SAAS,SAAS,SAAS,YAAY;AAAA,MAC7C,MAAM;AAAA,MACN,SACE,2BAA2B,aAAa,SAAS;AAAA,MACnD,SAAS;AAAA,IAAA;AAAA,IAEX;AAAA,MACE,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS,gBAAgB,SAAS,SAAS;AAAA,IAAA;AAAA,EAC7C,CACD;AAED,QAAM,OAAO,SAAS;AACtB,QAAM,OACJ,SAAS,SAAS,UAAU,SAAS,KAAK,YAAY,SAAS;AAEjE,MAAI,CAAC,aAAa,SAAS,IAAI,GAAG;AAChC,QAAI,uBAAuB,KAAK,KAAK,GAAG;AACxC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI;AACJ,MAAI,CAACD,MAAAA,KAAY,GAAG,GAAG;AACrB;AAAA,MACE,WAAW;AAAA,MACX;AAAA,IAAA;AAEF,WAAO,KAAK,IAAI;AAAA,EAClB,OAAO;AACL,WAAO,aAAa,IAAI;AACxB,QAAI,CAAC,GAAG,WAAW,WAAW,EAAG,IAAG,UAAU,WAAW;AAAA,EAC3D;AAEA,MAAI,GAAG,WAAW,IAAI,GAAG;AACvB,QAAI,4DAA4D;AAChE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,oBAAoB,KAAK,QAAQ,UAAU,KAAK,MAAM,KAAK;AAE/D,QAAM,eAAuC;AAAA,IAC3C;AAAA,IACA,QAAQ,UAAU;AAAA,IAClB,eAAeI,QAAAA;AAAAA,EAAA;AAGjB,QAAM,gBAAgB,OAAOG,MAAaC,UAAiB;AACzD,UAAM,QAAQ,GAAG,SAASD,IAAG;AAE7BC,YAAOA,MAAK,QAAQ,aAAa,EAAE;AACnC,QAAI,MAAM,eAAe;AACvB,YAAM,GAAG,SAAS,MAAMA,KAAI;AAC5B,YAAM,WAAW,MAAM,GAAG,SAAS,QAAQD,IAAG;AAC9C,iBAAW,SAAS;AAClB,cAAM,cAAc,KAAK,KAAKA,MAAK,KAAK,GAAG,KAAK,KAAKC,OAAM,KAAK,CAAC;AAAA,IACrE,OAAO;AAEL,YAAM,QAAQ,MAAM,GAAG,SAAS,SAASD,IAAG,GAAG,SAAA;AAC/C,YAAM,OAAO,YAAY,SAAS,KAAK,SAASC,KAAI,CAAC,IAAI,MAAQ;AAEjE,YAAM,GAAG,SAAS;AAAA,QAChBA;AAAAA,QACA,KAAK,QAAQ,cAAc,CAAC,GAAG,MAAM,aAAa,CAAC,KAAK,KAAK,CAAC,IAAI;AAAA,QAClE,EAAE,KAAA;AAAA,MAAK;AAAA,IAEX;AAAA,EACF;AAEA,QAAM,MAAM,KAAK,KAAK,WAAW,mBAAmB,IAAI,EAAE;AAE1D,aAAW,mCAAmC;AAC9C,QAAM,cAAc,KAAK,IAAI;AAE7B,aAAW,iDAAiD;AAC5D,MAAI;AACF,UAAMF,IAAAA,aAAA,EAAe,KAAK,IAAI;AAAA,EAChC,SAAS,GAAG;AACV,QAAI,4CAA4C,QAAQ,CAAC;AAAA,EAC3D;AAEA,MAAI,GAAG,WAAW,KAAK,KAAK,KAAK,cAAc,CAAC,GAAG;AACjD,eAAW,WAAW,QAAQ,QAAQ,iCAAiC;AACvE,QAAI;AACF,YAAM,KAAK,SAAS,EAAE,KAAK,MAAM;AAAA,IACnC,QAAQ;AACN,UAAI,iBAAiB,QAAQ,yCAAyC;AAAA,IACxE;AAAA,EACF;AAEA,MAAI,eAAe,KAAK,QAAQ,UAAU,GAAG,IAAI,GAAG,MAAM,eAAe;AAC3E;AAEA,SAAS,sBAAsB;AAC7B,QAAM,UAAU,WAAA;AAChB,MAAI,CAAC,SAAS;AACZ,QAAI,iCAAiC,cAAc,QAAQ,eAAe;AAC1E,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,SAAO,IAAIG,OAAAA,aAAa,OAAO;AACjC;AAGA,eAAe,WAAW,YAAoB;AAC5C,QAAM,eAAe,oBAAA;AACrB,QAAM,uBAAuB,GAC1B,YAAY,aAAa,IAAI,EAC7B,KAAK,CAAA,QAAO,IAAI,YAAA,MAAkB,WAAW,aAAa;AAC7D,MAAI,CAAC,sBAAsB;AACzB;AAAA,MACE,UAAU,UAAU;AAAA,IAAA;AAEtB,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,QAAMR,UAAS,MAAM,aAAa;AAAA,IAChC,KAAK,KAAK,aAAa,MAAM,oBAAoB;AAAA,EAAA;AAEnD,MAAI,CAACA,SAAQ;AACX,QAAI,UAAU,UAAU,kBAAkB;AAC1C,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,SAAOA;AACT;AAGA,SAAS,aAAaA,SAAgD;AACpE,QAAM,YAAYA,QAAO,iBAAA,GAAoB;AAC7C,MAAI,CAAC,WAAW;AACd,QAAI,UAAUA,QAAO,QAAA,EAAU,MAAM,6BAA6B;AAClE,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,SAAO;AACT;AAEA,eAAe,WAAW,YAAoB,OAAO,OAAO;AAC1D,QAAMA,UAAS,MAAM,WAAW,UAAU;AAE1C,QAAM,YAAYA,QAAO,iBAAA,GAAoB,UAAU,CAAA;AACvD,QAAMS,UAAU,MAAMT,QAAO,QAAQ,UAAA,KAAgB,CAAA;AACrD,MAAI,MAAM;AACR,YAAQ,IAAI,KAAK,UAAUS,SAAQ,MAAM,CAAC,CAAC;AAAA,EAC7C,OAAO;AACL,eAAW,OAAOA,SAAQ;AACxB,UAAI,CAAC,UAAU,GAAG,EAAG;AACrB,YAAM,QAAQA,QAAO,GAAG;AACxB;AAAA,QACE,IAAI;AAAA,QACJ;AAAA,QACA,OAAO,UAAU,YACf,OAAO,UAAU,YACjB,OAAO,UAAU,YACf,MAAM,WAAW,SACjB,KAAK,UAAU,KAAK,EAAE;AAAA,MAAA;AAAA,IAE9B;AAAA,EACF;AACA,UAAQ,KAAA;AACV;AAEA,eAAe,UAAU,YAAoB,YAAoB,OAAO,OAAO;AAC7E,QAAMT,UAAS,MAAM,WAAW,UAAU;AAC1C,QAAM,YAAY,aAAaA,OAAM,EAAE,UAAU;AACjD,MAAI,cAAc,QAAW;AAC3B,QAAI,UAAU,WAAW,MAAM,WAAW;AAC1C,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,QAAMS,UAAU,MAAMT,QAAO,QAAQ,UAAA,KAAgB,CAAA;AACrD,MAAI,MAAM;AACR,YAAQ,IAAI,KAAK,UAAUS,QAAO,UAAU,GAAG,MAAM,CAAC,CAAC;AAAA,EACzD,OAAO;AACL,QAAIA,QAAO,UAAU,CAAC;AAAA,EACxB;AACA,UAAQ,KAAA;AACV;AAEA,eAAe,UACb,YACA,YACA,aACA;AACA,QAAMT,UAAS,MAAM,WAAW,UAAU;AAC1C,QAAM,YAAY,aAAaA,OAAM,EAAE,UAAU;AACjD,MAAI,cAAc,QAAW;AAC3B,QAAI,UAAU,WAAW,MAAM,WAAW;AAC1C,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,MAAI;AACJ,UAAQ,UAAU,MAAA;AAAA,IAChB,KAAK;AACH,eAAS,gBAAgB;AACzB;AAAA,IACF,KAAK;AACH,eAAS,OAAO,WAAW;AAC3B,UAAI,MAAM,MAAM,GAAG;AACjB,YAAI,wBAAwB,YAAY,MAAM;AAC9C,gBAAQ,KAAK,CAAC;AAAA,MAChB;AACA;AAAA,IACF,KAAK;AACH,eAAS;AACT;AAAA,IACF,KAAK;AACH,UAAI,CAAC,UAAU,QAAQ,SAAS,WAAW,GAAG;AAC5C;AAAA,UACE;AAAA,UACA,WAAW;AAAA,UACX;AAAA,UACA,UAAU,QAAQ,KAAK,IAAI,EAAE;AAAA,QAAA;AAE/B,gBAAQ,KAAK,CAAC;AAAA,MAChB;AACA,eAAS;AACT;AAAA,IACF;AACE,UAAI,4CAA4C,UAAU,KAAK,IAAI;AACnE,cAAQ,KAAK,CAAC;AAAA,EAAA;AAGlB,QAAM,eAAgB,MAAMA,QAAO,QAAQ,UAAA,KAAgB,CAAA;AAC3D,eAAa,UAAU,IAAI;AAC3B,QAAMA,QAAO,QAAQ,UAAU,YAAY;AAC3C;AAAA,IACE;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACAA,QAAO,UAAU;AAAA,IACjB;AAAA,IACA,OAAO,WAAW;AAAA,EAAA;AAEpB,UAAQ,KAAA;AACV;AAEA,eAAe,gBAAgB,YAAoB,OAAgB;AACjE,QAAMA,UAAS,MAAM,WAAW,UAAU;AAC1C,MAAI,CAAC,OAAO;AACV,UAAM,EAAE,WAAW,MAAM,QAAQ;AAAA,MAC/B;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,QACN,SAAS;AAAA,QACT,SAAS;AAAA,MAAA;AAAA,IACX,CACD;AACD,QAAI,CAAC,QAAQ;AACX,UAAI,gBAAgB;AACpB,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AACA,QAAMA,QAAO,QAAQ,WAAA;AACrB,QAAMA,QAAO,QAAQ,KAAA;AACrB,MAAI,+BAA+BA,QAAO,QAAA,EAAU,IAAI;AACxD,UAAQ,KAAA;AACV;AAEA,eAAe,YAAY,YAAoB,YAAoB;AACjE,QAAMA,UAAS,MAAM,WAAW,UAAU;AAC1C,QAAM,YAAY,aAAaA,OAAM,EAAE,UAAU;AACjD,MAAI,cAAc,QAAW;AAC3B,QAAI,UAAU,WAAW,MAAM,WAAW;AAC1C,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,QAAM,eAAgB,MAAMA,QAAO,QAAQ,UAAA,KAAgB,CAAA;AAC3D,eAAa,UAAU,IAAIA,QAAO,QAAQ,iBAAA,EAAmB,UAAU;AACvE,QAAMA,QAAO,QAAQ,UAAU,YAAY;AAC3C;AAAA,IACE;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACAA,QAAO,UAAU;AAAA,IACjB;AAAA,IACA,aAAa,UAAU;AAAA,EAAA;AAEzB,UAAQ,KAAA;AACV;AAEA,MAAA,cAAe;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;;;"}