UNPKG

@iryu54/stack-monitor

Version:
7,013 lines 257 kB
#!/usr/bin/env node
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __typeError = (msg) => {
  throw TypeError(msg);
};
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __commonJS = (cb, mod) => function __require() {
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);

// helpers/args.js
var require_args = __commonJS({
  "helpers/args.js"(exports2, module2) {
    "use strict";
    var path = require("path");
    var yargs = require("yargs/yargs");
    var { hideBin } = require("yargs/helpers");
    var yarg = yargs(hideBin(process.argv)).usage("Usage: <path-to-your-stack> [options]").alias("pe", "pull-env").describe("pe", "Pull env from a service (need --environment and --service)").default("pe", false).alias("e", "environment").describe("e", "Choose your environment").default("e", void 0).alias("s", "service").describe("s", "Service").default("s", void 0).alias("ss", "services").describe("ss", "Services").default("ss", []).boolean(["pe"]).string(["e", "s"]).array(["ss"]).help("h").alias("h", "help").parse();
    var args2 = Object.assign({
      rootPath: path.resolve(yarg["_"]?.[0] || "."),
      initialCwd: ""
    }, yarg);
    if (args2.rootPath) {
      args2.initialCwd = process.cwd();
      process.chdir(args2.rootPath);
    }
    if (args2.pullEnv && !args2.service) {
      console.error("Error: --service needed");
      process.exit(1);
    }
    if (args2.pullEnv && !args2.environment) {
      console.error("Error: --environment needed");
      process.exit(1);
    }
    module2.exports = args2;
  }
});

// ../../common/socket-server/src/CustomObservable.js
var require_CustomObservable = __commonJS({
  "../../common/socket-server/src/CustomObservable.js"(exports2, module2) {
    "use strict";
    function CustomObservable() {
      this.funcs = [];
    }
    CustomObservable.prototype.subscribe = function(fun) {
      this.funcs.push(fun);
    };
    CustomObservable.prototype.next = function(...value) {
      this.funcs.forEach((f) => f(...value));
    };
    CustomObservable.prototype.off = function(fun) {
      this.funcs = this.funcs.filter((f) => f !== fun);
    };
    CustomObservable.prototype.destroy = function() {
      this.funcs = [];
    };
    module2.exports = CustomObservable;
  }
});

// ../../common/socket-server/src/sockets.js
var require_sockets = __commonJS({
  "../../common/socket-server/src/sockets.js"(exports2, module2) {
    "use strict";
    var WebSocket = require("ws");
    var CustomObservable = require_CustomObservable();
    var events = {};
    module2.exports = {
      /** @type {WebSocket.Server | null} */
      io: null,
      emit(channel, ...data) {
        events[channel]?.next(...data);
        this.io?.clients.forEach((ws) => {
          ws.send(JSON.stringify({ channel, data }));
        });
      },
      on: (event, cb) => {
        if (!events[event]) events[event] = new CustomObservable();
        events[event].subscribe(cb);
      },
      off(event, cb) {
        events[event]?.off(cb);
      },
      /**
       *
       * @param {*} server
       */
      connect(server) {
        this.io = new WebSocket.Server({ noServer: true, path: "/socket" });
        this.io.on("connection", function message(ws) {
          ws.on("message", function message2(event) {
            const { channel, data } = JSON.parse(event?.toString());
            events[channel]?.next(...data);
          });
        });
        const self = this;
        server.on("upgrade", function upgrade(request, socket, head) {
          if (request.url === "/socket") {
            self.io?.handleUpgrade(request, socket, head, function done(ws) {
              self.io?.emit("connection", ws, request);
            });
          }
        });
      }
    };
  }
});

// ../../common/socket-server/src/index.js
var require_src = __commonJS({
  "../../common/socket-server/src/index.js"(exports2, module2) {
    "use strict";
    module2.exports = {
      sockets: require_sockets()
    };
  }
});

// ../../modules/bugs/backend/routes.js
var require_routes = __commonJS({
  "../../modules/bugs/backend/routes.js"(exports2, module2) {
    "use strict";
    var express = require("express");
    var router = express.Router();
    var pathfs = require("path");
    var { fork } = require("child_process");
    module2.exports = (stackMonitor) => {
      router.get("/bugs/:service", async (req, res) => {
        const service = stackMonitor.findService(req.params.service);
        if (!service) return res.status(404).send("SERVICE_NOT_FOUND");
        const ts = fork(pathfs.resolve(__dirname, "checkJsFork"));
        ts.on("message", (results) => {
          res.json(results);
          ts.kill("SIGKILL");
        });
        ts.send(req.query.cwd || service.getRootPath() || __dirname);
        return null;
      });
      return router;
    };
  }
});

// ../../modules/bugs/backend/index.js
var require_backend = __commonJS({
  "../../modules/bugs/backend/index.js"(exports2, module2) {
    "use strict";
    var { existsSync } = require("fs");
    var pathfs = require("path");
    var plugin = {
      enabled: true,
      name: "Bugs",
      displayName: "Bugs",
      description: "Find bugs across a whole javascript project",
      icon: "fas fa-bug",
      placements: ["service"],
      order: 4,
      export: null,
      hidden: async (service) => {
        if (!service) return true;
        const npm = new service.Stack.npm(service);
        const serviceIsNpm = !!npm.getNpmPaths(service);
        return !serviceIsNpm;
      },
      routes: require_routes()
    };
    module2.exports = plugin;
  }
});

// ../../modules/base64/backend/Base64.js
var require_Base64 = __commonJS({
  "../../modules/base64/backend/Base64.js"(exports2, module2) {
    "use strict";
    var Base64 = (stackMonitor) => ({
      /**
       * Encode une chaîne en Base64
       * @param {string} value - La chaîne à encoder
       * @returns {string} - La chaîne encodée
       */
      encode: (value = "") => {
        try {
          const buffer = Buffer.from(value, "utf-8");
          return buffer.toString("base64");
        } catch (error) {
          console.error("Error encoding to base64:", error);
          return "";
        }
      },
      /**
       * Décode une chaîne Base64
       * @param {string} value - La chaîne Base64 à décoder
       * @returns {string} - La chaîne décodée
       */
      decode: (value = "") => {
        try {
          const buffer = Buffer.from(value, "base64");
          return buffer.toString("utf-8");
        } catch (error) {
          console.error("Error decoding from base64:", error);
          return "";
        }
      }
    });
    module2.exports = Base64;
  }
});

// ../../modules/base64/backend/routes.js
var require_routes2 = __commonJS({
  "../../modules/base64/backend/routes.js"(exports2, module2) {
    "use strict";
    var express = require("express");
    var router = express.Router();
    var Base64 = require_Base64();
    module2.exports = (stackMonitor) => {
      const base64 = Base64(stackMonitor);
      router.post("/base64/encode", (req, res) => {
        try {
          const { value } = req.body;
          const result = base64.encode(value);
          res.json({ result });
        } catch (error) {
          res.status(500).json({
            error: true,
            message: error instanceof Error ? error.message : "Unknown error"
          });
        }
      });
      router.post("/base64/decode", (req, res) => {
        try {
          const { value } = req.body;
          const result = base64.decode(value);
          res.json({ result });
        } catch (error) {
          res.status(500).json({
            error: true,
            message: error instanceof Error ? error.message : "Unknown error"
          });
        }
      });
      return router;
    };
  }
});

// ../../modules/base64/backend/index.js
var require_backend2 = __commonJS({
  "../../modules/base64/backend/index.js"(exports2, module2) {
    "use strict";
    var plugin = {
      enabled: true,
      name: "Base64",
      displayName: "Base64",
      description: "Encode and decode Base64 strings",
      icon: "fas fa-exchange-alt",
      placements: [
        {
          position: "toolbox",
          label: "Base64",
          icon: "fas fa-exchange-alt",
          goTo: { path: "/Base64" },
          active: "Base64"
        }
      ],
      export: require_Base64(),
      order: 8,
      routes: require_routes2()
    };
    module2.exports = plugin;
  }
});

// ../../modules/configuration/backend/index.js
var require_backend3 = __commonJS({
  "../../modules/configuration/backend/index.js"(exports2, module2) {
    "use strict";
    var plugin = {
      enabled: true,
      name: "Configuration",
      displayName: "Configuration",
      description: "Show all configurations used to launch the given service",
      icon: "fas fa-cog",
      export: null,
      placements: ["service"],
      order: 5
    };
    module2.exports = plugin;
  }
});

// ../../modules/dev-ops/backend/index.js
var require_backend4 = __commonJS({
  "../../modules/dev-ops/backend/index.js"(exports2, module2) {
    "use strict";
    var plugin = {
      enabled: false,
      name: "DevOps",
      displayName: "Dev ops",
      description: "Choose a tool used for dev ops purposes",
      icon: "fas fa-hard-hat",
      order: 7,
      export: null,
      placements: [{
        position: "sidebar",
        label: "DevOps",
        icon: "fas fa-hard-hat",
        goTo: { path: "/DevOps" },
        active: "DevOps"
      }]
    };
    module2.exports = plugin;
  }
});

// ../../modules/diff/backend/routes.js
var require_routes3 = __commonJS({
  "../../modules/diff/backend/routes.js"(exports2, module2) {
    "use strict";
    var express = require("express");
    var router = express.Router();
    var { v4 } = require("uuid");
    module2.exports = () => {
      router.get("/diff", async (req, res) => {
        res.json(v4());
      });
      return router;
    };
  }
});

// ../../modules/diff/backend/index.js
var require_backend5 = __commonJS({
  "../../modules/diff/backend/index.js"(exports2, module2) {
    "use strict";
    var plugin = {
      enabled: true,
      name: "Diff",
      displayName: "Differences",
      description: "Find differences between two strings",
      icon: "fas fa-columns",
      export: null,
      placements: [
        {
          position: "toolbox",
          label: "Diff",
          icon: "fas fa-columns",
          goTo: { path: "/Diff" },
          active: "Diff"
        }
      ],
      order: 6,
      routes: require_routes3()
    };
    module2.exports = plugin;
  }
});

// helpers/conflictStorage.js
var require_conflictStorage = __commonJS({
  "helpers/conflictStorage.js"(exports2, module2) {
    "use strict";
    var pendingConflicts = [];
    function storeConflict(conflict) {
      const conflictId = Date.now().toString();
      pendingConflicts.push({
        id: conflictId,
        timestamp: Date.now(),
        ...conflict
      });
      if (pendingConflicts.length > 20) {
        pendingConflicts.shift();
      }
      return conflictId;
    }
    function getPendingConflicts() {
      return pendingConflicts;
    }
    function removeConflict(conflictId) {
      const index = pendingConflicts.findIndex((c) => c.id === conflictId);
      if (index !== -1) {
        pendingConflicts.splice(index, 1);
      }
    }
    module2.exports = {
      storeConflict,
      getPendingConflicts,
      removeConflict
    };
  }
});

// helpers/reencrypt-nodered.js
var require_reencrypt_nodered = __commonJS({
  "helpers/reencrypt-nodered.js"(exports2, module2) {
    "use strict";
    var crypto = require("crypto");
    var { readFile, writeFile } = require("fs/promises");
    var encryptionAlgorithm = "aes-256-ctr";
    module2.exports = async function decryptCreds(oldSecret, newScret, path) {
      const oldKey = crypto.createHash("sha256").update(oldSecret).digest();
      const newKey = crypto.createHash("sha256").update(newScret).digest();
      const cipher = JSON.parse(await readFile(path, "utf-8"));
      let flows = cipher["$"];
      const vector = Buffer.from(flows.substring(0, 32), "hex");
      flows = flows.substring(32);
      const decipher = crypto.createDecipheriv(encryptionAlgorithm, oldKey, vector);
      const decrypted = decipher.update(flows, "base64", "utf8") + decipher.final("utf8");
      const newVector = crypto.randomBytes(16);
      const newCipher = crypto.createCipheriv(encryptionAlgorithm, newKey, newVector);
      const encrypted = newCipher.update(decrypted, "utf8", "base64") + newCipher.final("base64");
      await writeFile(path, JSON.stringify({
        "$": newVector.toString("hex") + encrypted
      }, null, 2), "utf-8");
    };
  }
});

// models/EncryptionKey.js
var require_EncryptionKey = __commonJS({
  "models/EncryptionKey.js"(exports2, module2) {
    "use strict";
    var { existsSync } = require("fs");
    var path = require("path");
    var { writeFile, readFile, appendFile } = require("fs/promises");
    var { randomUUID: randomUUID2 } = require("crypto");
    var dbs2 = require_dbs();
    var { generateKey, encrypt, decrypt } = require_crypto();
    var reencryptNodered = require_reencrypt_nodered();
    var pathfs = require("path");
    var args2 = require_args();
    var _EncryptionKey_instances, getDb_fn;
    var EncryptionKey = class {
      constructor() {
        __privateAdd(this, _EncryptionKey_instances);
        this.encryptionKey = "";
      }
      async init() {
        this.encryptionKey = (await __privateMethod(this, _EncryptionKey_instances, getDb_fn).call(this).read()).encryptionKey;
        const dirname = path.dirname(await dbs2.getDb("encryption-key", { encrypted: false }).getPath());
        const gitignorePath = path.resolve(dirname, ".gitignore");
        if (!existsSync(gitignorePath)) {
          writeFile(gitignorePath, "encryption-key.json");
        } else {
          const gitignoreFile = (await readFile(gitignorePath, "utf-8")).split("\n");
          const gitignoreHasKey = (key) => gitignoreFile.some((line) => line.trim() === key);
          if (!gitignoreHasKey("encryption-key.json")) await appendFile(gitignorePath, "\nencryption-key.json");
          if (!gitignoreHasKey("overrides")) await appendFile(gitignorePath, "\noverrides");
        }
      }
      async update() {
        console.log(JSON.stringify(["stack-monitor", "update"], (_, v) => typeof v === "function" ? `[func]` : v));
        return __privateMethod(this, _EncryptionKey_instances, getDb_fn).call(this).write(this.toStorage());
      }
      toStorage() {
        return {
          encryptionKey: this.encryptionKey
        };
      }
      async generateKey() {
        return generateKey();
      }
      async testKey(encryptionKey) {
        try {
          const variable = randomUUID2();
          const result = await encrypt(variable, { encryptionKey });
          const decrypted = await decrypt(result, { encryptionKey });
          if (decrypted === variable) return true;
          return false;
        } catch (error) {
          console.error(error);
          return false;
        }
      }
      async saveKey(key, { noReload } = { noReload: false }) {
        if (!await this.testKey(key)) throw new Error("Key not valid");
        try {
          const envSample = (await dbs2.getDbs("envs"))[0];
          if (envSample) await dbs2.getDb(`envs/${envSample}`).read();
          if (this.encryptionKey) {
            await reencryptNodered(this.encryptionKey, key, pathfs.resolve(require_stack().getRootPath(), "nodered/flow_cred.json"));
            await dbs2.reencrypt(this.encryptionKey, key);
          }
          ;
        } catch (error) {
          console.error(error);
        }
        const shouldRestart = this.encryptionKey !== key;
        this.encryptionKey = key;
        await this.update();
        if (!noReload) {
          await require_stack().selectConf();
        }
        if (shouldRestart) {
          console.log("Restart...");
          require("child_process").spawn(process.argv[0], process.argv.slice(1), {
            cwd: args2.initialCwd,
            detached: true,
            stdio: "inherit"
          }).unref();
          process.exit(0);
        }
        return key;
      }
    };
    _EncryptionKey_instances = new WeakSet();
    getDb_fn = function() {
      return dbs2.getDb("encryption-key", { encrypted: false });
    };
    module2.exports = new EncryptionKey();
  }
});

// helpers/crypto.js
var require_crypto = __commonJS({
  "helpers/crypto.js"(exports2, module2) {
    "use strict";
    var _sodium = require("libsodium-wrappers");
    var crypto = require("crypto");
    var { sockets } = require_src();
    var conflictStorage = require_conflictStorage();
    var path = require("path");
    module2.exports.generateKey = async () => {
      await _sodium.ready;
      const sodium = _sodium;
      const key = sodium.crypto_aead_aegis256_keygen();
      return sodium.to_base64(key);
    };
    module2.exports.encrypt = async (data, { additionnalNonce = "", encryptionKey = "" } = {}) => {
      await _sodium.ready;
      const sodium = _sodium;
      if (!encryptionKey) encryptionKey = require_EncryptionKey().encryptionKey;
      const key = sodium.from_base64(encryptionKey);
      if (!key || key.length !== sodium.crypto_secretbox_KEYBYTES) {
        throw new Error("Invalid encryption key length");
      }
      let nonce;
      if (additionnalNonce) {
        const combinedHash = crypto.createHash("blake2b512").update(data + additionnalNonce).digest();
        nonce = combinedHash.slice(0, sodium.crypto_secretbox_NONCEBYTES);
      } else {
        nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES);
      }
      const dataStr = typeof data === "string" ? data : String(data);
      const dataArray = new TextEncoder().encode(dataStr);
      const ciphertext = sodium.crypto_secretbox_easy(dataArray, nonce, key);
      return Buffer.concat([
        Buffer.from(nonce.buffer, nonce.byteOffset, nonce.byteLength),
        Buffer.from(ciphertext.buffer, ciphertext.byteOffset, ciphertext.byteLength)
      ]).toString("base64");
    };
    module2.exports.decryptFile = async (encryptedData, options = {}, filePath) => {
      if (typeof encryptedData === "string" && (encryptedData.includes("<<<<<<< HEAD") || encryptedData.includes("=======") || encryptedData.includes(">>>>>>>"))) {
        return await handleGitConflict(encryptedData, options, filePath);
      }
      return await module2.exports.decrypt(encryptedData, options);
    };
    module2.exports.decrypt = async (encryptedData, { additionnalNonce = "", encryptionKey = "" } = {}) => {
      if (typeof encryptedData === "string" && (encryptedData.includes("<<<<<<< HEAD") || encryptedData.includes("=======") || encryptedData.includes(">>>>>>>"))) {
        return await handleGitConflict(encryptedData, { additionnalNonce, encryptionKey });
      }
      await _sodium.ready;
      const sodium = _sodium;
      if (!encryptionKey) encryptionKey = require_EncryptionKey().encryptionKey;
      const key = sodium.from_base64(encryptionKey);
      if (!key || key.length !== sodium.crypto_secretbox_KEYBYTES) {
        throw new Error("Invalid decryption key length");
      }
      const encryptedBuffer = Buffer.from(encryptedData, "base64");
      const nonce = encryptedBuffer.slice(0, sodium.crypto_secretbox_NONCEBYTES);
      const ciphertext = encryptedBuffer.slice(sodium.crypto_secretbox_NONCEBYTES);
      let decrypted;
      try {
        const ciphertextArray = new Uint8Array(ciphertext);
        const nonceArray = new Uint8Array(nonce);
        decrypted = sodium.crypto_secretbox_open_easy(ciphertextArray, nonceArray, key);
      } catch (error) {
        throw new Error("Decryption failed");
      }
      if (!decrypted) {
        throw new Error("Decryption failed");
      }
      return Buffer.from(decrypted).toString("utf-8");
    };
    async function handleGitConflict(conflictedData, options, filePath) {
      const headMatch = conflictedData.match(/<<<<<<< HEAD\r?\n([\s\S]*?)\r?\n=======\r?\n([\s\S]*?)\r?\n>>>>>>>.*/);
      if (!headMatch) {
        throw new Error("Git conflict detected but could not be properly parsed");
      }
      try {
        const ourVersion = headMatch[1];
        const theirVersion = headMatch[2];
        let ourDecrypted = "";
        let theirDecrypted = "";
        try {
          ourDecrypted = await module2.exports.decrypt(ourVersion, options);
        } catch (err) {
          const errorMessage = err instanceof Error ? err.message : String(err);
          ourDecrypted = `[ERROR DECRYPTING OUR VERSION: ${errorMessage}]`;
        }
        try {
          theirDecrypted = await module2.exports.decrypt(theirVersion, options);
        } catch (err) {
          const errorMessage = err instanceof Error ? err.message : String(err);
          theirDecrypted = `[ERROR DECRYPTING THEIR VERSION: ${errorMessage}]`;
        }
        const conflictData = {
          original: conflictedData,
          ourVersion: ourDecrypted,
          theirVersion: theirDecrypted,
          filePath: filePath || null,
          filename: filePath ? path.basename(filePath) : null
        };
        const conflictId = conflictStorage.storeConflict(conflictData);
        conflictData.id = conflictId;
        sockets.emit("crypto:conflict", conflictData);
        return `${JSON.stringify({
          ourVersion: ourDecrypted,
          theirVersion: theirDecrypted
        })}`;
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        console.error("Error handling git conflict:", errorMessage);
        throw new Error(`Git conflict detected, but failed to process: ${errorMessage}`);
      }
    }
  }
});

// helpers/dbs.js
var require_dbs = __commonJS({
  "helpers/dbs.js"(exports2, module2) {
    "use strict";
    var {
      existsSync,
      mkdirSync,
      writeFileSync,
      readFileSync,
      unlinkSync
    } = require("fs");
    var pathfs = require("path");
    var {
      readdir,
      mkdir,
      writeFile,
      readFile,
      unlink
    } = require("fs/promises");
    var { fdir } = require("fdir");
    var PromiseB2 = require("bluebird");
    var { sockets } = require_src();
    var args2 = require_args();
    var { encrypt, decrypt, decryptFile } = require_crypto();
    var alasql = require("alasql");
    module2.exports = new class {
      constructor() {
        __publicField(this, "cache", {});
      }
      getRootPath() {
        const rootPath = pathfs.resolve(args2.rootPath, ".stackmonitor/dbs");
        if (!existsSync(rootPath)) mkdirSync(rootPath, { recursive: true });
        return rootPath;
      }
      async getDbs(namespace = "") {
        const pathToDbs = pathfs.resolve(this.getRootPath(), namespace);
        if (!existsSync(pathToDbs)) await mkdir(pathToDbs, { recursive: true });
        return (await readdir(pathToDbs)).map((id) => id.replace(pathfs.extname(id), "").replace(".encrypted", ""));
      }
      async reencrypt(oldKey, newKey) {
        const api = new fdir().withFullPaths().filter((path) => path.endsWith("encrypted.json")).crawl(this.getRootPath());
        await PromiseB2.map(api.withPromise(), async (file) => {
          const fileEncrypted = await readFile(file, "utf-8");
          const additionnalNonce = file.split(".stackmonitor").pop();
          const fileDecrypted = await decrypt(fileEncrypted, { additionnalNonce, encryptionKey: oldKey });
          const fileReEncrypted = await encrypt(fileDecrypted, { additionnalNonce, encryptionKey: newKey });
          await writeFile(file, fileReEncrypted, "utf-8");
        });
      }
      getDb(id, { encrypted, defaultData } = { encrypted: true, defaultData: {} }) {
        const getPath = async () => {
          const persistencePath = pathfs.resolve(`${this.getRootPath()}/${id}${encrypted ? ".encrypted" : ""}.json`);
          if (!existsSync(pathfs.dirname(persistencePath))) await mkdirSync(pathfs.dirname(persistencePath), { recursive: true });
          if (!existsSync(persistencePath)) {
            let defaultDB = JSON.stringify(defaultData || [], null, 2);
            if (encrypted) defaultDB = await encrypt(defaultDB, { additionnalNonce: persistencePath.split(".stackmonitor").pop() });
            await writeFile(persistencePath, defaultDB, "utf-8");
            this.cache[id] = defaultDB;
          }
          return persistencePath;
        };
        const table = id.replace(/[^a-z0-9]|\s+|\r?\n|\r/gmi, "_");
        const read = async () => {
          if (this.cache[id]) return this.cache[id];
          const path = await getPath();
          const additionnalNonce = path.split(".stackmonitor").pop();
          let db = readFileSync(path, "utf-8");
          if (encrypted) {
            try {
              if (typeof db === "string" && (db.includes("<<<<<<< HEAD") || db.includes("=======") || db.includes(">>>>>>>"))) {
                db = await decryptFile(db, { additionnalNonce }, path);
              } else {
                db = await decrypt(db, { additionnalNonce }).catch((err) => {
                  console.error(path, err);
                  sockets.emit("system:wrongKey");
                  throw err;
                });
              }
            } catch (err) {
              console.error(path, err);
              sockets.emit("system:wrongKey");
              throw err;
            }
          }
          this.cache[id] = JSON.parse(db);
          if (!alasql.tables[table]) {
            await alasql(`CREATE TABLE ${table}`);
          }
          alasql.tables[table].data = this.cache[id];
          return this.cache[id];
        };
        const write = async (data) => {
          let db = JSON.stringify(data, null, 2);
          const path = await getPath();
          const additionnalNonce = path.split(".stackmonitor").pop();
          if (encrypted) db = await encrypt(db, { additionnalNonce });
          writeFileSync(path, db, "utf-8");
          this.cache[id] = data;
          if (alasql.tables[table]) {
            alasql.tables[table].data = data;
          }
        };
        const escapeQuote = (data) => {
          if (typeof data === "string") {
            return data.replace(/'/g, "''");
          }
          return data;
        };
        const setValue = (item) => {
          if (item == null) {
            return "NULL";
          }
          if (item instanceof Date && item.toISOString) {
            return `'${item.toISOString()}'`;
          }
          if (typeof item === "string") {
            return `'${escapeQuote(item)}'`;
          }
          return `${item}`;
        };
        return {
          getPath,
          alasql: {
            table,
            buildUpdateQuery(data) {
              const set = [];
              Object.keys(data).forEach((key) => {
                set.push(`${key} = ${setValue(data[key])}`);
              });
              return set.join(", ");
            },
            read: async (sql) => {
              await read();
              return alasql.promise(sql);
            },
            write: async (sql, value) => {
              await read();
              await alasql.promise(sql, value);
              await write(await alasql(`select * from ${table}`));
            }
          },
          write,
          read,
          delete: async () => {
            delete this.cache[id];
            return unlink(await getPath());
          }
        };
      }
    }();
  }
});

// ../../modules/documentation/backend/Documentation.js
var require_Documentation = __commonJS({
  "../../modules/documentation/backend/Documentation.js"(exports2, module2) {
    "use strict";
    var PromiseB2 = require("bluebird");
    var { randomUUID: randomUUID2 } = require("crypto");
    var dbs2 = require_dbs();
    var db = dbs2.getDb(`documentations`);
    var dbDocumentationTree = dbs2.getDb(`documentations-tree`, { encrypted: true, defaultData: [] }).alasql;
    var Documentation = class {
      /**
       * @param {import('@clabroche/common-typings').NonFunctionProperties<Documentation>} documentation
       */
      constructor(documentation) {
        this.id = documentation.id || "";
        this.text = documentation.text || "";
      }
      static async load(id) {
      }
      static async all() {
        return db.alasql.select("Select * from ?");
      }
      static async find(envId) {
        const documentations = await this.all();
        return documentations.find((env) => env.id === envId);
      }
      async save() {
        const obj = this.toStorage();
        await dbs2.getDb(`documentations/${this.id}`).write(obj);
        return this;
      }
      async update(env) {
        this.transform = env.transform;
        this.label = env.label;
        await dbs2.getDb(`documentations/${this.id}`).write(this.toStorage());
      }
      async delete() {
        await dbs2.getDb(`documentations/${this.id}`).delete();
      }
      toStorage() {
        return {
          id: this.id,
          label: this.label,
          transform: this.transform
        };
      }
    };
    module2.exports = Documentation;
  }
});

// ../../modules/documentation/backend/Leaf.js
var require_Leaf = __commonJS({
  "../../modules/documentation/backend/Leaf.js"(exports2, module2) {
    "use strict";
    var PromiseB2 = require("bluebird");
    var { randomUUID: randomUUID2 } = require("crypto");
    var dbs2 = require_dbs();
    var { v4 } = require("uuid");
    var dbLeafTree = dbs2.getDb(`leafs-leafs`, { encrypted: true, defaultData: [] }).alasql;
    var Leaf = class _Leaf {
      /**
       * @param {import('@clabroche/common-typings').NonFunctionProperties<Leaf>} leaf
       */
      constructor(leaf) {
        this.id = leaf.id || v4();
        this.docId = leaf.docId || "";
        this.serviceId = leaf.serviceId || "";
        this.label = leaf.label || "";
        this.position = leaf.position || -1;
        this.text = leaf.text || "";
        this.parentId = leaf.parentId || "";
      }
      static async getTree(serviceLabel) {
        const leafs = await dbLeafTree.read(`Select * from ${dbLeafTree.table} ${serviceLabel ? `where serviceId = '${serviceLabel}'` : `where serviceId=''`}`);
        return leafs;
      }
      toStorage() {
        return {
          docId: this.docId,
          position: this.position,
          serviceId: this.serviceId,
          label: this.label,
          text: this.text,
          parentId: this.parentId
        };
      }
      async remove() {
        return dbLeafTree.write(`DELETE from ${dbLeafTree.table} where id = '${this.id}'`);
      }
      static async find({ id }) {
        const [leaf] = await dbLeafTree.read(`Select * from ${dbLeafTree.table} where id='${id}'`);
        return leaf ? new _Leaf(leaf) : null;
      }
      async save() {
        const leafExists = await _Leaf.find({ id: this.id });
        const storage = this.toStorage();
        const set = dbLeafTree.buildUpdateQuery(storage);
        await leafExists ? dbLeafTree.write(`update ${dbLeafTree.table} set ${set} where id='${this.id}'`) : dbLeafTree.write(`insert into ${dbLeafTree.table} (id, docId, position, serviceId, label, text, parentId) values ('${this.id}', '${this.docId}', ${this.position}, '${this.serviceId}', '${this.label.replace(/'/g, "''")}', '${this.text.replace(/'/g, "''")}', '${this.parentId}')`);
        return this;
      }
    };
    module2.exports = Leaf;
  }
});

// ../../modules/documentation/backend/routes.js
var require_routes4 = __commonJS({
  "../../modules/documentation/backend/routes.js"(exports2, module2) {
    "use strict";
    var express = require("express");
    var Leaf = require_Leaf();
    var PromiseB2 = require("bluebird");
    var router = express.Router();
    module2.exports = (stackMonitor) => {
      const { findService } = stackMonitor;
      router.get("/documentation/tree", async (req, res) => {
        const service = findService(req.query.serviceId?.toString() || "");
        const result = await Leaf.getTree(service?.label);
        return res.send(result);
      });
      router.post("/documentation/tree/sort", async (req, res) => {
        const leafs = await PromiseB2.mapSeries(req.body, async (_leaf, index) => {
          const leaf = await Leaf.find({ id: _leaf.id });
          if (!leaf) return;
          leaf.position = index;
          await leaf.save();
          return leaf;
        });
        return res.send(leafs);
      });
      router.post("/documentation/tree", async (req, res) => {
        const result = await new Leaf({
          ...req.body
        }).save();
        return res.send(result);
      });
      router.post("/documentation/tree/:key", async (req, res) => {
        const result = await new Leaf({
          ...req.body,
          id: req.params.key
        }).save();
        return res.send(result);
      });
      router.delete("/documentation/tree/:key", async (req, res) => {
        const result = await Leaf.find({
          id: req.params.key
        });
        if (result) return res.send(await result.remove());
        return res.send();
      });
      return router;
    };
  }
});

// ../../modules/documentation/backend/index.js
var require_backend6 = __commonJS({
  "../../modules/documentation/backend/index.js"(exports2, module2) {
    "use strict";
    var { readFile } = require("fs/promises");
    var PromiseB2 = require("bluebird");
    var pathfs = require("path");
    var Documentation = require_Documentation();
    var plugin = {
      enabled: true,
      name: "Documentation",
      displayName: "Documentation",
      description: "Read documentation for a given service",
      icon: "fas fa-book",
      // export: Documentation,
      placements: ["service"],
      order: 6,
      routes: require_routes4(),
      finder: async (search, stackMonitor) => {
      }
    };
    module2.exports = plugin;
  }
});

// ../../modules/finder/backend/routes.js
var require_routes5 = __commonJS({
  "../../modules/finder/backend/routes.js"(exports2, module2) {
    "use strict";
    var express = require("express");
    var router = express.Router();
    var PromiseB2 = require("bluebird");
    function pluginToUrl(plugin) {
      for (let i = 0; i < plugin.placements.length; i += 1) {
        const placement = plugin.placements[i];
        if (typeof placement !== "string") {
          if (placement.position === "toolbox") {
            return `/toolbox${placement.goTo?.path || placement.goTo}`;
          }
          if (placement.position === "sidebar") {
            return `${placement.goTo?.path || placement.goTo}`;
          }
        }
      }
      return "";
    }
    var routes = (stackMonitor) => {
      const {
        getServices,
        helpers: { searchString }
      } = stackMonitor;
      router.get("/finder/search", async (req, res) => {
        const search = req.query.q?.toString()?.toUpperCase() || "";
        const services = getServices().filter((service) => searchString(service?.label, search)).map((service) => ({
          title: service.label,
          description: service.description,
          group: "Service",
          url: `/stack-single/${service.label}`
        }));
        const { plugins } = stackMonitor;
        const _plugins = (await PromiseB2.map(Object.keys(plugins), (key) => plugins[key]).map(async (plugin) => [
          ...await plugin?.finder?.(search, stackMonitor)?.catch?.(() => []) || [],
          ...searchString(plugin.name, search) ? [{
            title: plugin.displayName || plugin.name,
            description: plugin.description,
            group: "Plugin",
            icon: plugin.icon,
            url: pluginToUrl(plugin) || ""
          }] : []
        ])).flat().filter((a) => a?.url);
        const result = [
          ...services,
          ..._plugins
        ].filter((a) => a);
        res.send(result);
      });
      return router;
    };
    module2.exports = routes;
  }
});

// ../../modules/finder/backend/index.js
var require_backend7 = __commonJS({
  "../../modules/finder/backend/index.js"(exports2, module2) {
    "use strict";
    var commandExists = require("command-exists");
    var plugin = {
      enabled: true,
      name: "Finder",
      displayName: "Finder",
      description: "Find all you want inside this app",
      icon: "fab fa-git-alt",
      export: null,
      order: -1,
      placements: ["global", {
        position: "sidebar",
        label: "Finder",
        icon: "fas fa-search",
        goTo: { path: "/Finder" },
        active: "Finder"
      }],
      hidden: () => commandExists("git").then(() => false).catch(() => true),
      routes: require_routes5()
    };
    module2.exports = plugin;
  }
});

// helpers/exec.js
var require_exec = __commonJS({
  "helpers/exec.js"(exports2, module2) {
    "use strict";
    var { exec } = require("child_process");
    module2.exports = {
      /**
       * @param {string} cmd
       * @param {import('child_process').ExecOptions} options
       * @returns {Promise<string>}
       */
      execAsync(cmd, options) {
        return new Promise((res, rej) => {
          exec(cmd, options, (err, stdout, stderr) => {
            if (err) return rej(stderr || err);
            return res(stdout);
          });
        });
      },
      /**
       * @param {string} cmd
       * @param {import('child_process').ExecOptions} options
       * @returns {Promise<string>}
       */
      execAsyncWithoutErr(cmd, options) {
        return new Promise((res) => {
          exec(cmd, options, (err, stdout) => {
            res(stdout);
          });
        });
      },
      /**
       * @param {string} cmd
       * @param {import('child_process').ExecOptions} options
       * @returns {Promise<string>}
       */
      execAsyncGetError(cmd, options) {
        return new Promise((res) => {
          exec(cmd, options, (err, stdout, stderr) => {
            res(stderr);
          });
        });
      }
    };
  }
});

// ../../common/express-http-error/src/HTTPError.js
var require_HTTPError = __commonJS({
  "../../common/express-http-error/src/HTTPError.js"(exports2, module2) {
    "use strict";
    var dayjs = require("dayjs");
    var { v4: uuid } = require("uuid");
    var statusMessage = {
      403: "Not allowed",
      404: "Resource not found",
      500: "We cannot respond to your request for moment. Contact support for more information"
    };
    module2.exports.statusMessage = statusMessage;
    var HTTPError = class extends Error {
      /**
       *
       * @param {string} message
       * @param {number} code
       * @param {string} errorId
       * @param {string} date
       * @param {string} stack
       */
      constructor(message, code = 500, errorId = uuid(), date = dayjs().format("YYYY-MM-DD HH:mm:ss"), stack = "") {
        super(message);
        this.code = code || 500;
        this.errorId = errorId;
        this.date = date;
        this.message = process.env.NODE_ENV === "production" ? statusMessage[this.code] || message?.toString() || message : message?.toString() || message || statusMessage[this.code];
        this.originalMessage = message;
        this.originalStack = stack || new Error().stack;
      }
    };
    module2.exports = HTTPError;
  }
});

// ../../common/express-http-error/src/index.js
var require_src2 = __commonJS({
  "../../common/express-http-error/src/index.js"(exports2, module2) {
    "use strict";
    var HTTPError = require_HTTPError();
    module2.exports = HTTPError;
  }
});

// ../../modules/git/backend/Git.js
var require_Git = __commonJS({
  "../../modules/git/backend/Git.js"(exports2, module2) {
    "use strict";
    var { existsSync } = require("fs");
    var pathfs = require("path");
    var { execAsync, execAsyncWithoutErr } = require_exec();
    var HTTPError = require_src2();
    var Git = (stackMonitor) => {
      const { findService } = stackMonitor;
      const searchGit = (path) => {
        if (existsSync(pathfs.resolve(path, ".git"))) return path;
        const parentPath = pathfs.resolve(path, "..");
        if (parentPath === pathfs.resolve("/")) return null;
        return searchGit(parentPath);
      };
      const getGitRootPath = (service) => searchGit(service.getRootPath());
      async function requirements(service) {
        if (!service) throw new Error("Git Error: Service not found");
        if (!service.git) throw new Error(`Git Error - ${service?.label}: Git option not set`);
        const path = getGitRootPath(service);
        if (!path) return false;
        if (!existsSync(path)) return false;
        return true;
      }
      return {
        /**
         * @param {string} serviceName
         * @param {{graphOnAll?: boolean}} param1
         */
        async getGraph(serviceName, { graphOnAll } = {}) {
          const service = findService(serviceName);
          if (!await requirements(service)) return [];
          const cmd = `git -c color.ui=always log  --decorate=full --oneline --graph ${graphOnAll ? "--all" : ""} -500`;
          const result = await execAsync(cmd, { cwd: getGitRootPath(service), env: process.env });
          return result.split("\n");
        },
        /** @param {string} serviceName */
        async getBranches(serviceName, fetch = false) {
          const service = findService(serviceName);
          if (!await requirements(service)) return [];
          if (fetch) await this.fetch(serviceName);
          const origin = await this.getOrigin(serviceName);
          const unmergeableBranches = ["dev", "develop", "main", "master"];
          const currentBranch = await this.getCurrentBranch(service.label);
          const mergedBranches = await execAsyncWithoutErr("git branch --no-color --merged develop", { cwd: getGitRootPath(service) }).then((branches) => branches.trim().split("\n").map((a) => a.replace("*", "").trim()));
          const localBranches = await execAsyncWithoutErr("git branch --no-color", { cwd: getGitRootPath(service) }).then((res) => res.toString().trim().split("\n").map((branch) => {
            const name = branch.replace("*", "").trim();
            return {
              name,
              isRemote: false,
              isCurrentBranch: currentBranch === name,
              canDelete: !unmergeableBranches.includes(name) && currentBranch !== name,
              merged: mergedBranches.includes(name) && !unmergeableBranches.includes(name) && currentBranch !== name
            };
          }));
          const remoteBranches = await execAsyncWithoutErr("git branch --no-color -r", { cwd: getGitRootPath(service) }).then((res) => res.toString().trim().split("\n").map((branch) => {
            const name = branch.replace("*", "").trim();
            return {
              name: name.replace(`${origin}/`, ""),
              isRemote: true,
              canDelete: false,
              merged: false
            };
          }).filter((remoteBranch) => !localBranches.find((localBranch) => remoteBranch.name === localBranch.name)));
          return [
            ...localBranches,
            ...remoteBranches
          ];
        },
        /** @param {string} serviceName */
        async getCurrentBranch(serviceName) {
          const service = findService(serviceName);
          if (!await requirements(service)) return "";
          return (await execAsync("git rev-parse --abbrev-ref HEAD", { cwd: getGitRootPath(service) }).catch((err) => {
          }))?.trim();
        },
        /**
         *
         * @param {string} serviceName
         * @param {string} branchName
         * @param {boolean} shouldPush
         * @returns
         */
        async addBranch(serviceName, branchName, shouldPush = false) {
          if (!branchName) throw new Error("Branch name is empty");
          const service = findService(serviceName);
          if (!await requirements(service)) return "";
          const res = (await execAsync(`git checkout -b ${branchName}`, { cwd: getGitRootPath(service) }))?.trim();
          if (shouldPush) await this.push(serviceName);
          return res;
        },
        /** @param {string} serviceName */
        async getStatus(serviceName) {
          const service = findService(serviceName);
          if (!await requirements(service)) return [];
          return execAsyncWithoutErr("git -c color.status=no status -s", { cwd: getGitRootPath(service) }).then((res) => res.toString().trim().split("\n")?.filter((a) => a));
        },
        /** @param {string} serviceName */
        async getDiff(serviceName) {
          const service = findService(serviceName);
          if (!await requirements(service)) return "";
          return execAsync("git diff --minimal", { cwd: getGitRootPath(service) });
        },
        /**
         * @param {string} serviceName
         * @param {string} branchName
         */
        async changeBranch(serviceName, branchName) {
          const service = findService(serviceName);
          if (!await requirements(service)) return "ko";
          await execAsync(`git checkout ${branchName}`, { cwd: getGitRootPath(service) });
          return "ok";
        },
        /**
         * @param {string} serviceName
         * @param {string} branchName
         */
        async deleteBranch(serviceName, branchName) {
          const service = findService(serviceName);
          if (!await requirements(service)) return "ko";
          await execAsync(`git branch --delete ${branchName}`, { cwd: getGitRootPath(service) });
          return "ok";
        },
        /**
         * @param {string} serviceName
         * @param {string} branchName
         */
        async remoteDelta(serviceName, branchName) {
          const service = findService(serviceName);
          if (!await requirements(service)) return 0;
          const upstream = await execAsync("git rev-parse --abbrev-ref --symbolic-full-name @{u}", { cwd: getGitRootPath(service) }).catch(() => {
          });
          if (!upstream) {
            throw new HTTPError("BRANCH_NOT_PUSHED", 500.12578);
          }
          await execAsync(`git fetch origin ${branchName}`, { cwd: getGitRootPath(service) });
          const localCommit = await execAsync(`git log --oneline ${branchName}`, { cwd: getGitRootPath(service) });
          const remoteCommit = await execAsync(`git log --oneline origin/${branchName}`, { cwd: getGitRootPath(service) });
          return localCommit.trim().split("\n").length - remoteCommit.trim().split("\n").length;
        },
        /** @param {string} serviceName */
        async fetch(serviceName) {
          const service = findService(serviceName);
          if (!await requirements(service)) return "ko";
          await execAsync("git fetch", { cwd: getGitRootPath(service) });
          return "ok";
        },
        /** @param {string} serviceName */
        async reset(serviceName) {
          const service = findService(serviceName);
          if (!await requirements(service)) return "ko";
          await execAsync("git reset --hard", { cwd: getGitRootPath(service) });
          return "ok";
        },
        /** @param {string} serviceName */
        async pull(serviceName) {
          const service = findService(serviceName);
          if (!await requirements(service)) return "ko";
          const origin = await this.getOrigin(serviceName);
          const currentBranch = await this.getCurrentBranch(service.label);
          await execAsync(`git pull ${origin} ${currentBranch}`, { cwd: getGitRootPath(service) });
          return "ok";
        },
        /** @param {string} serviceName */
        async getOrigin(serviceName) {
          const service = findService(serviceName);
          if (!await requirements(service)) return "";
          return (await execAsync("git remote -v", { cwd: getGitRootPath(service) })).trim().split("\n").find((a) => a?.includes("fetch"))?.split("	")[0]?.trim() || "";
        },
        /** @param {string} serviceName */
        async push(serviceName) {
          const service = findService(serviceName);
          if (!await requirements(service)) return "ko";
          const origin = await this.getOrigin(serviceName);
          const currentBranch = await this.getCurrentBranch(service.label);
          await execAsync(`git push ${origin} ${currentBranch}`, { cwd: getGitRootPath(service) });
          return "ok";
        },
        /** @param {string} serviceName */
        async stash(serviceName) {
          const service = findService(serviceName);
          if (!await requirements(service)) return "ko";
          await execAsync("git add .", { cwd: getGitRootPath(service) });
          await execAsync("git stash", { cwd: getGitRootPath(service) });
          return "ok";
        },
        /** @param {string} serviceName */
        async stashPop(serviceName) {
          const service = findService(serviceName);
          if (!await requirements(service)) return "ko";
          await execAsync("git stash pop", { cwd: getGitRootPath(service) });
          await execAsync("git reset HEAD", { cwd: getGitRootPath(service) });
          return "ok";
        },
        /** @param {string} serviceName */
        async stashList(serviceName) {
          const service = findService(serviceName);
          if (!await requirements(service)) return "";
          const list = await execAsync("git stash show", { cwd: getGitRootPath(service) }).catch(() => "");
          return list;
        },
        /**
         * @param {string} serviceName
         * @param {string} filePath
         */
        async checkoutFile(serviceName, filePath) {
          const service = findService(serviceName);
          if (!await requirements(service)) return "ko";
          await execAsync(`git checkout ${filePath}`, { cwd: getGitRootPath(service) });
          return "ok";
        }
      };
    };
    module2.exports = Git;
  }
});

// ../../modules/git/backend/routes.js
var require_routes6 = __commonJS({
  "../../modules/git/backend/routes.js"(exports2, module2) {
    "use strict";
    var express = require("express");
    var router = express.Router();
    module2.exports = (stackMonitor) => {
      const { git } = stackMonitor;
      router.get("/git/:service/graph", async (req, res) => {
        const graph = await git.getGraph(req.params.service, { graphOnAll: req.query.graphOnAll === "true" });
        res.json(graph);
      });
      router.get("/git/:service/branches", async (req, res) => {
        const branches = await git.getBranches(req.params.service);
        res.json(branches);
      });
      router.get("/git/:service/status", async (req, res) => {
        const status = await git.getStatus(req.params.service);
        res.json(status);
      });
      router.get("/git/:service/diff", async (req, res) => {
        const diff = await git.getDiff(req.params.service);
        res.json(diff);
      });
      router.post("/git/:service/branch/:branchName/change", async (req, res) => {
        await git.changeBranch(req.params.service, req.params.branchName).then((result) => res.json(result));
      });
      router.delete("/git/:service/branch/:branchName", async (req, res) => {
        await git.deleteBranch(req.params.service, req.params.branchName).then((result) => res.json(result));
      });
      router.get("/git/:service/branch/:branchName/remote-delta", async (req, res) => {
        await git.remoteDelta(req.params.service, req.params.branchName).then((result) => res.json(result));
      });
      router.post("/git/:service/fetch", async (req, res) => {
        await git.fetch(req.params.service).then((result) => res.json(result));
      });
      router.delete("/git/:service/reset", async (req, res) => {
        await git.reset(req.params.service).then((result) => res.json(result));
      });
      router.get("/git/:service/current-branch", async (req, res) => {
        const currentBranch = await git.getCurrentBranch(req.params.service);
        res.json(currentBranch);
      });
      router.post("/git/:service/add-branch", async (req, res) => {
        const currentBranch = await git.addBranch(req.params.service, req.body.name, !!req.body.shouldPush);
        res.json(currentBranch);
      });
      router.post("/git/:service/pull", async (req, res) => {
        await git.pull(req.params.service).then((result) => res.json(result));
      });
      router.post("/git/:service/stash", async (req, res) => {
        await git.stash(req.params.service).then((result) => res.json(result));
      });
      router.post("/git/:service/stash-pop", async (req, res) => {
        await git.stashPop(req.params.service).then((result) => res.json(result));
      });
      router.post("/git/:service/stash-list", async (req, res) => {
        await git.stashList(req.params.service).then((result) => res.json(result));
      });
      router.delete("/git/:service/checkout/:file", async (req, res) => {
        await git.checkoutFile(req.params.service, req.params.file.trim()).then((result) => res.json(result));
      });
      return router;
    };
  }
});

// ../../modules/git/backend/index.js
var require_backend8 = __commonJS({
  "../../modules/git/backend/index.js"(exports2, module2) {
    "use strict";
    var commandExists = require("command-exists");
    var plugin = {
      enabled: true,
      name: "Git",
      displayName: "Git",
      description: "View and manage git across whole projects",
      icon: "fab fa-git-alt",
      export: require_Git(),
      placements: ["service", {
        position: "sidebar",
        label: "GIT",
        icon: "fab fa-git-alt",
        goTo: { path: "/Git-NotUpToDate" },
        active: "Git-NotUpToDate"
      }],
      order: 2,
      hidden: (service, stack, placement) => {
        if (placement === "sidebar") return commandExists("git").then(() => false).catch(() => true);
        if (placement === "service" && service?.git?.remote) return false;
        return true;
      },
      routes: require_routes6()
    };
    module2.exports = plugin;
  }
});

// ../../modules/github/backend/routes.js
var require_routes7 = __commonJS({
  "../../modules/github/backend/routes.js"(exports2, module2) {
    "use strict";
    var express = require("express");
    var { Octokit } = require("fix-esm").require("@octokit/core");
    var { restEndpointMethods } = require("fix-esm").require("@octokit/plugin-rest-endpoint-methods");
    var router = express.Router();
    var MyOctokit = Octokit.plugin(restEndpointMethods);
    var octokit = process.env.STACK_MONITOR_GH_APIKEY ? new MyOctokit({ auth: process.env.STACK_MONITOR_GH_APIKEY }) : null;
    module2.exports = (stackMonitor) => {
      const { findService } = stackMonitor;
      router.get("/github/service/:label/ready", async (req, res) => {
        requirements();
        if (!octokit) return null;
        const { data: { login } } = await octokit.rest.users.getAuthenticated();
        return res.send(login);
      });
      router.post("/github/service/:label/apikey", async (req, res) => {
        const { apikey } = req.body;
        if (!apikey) return res.status(400).send("apikey not found in body");
        const _octokit = new Octokit.Octokit({ auth: apikey });
        const { data: { login } } = await _octokit.rest.users.getAuthenticated();
        octokit = _octokit;
        return res.send(login);
      });
      router.get("/github/service/:label/whoami", async (req, res) => {
        requirements();
        if (!octokit) return;
        const { data: { login } } = await octokit.rest.users.getAuthenticated();
        res.json({ loggedAs: login });
      });
      router.get("/github/service/:label/pull-requests", async (req, res) => {
        const service = findService(req.params.label);
        if (!service) return res.status(404).send("Service not found");
        const [owner, repo] = `${new URL(service.git.home).pathname.replace(".git", "")}`.split("/").slice(-2);
        requirements();
        if (!octokit) return null;
        const { data } = await octokit.rest.pulls.list({ owner, repo });
        return res.json(data);
      });
      return router;
    };
    function requirements() {
      if (!octokit && !process.env.STACK_MONITOR_GH_APIKEY) {
        throw new Error("github api is not initialized");
      } else if (!octokit) {
        octokit = new MyOctokit({ auth: process.env.STACK_MONITOR_GH_APIKEY });
      }
    }
  }
});

// ../../modules/github/backend/index.js
var require_backend9 = __commonJS({
  "../../modules/github/backend/index.js"(exports2, module2) {
    "use strict";
    var plugin = {
      enabled: true,
      name: "Github",
      displayName: "Github",
      description: "View pull requests for a given service",
      icon: "fab fa-github",
      export: null,
      placements: ["service"],
      order: 3,
      /** @param {import('../../../servers/server/models/Service')} service */
      hidden: async (service) => {
        if (!service) return true;
        return !service.git?.remote?.includes("github.com");
      },
      routes: require_routes7()
    };
    module2.exports = plugin;
  }
});

// ../../modules/global-scripts/backend/GlobalScripts.js
var require_GlobalScripts = __commonJS({
  "../../modules/global-scripts/backend/GlobalScripts.js"(exports2, module2) {
    "use strict";
    var { v4 } = require("uuid");
    var globalScripts = [];
    var currentScriptsByCommunicationId = {};
    var GlobalScripts = (stackMonitor) => {
      const { Socket } = stackMonitor;
      return {
        /** @param {GlobalScript} script */
        addScript(script) {
          const index = globalScripts.findIndex((s) => s.label === script.label);
          if (index >= 0) {
            globalScripts.splice(index, 1);
          }
          globalScripts.push(script);
          stackMonitor.Socket.emit("reloadScripts");
        },
        getScripts() {
          return globalScripts;
        },
        /** @param {string} label */
        getScript(label) {
          const scripts = this.getScripts();
          const script = scripts.find((s) => s.label === label);
          return script;
        },
        /**
         * @param {string} scriptId
         * @param {string} stepLabel
         */
        getStep(scriptId, stepLabel) {
          const script = this.getScript(scriptId);
          if (!script) return null;
          const step = script.pipeline.find((st) => st.id === stepLabel);
          if (!step) return null;
          return step;
        },
        /** @param {string} id */
        launchScript(id) {
          const script = this.getScript(id);
          if (!script) throw new Error(`Script ${id} not found`);
          const communicationId = v4();
          const track = {
            scriptId: script.label,
            currentStep: script.pipeline[0].id,
            loadingStep: script.pipeline[0].id,
            steps: {},
            output: {},
            prompts: {}
          };
          currentScriptsByCommunicationId[communicationId] = track;
          function getStepIndex(step) {
            return (script?.pipeline || []).indexOf(step);
          }
          Socket.on(communicationId, async (socket, event, data) => {
            let step = null;
            while (step = this.getStep(script.label, track.currentStep)) {
              const index = getStepIndex(step);
              const nextStep = index >= 0 ? script.pipeline[index + 1]?.id : "";
              track.loadingStep = step.id;
              track.steps[track.currentStep] = {
                ...track.steps[track.currentStep],
                isValidated: false,
                error: void 0,
                printData: void 0
              };
              socket.emit(communicationId, "track", track);
              try {
                if (step.skip) {
                  const shouldSkip = await step.skip(track.output, track.prompts);
                  if (shouldSkip) {
                    track.steps[track.currentStep].skipped = true;
                    track.loadingStep = "";
                    track.currentStep = nextStep;
                    socket.emit(communicationId, "track", track);
                    continue;
                  }
                }
              } catch (error) {
                console.error("Script error");
                console.error(error);
                track.steps[track.currentStep].error = error.message || error;
                break;
              }
              const shouldBreak = await prompt(step, track, event, data);
              if (shouldBreak) break;
              if (event === "validate-prompt") event = "";
              if (step.script) {
                try {
                  track.output[track.currentStep] = await step.script(track.output, track.prompts);
                } catch (error) {
                  console.error("Script error");
                  console.error(error);
                  track.steps[track.currentStep].error = error.message || error;
                  break;
                }
              }
              if (step.print) {
                try {
                  track.steps[track.currentStep].printData = await step.print(track.output, track.prompts);
                  await new Promise((resolve) => setTimeout(() => {
                    resolve(null);
                  }, 0));
                } catch (error) {
                  console.error("Print error");
                  console.error(error);
                  track.steps[track.currentStep].error = error.message || error;
                  break;
                }
              }
              track.steps[track.currentStep].isValidated = true;
              track.currentStep = nextStep;
            }
            track.loadingStep = "";
            socket.emit(communicationId, "track", track);
          });
          return communicationId;
        }
      };
    };
    module2.exports = GlobalScripts;
    async function prompt(step, track, event, data) {
      if (!step.prompt) return false;
      if (event === "validate-prompt") {
        if (step.prompt?.validation) {
          try {
            const msgError = await step.prompt.validation(data);
            if (msgError) {
              track.steps[track.currentStep].error = msgError;
              return true;
            }
            track.prompts[track.currentStep] = data;
          } catch (error) {
            console.error("Script error");
            console.error(error);
            track.steps[track.currentStep].error = error.message || error;
            return true;
          }
        } else {
          track.prompts[track.currentStep] = data;
        }
        return false;
      }
      if (step.prompt.defaultValue) {
        try {
          const defaultValue = await step.prompt.defaultValue(track.output);
          track.steps[track.currentStep].promptValue = defaultValue;
        } catch (error) {
          console.error("Script error");
          console.error(error);
          track.steps[track.currentStep].error = error.message || error;
          return true;
        }
      }
      if (step.prompt.options) {
        try {
          const options = await step.prompt.options(track.output);
          track.steps[track.currentStep].promptOptions = options;
        } catch (error) {
          console.error("Script error");
          console.error(error);
          track.steps[track.currentStep].error = error.message || error;
          return true;
        }
      }
      return true;
    }
  }
});

// ../../modules/global-scripts/backend/routes.js
var require_routes8 = __commonJS({
  "../../modules/global-scripts/backend/routes.js"(exports2, module2) {
    "use strict";
    var express = require("express");
    var router = express.Router();
    module2.exports = (Stack) => {
      const { globalScripts } = Stack;
      router.get("/global-scripts/", async (req, res) => {
        res.json(globalScripts.getScripts());
      });
      router.get("/global-scripts/:id", async (req, res) => {
        const script = await globalScripts.getScript(req.params.id);
        res.json(script);
      });
      router.post("/global-scripts/:id", async (req, res) => {
        const communicationId = await globalScripts.launchScript(req.params.id);
        res.json(communicationId);
      });
      return router;
    };
  }
});

// ../../modules/global-scripts/backend/index.js
var require_backend10 = __commonJS({
  "../../modules/global-scripts/backend/index.js"(exports2, module2) {
    "use strict";
    var GlobalScripts = require_GlobalScripts();
    var plugin = {
      enabled: true,
      name: "Global scripts",
      displayName: "Global scripts",
      description: "Launch scripts and rule the world",
      icon: "fas fa-columns",
      export: GlobalScripts,
      placements: [
        {
          position: "dev-ops",
          label: "Global scripts",
          iconText: "{}",
          goTo: { path: "/GlobalScripts" },
          active: "GlobalScripts"
        }
      ],
      finder: (search, stackMonitor) => {
        const scripts = stackMonitor.globalScripts.getScripts().filter((script) => script.label.toUpperCase()?.includes(search?.toUpperCase()));
        return [
          ...scripts.map((script) => ({
            icon: "fas fa-cog",
            title: script.label,
            group: "Global scripts",
            description: "",
            secondaryTitle: "",
            url: { path: "/DevOps/GlobalScripts", query: { script: script.label } }
          }))
        ];
      },
      routes: require_routes8()
    };
    module2.exports = plugin;
  }
});

// ../../modules/http-client/backend/HttpClient.js
var require_HttpClient = __commonJS({
  "../../modules/http-client/backend/HttpClient.js"(exports2, module2) {
    "use strict";
    var axios = require("axios").default;
    var HttpClient = (stackMonitor) => ({
      /**
       * Send HTTP request
       * @param {Object} options - Request options
       * @param {string} options.method - HTTP method (GET, POST, PUT, DELETE, etc.)
       * @param {string} options.url - Request URL
       * @param {Object} [options.headers] - Request headers
       * @param {Object|string} [options.body] - Request body
       * @param {Object} [options.params] - Query parameters
       * @returns {Promise<Object>} Response data
       */
      sendRequest: async ({ method, url, headers = {}, body = null, params = {} }) => {
        try {
          const config = {
            method,
            url,
            headers,
            params
          };
          if (body) {
            config.data = body;
          }
          const startTime = Date.now();
          const response = await axios(config);
          const endTime = Date.now();
          return {
            status: response.status,
            statusText: response.statusText,
            headers: response.headers,
            data: response.data,
            responseTime: endTime - startTime
          };
        } catch (error) {
          if (error && typeof error === "object" && "response" in error) {
            const axiosError = (
              /** @type {import('axios').AxiosError} */
              error
            );
            return {
              status: axiosError.response?.status,
              statusText: axiosError.response?.statusText,
              headers: axiosError.response?.headers,
              data: axiosError.response?.data,
              error: true
            };
          }
          throw error;
        }
      }
    });
    module2.exports = HttpClient;
  }
});

// ../../modules/http-client/backend/routes.js
var require_routes9 = __commonJS({
  "../../modules/http-client/backend/routes.js"(exports2, module2) {
    "use strict";
    var express = require("express");
    var router = express.Router();
    var HttpClient = require_HttpClient();
    module2.exports = (stackMonitor) => {
      const httpClient = HttpClient(stackMonitor);
      router.post("/http-client/request", async (req, res) => {
        try {
          const { method, url, headers, body, params } = req.body;
          const response = await httpClient.sendRequest({
            method,
            url,
            headers,
            body,
            params
          });
          res.json(response);
        } catch (error) {
          res.status(500).json({
            error: true,
            message: error instanceof Error ? error.message : "Unknown error",
            stack: error instanceof Error ? error.stack : void 0
          });
        }
      });
      router.get("/http-client/history", (req, res) => {
        res.json([]);
      });
      router.post("/http-client/history", (req, res) => {
        res.json({ success: true });
      });
      return router;
    };
  }
});

// ../../modules/http-client/backend/index.js
var require_backend11 = __commonJS({
  "../../modules/http-client/backend/index.js"(exports2, module2) {
    "use strict";
    var plugin = {
      enabled: true,
      name: "HttpClient",
      displayName: "HTTP Client",
      description: "Test HTTP endpoints like Postman",
      icon: "fas fa-exchange-alt",
      placements: [
        {
          position: "toolbox",
          label: "HTTP Client",
          icon: "fas fa-exchange-alt",
          goTo: { path: "/HttpClient" },
          active: "HttpClient"
        }
      ],
      export: require_HttpClient(),
      order: 7,
      routes: require_routes9()
    };
    module2.exports = plugin;
  }
});

// ../../modules/json-formatter/backend/JSONFormatter.js
var require_JSONFormatter = __commonJS({
  "../../modules/json-formatter/backend/JSONFormatter.js"(exports2, module2) {
    "use strict";
    var fs = require("fs");
    var path = require("path");
    var yaml = require("js-yaml");
    var xmljs = require("xml-js");
    var Ajv = require("ajv").default;
    var JSONFormatter = class {
      /**
       * Validate JSON against a schema
       * @param {Object} json - The JSON object to validate
       * @param {Object} schema - The JSON schema
       * @returns {Object} Validation result
       */
      validateSchema(json, schema) {
        const ajv = new Ajv();
        const validate = ajv.compile(schema);
        const valid = validate(json);
        return {
          valid,
          errors: validate.errors || []
        };
      }
      /**
       * Convert JSON to YAML
       * @param {Object} json - The JSON object to convert
       * @returns {string} YAML string
       */
      jsonToYaml(json) {
        return yaml.dump(json);
      }
      /**
       * Convert YAML to JSON
       * @param {string} yamlStr - The YAML string to convert
       * @returns {Object} JSON object
       */
      yamlToJson(yamlStr) {
        return yaml.load(yamlStr);
      }
      /**
       * Convert JSON to XML
       * @param {Object} json - The JSON object to convert
       * @returns {string} XML string
       */
      jsonToXml(json) {
        return xmljs.js2xml(json, { compact: true, spaces: 2 });
      }
      /**
       * Convert XML to JSON
       * @param {string} xmlStr - The XML string to convert
       * @returns {Object} JSON object
       */
      xmlToJson(xmlStr) {
        return xmljs.xml2js(xmlStr, { compact: true });
      }
      /**
       * Compress JSON (remove whitespace)
       * @param {Object|string} json - The JSON object or string to compress
       * @returns {string} Compressed JSON string
       */
      compressJson(json) {
        const jsonObj = typeof json === "string" ? JSON.parse(json) : json;
        return JSON.stringify(jsonObj);
      }
      /**
       * Pretty print JSON (add indentation)
       * @param {Object|string} json - The JSON object or string to format
       * @param {number} indentation - Number of spaces for indentation
       * @returns {string} Formatted JSON string
       */
      prettyPrintJson(json, indentation = 2) {
        const jsonObj = typeof json === "string" ? JSON.parse(json) : json;
        return JSON.stringify(jsonObj, null, indentation);
      }
      /**
       * Calculate statistics about a JSON object
       * @param {Object} json - The JSON object
       * @returns {Object} Statistics object
       */
      getJsonStats(json) {
        const stats = {
          size: 0,
          stringifiedSize: 0,
          depth: 0,
          keys: 0,
          arrays: 0,
          objects: 0,
          primitives: 0
        };
        stats.stringifiedSize = JSON.stringify(json).length;
        stats.size = Buffer.from(JSON.stringify(json)).length;
        const traverse = (obj, depth = 0) => {
          if (depth > stats.depth) stats.depth = depth;
          if (Array.isArray(obj)) {
            stats.arrays++;
            for (const item of obj) {
              if (typeof item === "object" && item !== null) {
                traverse(item, depth + 1);
              } else {
                stats.primitives++;
              }
            }
          } else if (typeof obj === "object" && obj !== null) {
            stats.objects++;
            const objKeys = Object.keys(obj);
            stats.keys += objKeys.length;
            for (const key of objKeys) {
              if (typeof obj[key] === "object" && obj[key] !== null) {
                traverse(obj[key], depth + 1);
              } else {
                stats.primitives++;
              }
            }
          }
        };
        traverse(json);
        return stats;
      }
      /**
       * Query JSON using JSONPath
       * @param {Object} json - The JSON object
       * @param {string} query - JSONPath query
       * @returns {Array} Query results
       */
      queryJson(json, query) {
        if (query === "$") return json;
        return [{ message: "JSONPath implementation would return results here" }];
      }
    };
    module2.exports = new JSONFormatter();
  }
});

// ../../modules/json-formatter/backend/routes.js
var require_routes10 = __commonJS({
  "../../modules/json-formatter/backend/routes.js"(exports2, module2) {
    "use strict";
    var express = require("express");
    var router = express.Router();
    var JSONFormatter = require_JSONFormatter();
    module2.exports = function(stackMonitor) {
      router.post("/JSONFormatterEnhanced/validate", (req, res) => {
        try {
          const { json, schema } = req.body;
          const result = JSONFormatter.validateSchema(json, schema);
          res.json(result);
        } catch (error) {
          res.status(400).json({ error: error instanceof Error ? error.message : String(error) });
        }
      });
      router.post("/JSONFormatterEnhanced/to-yaml", (req, res) => {
        try {
          const { json } = req.body;
          const yaml = JSONFormatter.jsonToYaml(json);
          res.json({ yaml });
        } catch (error) {
          res.status(400).json({ error: error instanceof Error ? error.message : String(error) });
        }
      });
      router.post("/JSONFormatterEnhanced/from-yaml", (req, res) => {
        try {
          const { yaml } = req.body;
          const json = JSONFormatter.yamlToJson(yaml);
          res.json({ json });
        } catch (error) {
          res.status(400).json({ error: error instanceof Error ? error.message : String(error) });
        }
      });
      router.post("/JSONFormatterEnhanced/to-xml", (req, res) => {
        try {
          const { json } = req.body;
          const xml = JSONFormatter.jsonToXml(json);
          res.json({ xml });
        } catch (error) {
          res.status(400).json({ error: error instanceof Error ? error.message : String(error) });
        }
      });
      router.post("/JSONFormatterEnhanced/from-xml", (req, res) => {
        try {
          const { xml } = req.body;
          const json = JSONFormatter.xmlToJson(xml);
          res.json({ json });
        } catch (error) {
          res.status(400).json({ error: error instanceof Error ? error.message : String(error) });
        }
      });
      router.post("/JSONFormatterEnhanced/stats", (req, res) => {
        try {
          const { json } = req.body;
          const stats = JSONFormatter.getJsonStats(json);
          res.json(stats);
        } catch (error) {
          res.status(400).json({ error: error instanceof Error ? error.message : String(error) });
        }
      });
      router.post("/JSONFormatterEnhanced/query", (req, res) => {
        try {
          const { json, query } = req.body;
          const results = JSONFormatter.queryJson(json, query);
          res.json(results);
        } catch (error) {
          res.status(400).json({ error: error instanceof Error ? error.message : String(error) });
        }
      });
      return router;
    };
  }
});

// ../../modules/json-formatter/backend/index.js
var require_backend12 = __commonJS({
  "../../modules/json-formatter/backend/index.js"(exports2, module2) {
    "use strict";
    var plugin = {
      enabled: true,
      name: "JSONFormatter",
      displayName: "JSON",
      description: "View, edit, validate, transform and explore JSON data",
      icon: "fas fa-brackets-curly",
      export: require_JSONFormatter(),
      placements: [
        {
          position: "toolbox",
          label: "JSON",
          iconText: "{}",
          goTo: { path: "/JSONFormatter" },
          active: "JSONFormatter"
        }
      ],
      routes: require_routes10(),
      order: 6
    };
    module2.exports = plugin;
  }
});

// ../../modules/jwt/backend/JWT.js
var require_JWT = __commonJS({
  "../../modules/jwt/backend/JWT.js"(exports2, module2) {
    "use strict";
    var jsonwebtoken = require("jsonwebtoken");
    var JWT = class {
      /**
       * Decode a JWT without verifying signature
       * @param {string} token JWT token string
       * @returns {object} Decoded JWT payload
       */
      decode(token) {
        return jsonwebtoken.decode(token, { complete: true });
      }
      /**
       * Verify and decode a JWT token
       * @param {string} token JWT token string
       * @param {string} secret Secret key for verification
       * @param {object} options JWT verify options
       * @returns {object} Decoded JWT if valid
       */
      verify(token, secret, options = {}) {
        try {
          return jsonwebtoken.verify(token, secret, options);
        } catch (err) {
          const error = err instanceof Error ? err.message : String(err);
          return { error };
        }
      }
      /**
       * Generate a new JWT token
       * @param {object} payload Data to encode in the token
       * @param {string} secret Secret key for signing
       * @param {object} options JWT sign options
       * @returns {string} Signed JWT token
       */
      generate(payload, secret, options = {}) {
        return jsonwebtoken.sign(payload, secret, options);
      }
      /**
       * Analyze token structure and details
       * @param {string} token JWT token string
       * @returns {object} Analysis results
       */
      analyze(token) {
        try {
          const decoded = this.decode(token);
          if (!decoded) return { error: "Invalid token format" };
          const parts = token.split(".");
          const analysis = {
            structure: {
              header: decoded.header,
              payload: decoded.payload,
              signature: parts[2] ? true : false
            },
            validation: {
              hasExpiry: decoded.payload.exp ? true : false,
              hasIssuedAt: decoded.payload.iat ? true : false,
              hasNotBefore: decoded.payload.nbf ? true : false
            }
          };
          if (analysis.validation.hasExpiry) {
            const expiry = new Date(decoded.payload.exp * 1e3);
            const now = /* @__PURE__ */ new Date();
            analysis.validation.isExpired = now > expiry;
            analysis.validation.expiresIn = analysis.validation.isExpired ? "Expired" : this.formatTimeRemaining(expiry.getTime() - now.getTime());
          }
          if (decoded.header.alg) {
            analysis.security = {
              algorithm: decoded.header.alg,
              isSecure: decoded.header.alg !== "none" && !decoded.header.alg.startsWith("HS")
            };
          }
          return analysis;
        } catch (err) {
          const error = err instanceof Error ? err.message : String(err);
          return { error };
        }
      }
      /**
       * Format milliseconds to human readable time
       * @param {number} ms Milliseconds
       * @returns {string} Formatted time string
       */
      formatTimeRemaining(ms) {
        if (ms <= 0) return "Expired";
        const seconds = Math.floor(ms / 1e3);
        const minutes = Math.floor(seconds / 60);
        const hours = Math.floor(minutes / 60);
        const days = Math.floor(hours / 24);
        if (days > 0) return `${days} day(s)`;
        if (hours > 0) return `${hours} hour(s)`;
        if (minutes > 0) return `${minutes} minute(s)`;
        return `${seconds} second(s)`;
      }
    };
    module2.exports = new JWT();
  }
});

// ../../modules/jwt/backend/routes.js
var require_routes11 = __commonJS({
  "../../modules/jwt/backend/routes.js"(exports2, module2) {
    "use strict";
    var express = require("express");
    var JWT = require_JWT();
    var router = express.Router();
    module2.exports = () => {
      router.post("/JWT/", async (req, res) => {
        const { jwt } = req.body;
        try {
          res.json(JWT.decode(jwt));
        } catch (err) {
          res.status(400).json({ error: err instanceof Error ? err.message : String(err) });
        }
      });
      router.post("/JWT/analyze", async (req, res) => {
        const { jwt } = req.body;
        try {
          res.json(JWT.analyze(jwt));
        } catch (err) {
          res.status(400).json({ error: err instanceof Error ? err.message : String(err) });
        }
      });
      router.post("/JWT/verify", async (req, res) => {
        const { jwt, secret, options } = req.body;
        try {
          res.json(JWT.verify(jwt, secret, options));
        } catch (err) {
          res.status(400).json({ error: err instanceof Error ? err.message : String(err) });
        }
      });
      router.post("/JWT/generate", async (req, res) => {
        const { payload, secret, options } = req.body;
        try {
          const token = JWT.generate(payload, secret, options);
          res.json({ token });
        } catch (err) {
          res.status(400).json({ error: err instanceof Error ? err.message : String(err) });
        }
      });
      return router;
    };
  }
});

// ../../modules/jwt/backend/index.js
var require_backend13 = __commonJS({
  "../../modules/jwt/backend/index.js"(exports2, module2) {
    "use strict";
    var plugin = {
      enabled: true,
      name: "JWT",
      displayName: "JWT",
      description: "Decode, verify and create JSON Web Tokens",
      icon: "fas fa-key",
      export: require_JWT(),
      placements: [
        {
          position: "toolbox",
          label: "JWT",
          icon: "fas fa-key",
          goTo: { path: "/JWT" },
          active: "JWT"
        }
      ],
      order: 6,
      routes: require_routes11()
    };
    module2.exports = plugin;
  }
});

// ../../modules/kanban/backend/save.js
var require_save = __commonJS({
  "../../modules/kanban/backend/save.js"(exports2, module2) {
    "use strict";
    var kanban = {
      /** @param {import('@clabroche/common-typings').StackMonitor} stackMonitor */
      getSave(stackMonitor) {
        return stackMonitor.getSave("kanban.json", {
          /** @type {Partial<import('@clabroche/common-typings').NonFunctionProperties<import('./Kanban').BoardType>['prototype']>[]} */
          boards: [],
          /** @type {Partial<import('@clabroche/common-typings').NonFunctionProperties<import('./Kanban').ColumnType>['prototype']>[]} */
          columns: [],
          /** @type {Partial<import('@clabroche/common-typings').NonFunctionProperties<import('./Kanban').CardType>['prototype']>[]} */
          cards: []
        }, {
          afterGet(data) {
            if (!data.boards) data.boards = [];
            if (!data.columns) data.columns = [];
            if (!data.cards) data.cards = [];
          },
          beforeSave(data) {
            if (!data.boards) data.boards = [];
            if (!data.columns) data.columns = [];
            if (!data.cards) data.cards = [];
          }
        });
      }
    };
    module2.exports = kanban;
  }
});

// ../../modules/kanban/backend/Kanban.js
var require_Kanban = __commonJS({
  "../../modules/kanban/backend/Kanban.js"(exports2, module2) {
    "use strict";
    var { v4 } = require("uuid");
    var { getSave: _getSave } = require_save();
    var stackMonitor = null;
    function getSave() {
      if (!stackMonitor) throw new Error("Call init before");
      const { data, save } = _getSave(stackMonitor);
      return { data, save };
    }
    var Board = class _Board {
      /** @param {Partial<import('@clabroche/common-typings').NonFunctionProperties<Board>>} board */
      constructor(board = {}) {
        this.id = board.id || v4();
        this.name = board.name;
        this.columnIds = board.columnIds || [];
      }
      /** @returns {Board[]} */
      static all() {
        const { data } = getSave();
        return data.boards.map((b) => b ? new _Board(b) : null).filter((a) => a);
      }
      /** @param {string} id */
      static get(id) {
        const { data } = getSave();
        const board = data.boards.find((d) => d?.id === id);
        return board ? new _Board(board) : null;
      }
      /**
       *
       * @returns {Column[]}
       */
      getColumns() {
        return this.columnIds.map((columnId) => Column.get(columnId)).filter((a) => a);
      }
      delete() {
        const { data, save } = getSave();
        this.getColumns().forEach((c) => c.delete());
        data.boards = data.boards.filter((b) => b?.id !== this.id);
        save();
      }
      /** @param {string} id */
      getColumn(id) {
        return this.getColumns().find((c) => c?.id === id);
      }
      /** @param {Partial<import('@clabroche/common-typings').NonFunctionProperties<Column>>} column */
      addColumn(column) {
        const savedColumn = new Column({ ...column, boardId: this.id }).save();
        this.columnIds = [.../* @__PURE__ */ new Set([...this.columnIds, savedColumn.id])];
        this.save();
        return savedColumn;
      }
      save() {
        const { data, save } = getSave();
        const existing = data.boards.find((b) => b?.id === this.id);
        if (existing) {
          Object.assign(existing, { ...this });
        } else {
          data.boards.push(this);
        }
        save();
        return this;
      }
    };
    var Column = class _Column {
      /** @param {Partial<import('@clabroche/common-typings').NonFunctionProperties<Column>>} column */
      constructor(column = {}) {
        this.id = column.id || v4();
        this.name = column.name;
        this.color = column.color;
        this.boardId = column.boardId;
        this.cardIds = column.cardIds || [];
      }
      /** @param {string} id */
      static get(id) {
        const { data } = getSave();
        const column = data.columns.find((d) => d?.id === id);
        return column ? new _Column(column) : null;
      }
      /** @returns {Card[]} */
      getCards() {
        return this.cardIds.map((columnId) => Card.get(columnId)).filter((a) => a);
      }
      /** @param {string} id */
      getCard(id) {
        return this.getCards().find((c) => c?.id === id);
      }
      /** @param {Partial<import('@clabroche/common-typings').NonFunctionProperties<Card>>} card */
      addCard(card) {
        const savedColumn = new Card({ ...card, boardId: this.boardId, columnId: this.id }).save();
        this.cardIds = [.../* @__PURE__ */ new Set([...this.cardIds, savedColumn.id])];
        this.save();
        return savedColumn;
      }
      delete() {
        const { data, save } = getSave();
        this.getCards().forEach((c) => c.delete());
        data.columns = data.columns.filter((b) => b?.id !== this.id);
        save();
      }
      save() {
        const { data, save } = getSave();
        const existing = data.columns.find((b) => b?.id === this.id);
        if (existing) {
          Object.assign(existing, { ...this });
        } else {
          data.columns.push(this);
        }
        save();
        return this;
      }
    };
    var Card = class _Card {
      /** @param {Partial<import('@clabroche/common-typings').NonFunctionProperties<Card>>} card */
      constructor(card = {}) {
        this.id = card.id || v4();
        this.name = card.name;
        this.description = card.description;
        this.boardId = card.boardId;
        this.columnId = card.columnId;
      }
      /** @param {string} id */
      static get(id) {
        const { data } = getSave();
        const cards = data.cards.find((d) => d?.id === id);
        return cards ? new _Card(cards) : null;
      }
      delete() {
        const { data, save } = getSave();
        data.cards = data.cards.filter((b) => b?.id !== this.id);
        save();
      }
      save() {
        const { data, save } = getSave();
        const existing = data.cards.find((b) => b?.id === this.id);
        if (existing) {
          Object.assign(existing, { ...this });
        } else {
          data.cards.push(this);
        }
        save();
        return this;
      }
    };
    var Kanban = (_stackMonitor) => {
      stackMonitor = _stackMonitor;
      return {
        Card,
        Column,
        Board
      };
    };
    module2.exports = Kanban;
  }
});

// ../../modules/kanban/backend/routes.js
var require_routes12 = __commonJS({
  "../../modules/kanban/backend/routes.js"(exports2, module2) {
    "use strict";
    var express = require("express");
    var router = express.Router();
    module2.exports = (stackMonitor) => {
      const { Board } = require_Kanban()(stackMonitor);
      router.get("/kanban/boards", async (req, res) => {
        res.json(Board.all());
      });
      router.post("/kanban/boards", async (req, res) => {
        const board = new Board(req.body).save();
        res.json(board);
      });
      router.delete("/kanban/boards/:boardId", async (req, res) => {
        const board = Board.get(req.params.boardId);
        board?.delete();
        res.json(board);
      });
      router.get("/kanban/boards/:boardId/columns", async (req, res) => {
        res.json(Board.get(req.params.boardId)?.getColumns());
      });
      router.get("/kanban/boards/:boardId/columns/:columnId", async (req, res) => {
        res.json(Board.get(req.params.boardId)?.getColumn(req.params.columnId));
      });
      router.delete("/kanban/boards/:boardId/columns/:columnId", async (req, res) => {
        const column = Board.get(req.params.boardId)?.getColumn(req.params.columnId);
        column?.delete();
        res.json(column);
      });
      router.post("/kanban/boards/:boardId/columns", async (req, res) => {
        const board = Board.get(req.params.boardId)?.addColumn(req.body);
        res.json(board);
      });
      router.get("/kanban/boards/:boardId/columns/:columnId/cards", async (req, res) => {
        res.json(Board.get(req.params.boardId)?.getColumn(req.params.columnId)?.getCards());
      });
      router.delete("/kanban/boards/:boardId/columns/:columnId/cards/:cardId", async (req, res) => {
        const card = Board.get(req.params.boardId)?.getColumn(req.params.columnId)?.getCard(req.params.cardId);
        card?.delete();
        res.json(card);
      });
      router.post("/kanban/boards/:boardId/columns/:columnId/cards", async (req, res) => {
        res.json(Board.get(req.params.boardId)?.getColumn(req.params.columnId)?.addCard(req.body));
      });
      return router;
    };
  }
});

// ../../modules/kanban/backend/index.js
var require_backend14 = __commonJS({
  "../../modules/kanban/backend/index.js"(exports2, module2) {
    "use strict";
    var Kanban = require_Kanban();
    var plugin = {
      enabled: true,
      name: "kanban",
      displayName: "Kanban",
      description: "Manage your project with cards",
      icon: "fas fa-columns",
      placements: [
        {
          position: "toolbox",
          label: "Kanban",
          icon: "fas fa-columns",
          goTo: { path: "/kanban" },
          active: "kanban"
        }
      ],
      finder: (search, stackMonitor) => {
        const boards = Kanban(stackMonitor).Board.all().filter((board) => stackMonitor.helpers.searchString(board?.name || "", search));
        const cards = Kanban(stackMonitor).Board.all().flatMap((b) => b?.getColumns())?.flatMap((c) => c?.getCards())?.filter((board) => stackMonitor.helpers.searchString(board?.name || "", search));
        return [
          ...boards.map((board) => ({
            icon: "fas fa-columns",
            title: board?.name || "",
            group: "Kanban",
            description: "Manage your project with cards",
            secondaryTitle: "Board",
            url: { path: "/toolbox/kanban", query: { boardId: board?.id } }
          })),
          ...cards.map((card) => ({
            icon: "fas fa-columns",
            title: card?.name || "",
            group: "Kanban",
            description: "Manage your project with cards",
            secondaryTitle: "Card",
            url: { path: "/toolbox/kanban", query: { boardId: card?.boardId, cardId: card?.id } }
          }))
        ];
      },
      export: Kanban,
      order: 6,
      routes: require_routes12()
    };
    module2.exports = plugin;
  }
});

// ../../modules/logs/backend/routes.js
var require_routes13 = __commonJS({
  "../../modules/logs/backend/routes.js"(exports2, module2) {
    "use strict";
    var express = require("express");
    var { v4 } = require("uuid");
    var router = express.Router();
    module2.exports = (stackMonitor) => {
      const history = stackMonitor.getSave("history.json", {
        /** @type {History[]} */
        history: []
      });
      const { findService, Socket } = stackMonitor;
      router.get("/logs/:service/logs", (req, res) => {
        const service = findService(req.params.service);
        res.send(service ? service.store : []);
      });
      router.get("/logs/:service/autocomplete", (req, res) => {
        const msg = req.query.message;
        if (!msg && !req.query.force) return res.json([]);
        const historyToSend = groupBy(history.data.history, "raw").sort((a, b) => a.timestamp - b.timestamp).filter((a) => a?.raw?.startsWith(msg) || a.cmd?.startsWith(msg)).slice(-10);
        return res.json(historyToSend);
      });
      router.post("/logs/:service/prompt", async (req, res) => {
        const service = findService(req.params.service);
        let command = req.body.command || {};
        const pid = req.body.pid ? +req.body.pid : void 0;
        if (!command.spawnCmd) command.spawnCmd = "\n";
        let result = {
          id: v4(),
          pid,
          cmd: command.spawnCmd,
          args: [],
          raw: command.spawnCmd,
          timestamp: Date.now(),
          service: service.label || ""
        };
        if (pid) service.respondToProcess(pid, command.spawnCmd);
        else if (command) {
          const { spawnProcess, launchMessage } = await service.launchProcess(
            { spawnCmd: command.spawnCmd, spawnArgs: command.spawnArgs || [], spawnOptions: command.spawnOptions || {} },
            false
          );
          result = {
            ...result,
            pid: spawnProcess?.pid,
            raw: launchMessage.raw,
            timestamp: launchMessage.timestamp
          };
          history.data.history.push(result);
          history.save();
        }
        res.json(result);
      });
      router.post("/logs/:service/terminate", (req, res) => {
        const service = findService(req.params.service);
        const {
          /** @type {number | undefined} */
          pid,
          /** @type {boolean | undefined} */
          forceKill
        } = req.body;
        if (!pid) throw new Error("Pid is required");
        if (pid) service.terminate(pid, !!forceKill);
        Socket.emit("logs:update", []);
        res.send("ok");
      });
      router.delete("/logs/:service/logs", (req, res) => {
        const service = findService(req.params.service);
        service.store = [];
        Socket.emit("logs:clear", { label: service.label });
        res.send(service.store);
      });
      return router;
    };
    var groupBy = (xs, key) => {
      const group = xs.reduce((rv, x) => {
        if (!rv[x[key]]) rv[x[key]] = { ...x, number: 0, timestamps: [] };
        rv[x[key]].timestamps.push(x.timestamp);
        rv[x[key]].timestamp = x.timestamp;
        return rv;
      }, {});
      return Object.keys(group).map((key2) => group[key2]);
    };
  }
});

// ../../modules/logs/backend/index.js
var require_backend15 = __commonJS({
  "../../modules/logs/backend/index.js"(exports2, module2) {
    "use strict";
    var dayjs = require("dayjs");
    var plugin = {
      enabled: true,
      name: "Logs",
      displayName: "Logs",
      description: "Show, parse and communicate with logs produced by yours commands",
      icon: "fas fa-terminal",
      export: null,
      placements: ["service"],
      order: 1,
      /**
       * @param {*} search
       * @param {import('@clabroche/common-typings').StackMonitor} stackMonitor
       */
      finder: (search, stackMonitor) => {
        const services = stackMonitor.getServices()?.flatMap((s) => s.store?.slice(-300).reverse().map((line) => ({ log: line, service: s })))?.filter(({ log }) => stackMonitor.helpers.searchString(log.raw, search));
        return [
          ...services.map((service) => ({
            icon: "fas fa-terminal",
            title: service.log.raw,
            group: "Logs",
            description: `Log from ${service.service.label}`,
            secondaryTitle: service.service.label,
            secondaryDescription: dayjs(service.log.timestamp).format("YYYY-DD-MM HH:mm:ss"),
            url: { path: `/stack-single/${encodeURIComponent(service.service.label)}`, query: { tab: "Logs" } }
          }))
        ];
      },
      routes: require_routes13()
    };
    module2.exports = plugin;
  }
});

// ../../modules/mongo/backend/Mongo.js
var require_Mongo = __commonJS({
  "../../modules/mongo/backend/Mongo.js"(exports2, module2) {
    "use strict";
    var { ObjectId } = require("mongodb");
    var { MongoClient } = require("mongodb");
    var MongoDB = class {
      /**
       * Generate a new ObjectId
       * @returns {ObjectId} A new MongoDB ObjectId
       */
      generateObjectId() {
        return new ObjectId();
      }
      /**
       * Check if a string is a valid MongoDB ObjectId
       * @param {string} id - The string to check
       * @returns {boolean} True if valid, false otherwise
       */
      isValidObjectId(id) {
        if (!id) return false;
        try {
          new ObjectId(id);
          return true;
        } catch (error) {
          return false;
        }
      }
      /**
       * Decode an ObjectId to get its components
       * @param {string} objectId - The ObjectId to decode
       * @returns {Object} The decoded information
       */
      decodeObjectId(objectId) {
        if (!this.isValidObjectId(objectId)) {
          return {
            isValid: false,
            error: "Invalid ObjectId format"
          };
        }
        try {
          const timestamp = parseInt(objectId.substring(0, 8), 16);
          const date = new Date(timestamp * 1e3);
          const buffer = Buffer.from(objectId, "hex");
          return {
            isValid: true,
            timestamp,
            date,
            machineId: objectId.substring(8, 14),
            processId: objectId.substring(14, 18),
            counter: objectId.substring(18, 24),
            format: {
              hex: objectId,
              binary: buffer.toString("binary"),
              base64: buffer.toString("base64")
            }
          };
        } catch (error) {
          const errorMessage = error instanceof Error ? error.message : String(error);
          return {
            isValid: false,
            error: errorMessage
          };
        }
      }
      /**
       * Create an ObjectId from a timestamp
       * @param {Date|string} date - The date to use
       * @returns {string} The generated ObjectId
       */
      objectIdFromDate(date) {
        const timestamp = Math.floor(new Date(date).getTime() / 1e3);
        return `${timestamp.toString(16)}0000000000000000`;
      }
      /**
       * Compare two ObjectIds
       * @param {string} objectId1 - First ObjectId
       * @param {string} objectId2 - Second ObjectId
       * @returns {Object} Comparison results
       */
      compareObjectIds(objectId1, objectId2) {
        const obj1 = this.decodeObjectId(objectId1);
        const obj2 = this.decodeObjectId(objectId2);
        if (!obj1.isValid || !obj2.isValid) {
          return {
            isValid: false,
            error: "One or both ObjectIds are invalid"
          };
        }
        const timeDifference = obj1.timestamp - obj2.timestamp;
        const millisecondsDiff = timeDifference * 1e3;
        return {
          isValid: true,
          timeDifference: {
            seconds: timeDifference,
            milliseconds: millisecondsDiff,
            humanReadable: this.formatTimeDifference(millisecondsDiff)
          },
          sameServer: obj1.machineId === obj2.machineId,
          sameProcess: obj1.processId === obj2.processId,
          counterDifference: parseInt(obj1.counter, 16) - parseInt(obj2.counter, 16)
        };
      }
      /**
       * Format time difference in a human readable format
       * @param {number} milliseconds - Time difference in milliseconds
       * @returns {string} Formatted time difference
       */
      formatTimeDifference(milliseconds) {
        const absMs = Math.abs(milliseconds);
        const prefix = milliseconds >= 0 ? "newer by " : "older by ";
        if (absMs < 1e3) return `${prefix}${absMs} milliseconds`;
        if (absMs < 6e4) return `${prefix}${Math.floor(absMs / 1e3)} seconds`;
        if (absMs < 36e5) return `${prefix}${Math.floor(absMs / 6e4)} minutes`;
        if (absMs < 864e5) return `${prefix}${Math.floor(absMs / 36e5)} hours`;
        return `${prefix}${Math.floor(absMs / 864e5)} days`;
      }
      /**
       * Test MongoDB connection
       * @param {string} connectionString - MongoDB connection string
       * @returns {Promise<Object>} Connection test results
       */
      async testConnection(connectionString) {
        let client;
        try {
          client = new MongoClient(connectionString, {
            serverSelectionTimeoutMS: 5e3,
            connectTimeoutMS: 5e3
          });
          await client.connect();
          const admin = client.db().admin();
          const serverInfo = await admin.serverInfo();
          const dbList = await client.db().admin().listDatabases();
          return {
            success: true,
            version: serverInfo.version,
            databases: dbList.databases.map((db) => ({
              name: db.name,
              sizeOnDisk: db.sizeOnDisk,
              empty: db.empty
            }))
          };
        } catch (error) {
          const errorMessage = error instanceof Error ? error.message : String(error);
          return {
            success: false,
            error: errorMessage
          };
        } finally {
          if (client) await client.close();
        }
      }
    };
    module2.exports = new MongoDB();
  }
});

// ../../modules/mongo/backend/routes.js
var require_routes14 = __commonJS({
  "../../modules/mongo/backend/routes.js"(exports2, module2) {
    "use strict";
    var express = require("express");
    var MongoDB = require_Mongo();
    var router = express.Router();
    router.get("/mongo/generate", async (req, res) => {
      res.json(MongoDB.generateObjectId());
    });
    router.post("/mongo/validate", async (req, res) => {
      const { objectId } = req.body;
      res.json({
        isValid: MongoDB.isValidObjectId(objectId)
      });
    });
    router.post("/mongo/decode", async (req, res) => {
      const { objectId } = req.body;
      res.json(MongoDB.decodeObjectId(objectId));
    });
    router.post("/mongo/from-date", async (req, res) => {
      const { date } = req.body;
      res.json({
        objectId: MongoDB.objectIdFromDate(date)
      });
    });
    router.post("/mongo/compare", async (req, res) => {
      const { objectId1, objectId2 } = req.body;
      res.json(MongoDB.compareObjectIds(objectId1, objectId2));
    });
    router.post("/mongo/test-connection", async (req, res) => {
      const { connectionString } = req.body;
      try {
        const result = await MongoDB.testConnection(connectionString);
        res.json(result);
      } catch (error) {
        res.status(500).json({
          success: false,
          error: error instanceof Error ? error.message : String(error)
        });
      }
    });
    module2.exports = () => router;
  }
});

// ../../modules/mongo/backend/index.js
var require_backend16 = __commonJS({
  "../../modules/mongo/backend/index.js"(exports2, module2) {
    "use strict";
    var plugin = {
      enabled: true,
      name: "Mongo",
      displayName: "MongoDB",
      description: "MongoDB toolkit with ObjectID operations, validation, comparison and connection testing",
      icon: "fab fa-envira",
      export: null,
      placements: [
        {
          position: "toolbox",
          label: "MongoDB",
          icon: "fab fa-envira",
          goTo: { path: "/Mongo" },
          active: "Mongo"
        }
      ],
      order: 6,
      routes: require_routes14()
    };
    module2.exports = plugin;
  }
});

// ../../modules/node-repl/backend/routes.js
var require_routes15 = __commonJS({
  "../../modules/node-repl/backend/routes.js"(exports2, module2) {
    "use strict";
    var express = require("express");
    var { v4 } = require("uuid");
    var { mkdir, writeFile } = require("fs/promises");
    var { writeFileSync, readFileSync, unlinkSync } = require("fs");
    var pathfs = require("path");
    var { mkdirSync, existsSync } = require("fs");
    var { spawn } = require("child_process");
    var { promisify } = require("util");
    var setTimeoutAsync = promisify(setTimeout);
    var prettier = require("prettier");
    var { exec } = require("child_process");
    var execAsync = promisify(exec);
    var path = require("path");
    var fs = require("fs").promises;
    var router = express.Router();
    var homedir = require("os").homedir();
    var MAX_SCRIPT_SIZE = 1024 * 1024;
    var MAX_EXECUTION_TIME = 3e4;
    var MAX_MEMORY = 512 * 1024 * 1024;
    var confDir = pathfs.resolve(homedir, ".stack-monitor");
    if (!existsSync(confDir)) mkdirSync(confDir);
    var confPath = pathfs.resolve(confDir, "node-repl");
    if (!existsSync(confPath)) writeFileSync(confPath, "{}", "utf-8");
    var conf = JSON.parse(readFileSync(confPath, "utf-8"));
    var packagesDir = pathfs.resolve(confDir, "node-repl-packages");
    if (!existsSync(packagesDir)) mkdirSync(packagesDir);
    function getRoomPackagesDir(room) {
      return pathfs.resolve(packagesDir, room);
    }
    async function initRoomPackageJson(room) {
      const roomDir = getRoomPackagesDir(room);
      if (!existsSync(roomDir)) {
        mkdirSync(roomDir, { recursive: true });
      }
      const packageJsonPath = pathfs.resolve(roomDir, "package.json");
      if (!existsSync(packageJsonPath)) {
        await fs.writeFile(packageJsonPath, JSON.stringify({ dependencies: {} }, null, 2), "utf-8");
      }
      return packageJsonPath;
    }
    async function cleanupRoomPackages(room) {
      const roomDir = getRoomPackagesDir(room);
      if (existsSync(roomDir)) {
        try {
          await fs.rm(roomDir, { recursive: true, force: true });
        } catch (err) {
          console.error(`Error cleaning up packages for room ${room}:`, err);
        }
      }
    }
    async function cleanupTempFiles() {
      const sandboxDir = pathfs.resolve(__dirname, "sandbox-node-repl");
      if (existsSync(sandboxDir)) {
        const files = await promisify(require("fs").readdir)(sandboxDir);
        for (const file of files) {
          try {
            await promisify(require("fs").unlink)(pathfs.join(sandboxDir, file));
          } catch (err) {
            console.error(`Error cleaning up file ${file}:`, err);
          }
        }
      }
    }
    function validateScript(script) {
      if (script.length > MAX_SCRIPT_SIZE) {
        throw new Error(`Script too large. Maximum size is ${MAX_SCRIPT_SIZE / 1024 / 1024}MB`);
      }
      const dangerousImports = script.match(/require\(['"](?!${ALLOWED_MODULES.join('|')})['"]\)/g);
      if (dangerousImports) {
        throw new Error("Script contains unauthorized module imports");
      }
      return true;
    }
    async function checkNpmAvailable() {
      try {
        await execAsync("npm --version");
        return true;
      } catch (error) {
        return false;
      }
    }
    module2.exports = (stackMonitor) => {
      const { Socket } = stackMonitor;
      cleanupTempFiles().catch(console.error);
      router.post("/node-repl/chat/:room", async (req, res) => {
        try {
          const { room } = req.params;
          const { script } = req.body;
          validateScript(script);
          if (!conf.chat?.[room]) {
            if (!conf.chat) conf.chat = {};
            if (!conf.chat?.[room]) conf.chat[room] = {};
          }
          conf.chat[room].script = script;
          await initRoomPackageJson(room);
          const sessionId = v4();
          const sessionDir = path.join(__dirname, "sandbox-node-repl", sessionId);
          await fs.mkdir(sessionDir, { recursive: true });
          const scriptPath = path.join(sessionDir, "script.js");
          await fs.writeFile(scriptPath, script);
          let result = "";
          let error = null;
          let timeoutId;
          const customEnv = conf.chat[room]?.env || {};
          const env = {
            ...process.env,
            ...customEnv,
            NODE_OPTIONS: `--max-old-space-size=${MAX_MEMORY / 1024 / 1024}`,
            NODE_PATH: path.join(getRoomPackagesDir(room), "node_modules")
          };
          const spawnCmd = spawn("node", [scriptPath], {
            env,
            cwd: sessionDir
          });
          timeoutId = setTimeout(() => {
            spawnCmd.kill();
            error = new Error("Script execution timed out");
          }, MAX_EXECUTION_TIME);
          Socket.emit("node-repl:update", { clear: true });
          spawnCmd.stdout.on("data", (data) => {
            const output = data.toString("utf-8");
            Socket.emit("node-repl:update", { msg: output, type: "stdout" });
            result += output;
          });
          spawnCmd.stderr.on("data", (data) => {
            const output = data.toString("utf-8");
            Socket.emit("node-repl:update", { msg: output, type: "stderr" });
            result += output;
          });
          spawnCmd.on("error", (err) => {
            error = err;
            Socket.emit("node-repl:update", { msg: `Error: ${err.message}`, type: "error" });
          });
          spawnCmd.on("close", (code) => {
            clearTimeout(timeoutId);
            Socket.emit("node-repl:update", { close: true });
            try {
              conf.chat[room].result = result;
              conf.chat[room].lastExecution = (/* @__PURE__ */ new Date()).toISOString();
              conf.chat[room].error = error ? error.message : null;
              save();
            } catch (err) {
              console.error("Error saving result:", err);
            }
            try {
              require("fs").rmSync(sessionDir, { recursive: true, force: true });
            } catch (err) {
              console.error("Error cleaning up temporary directory:", err);
            }
          });
          save();
          res.json(sessionId);
        } catch (err) {
          Socket.emit("node-repl:update", {
            msg: `Error: ${err.message}`,
            type: "error",
            clear: true
          });
          res.status(400).json({ error: err.message });
        }
      });
      router.get("/node-repl/rooms", async (req, res) => {
        res.json(Object.keys(conf?.chat || {}));
      });
      router.post("/node-repl/rooms", async (req, res) => {
        const { room } = req.body;
        if (!conf?.chat?.[room]) {
          if (!conf?.chat) conf.chat = {};
          if (!conf?.chat?.[room]) conf.chat[room] = {};
        }
        save();
        res.json(Object.keys(conf?.chat || {}));
      });
      router.delete("/node-repl/rooms/:room", async (req, res) => {
        const { room } = req.params;
        if (conf?.chat?.[room]) {
          delete conf.chat[room];
          await cleanupRoomPackages(room);
        }
        save();
        res.json(Object.keys(conf?.chat || {}));
      });
      router.get("/node-repl/chat/:room", async (req, res) => {
        res.json(conf?.chat?.[req.params.room]);
      });
      router.post("/node-repl/format", async (req, res) => {
        try {
          const { code } = req.body;
          if (!code) {
            throw new Error("No code provided");
          }
          const formatted = await prettier.format(code, {
            parser: "babel",
            semi: true,
            singleQuote: true,
            trailingComma: "es5",
            printWidth: 80,
            tabWidth: 2,
            useTabs: false,
            bracketSpacing: true,
            arrowParens: "avoid"
          });
          res.json({ formatted });
        } catch (err) {
          res.status(400).json({ error: err.message });
        }
      });
      router.post("/node-repl/env", async (req, res) => {
        try {
          const { room, env } = req.body;
          if (!room || !env) {
            throw new Error("Room and environment variables are required");
          }
          if (!conf.chat?.[room]) {
            if (!conf.chat) conf.chat = {};
            if (!conf.chat?.[room]) conf.chat[room] = {};
          }
          conf.chat[room].env = env;
          save();
          res.json({ success: true });
        } catch (err) {
          res.status(400).json({ error: err.message });
        }
      });
      router.get("/node-repl/env/:room", async (req, res) => {
        try {
          const { room } = req.params;
          res.json(conf?.chat?.[room]?.env || {});
        } catch (err) {
          res.status(400).json({ error: err.message });
        }
      });
      router.post("/node-repl/install", async (req, res) => {
        try {
          const { package: packageName, room } = req.body;
          if (!packageName || !room) {
            return res.status(400).json({ error: "Package name and room are required" });
          }
          const npmAvailable = await checkNpmAvailable();
          if (!npmAvailable) {
            return res.status(500).json({
              error: "npm is not available in the system PATH. Please install Node.js and npm first."
            });
          }
          const packageJsonPath = await initRoomPackageJson(room);
          const roomDir = getRoomPackagesDir(room);
          await execAsync(`npm install ${packageName}`, { cwd: roomDir });
          const installedPackageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
          res.json(installedPackageJson.dependencies || {});
        } catch (error) {
          console.error("Error installing package:", error);
          res.status(500).json({ error: error.message });
        }
      });
      router.get("/node-repl/packages/:room", async (req, res) => {
        try {
          const { room } = req.params;
          const packageJsonPath = pathfs.resolve(getRoomPackagesDir(room), "package.json");
          try {
            const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
            res.json(packageJson.dependencies || {});
          } catch (err) {
            res.json({});
          }
        } catch (error) {
          console.error("Error listing packages:", error);
          res.status(500).json({ error: error.message });
        }
      });
      router.delete("/node-repl/packages/:room/:name", async (req, res) => {
        try {
          const { room, name } = req.params;
          if (!name || !room) {
            return res.status(400).json({ error: "Package name and room are required" });
          }
          const npmAvailable = await checkNpmAvailable();
          if (!npmAvailable) {
            return res.status(500).json({
              error: "npm is not available in the system PATH. Please install Node.js and npm first."
            });
          }
          const roomDir = getRoomPackagesDir(room);
          const packageJsonPath = pathfs.resolve(roomDir, "package.json");
          await execAsync(`npm uninstall ${name}`, { cwd: roomDir });
          const updatedPackageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
          res.json(updatedPackageJson.dependencies || {});
        } catch (error) {
          console.error("Error uninstalling package:", error);
          res.status(500).json({ error: error.message });
        }
      });
      function save() {
        try {
          writeFileSync(confPath, JSON.stringify(conf, null, 2), "utf-8");
        } catch (err) {
          console.error("Error saving configuration:", err);
        }
      }
      return router;
    };
  }
});

// ../../modules/node-repl/backend/index.js
var require_backend17 = __commonJS({
  "../../modules/node-repl/backend/index.js"(exports2, module2) {
    "use strict";
    var plugin = {
      enabled: true,
      name: "NodeREPL",
      displayName: "Node REPL",
      description: "Create nodejs scripts for test purposes",
      icon: "fab fa-node",
      export: null,
      placements: [
        {
          position: "toolbox",
          label: "Node Sandbox",
          icon: "fab fa-node",
          goTo: { path: "/NodeREPL" },
          active: "NodeREPL"
        }
      ],
      order: 6,
      routes: require_routes15()
    };
    module2.exports = plugin;
  }
});

// helpers/specialCaracters.helper.js
var require_specialCaracters_helper = __commonJS({
  "helpers/specialCaracters.helper.js"(exports2, module2) {
    "use strict";
    module2.exports = {};
    module2.exports.specialCaracters = [
      ["\xE0", "&agrave;", "agrave"],
      ["\xC0", "&Agrave;", "agrave"],
      ["\xE1", "&aacute;", "aacute"],
      ["\xC1", "&Aacute;", "aacute"],
      ["\xE2", "&acirc;", "acirc"],
      ["\xC2", "&Acirc;", "acirc"],
      ["\xE3", "&atilde;", "atilde"],
      ["\xC3", "&Atilde;", "atilde"],
      ["\xE4", "&auml;", "auml"],
      ["\xC4", "&Auml;", "auml"],
      ["\xE5", "&aring;", "aring"],
      ["\xC5", "&Aring;", "aring"],
      ["\xE6", "&aelig;", "aelig"],
      ["\xC6", "&AElig;", "aelig"],
      ["\xE8", "&egrave;", "egrave"],
      ["\xC8", "&Egrave;", "egrave"],
      ["\xE9", "&eacute;", "eacute"],
      ["\xC9", "&Eacute;", "eacute"],
      ["\xEA", "&ecirc;", "ecirc"],
      ["\xCA", "&Ecirc;", "ecirc"],
      ["\xEB", "&euml;", "euml"],
      ["\xCB", "&Euml;", "euml"],
      ["\xEC", "&igrave;", "igrave"],
      ["\xCC", "&Igrave;", "igrave"],
      ["\xED", "&iacute;", "iacute"],
      ["\xCD", "&Iacute;", "iacute"],
      ["\xEE", "&icirc;", "icirc"],
      ["\xCE", "&Icirc;", "icirc"],
      ["\xEF", "&iuml;", "iuml"],
      ["\xCF", "&Iuml;", "iuml"],
      ["\xF2", "&ograve;", "ograve"],
      ["\xD2", "&Ograve;", "ograve"],
      ["\xF3", "&oacute;", "oacute"],
      ["\xD3", "&Oacute;", "oacute"],
      ["\xF4", "&ocirc;", "ocirc"],
      ["\xD4", "&Ocirc;", "ocirc"],
      ["\xF5", "&otilde;", "otilde"],
      ["\xD5", "&Otilde;", "otilde"],
      ["\xF6", "&ouml;", "ouml"],
      ["\xD6", "&Ouml;", "ouml"],
      ["\xF8", "&oslash;", "oslash"],
      ["\xD8", "&Oslash;", "oslash"],
      ["\xF9", "&ugrave;", "ugrave"],
      ["\xD9", "&Ugrave;", "ugrave"],
      ["\xFA", "&uacute;", "uacute"],
      ["\xDA", "&Uacute;", "uacute"],
      ["\xFB", "&ucirc;", "ucirc"],
      ["\xDB", "&Ucirc;", "ucirc"],
      ["\xFC", "&uuml;", "uuml"],
      ["\xDC", "&Uuml;", "uuml"],
      ["\xF1", "&ntilde;", "ntilde"],
      ["\xD1", "&Ntilde;", "ntilde"],
      ["\xE7", "&ccedil;", "ccedil"],
      ["\xC7", "&Ccedil;", "ccedil"],
      ["\xFD", "&yacute;", "yacute"],
      ["\xDD", "&Yacute;", "yacute"],
      ["\xDF", "&szlig;", "szlig"],
      ["\xAB", "&laquo;"],
      ["\xBB", "&raquo;"],
      ["&", "&amp;"],
      ["<", "&lt;"],
      [">", "&gt;"],
      ['"', "&quot;"],
      ["\xA7", "&para;"],
      ["\xA9", "&copy;"]
    ];
  }
});

// helpers/stringTransformer.helper.js
var require_stringTransformer_helper = __commonJS({
  "helpers/stringTransformer.helper.js"(exports2, module2) {
    "use strict";
    var slugify = require("slugify").default;
    var { specialCaracters } = require_specialCaracters_helper();
    var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    function numberToLetters(number, result = "") {
      let charIndex = number % alphabet.length;
      let quotient = number / alphabet.length;
      if (charIndex - 1 === -1) {
        charIndex = alphabet.length;
        quotient -= 1;
      }
      result = alphabet.charAt(charIndex - 1) + result;
      return quotient >= 1 ? numberToLetters(+quotient, result) : result;
    }
    function transformBeginingNumber(str) {
      let transformedString = str;
      const beginningNumber = Number.parseInt(transformedString, 10);
      const isBeginningWithNumber = Number.isInteger(beginningNumber);
      if (isBeginningWithNumber) {
        const hasAlreadySeparator = transformedString.split(beginningNumber.toString())?.[1]?.charAt(0) === "_";
        transformedString = transformedString.replace(beginningNumber.toString(), `${numberToLetters(beginningNumber)}${hasAlreadySeparator ? "" : "_"}`);
      }
      return transformedString;
    }
    module2.exports.humanStringToKey = (str, separator = "_") => {
      let transformedString = str.trim().toLowerCase();
      specialCaracters.forEach((sc) => {
        const [char, eacute, slug] = sc;
        if (eacute) transformedString = transformedString.replaceAll(eacute.toLowerCase(), char);
        if (slug) transformedString = transformedString.replaceAll(slug.toLowerCase(), char);
      });
      transformedString = transformBeginingNumber(transformedString);
      slugify.extend({
        "'": separator,
        "-": "_",
        "(": separator,
        ")": separator
      });
      return slugify(transformedString, separator);
    };
    module2.exports.replaceEnvs = (str) => {
      Object.keys(process.env).forEach((env) => {
        if (!env) return;
        str = str.replaceAll(`$${env}`, process.env[env]);
      });
      return str;
    };
  }
});

// ../../modules/npm/backend/Npm.js
var require_Npm = __commonJS({
  "../../modules/npm/backend/Npm.js"(exports2, module2) {
    "use strict";
    var pathfs = require("path");
    var { execAsync } = require_exec();
    var { readFile } = require("fs/promises");
    var { replaceEnvs } = require_stringTransformer_helper();
    var { existsSync } = require("fs");
    var Npm = class {
      /** @param {import('@clabroche/servers-server/models/Service')} service */
      constructor(service) {
        this.service = service;
      }
      async isNpm(path) {
        if (path) {
          return existsSync(pathfs.resolve(path?.toString(), "package.json"));
        }
        return null;
      }
      getNpmPaths() {
        return [
          ...this.service.commands.filter((cmd) => cmd.spawnOptions.cwd?.toString()?.trim() && cmd.spawnOptions.cwd !== ".").map((cmd) => replaceEnvs(cmd.spawnOptions.cwd)),
          this.service.getRootPath()
        ].filter((cwd) => this.isNpm(cwd));
      }
      async packageJSON(path) {
        if (path) {
          return JSON.parse(await readFile(pathfs.resolve(path?.toString(), "package.json"), "utf-8"));
        }
        return {};
      }
      async packageLock(path) {
        if (path) {
          return JSON.parse(await readFile(pathfs.resolve(path?.toString(), "package-lock.json"), "utf-8")).catch((err) => {
            console.error(err);
            return {};
          });
        }
        return {};
      }
      /**
         * @returns {Promise<import('./index').Outdated>}
         */
      async outdated(path) {
        const result = await execAsync("npm outdated --json || true", { cwd: path });
        return JSON.parse(result);
      }
    };
    module2.exports = Npm;
  }
});

// ../../modules/npm/backend/routes.js
var require_routes16 = __commonJS({
  "../../modules/npm/backend/routes.js"(exports2, module2) {
    "use strict";
    var express = require("express");
    var router = express.Router();
    var Npm = require_Npm();
    module2.exports = (stackMonitor) => {
      const { findService } = stackMonitor;
      router.get("/npm/:service", async (req, res) => {
        const service = findService(req.params.service);
        const npm = new Npm(service);
        const isNpm = await npm.isNpm(req.query.cwd);
        res.json(isNpm);
      });
      router.get("/npm/:service/paths", async (req, res) => {
        const service = findService(req.params.service);
        const npm = new Npm(service);
        const paths = await npm.getNpmPaths();
        res.json(paths);
      });
      router.get("/npm/:service/packagejson", async (req, res) => {
        const service = findService(req.params.service);
        const npm = new Npm(service);
        const packagejson = await npm.packageJSON(req.query.cwd);
        res.json(packagejson);
      });
      router.get("/npm/:service/outdated", async (req, res) => {
        const service = findService(req.params.service);
        const npm = new Npm(service);
        const packagejson = await npm.outdated(req.query.cwd);
        res.json(packagejson);
      });
      return router;
    };
  }
});

// ../../modules/npm/backend/index.js
var require_backend18 = __commonJS({
  "../../modules/npm/backend/index.js"(exports2, module2) {
    "use strict";
    var Npm = require_Npm();
    var plugin = {
      enabled: true,
      name: "Npm",
      displayName: "Npm",
      description: "View your dependencies and execute your scripts on a service",
      icon: "fab fa-npm",
      export: Npm,
      placements: ["service"],
      order: 4,
      /**
       *
       * @param {import('../../../servers/server/models/Service')} service
       * @returns
       */
      hidden: async (service) => {
        if (!service) return false;
        const project = new Npm(service);
        const serviceIsNpm = !!project.getNpmPaths().length;
        return !serviceIsNpm;
      },
      routes: require_routes16()
    };
    module2.exports = plugin;
  }
});

// models/ports.js
var require_ports = __commonJS({
  "models/ports.js"(exports2, module2) {
    "use strict";
    module2.exports = {
      http: process.env.STACK_MONITOR_HTTP_PORT || 0,
      /** @param {number} port */
      setHttpPort(port) {
        if (!this.http) this.http = port;
      }
    };
  }
});

// ../../modules/openai/backend/routes.js
var require_routes17 = __commonJS({
  "../../modules/openai/backend/routes.js"(exports2, module2) {
    "use strict";
    var express = require("express");
    var pathfs = require("path");
    var OpenAIApi = require("openai").OpenAI;
    var {
      existsSync,
      mkdirSync,
      createReadStream,
      writeFileSync,
      readFileSync
    } = require("fs");
    var { encode } = require("gpt-3-encoder");
    var { v4 } = require("uuid");
    var { writeFile, unlink } = require("fs/promises");
    var homedir = require("os").homedir();
    var axios = require("axios").default;
    var PromiseB2 = require("bluebird");
    var ports = require_ports();
    var router = express.Router();
    var confDir = pathfs.resolve(homedir, ".stack-monitor");
    if (!existsSync(confDir)) mkdirSync(confDir);
    var openaiConfPath = pathfs.resolve(confDir, "openaiconf.json");
    if (!existsSync(openaiConfPath)) writeFileSync(openaiConfPath, "{}", "utf-8");
    var openaiStoragePath = pathfs.resolve(confDir, "storage");
    if (!existsSync(openaiStoragePath)) mkdirSync(openaiStoragePath);
    var openaiconf = JSON.parse(readFileSync(openaiConfPath, "utf-8"));
    var openai = process.env.STACK_MONITOR_OPENAI_APIKEY ? new OpenAIApi({
      apiKey: process.env.STACK_MONITOR_OPENAI_APIKEY
    }) : null;
    module2.exports = () => {
      router.get("/openai", async (req, res) => {
        res.json("hello");
      });
      router.get("/openai/image/:id", async (req, res) => {
        res.setHeader("content-type", "image/png");
        res.setHeader("content-disposition", "filename=image.png");
        const path = pathfs.resolve(openaiStoragePath, `${req.params.id}.png`);
        if (existsSync(path)) createReadStream(path).pipe(res);
        else res.status(404).send("file not found");
      });
      router.get("/openai/models", async (req, res) => {
        if (!openai) return res.status(400).send("Openai not initialized");
        const models = await openai.models.list({});
        return res.json(models.data);
      });
      router.post("/openai/tokenize", async (req, res) => {
        const encoded = encode(req.body.data);
        res.json({
          nbTokens: encoded.length,
          price: encoded.length * 2e-3 / 1e3
        });
      });
      router.post("/openai/review", async (req, res) => {
        if (!openai) return res.status(400).send("Openai not initialized");
        const result = await openai.chat.completions.create({
          model: "gpt-3.5-turbo",
          temperature: 0,
          n: 1,
          max_tokens: Infinity,
          messages: [
            { role: "system", content: 'You are a developer for 10 years. You are expert in git and you should write a commit. I gave you a git diff after. Write me a resume of this diff and write me a commit for this diff in this format: "<fix|feat|major>: <your message not exceeding 60 characters>"' },
            { role: "assistant", content: req.body.data }
          ]
        }).catch((err) => {
          console.error(err.response.data);
          return Promise.reject(err);
        });
        return res.json(result.choices[0]?.message?.content || "Cannot respond");
      });
      router.post("/openai/error", async (req, res) => {
        if (!openai) return res.status(400).send("Openai not initialized");
        const result = await openai.chat.completions.create({
          model: "gpt-3.5-turbo",
          temperature: 0,
          n: 1,
          max_tokens: Infinity,
          messages: [
            { role: "system", content: "You are a developer for 20 years. You are expert in it and you should find a solution to this following error. Resume the following error and try to fix it" },
            { role: "assistant", content: req.body.data }
          ]
        }).catch((err) => {
          console.error(err.response.data);
          return Promise.reject(err);
        });
        return res.json(result.choices[0]?.message?.content || "Cannot respond");
      });
      router.post("/openai/chat/:room/image", async (req, res) => {
        if (!openai) return res.status(400).send("Openai not initialized");
        const { message, quality, resolution } = req.body;
        if (!openaiconf?.chat) openaiconf.chat = {};
        if (!openaiconf?.chat?.[req.params.room]) openaiconf.chat[req.params.room] = {};
        if (!openaiconf?.chat?.[req.params.room]?.messages) {
          openaiconf.chat[req.params.room].messages = [
            { role: "system", content: "Tu es un assistant utile." }
          ];
        }
        const messages = openaiconf?.chat[req.params.room]?.messages || [];
        messages.push({
          _id: v4(),
          // @ts-ignore
          role: "user",
          content: message,
          created_at: (/* @__PURE__ */ new Date()).toISOString()
        });
        const response = await openai.images.generate({
          quality,
          model: "dall-e-3",
          prompt: message,
          n: 1,
          size: resolution
        });
        if (response?.data?.[0]?.url) {
          const { url, revised_prompt } = response.data[0];
          const uuid = v4();
          const { data: fileBuffer } = await axios.get(url, {
            responseType: "arraybuffer",
            maxContentLength: Infinity,
            maxBodyLength: Infinity
          });
          await writeFile(pathfs.resolve(openaiStoragePath, `${uuid}.png`), fileBuffer);
          messages.push({
            url: `http://localhost:${ports.http}/openai/image/${uuid}`,
            contentId: uuid,
            revised_prompt,
            _id: v4(),
            created_at: (/* @__PURE__ */ new Date()).toISOString()
          });
          save();
        }
        return res.json(response.data[0].url);
      });
      router.post("/openai/chat/:room", async (req, res) => {
        if (!openai) return res.status(400).send("Openai not initialized");
        const { message, model, temperature } = req.body;
        if (!openaiconf?.chat) openaiconf.chat = {};
        if (!openaiconf?.chat?.[req.params.room]) openaiconf.chat[req.params.room] = {};
        if (!openaiconf?.chat?.[req.params.room]?.messages) {
          openaiconf.chat[req.params.room].messages = [
            { role: "system", content: "Tu es un assistant utile." }
          ];
        }
        const messages = openaiconf?.chat[req.params.room]?.messages || [];
        messages.push({
          _id: v4(),
          // @ts-ignore
          role: "user",
          content: message,
          created_at: (/* @__PURE__ */ new Date()).toISOString()
        });
        const result = await openai.chat.completions.create({
          model: model || "gpt-3.5-turbo",
          temperature: Number.isNaN(+temperature) ? 0 : +temperature,
          n: 1,
          max_tokens: Infinity,
          // @ts-ignore
          messages: messages.filter((f) => f?.content).map((a) => ({ role: a.role || "user", content: a.content || "" })).slice(-15)
        }).catch((err) => Promise.reject(err));
        if (result?.choices?.[0]?.message) {
          const { usage } = result;
          const { message: message2 } = result.choices[0];
          messages.push({
            ...message2,
            ...usage,
            _id: v4(),
            created_at: (/* @__PURE__ */ new Date()).toISOString()
          });
        }
        save();
        return res.json(messages);
      });
      router.get("/openai/rooms", async (req, res) => {
        res.json(Object.keys(openaiconf?.chat || {}));
      });
      router.post("/openai/rooms", async (req, res) => {
        const { room } = req.body;
        if (!openaiconf?.chat?.[room]) {
          if (!openaiconf?.chat) openaiconf.chat = {};
          if (!openaiconf?.chat?.[room]) openaiconf.chat[room] = {};
        }
        save();
        res.json(Object.keys(openaiconf?.chat || {}));
      });
      router.delete("/openai/rooms/:room", async (req, res) => {
        const { room } = req.params;
        if (openaiconf?.chat?.[room]) {
          await PromiseB2.map(openaiconf?.chat?.[room].messages, async (message) => {
            if (message.contentId && existsSync(pathfs.resolve(openaiStoragePath, `${message.contentId}.png`))) {
              await unlink(pathfs.resolve(openaiStoragePath, `${message.contentId}.png`));
            }
          }, { concurrency: 4 });
          delete openaiconf.chat[room];
        }
        save();
        res.json(Object.keys(openaiconf?.chat || {}));
      });
      router.get("/openai/chat/:room", async (req, res) => {
        res.json(openaiconf?.chat?.[req.params.room]?.messages);
      });
      router.post("/openai/apikey", async (req, res) => {
        const { apikey } = req.body;
        process.env.STACK_MONITOR_OPENAI_APIKEY = apikey;
        save();
        openai = process.env.STACK_MONITOR_OPENAI_APIKEY ? new OpenAIApi({
          apiKey: process.env.STACK_MONITOR_OPENAI_APIKEY
        }) : null;
        res.json(true);
      });
      router.get("/openai/ready", async (req, res) => {
        if (!openai) return res.json(false);
        console.log(process.env.STACK_MONITOR_OPENAI_APIKEY, openaiconf?.apikey);
        try {
          const { data } = await openai.models.list({});
          res.json(!!data?.length);
        } catch (error) {
          res.json(false);
        }
        return void 0;
      });
      function save() {
        writeFileSync(openaiConfPath, JSON.stringify(openaiconf), "utf-8");
      }
      return router;
    };
  }
});

// ../../modules/openai/backend/index.js
var require_backend19 = __commonJS({
  "../../modules/openai/backend/index.js"(exports2, module2) {
    "use strict";
    var plugin = {
      enabled: true,
      name: "OpenAI",
      displayName: "Open A.I.",
      description: "Just chat with Chat GPT",
      icon: "fas fa-brain",
      export: null,
      placements: [
        {
          position: "toolbox",
          label: "OpenAi",
          icon: "fas fa-brain",
          goTo: { path: "/OpenAI" },
          active: "OpenAI"
        }
      ],
      order: 6,
      routes: require_routes17()
    };
    module2.exports = plugin;
  }
});

// ../../modules/regex/backend/index.js
var require_backend20 = __commonJS({
  "../../modules/regex/backend/index.js"(exports2, module2) {
    "use strict";
    var plugin = {
      enabled: true,
      name: "Regex",
      displayName: "Regex",
      description: "Test your regular expression",
      icon: "fas fa-key",
      export: null,
      placements: [
        {
          position: "toolbox",
          label: "Regex",
          icon: "fas fa-key",
          goTo: { path: "/Regex" },
          active: "Regex"
        }
      ],
      order: 6
    };
    module2.exports = plugin;
  }
});

// ../../modules/sql-beautifier/backend/SQLBeautifier.js
var require_SQLBeautifier = __commonJS({
  "../../modules/sql-beautifier/backend/SQLBeautifier.js"(exports2, module2) {
    "use strict";
    var sqlFormatter = require("sql-formatter");
    var SQLBeautifier = class {
      /**
       * Beautify SQL query
       * @param {string} sql - The SQL query to beautify
       * @returns {BeautifyResult} The beautified SQL query
       * @throws {Error} If the SQL query is invalid or formatting fails
       */
      beautify(sql) {
        if (!sql || typeof sql !== "string") {
          throw new Error("SQL query must be a non-empty string");
        }
        try {
          const formattedSQL = sqlFormatter.format(sql);
          return {
            result: formattedSQL
          };
        } catch (error) {
          const errorMessage = error instanceof Error ? error.message : "Unknown error";
          throw new Error(`Failed to beautify SQL: ${errorMessage}`);
        }
      }
    };
    module2.exports = new SQLBeautifier();
  }
});

// ../../modules/sql-beautifier/backend/routes.js
var require_routes18 = __commonJS({
  "../../modules/sql-beautifier/backend/routes.js"(exports2, module2) {
    "use strict";
    var express = require("express");
    var beautifier = require_SQLBeautifier();
    function routes(stackMonitor) {
      const router = express.Router();
      router.post("/sqlbeautifier/beautify", (req, res) => {
        const { sql } = req.body;
        if (!sql || typeof sql !== "string") {
          return res.status(400).json({
            error: "SQL query must be a non-empty string"
          });
        }
        try {
          const result = beautifier.beautify(sql);
          res.json(result);
        } catch (error) {
          const errorMessage = error instanceof Error ? error.message : "An unknown error occurred while beautifying SQL";
          res.status(500).json({ error: errorMessage });
        }
      });
      return router;
    }
    module2.exports = routes;
  }
});

// ../../modules/sql-beautifier/backend/index.js
var require_backend21 = __commonJS({
  "../../modules/sql-beautifier/backend/index.js"(exports2, module2) {
    "use strict";
    var plugin = {
      enabled: true,
      name: "sqlbeautifier",
      displayName: "SQL Beautifier",
      description: "Format and beautify SQL queries with syntax highlighting and real-time updates",
      icon: "fas fa-database",
      order: 8,
      export: require_SQLBeautifier(),
      routes: require_routes18(),
      placements: [
        {
          position: "toolbox",
          label: "SQL Beautifier",
          icon: "fas fa-database",
          goTo: { path: "/SQLBeautifier" },
          active: "SQLBeautifier"
        }
      ]
    };
    module2.exports = plugin;
  }
});

// ../../modules/toolbox/backend/index.js
var require_backend22 = __commonJS({
  "../../modules/toolbox/backend/index.js"(exports2, module2) {
    "use strict";
    var plugin = {
      enabled: true,
      name: "Toolbox",
      displayName: "Toolbox",
      description: "Choose from a bunch of tool",
      icon: "fas fa-plus",
      export: null,
      placements: [{
        position: "sidebar",
        label: "Toolbox",
        icon: "fas fa-toolbox",
        goTo: "/toolbox",
        active: "toolbox"
      }],
      order: 6
    };
    module2.exports = plugin;
  }
});

// ../../modules/uuid/backend/UUID.js
var require_UUID = __commonJS({
  "../../modules/uuid/backend/UUID.js"(exports2, module2) {
    "use strict";
    var { v4 } = require("uuid");
    var UUID = (stackMonitor) => ({
      generate: ({ count = 1, noDash = false, uppercase = false } = {}) => {
        const uuids = Array(count).fill().map(() => {
          let uuid = v4();
          if (noDash) {
            uuid = uuid.replace(/-/g, "");
          }
          if (uppercase) {
            uuid = uuid.toUpperCase();
          }
          return uuid;
        });
        return count === 1 ? uuids[0] : uuids;
      }
    });
    module2.exports = UUID;
  }
});

// ../../modules/uuid/backend/routes.js
var require_routes19 = __commonJS({
  "../../modules/uuid/backend/routes.js"(exports2, module2) {
    "use strict";
    var express = require("express");
    var router = express.Router();
    var UUID = require_UUID();
    module2.exports = (stackMonitor) => {
      const uuid = UUID(stackMonitor);
      router.get("/uuid/", async (req, res) => {
        const { count, noDash, uppercase } = req.query;
        res.json(uuid.generate({
          count: count ? parseInt(count, 10) : void 0,
          noDash: noDash === "true",
          uppercase: uppercase === "true"
        }));
      });
      return router;
    };
  }
});

// ../../modules/uuid/backend/index.js
var require_backend23 = __commonJS({
  "../../modules/uuid/backend/index.js"(exports2, module2) {
    "use strict";
    var plugin = {
      enabled: true,
      name: "UUID",
      displayName: "UUID",
      description: "Generate an UUID",
      icon: "fas fa-random",
      placements: [
        {
          position: "toolbox",
          label: "UUID",
          icon: "fas fa-random",
          goTo: { path: "/UUID" },
          active: "UUID"
        }
      ],
      export: require_UUID(),
      order: 6,
      routes: require_routes19()
    };
    module2.exports = plugin;
  }
});

// ../../modules/help/backend/index.js
var require_backend24 = __commonJS({
  "../../modules/help/backend/index.js"(exports2, module2) {
    "use strict";
    var plugin = {
      enabled: true,
      name: "Help",
      displayName: "Help",
      description: "Show help",
      icon: "fa-question-circle",
      export: null,
      placements: [{
        position: "sidebar",
        label: "Help",
        icon: "fas fa-question-circle",
        goTo: { path: "/Help" },
        active: "Help"
      }],
      order: Infinity
    };
    module2.exports = plugin;
  }
});

// ../../modules/openapi/backend/routes.js
var require_routes20 = __commonJS({
  "../../modules/openapi/backend/routes.js"(exports2, module2) {
    "use strict";
    var express = require("express");
    var axios = require("axios").default;
    var router = express.Router();
    module2.exports = () => {
      router.post("/openapi/swagger.json", async (req, res) => {
        const { data } = await axios.get(req.body.url);
        res.json(data);
      });
      return router;
    };
  }
});

// ../../modules/openapi/backend/index.js
var require_backend25 = __commonJS({
  "../../modules/openapi/backend/index.js"(exports2, module2) {
    "use strict";
    var plugin = {
      enabled: true,
      name: "OpenApi",
      displayName: "OpenApi",
      export: null,
      description: "Open an open api json into swagger",
      icon: "fas fa-network-wired",
      placements: ["service"],
      order: 4,
      hidden: (service) => {
        if (!service) return false;
        return !service?.openapiURL;
      },
      routes: require_routes20()
    };
    module2.exports = plugin;
  }
});

// ../../modules/vscode/backend/routes.js
var require_routes21 = __commonJS({
  "../../modules/vscode/backend/routes.js"(exports2, module2) {
    "use strict";
    var express = require("express");
    var open = require("open");
    var pathfs = require("path");
    var ws = require("workspace-tools");
    var { readFile } = require("fs/promises");
    var { readFileSync, existsSync } = require("fs");
    var { execAsync } = require_exec();
    var router = express.Router();
    var routes = (stackMonitor) => {
      router.post("/vscode/install", async (req, res) => {
        await execAsync("code --install-extension ./stack-monitor.vsix", { cwd: __dirname });
        res.send("ok");
      });
      router.delete("/vscode/uninstall", async (req, res) => {
        await execAsync("code --uninstall-extension clabroche.stack-monitor", { cwd: __dirname });
        res.send("ok");
      });
      router.get("/vscode/open-url", async (req, res) => {
        const url = req.query.url?.toString();
        if (url) open(url);
        res.send("ok");
      });
      router.get("/vscode/get-services-from-file", async (req, res) => {
        try {
          if (!req.query.file) return res.status(400).send("file params is required");
          const packageRoot = ws.findPackageRoot(req.query.file?.toString());
          let projectRoot;
          try {
            projectRoot = ws.findProjectRoot(req.query.file?.toString());
          } catch (error) {
            projectRoot = packageRoot;
          }
          if (!packageRoot) return res.status(400).send("no package found");
          if (!projectRoot) return res.status(400).send("no package found");
          const monorepo = packageRoot !== projectRoot;
          if (monorepo) {
            const packageInfos = await ws.getPackageInfosAsync(projectRoot);
            const { dependents: dependentsMap } = ws.createDependencyMap(packageInfos);
            const packageJSON = JSON.parse(await readFile(pathfs.resolve(packageRoot, "./package.json"), "utf-8"));
            const dependents = [
              ...dependentsMap.get(packageJSON.name) || []
            ];
            const services = stackMonitor.getServices().filter((service) => {
              const servicePackageNames = [
                service.getRootPath(),
                ...service.commands.map((cmd) => cmd.spawnOptions?.env)
              ].flat(100).filter((a, i, arr) => a && !arr.slice(0, i).includes(a) && a.toString() !== projectRoot && existsSync(pathfs.resolve(a.toString(), "./package.json"))).map((path) => JSON.parse(readFileSync(pathfs.resolve(path?.toString() || "", "./package.json"), "utf-8")).name);
              return dependents.some((packageName) => servicePackageNames.includes(packageName)) || servicePackageNames.includes(packageJSON.name);
            });
            res.json(services);
          } else {
            const packageRoot2 = ws.findPackageRoot(req.query.file?.toString());
            let projectRoot2;
            try {
              projectRoot2 = ws.findProjectRoot(req.query.file?.toString());
            } catch (error) {
              projectRoot2 = packageRoot2;
            }
            if (!packageRoot2) return res.status(400).send("no package found");
            if (!projectRoot2) return res.status(400).send("no package found");
            const services = stackMonitor.getServices().filter((service) => {
              const servicePaths = [
                service.getRootPath(),
                ...service.commands.map((cmd) => cmd.spawnOptions?.env)
              ].flat(100).filter((a, i, arr) => a && !arr.slice(0, i).includes(a) && existsSync(pathfs.resolve(a.toString(), "./package.json")));
              return servicePaths.includes(projectRoot2);
            });
            res.json(services);
          }
        } catch (error) {
          console.error(error);
          res.json([]);
        }
        return null;
      });
      router.get("/vscode/download", (req, res) => {
        const vsixPath = pathfs.resolve(__dirname, "./stack-monitor.vsix");
        res.download(vsixPath, "stack-monitor.vsix");
      });
      return router;
    };
    module2.exports = routes;
  }
});

// ../../modules/vscode/backend/index.js
var require_backend26 = __commonJS({
  "../../modules/vscode/backend/index.js"(exports2, module2) {
    "use strict";
    var commandExists = require("command-exists");
    var plugin = {
      enabled: true,
      name: "Vscode",
      displayName: "Vscode",
      description: "interact with vscode extension",
      icon: "fab fa-git-alt",
      export: null,
      order: Infinity,
      placements: [{
        position: "sidebar",
        label: "VScode extension",
        img: "https://upload.wikimedia.org/wikipedia/commons/thumb/9/9a/Visual_Studio_Code_1.35_icon.svg/512px-Visual_Studio_Code_1.35_icon.svg.png",
        goTo: { path: "/Vscode" },
        active: "Vscode"
      }],
      hidden: () => commandExists("code").then(() => false).catch(() => true),
      routes: require_routes21()
    };
    module2.exports = plugin;
  }
});

// ../../modules/docker/backend/routes.js
var require_routes22 = __commonJS({
  "../../modules/docker/backend/routes.js"(exports2, module2) {
    "use strict";
    var express = require("express");
    var { execAsync } = require_exec();
    var router = express.Router();
    module2.exports = (stackMonitor) => {
      router.get("/docker/:service", async (req, res) => {
        const service = stackMonitor.findService(req.params.service);
        if (!service) return res.status(404).send("Service not found");
        const isAlive = await execAsync(`docker inspect --format {{.State.Pid}} ${service.container.name}`, {}).catch(() => null);
        const dockerInfos = await execAsync(`docker inspect ${service.container.name}`, {}).then((data) => {
          try {
            return JSON.parse(data)[0];
          } catch (error) {
            console.error(error);
            return "";
          }
        }).catch(() => null);
        let dockerImage = null;
        if (dockerInfos) {
          dockerImage = await execAsync(`docker history --no-trunc --human --format json ${dockerInfos.Image}`, {}).then((data) => {
            try {
              return data.trim().split("\n").map((a) => JSON.parse(a)).sort((a, b) => {
                if (a && b) return a.CreatedAt.localeCompare(b.CreatedAt);
                return 0;
              }).map((a) => `${a.Size} ${a.CreatedBy}    `);
            } catch (error) {
              console.error(error);
              return "";
            }
          }).catch(() => null);
        }
        res.json([
          { label: "containerName", value: service.container.name },
          { label: "isAlive", value: !!isAlive },
          { label: "pid", value: isAlive },
          { label: "dockerInfos", value: dockerInfos, type: "json" },
          { label: "image", value: dockerImage, type: "json" }
        ]);
        return null;
      });
      return router;
    };
  }
});

// ../../modules/docker/backend/index.js
var require_backend27 = __commonJS({
  "../../modules/docker/backend/index.js"(exports2, module2) {
    "use strict";
    var plugin = {
      enabled: true,
      name: "Docker",
      displayName: "Docker",
      description: "Show docker configs",
      icon: "fab fa-docker",
      export: null,
      placements: ["service"],
      order: 4,
      /** @param {import('../../../servers/server/models/Service') | null} service */
      hidden: async (service) => {
        if (!service?.container) return true;
        return !service.container.enabled;
      },
      routes: require_routes22()
    };
    module2.exports = plugin;
  }
});

// ../../modules/workflows/backend/Workflows.js
var require_Workflows = __commonJS({
  "../../modules/workflows/backend/Workflows.js"(exports2, module2) {
    "use strict";
    var NodeRed = (stackMonitor) => {
    };
    module2.exports = NodeRed;
  }
});

// ../../common/express-health-check/src/index.js
var require_src3 = __commonJS({
  "../../common/express-health-check/src/index.js"(exports2, module2) {
    "use strict";
    module2.exports = (express, { name = "", version = "" } = {}) => express.Router().use("/health", (req, res) => {
      res.json({
        name,
        version,
        health: true
      });
    });
  }
});

// ../../common/express-error-handler/src/errorCodes.js
var require_errorCodes = __commonJS({
  "../../common/express-error-handler/src/errorCodes.js"(exports2, module2) {
    "use strict";
    module2.exports = [
      { code: 100, label: "Continue" },
      { code: 101, label: "Switching Protocols" },
      { code: 102, label: "Processing (WebDAV; RFC 2518)" },
      { code: 103, label: "Early Hints (RFC 8297)" },
      { code: 200, label: "OK" },
      { code: 201, label: "Created" },
      { code: 202, label: "Accepted" },
      { code: 203, label: "Non-Authoritative Information (since HTTP/1.1)" },
      { code: 204, label: "No Content" },
      { code: 205, label: "Reset Content" },
      { code: 206, label: "Partial Content" },
      { code: 207, label: "Multi-Status (WebDAV; RFC 4918)" },
      { code: 208, label: "Already Reported (WebDAV; RFC 5842)" },
      { code: 226, label: "IM Used (RFC 3229)" },
      { code: 300, label: "Multiple Choices" },
      { code: 301, label: "Moved Permanently" },
      { code: 302, label: 'Found (Previously "Moved temporarily")' },
      { code: 303, label: "See Other (since HTTP/1.1)" },
      { code: 304, label: "Not Modified" },
      { code: 305, label: "Use Proxy (since HTTP/1.1)" },
      { code: 306, label: "Switch Proxy" },
      { code: 307, label: "Temporary Redirect (since HTTP/1.1)" },
      { code: 308, label: "Permanent Redirect" },
      { code: 400, label: "Bad Request" },
      { code: 401, label: "Unauthorized" },
      { code: 402, label: "Payment Required" },
      { code: 403, label: "Forbidden" },
      { code: 404, label: "Not Found" },
      { code: 405, label: "Method Not Allowed" },
      { code: 406, label: "Not Acceptable" },
      { code: 407, label: "Proxy Authentication Required" },
      { code: 408, label: "Request Timeout" },
      { code: 409, label: "Conflict" },
      { code: 410, label: "Gone" },
      { code: 411, label: "Length Required" },
      { code: 412, label: "Precondition Failed" },
      { code: 413, label: "Payload Too Large" },
      { code: 414, label: "URI Too Long" },
      { code: 415, label: "Unsupported Media Type" },
      { code: 416, label: "Range Not Satisfiable" },
      { code: 417, label: "Expectation Failed" },
      { code: 418, label: "I'm a teapot (RFC 2324, RFC 7168)" },
      { code: 421, label: "Misdirected Request" },
      { code: 422, label: "Unprocessable Entity" },
      { code: 423, label: "Locked (WebDAV; RFC 4918)" },
      { code: 424, label: "Failed Dependency (WebDAV; RFC 4918)" },
      { code: 425, label: "Too Early (RFC 8470)" },
      { code: 426, label: "Upgrade Required" },
      { code: 428, label: "Precondition Required (RFC 6585)" },
      { code: 429, label: "Too Many Requests (RFC 6585)" },
      { code: 431, label: "Request Header Fields Too Large (RFC 6585)" },
      { code: 451, label: "Unavailable For Legal Reasons (RFC 7725)" },
      { code: 500, label: "Internal Server Error" },
      { code: 501, label: "Not Implemented" },
      { code: 502, label: "Bad Gateway" },
      { code: 503, label: "Service Unavailable" },
      { code: 504, label: "Gateway Timeout" },
      { code: 505, label: "HTTP Version Not Supported" },
      { code: 506, label: "Variant Also Negotiates (RFC 2295)" },
      { code: 507, label: "Insufficient Storage (WebDAV; RFC 4918)" },
      { code: 508, label: "Loop Detected (WebDAV; RFC 5842)" },
      { code: 510, label: "Not Extended (RFC 2774)" },
      { code: 511, label: "Network Authentication Required (RFC 6585)" },
      { code: 419, label: "Page Expired (Laravel Framework)" },
      { code: 420, label: "Method Failure (Spring Framework)" },
      { code: 420, label: "Enhance Your Calm (Twitter)" },
      { code: 430, label: "Request Header Fields Too Large (Shopify)" },
      { code: 450, label: "Blocked by Windows Parental Controls (Microsoft)" },
      { code: 498, label: "Invalid Token (Esri)" },
      { code: 499, label: "Token Required (Esri)" },
      { code: 509, label: "Bandwidth Limit Exceeded (Apache Web Server/cPanel)" },
      { code: 529, label: "Site is overloaded" },
      { code: 530, label: "Site is frozen" },
      { code: 598, label: "(Informal convention) Network read timeout error" },
      { code: 599, label: "Network Connect Timeout Error" },
      { code: 440, label: "Login Time-out" },
      { code: 449, label: "Retry With" },
      { code: 451, label: "Redirect" },
      { code: 444, label: "No Response" },
      { code: 494, label: "Request header too large" },
      { code: 495, label: "SSL Certificate Error" },
      { code: 496, label: "SSL Certificate Required" },
      { code: 497, label: "HTTP Request Sent to HTTPS Port" },
      { code: 499, label: "Client Closed Request" },
      { code: 520, label: "Web Server Returned an Unknown Error" },
      { code: 521, label: "Web Server Is Down" },
      { code: 522, label: "Connection Timed Out" },
      { code: 523, label: "Origin Is Unreachable" },
      { code: 524, label: "A Timeout Occurred" },
      { code: 525, label: "SSL Handshake Failed" },
      { code: 526, label: "Invalid SSL Certificate" },
      { code: 527, label: "Railgun Error" },
      { code: 460, label: "" },
      { code: 463, label: "" },
      { code: 464, label: "" },
      { code: 561, label: "Unauthorized" },
      { code: 110, label: "Response is Stale" },
      { code: 111, label: "Revalidation Failed" },
      { code: 112, label: "Disconnected Operation" },
      { code: 113, label: "Heuristic Expiration" },
      { code: 199, label: "Miscellaneous Warning" },
      { code: 214, label: "Transformation Applied" },
      { code: 299, label: "Miscellaneous Persistent Warning" }
    ];
  }
});

// ../../common/express-error-handler/src/index.js
var require_src4 = __commonJS({
  "../../common/express-error-handler/src/index.js"(exports2, module2) {
    "use strict";
    var HTTPError = require_src2();
    var errorCodes = require_errorCodes();
    module2.exports = () => (err, req, res, next) => {
      const isValidCode = (!Number.isNaN(Number(err.code)) || typeof err.code !== "number") && errorCodes.map((a) => a.code).includes(Math.floor(+err.code));
      let httpErr;
      if (err instanceof HTTPError && isValidCode) {
        httpErr = err;
      } else {
        httpErr = !err.code || !isValidCode ? new HTTPError(err, 500, err?.errorId, err?.date, err?.stack) : new HTTPError(err.message, err.code, err?.errorId, err?.date, err?.stack);
      }
      console.error(httpErr);
      res.status(Math.floor(httpErr.code)).json({
        errorId: httpErr.errorId,
        date: httpErr.date,
        code: httpErr.code,
        message: httpErr.message,
        details: httpErr.details || err?.details,
        ...err?.customCode ? { customCode: err.customCode } : {}
      });
      next(err);
    };
  }
});

// ../../common/express-404/src/index.js
var require_src5 = __commonJS({
  "../../common/express-404/src/index.js"(exports2, module2) {
    "use strict";
    module2.exports = (req, res) => {
      res.status(404).send(`${req.method} ${req.url} not found`);
    };
  }
});

// ../../common/express/src/index.js
var require_src6 = __commonJS({
  "../../common/express/src/index.js"(exports2, module2) {
    "use strict";
    require("express-async-errors");
    var express = require("express");
    var cors = require("cors");
    var pathfs = require("path");
    var fs = require("fs");
    var healthCheck = require_src3();
    var helmet = require("helmet").default;
    var cookieParser = require("cookie-parser");
    var compression = require("compression");
    var http = require("http");
    var Socket = require_src();
    var CustomObservable = require_CustomObservable();
    var onServerLaunch = new CustomObservable();
    var _server;
    require("express-async-errors");
    module2.exports = {
      onServerLaunch,
      getServer: () => {
        return new Promise((res) => {
          if (_server) return res(_server);
          onServerLaunch.subscribe(() => {
            res(_server);
          });
        });
      },
      /**
       * @param {{
       *  port: number | string,
       *  baseUrl?: string,
       *  helmetConf?: import('helmet').HelmetOptions | null,
       *  corsConf?: import('cors').CorsOptions,
       *  beforeAll?: ({app, server}) => any,
       *  afterAll?: () => any,
       *  socket?: boolean,
       *  onListening?: (server: {server: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>, app: import('express').Express}) => any,
       *  beforeStatic?: (server: {server: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>, app: import('express').Express}) => any,
       *  afterControllers?: (server: {server: http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>, app: import('express').Express}) => any,
       *  controllers?: () => import('express').RequestHandler,
       *  staticController?: string,
       *  noGreetings?: boolean,
       *  apiPrefix?: string
       *  bodyLimit?: string
       *  healthPath?: string
       * }} param0
       * @returns
       */
      async launch({
        port = process.env.PORT || 4002,
        baseUrl = __dirname,
        helmetConf = {},
        corsConf = {},
        beforeAll = () => {
        },
        afterAll = () => {
        },
        onListening = () => {
        },
        beforeStatic = () => {
        },
        afterControllers = () => {
        },
        controllers = () => () => {
        },
        staticController = void 0,
        noGreetings = false,
        socket = false,
        apiPrefix = "/api",
        bodyLimit = "50mb",
        healthPath = apiPrefix
      }) {
        const pkgJSONPath = pathfs.resolve(baseUrl, "package.json");
        const isPkgJSONExists = fs.existsSync(pkgJSONPath);
        const pkgJSON = isPkgJSONExists ? require(pkgJSONPath) : { name: "unknown", version: "unknown" };
        const appVersion = pkgJSON.version;
        const appName = pkgJSON.name;
        console.log(`<h2 style="background: linear-gradient(90deg, rgba(52,70,91,1) 0%, rgba(28,92,227,1) 100%);color:white;border-radius:10px;padding:10px;width: max-content">${appName.replace("@clabroche/", "")}</h2>`);
        console.log(`v${appVersion} started, listening on port ${port}.`);
        const app = express();
        const server = http.createServer(app);
        if (helmetConf) {
          app.use((req, res, next) => {
            if (req.url.startsWith("/red")) return next();
            helmet(
              helmetConf
            )(req, res, next);
          });
        }
        console.log("Enable cors...");
        app.use(cors(corsConf));
        app.options("*", (req, res, next) => {
          res.setHeader("Access-Control-Allow-Credentials", "true");
          next();
        });
        console.log("Enable JSON body...");
        app.use(express.json({ limit: bodyLimit }));
        app.use(express.urlencoded({ extended: true }));
        console.log("Enable Cookie parser...");
        app.use(cookieParser());
        console.log("Enable Compression...");
        app.use(compression());
        console.log("Enable health check...");
        app.use(healthPath, healthCheck(express, { name: appName, version: appVersion }));
        beforeStatic?.({ server, app });
        console.log("Apply additional routes...");
        if (staticController) {
          app.use("/", express.static(staticController));
        }
        if (socket) {
          console.log("Enable socket...");
          Socket.sockets.connect(server);
        }
        app.use(apiPrefix, controllers());
        if (!noGreetings) {
          app.get("/", (req, res) => res.json({ appName, appVersion }));
        }
        afterControllers?.({ server, app });
        if (process.env.NODE_ENV === "HFBXdZMJxLyJoua28asEaxRixJ6LriR7FnRzX6pwA7pFjZ") {
          const createProxyMiddleware = require("http-proxy-middleware").createProxyMiddleware;
          app.use("/", createProxyMiddleware({
            target: "http://127.0.0.1:5173",
            changeOrigin: false,
            ws: true,
            logger: console
          }));
          server.on("upgrade", (req, res) => {
            if (req.url === "/") {
              proxy.ws(req, res, {
                target: "ws://127.0.0.1:5173"
              });
            }
          });
        }
        console.log("Enable error handling...");
        app.use(require_src4()());
        console.log("Enable 404 handling...");
        app.use(require_src5());
        console.log("Apply additional tasks before launch...");
        await beforeAll({ app, server });
        console.log("Launch...");
        server.on("listening", () => onListening({ server, app }));
        server.listen(port, async () => {
          _server = server;
          onServerLaunch.next({ server, app });
          console.log("<h1>\u2713 Launched</h1>");
        });
        server.on("close", async () => {
          await afterAll();
        });
        return server;
      },
      express
    };
  }
});

// ../../modules/workflows/backend/routes.js
var require_routes23 = __commonJS({
  "../../modules/workflows/backend/routes.js"(exports2, module2) {
    "use strict";
    var { express, getServer } = require_src6();
    var { sockets } = require_src();
    var RED = require("node-red");
    var compressing = require("compressing");
    var router = express.Router();
    var pathfs = require("path");
    var { existsSync } = require("fs");
    var { rm, readFile, writeFile, mkdir, readdir } = require("fs/promises");
    var ports = require_ports();
    var { execAsync } = require_exec();
    var EncryptionKey = require_EncryptionKey();
    var PromiseB2 = require("bluebird");
    var { v4 } = require("uuid");
    module2.exports = (Stack) => {
      getServer().then(async (server) => {
        const userDir = pathfs.resolve(Stack.getRootPath(), "nodered");
        if (!existsSync(userDir)) await mkdir(userDir, { recursive: true });
        const settings = {
          httpAdminRoot: "/red",
          httpNodeRoot: "/node-red",
          userDir,
          credentialSecret: EncryptionKey.encryptionKey,
          functionGlobalContext: {},
          flowFile: "flow.json",
          paletteCategories: ["Stack Monitor"]
        };
        settings.functionGlobalContext.stackmonitor = {
          sockets,
          url: `http://localhost:${ports.http}`,
          stack: require_stack()
        };
        RED.init(server, {
          ...settings
        });
        router.use(settings.httpAdminRoot, RED.httpAdmin);
        router.use(settings.httpNodeRoot, RED.httpNode);
        const moduleName = "node-red-contrib-stack-monitor";
        const modulePath = pathfs.resolve(userDir, "node_modules", moduleName);
        const localNodeModuleTarPath = pathfs.resolve(__dirname, `${moduleName}.tar`);
        const packageJSONPath = pathfs.resolve(userDir, "package.json");
        if (existsSync(modulePath)) {
          console.log(moduleName, "found, delete it before start nodered");
          await rm(modulePath, { recursive: true, force: true });
          await execAsync("npm uninstall node-red-contrib-stack-monitor", { cwd: userDir });
        }
        await PromiseB2.map(await readdir(userDir), async (file) => {
          if (file.startsWith(`node-red-contrib-stack-monitor-`) && file.endsWith(".tgz")) {
            await rm(pathfs.resolve(userDir, file));
          }
        });
        let buffer;
        if (existsSync(localNodeModuleTarPath)) {
          console.log(`Read ${moduleName} from local tar`);
          buffer = await readFile(localNodeModuleTarPath);
        } else {
          let stream2buffer2 = function(stream2) {
            return new Promise((resolve, reject) => {
              const _buf = [];
              stream2.on("data", (chunk) => _buf.push(chunk));
              stream2.on("end", () => resolve(Buffer.concat(_buf)));
              stream2.on("error", (err) => reject(err));
            });
          };
          var stream2buffer = stream2buffer2;
          console.log(`Build ${moduleName} from dir`);
          const stream = new compressing.tar.Stream();
          stream.addEntry(pathfs.resolve(__dirname, "./nodes"), { ignoreBase: true, relativePath: "package" });
          buffer = await stream2buffer2(stream);
        }
        let packageJSON = {
          "name": "node-red-project",
          "description": "A Node-RED Project",
          "version": "0.0.1",
          "private": true,
          "dependencies": {}
        };
        if (existsSync(packageJSONPath)) {
          Object.assign(packageJSON, JSON.parse(await readFile(packageJSONPath, "utf-8")));
        }
        const tgzFileName = `${moduleName}.tgz`;
        packageJSON.dependencies[moduleName] = `file:${tgzFileName}`;
        const pathToTgz = pathfs.resolve(userDir, `${tgzFileName}`);
        await writeFile(pathToTgz, buffer);
        await writeFile(packageJSONPath, JSON.stringify(packageJSON, null, 2), "utf-8");
        await execAsync("npm i", { cwd: userDir });
        await writeFile(pathfs.resolve(userDir, ".gitignore"), `*
!flow.json
!flow_cred.json
!package.json
    `);
        await RED.start();
        sockets.emit("scenarios:start");
      });
      return router;
    };
  }
});

// ../../modules/workflows/backend/index.js
var require_backend28 = __commonJS({
  "../../modules/workflows/backend/index.js"(exports2, module2) {
    "use strict";
    var Workflows = require_Workflows();
    var plugin = {
      enabled: true,
      name: "Workflows",
      displayName: "Workflows",
      description: "Make workflows based on events or manual triggers",
      icon: "fas fa-sitemap",
      export: Workflows,
      order: 10,
      placements: [
        {
          position: "sidebar-top",
          label: "Workflows",
          icon: "fas fa-sitemap",
          goTo: { path: "/Workflows" },
          active: "Workflows"
        }
      ],
      finder: (search, stackMonitor) => {
        return [];
      },
      routes: require_routes23()
    };
    module2.exports = plugin;
  }
});

// ../../modules/plugins-loader/backend/plugins.js
var require_plugins = __commonJS({
  "../../modules/plugins-loader/backend/plugins.js"(exports2, module2) {
    "use strict";
    var plugins = {
      bugs: require_backend(),
      base64: require_backend2(),
      configuration: require_backend3(),
      devOps: require_backend4(),
      diff: require_backend5(),
      documentation: require_backend6(),
      finder: require_backend7(),
      git: require_backend8(),
      github: require_backend9(),
      globalScripts: require_backend10(),
      httpClient: require_backend11(),
      jsonFormatter: require_backend12(),
      jwt: require_backend13(),
      kanban: require_backend14(),
      logs: require_backend15(),
      mongo: require_backend16(),
      nodeRepl: require_backend17(),
      npm: require_backend18(),
      openai: require_backend19(),
      regex: require_backend20(),
      sqlBeautifier: require_backend21(),
      toolbox: require_backend22(),
      uuid: require_backend23(),
      help: require_backend24(),
      openapi: require_backend25(),
      vscode: require_backend26(),
      docker: require_backend27(),
      nodered: require_backend28()
    };
    module2.exports = Object.keys(plugins).reduce((acc, key) => {
      if (plugins[key].enabled) acc[key] = plugins[key];
      return acc;
    }, {});
  }
});

// helpers/readline.js
var require_readline = __commonJS({
  "helpers/readline.js"(exports2, module2) {
    "use strict";
    var { EventEmitter } = require("events");
    var _instances, emit_fn, _a;
    module2.exports = (_a = class extends EventEmitter {
      /**
       *
       * @param {{input?: import('stream').Readable, emitAfterNoDataMs?: number}} options
       */
      constructor(options = {}) {
        super();
        __privateAdd(this, _instances);
        this.options = options;
        if (options.input) this.linkToInput(options.input);
        this.all = [];
        this.timeout = void 0;
      }
      /**
       *
       * @param {import('stream').Readable} readStream
       */
      linkToInput(readStream) {
        readStream.on("data", (data) => {
          clearTimeout(this.timeout);
          data = data.toString("utf-8");
          const dataLen = data.length;
          for (let i = 0; i < dataLen; i += 1) {
            const char = data[i];
            if (char === "\n" || char === "\r\n") {
              __privateMethod(this, _instances, emit_fn).call(this);
            } else {
              this.all.push(char);
            }
          }
          if (this.options.emitAfterNoDataMs) {
            this.timeout = setTimeout(() => {
              __privateMethod(this, _instances, emit_fn).call(this);
            }, this.options.emitAfterNoDataMs);
          }
        });
      }
    }, _instances = new WeakSet(), emit_fn = function() {
      if (!this.all.length) return;
      const line = this.all.splice(0, this.all.length).join("");
      this.emit("line", line);
    }, _a);
  }
});

// helpers/ansiconvert.js
var require_ansiconvert = __commonJS({
  "helpers/ansiconvert.js"(exports2, module2) {
    "use strict";
    var ansiconvert = new (require("ansi-to-html"))({
      newline: true,
      escapeXML: false,
      bg: "#FFFFFFFF",
      fg: "#4c4c4c"
    });
    function stripAnsi(msg) {
      return msg.replaceAll(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/gm, "");
    }
    function unescapeAnsi(msg) {
      return msg.replaceAll(/\\u001b\[/gm, "\x1B[").replaceAll(/\\u009b\[/gm, "\x9B[");
    }
    module2.exports = { ansiconvert, stripAnsi, unescapeAnsi };
  }
});

// helpers/isWindows.js
var require_isWindows = __commonJS({
  "helpers/isWindows.js"(exports2, module2) {
    "use strict";
    module2.exports = process.platform === "win32";
  }
});

// models/Environment.js
var require_Environment = __commonJS({
  "models/Environment.js"(exports2, module2) {
    "use strict";
    var PromiseB2 = require("bluebird");
    var { cloneDeep, merge, over } = require("lodash");
    var dbs2 = require_dbs();
    var { existsSync } = require("fs");
    var Environment = class _Environment {
      /**
       * @param {import('@clabroche/common-typings').NonFunctionProperties<Environment>} environment
       */
      constructor(environment) {
        this.label = environment.label || "";
        this.default = environment.default || false;
        this.color = environment.color || "";
        this.bgColor = environment.bgColor || "";
        this.envs = environment.envs || {};
        this.extends = environment.extends || [];
        this.overrideEnvs = environment.overrideEnvs || {};
      }
      static async load(label, Stack) {
        const environmentDB = await dbs2.getDb(`envs/${label}`).read();
        const overridesDB = dbs2.getDb(`overrides/${label}-environment`);
        if (!existsSync(await overridesDB.getPath())) await new _Environment(environmentDB).save();
        const overrides = await dbs2.getDb(`overrides/${label}-environment`).read();
        merge(environmentDB?.envs || {}, overrides?.envs || {});
        return new _Environment(environmentDB, Stack);
      }
      static async all() {
        const allDbds = await dbs2.getDbs("envs");
        const environments = await PromiseB2.map(allDbds, (id) => _Environment.load(id));
        if (!environments.length) {
          const localEnv = new _Environment({
            bgColor: "#FFFFFF",
            color: "#000000",
            default: true,
            label: "Local",
            envs: {}
          });
          await localEnv.save();
          environments.push(localEnv);
        }
        return environments;
      }
      static async find(envLabel) {
        const envs = await this.all();
        return envs.find((env) => env.label === envLabel);
      }
      /** @returns {Promise<Environment[]>} */
      async getExtendedEnvironments() {
        return [
          this,
          ...await PromiseB2.mapSeries(this.extends || [], (environmentLabel) => _Environment.find(environmentLabel)).filter((a) => !!a)
        ];
      }
      async getBank() {
        const bank = {};
        const environments = await this.getExtendedEnvironments();
        await PromiseB2.map(environments.reverse(), async (environment) => {
          Object.assign(bank, environment?.envs);
        });
        Object.assign(bank, this.envs);
        return bank;
      }
      async save() {
        const dbToWrite = this.toStorage();
        const overrideDbToWrite = { envs: {} };
        Object.keys(dbToWrite.envs).forEach((key) => {
          if (key.endsWith("_STACK_MONITOR_OVERRIDE")) {
            overrideDbToWrite.envs[key] = dbToWrite.envs[key];
            delete dbToWrite.envs[key];
          }
        });
        await dbs2.getDb(`overrides/${this.label}-environment`).write(overrideDbToWrite);
        await dbs2.getDb(`envs/${this.label}`).write(dbToWrite);
      }
      async update(env) {
        this.default = env.default;
        this.color = env.color;
        this.bgColor = env.bgColor;
        this.envs = env.envs;
        this.extends = env.extends;
        return this.save();
      }
      async delete() {
        await dbs2.getDb(`envs/${this.label}`).delete();
      }
      toStorage() {
        return cloneDeep({
          label: this.label,
          default: this.default,
          color: this.color,
          bgColor: this.bgColor,
          extends: this.extends,
          envs: this.envs
        });
      }
    };
    module2.exports = Environment;
  }
});

// parser/json.js
var require_json = __commonJS({
  "parser/json.js"(exports2, module2) {
    "use strict";
    var parser2 = {
      id: "stack-monitor-parser-jsons",
      label: "Parse jsons",
      readonly: true,
      transform: (line) => {
        if (!line.raw) return line;
        const firstChar = line.raw.trim().charAt(0);
        if (firstChar === "[" || firstChar === "{") {
          try {
            line.json = JSON.parse(line.raw);
          } catch (error) {
          }
        }
        return line;
      }
    };
    module2.exports = parser2;
  }
});

// parser/debug.js
var require_debug = __commonJS({
  "parser/debug.js"(exports2, module2) {
    "use strict";
    var jsonParser = require_json();
    var parser2 = {
      id: "stack-monitor-parser-debug",
      label: "Stackmonitor debug",
      readonly: true,
      transform: (line, ...rest) => {
        if (!line.json) line = jsonParser.transform(line, ...rest);
        if (!line.json) return line;
        if (
          /** @type {Array<any>} */
          line?.json?.[0] === "stack-monitor"
        ) {
          line.debug = line.json.length === 2 ? (
            /** @type {Array<any>} */
            line?.json?.[1]
          ) : line.json.slice(1);
        }
        return line;
      }
    };
    module2.exports = parser2;
  }
});

// parser/link.js
var require_link = __commonJS({
  "parser/link.js"(exports2, module2) {
    "use strict";
    var urlRegex = /(http|ftp|https):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])/gi;
    var parser2 = {
      id: "stack-monitor-parser-links",
      label: "Parse links",
      readonly: true,
      transform: (line) => {
        line.msg = line.msg.replaceAll(urlRegex, (url) => `<a href="${url}" target="_blank">${url}</a>`);
        return line;
      }
    };
    module2.exports = parser2;
  }
});

// models/Parser.js
var require_Parser = __commonJS({
  "models/Parser.js"(exports, module) {
    "use strict";
    var PromiseB = require("bluebird");
    var { randomUUID } = require("crypto");
    var dbs = require_dbs();
    var nativeParsers = {
      [require_debug().id]: require_debug(),
      [require_json().id]: require_json(),
      [require_link().id]: require_link()
    };
    var Parser = class _Parser {
      /**
       * @param {import('@clabroche/common-typings').NonFunctionProperties<Parser>} parser
       */
      constructor(parser) {
        this.label = parser.label || "";
        this.id = parser.id || randomUUID();
        this.transform = parser.transform || "";
        this.readonly = parser.readonly || false;
        try {
          this.transformFunction = typeof parser.transform === "string" ? eval(parser.transform) : parser.transform;
        } catch (error) {
          console.error("[PARSER]:", this.label, "Cannot interpret your parser", error);
        }
      }
      static async load(id) {
        if (nativeParsers[id]) return new _Parser(nativeParsers[id]);
        return new _Parser(await dbs.getDb(`parsers/${id}`).read());
      }
      static async all() {
        const allDbds = [
          ...await dbs.getDbs("parsers"),
          ...Object.keys(nativeParsers)
        ];
        return PromiseB.map(allDbds, (id) => this.load(id));
      }
      static async find(envId) {
        const parsers = await this.all();
        return parsers.find((env) => env.id === envId);
      }
      async save() {
        const obj = this.toStorage();
        await dbs.getDb(`parsers/${this.id}`).write(obj);
        return this;
      }
      async update(env) {
        this.transform = env.transform;
        this.label = env.label;
        await dbs.getDb(`parsers/${this.id}`).write(this.toStorage());
      }
      async delete() {
        await dbs.getDb(`parsers/${this.id}`).delete();
      }
      toStorage() {
        return {
          id: this.id,
          label: this.label,
          transform: this.transform
        };
      }
    };
    module.exports = Parser;
  }
});

// models/Service.js
var require_Service = __commonJS({
  "models/Service.js"(exports2, module2) {
    "use strict";
    var os = require("os");
    var { spawn } = require("child_process");
    var killport = require("kill-port");
    var URL2 = require("url");
    var path = require("path");
    var PromiseB2 = require("bluebird");
    var dayjs = require("dayjs");
    var { v4 } = require("uuid");
    var { existsSync, readFileSync } = require("fs");
    var kill = require("tree-kill");
    var axios = require("axios").default;
    var pathfs = require("path");
    var net = require("net");
    var { sockets } = require_src();
    var { mkdir, writeFile } = require("fs/promises");
    var { cloneDeep, over } = require("lodash");
    var CreateInterface = require_readline();
    var { stripAnsi, ansiconvert, unescapeAnsi } = require_ansiconvert();
    var isWindows = require_isWindows();
    var { execAsync } = require_exec();
    var { humanStringToKey, replaceEnvs } = require_stringTransformer_helper();
    var dbs2 = require_dbs();
    var Environment = require_Environment();
    var ParserModel = require_Parser();
    var userInfo = os.userInfo();
    var { gid, uid, username } = userInfo;
    var alias = {
      ls: { cmd: "ls", args: ["--color=force"] },
      gco: { cmd: "git", args: ["checkout"] }
    };
    function Service(service, Stack, { isUpdate } = { isUpdate: false }) {
      return (async () => {
        this.Stack = Stack;
        this.queue = service.queue || [];
        this.stopQueue = false;
        if (!isUpdate) {
          this.processQueue();
        }
        this.label = service.label || "";
        this.description = service.description || "";
        this.git = {
          home: service.git?.home || "",
          remote: service.git?.remote || ""
        };
        this.documentation = service.documentation || "";
        this.openapiURL = service.openapiURL || "";
        this.url = service.url || "";
        this.rootPath = service.rootPath || ".";
        this.urls = service.urls || [];
        this.groups = service.groups || [];
        this.parsers = service.parsers || [
          "stack-monitor-parser-debug",
          "stack-monitor-parser-jsons",
          "stack-monitor-parser-links"
        ];
        this.enabled = service.enabled || false;
        this.crashed = service.crashed || false;
        this.container = service.container || {};
        if (this.container) {
          this.container.name = humanStringToKey(this.label);
          if (!this.container.volumes?.length) this.container.volumes = [];
          if (!this.container.build) this.container.build = "";
          this.container.ports = this.container.ports || [];
          this.container.sharedVolume = this.container.sharedVolume || "~/.stack-monitor";
          this.container.ignoreVolumes = this.container.ignoreVolumes?.length ? this.container.ignoreVolumes.filter((f) => !f.startsWith(this.container?.sharedVolume || "")) : [];
          if (!this.container.bootstrap) this.container.bootstrap = { commands: [] };
          if (!this.container.bootstrap.commands) this.container.bootstrap.commands = [];
        }
        this.exited = service.exited || false;
        this.envs = service.envs || {};
        this.commands = service.commands || [];
        this.shortcuts = service.shortcuts || [];
        this.store = service.store || [];
        this.pids = service.pids || [];
        this.lastDatePrinted = service.lastDatePrinted || null;
        this.health = service.health || {
          enabled: false,
          url: "",
          interval: "",
          method: "GET",
          returnCode: 200,
          responseText: "",
          timeout: 0,
          startAfter: 0
        };
        const environments = await Environment.all();
        const overrides = await dbs2.getDb(`overrides/${this.label}-envs`).read();
        if (!isUpdate) {
          environments.forEach((environment) => {
            const envs = this.envs[environment.label] || {};
            Object.keys(envs).forEach((key) => {
              const env = envs[key];
              const tag = extractTag(env.value);
              if (tag) envs[key].override = `{{${tag}_STACK_MONITOR_OVERRIDE}}`;
              const override = overrides[environment.label]?.[key];
              if (!env || !override) return;
              envs[key].override = override;
            });
          });
        }
        this.meta = service.meta || {};
        await PromiseB2.map(this.commands || [], async (command) => {
          command.effectiveParsers = await PromiseB2.map([
            ...this.parsers || [],
            ...command.parsers || []
          ], (parserId) => ParserModel.find(parserId)).filter((a) => !!a);
        });
        return this;
      })();
    }
    Service.prototype.getRootPath = function() {
      return replaceEnvs(this.rootPath);
    };
    Service.prototype.processQueue = function() {
      if (this.queue.length) {
        const messages = this.queue.splice(0, this.queue.length);
        sockets.emit("logs:update", messages);
      }
      if (this.stopQueue) return;
      setTimeout(() => {
        this.processQueue();
      }, 100);
    };
    Service.prototype.save = async function() {
      const obj = this.toStorage();
      const overrides = {};
      await PromiseB2.map(Object.keys(obj.envs), (environmentLabel) => {
        if (!overrides[environmentLabel]) overrides[environmentLabel] = {};
        Object.keys(obj.envs[environmentLabel]).forEach((key) => {
          const env = obj.envs[environmentLabel][key];
          overrides[environmentLabel][key] = env.override;
          delete env.systemOverride;
          delete env.override;
        });
      });
      await dbs2.getDb(`overrides/${this.label}-envs`).write(overrides);
      await dbs2.getDb(`services/${this.label}`).write(obj);
    };
    Service.prototype.delete = async function() {
      await dbs2.getDb(`overrides/${this.label}-envs`).delete();
      await dbs2.getDb(`services/${this.label}`).delete();
    };
    Service.load = async function(label, Stack) {
      return new Service(await dbs2.getDb(`services/${label}`).read(), Stack);
    };
    Service.prototype.loadCustomEnv = function(path2) {
      const dotEnvPath = pathfs.resolve(path2, ".env");
      if (existsSync(dotEnvPath) && readFileSync(dotEnvPath, { encoding: "utf-8" }).trim()) {
        console.log(`! A .env will override your ${this.label} service !`);
        return require("dotenv").parse(readFileSync(dotEnvPath, "utf-8"));
      }
      return null;
    };
    Service.prototype.exportInApi = function() {
      const res = { ...this };
      delete res.pids;
      delete res.store;
      delete res.Stack;
      return res;
    };
    Service.prototype.toStorage = function() {
      const service = cloneDeep({
        label: this.label,
        commands: this.commands,
        description: this.description,
        git: this.git,
        groups: this.groups,
        url: this.url,
        rootPath: this.rootPath,
        openapiURL: this.openapiURL,
        health: this.health,
        urls: this.urls,
        parsers: this.parsers,
        container: this.container,
        meta: this.meta,
        envs: this.envs,
        shortcuts: this.shortcuts
      });
      return service;
    };
    Service.prototype.restart = async function() {
      await this.kill();
      await this.launch();
    };
    Service.prototype.sendHasBeenModified = function() {
      sockets.emit("conf:update", [this.label]);
    };
    Service.prototype.kill = async function(keepEnabled = false) {
      if (this.container?.enabled && this.container?.name) {
        await execAsync(`docker stop ${this.container.name}`, {}).catch(console.error);
        await execAsync(`docker rm ${this.container.name}`, {}).catch(console.error);
      } else {
        await PromiseB2.map(this.pids, async (spawnedProcess) => {
          if (!spawnedProcess.pid) return;
          await killAsync(spawnedProcess.pid);
          spawnedProcess.kill("SIGKILL");
        });
        const urls = [...this.urls || [], this.url].filter((a) => a);
        if (urls.length) {
          await PromiseB2.mapSeries(urls, async (url) => {
            const { port } = URL2.parse(url);
            if (port && !Number.isNaN(+port)) {
              let free = false;
              for (let i = 0; i < 16; i += 1) {
                await wait(100);
                free = await checkport(+port);
                if (free) break;
              }
              if (!free) {
                await killport(+port).catch((err) => console.error("Error: (Kill port):", err?.message || err));
              }
            }
          });
        }
      }
      this.pids = [];
      sockets.emit("logs:clear", { label: this.label });
      this.store = [];
      await wait(100);
      this.enabled = keepEnabled;
      this.crashed = false;
      this.sendHasBeenModified();
    };
    Service.prototype.launch = async function() {
      this.store = [];
      await this.kill(true).catch(console.error);
      if (this.container?.enabled) {
        await this.buildDocker({ isMainProcess: true });
      }
      if (this.commands?.length) {
        await PromiseB2.map(this.commands, async (command) => {
          if (command?.spawnCmd) {
            await this.launchProcess(command);
          }
        });
      }
      this.enabled = true;
      this.sendHasBeenModified();
    };
    Service.prototype.add = async function(data, logMessageOverride, {
      pid,
      isMainProcess,
      command
    }) {
      const timestamp = Date.now();
      const ansiMsg = unescapeAnsi(data.toString());
      const stripMsg = stripAnsi(ansiMsg);
      const htmlMessage = ansiMsg ? ansiconvert.toHtml(ansiMsg) : "<br/>";
      let line = {
        pid,
        msg: htmlMessage,
        raw: stripMsg,
        timestamp,
        label: this.label,
        json: null,
        debug: null,
        ...logMessageOverride,
        id: v4()
      };
      line = await PromiseB2.reduce(command.effectiveParsers || [], async (line2, parser2) => {
        try {
          const result = await parser2.transformFunction(line2, this);
          if (!result?.id) {
            console.error(`It seems your parser "${parser2.label}" not return correct value. Please verify or disable it..`);
            return line2;
          }
          return result;
        } catch (err) {
          console.error("[PARSER]:", parser2.label, ":", err);
          return line2;
        }
      }, line);
      if (line.hide) return;
      if (line.source === "stderr" && isMainProcess) {
        sockets.emit("alert", { label: this.label, message: line.raw.toString(), type: "error", commandId: command?.id });
      }
      if (timestamp > (this.lastDatePrinted || Date.now()) + 2e3) {
        const date = `\u{1F551}  ${dayjs().format("YYYY-MM-DD HH:mm:ss")}`;
        const line2 = {
          id: v4(),
          raw: date,
          label: this.label,
          msg: date,
          timestamp,
          isSeparator: true
        };
        this.store.push(line2);
        this.queue.push(line2);
      }
      this.lastDatePrinted = Date.now();
      if (line.msg.length > 1e5 && !line.msg.startsWith('["stack-monitor"')) line.msg = line.msg.slice(0, 1e4);
      this.store.push(line);
      this.queue.push(line);
    };
    Service.prototype.launchProcess = async function(command, isMainProcess = true) {
      try {
        this.enabled = true;
        this.sendHasBeenModified();
        this.crashed = false;
        this.exited = false;
        let { cmd, args: args2, options } = this.container.enabled ? await this.parseIncomingCommandDocker(command) : await this.parseIncomingCommand(command);
        if (!existsSync(options.cwd)) {
          this.crashed = true;
          this.exited = true;
          const launchMessage2 = {
            id: v4(),
            timestamp: Date.now(),
            label: this.label,
            pid: null,
            msg: `Path does not exists (${options.cwd})`,
            raw: `Path does not exists (${options.cwd})`,
            cmd: {
              cmd,
              args: args2,
              options,
              status: "exited"
            }
          };
          console.error(launchMessage2.msg);
          this.add(launchMessage2.msg, { source: "stderr" }, { pid: null, command, isMainProcess });
          return { spawnProcess: null, launchMessage: launchMessage2 };
        }
        const spawnProcess = spawn(cmd, args2, { ...options, detached: !isWindows });
        let pid = 0;
        pid = spawnProcess.pid;
        if (this.container?.customPid) {
          this.container.customPid({ pid: spawnProcess.pid, cmd, args: args2 }).then((_pid) => {
            pid = _pid;
          });
        }
        if (!this.pids) this.pids = [];
        this.pids.push(spawnProcess);
        spawnProcess.title = this.label;
        this.lastDatePrinted = Date.now();
        this.queue = [];
        new CreateInterface({
          input: spawnProcess.stdout,
          emitAfterNoDataMs: 100
        }).on("line", (message) => {
          this.add(message, { source: "stdout" }, { isMainProcess, pid, command });
        });
        new CreateInterface({
          input: spawnProcess.stderr,
          emitAfterNoDataMs: 100
        }).on("line", (message) => {
          this.add(message, { source: "stderr" }, { isMainProcess, pid, command });
        });
        const launchMessage = {
          id: v4(),
          timestamp: Date.now(),
          label: this.label,
          pid,
          msg: `${cmd} ${args2.join(" ")}`,
          raw: `${cmd} ${args2.join(" ")}`,
          cmd: {
            cmd,
            args: args2,
            options,
            status: "running"
          }
        };
        spawnProcess.on("exit", (code, signal) => {
          if (code) {
            if (launchMessage.cmd) launchMessage.cmd.status = "error";
            if (isMainProcess) {
              sockets.emit("service:crash", {
                label: this.label,
                code,
                signal,
                pid
              });
              this.crashed = true;
            }
          } else if (launchMessage.cmd) {
            launchMessage.cmd.status = "exited";
            if (isMainProcess) {
              this.exited = true;
            }
          }
          sockets.emit("service:exit", {
            label: this.label,
            code,
            signal,
            pid
          });
          sockets.emit("logs:update:lines", [launchMessage]);
        });
        const date = `\u{1F551}  ${dayjs().format("YYYY-MM-DD HH:mm:ss")}`;
        const line = {
          id: v4(),
          raw: date,
          label: this.label,
          msg: date,
          timestamp: this.lastDatePrinted,
          isSeparator: true
        };
        this.store.push(line, launchMessage);
        sockets.emit("logs:update", [line, launchMessage]);
        if (isMainProcess) {
          setTimeout(() => {
            this.launchHealthChecker(spawnProcess);
          }, this.health.startAfter || 0);
          sockets.emit("service:start", {
            label: this.label,
            pid
          });
        }
        return {
          launchMessage,
          spawnProcess
        };
      } catch (error) {
        const launchMessage = {
          id: v4(),
          timestamp: Date.now(),
          label: this.label,
          pid: null,
          msg: `ERROR: ${error?.message || error}`,
          raw: `ERROR: ${error?.message || error}`,
          cmd: {
            cmd: command.spawnCmd,
            args: command.spawnArgs,
            options: command.spawnOptions,
            status: "exited"
          }
        };
        const date = `\u{1F551}  ${dayjs().format("YYYY-MM-DD HH:mm:ss")}`;
        const line = {
          id: v4(),
          raw: date,
          label: this.label,
          msg: date,
          timestamp: this.lastDatePrinted,
          isSeparator: true
        };
        sockets.emit("logs:update", [line, launchMessage]);
        throw error;
      }
    };
    Service.prototype.launchHealthChecker = async function(spawnProcess) {
      if (!spawnProcess.pid && !this.container?.name || !this.health?.enabled) return;
      const healthy = await axios({
        method: this.health.method || "GET",
        url: this.health.url || this.url,
        headers: { Accept: "*/*" },
        timeout: +(this.health.timeout || 0),
        validateStatus: (status) => status === +(this.health.returnCode || 200)
      }).then(({ data: response }) => {
        if (this.health.responseText && this.health.responseText !== JSON.stringify(response)) {
          return false;
        }
        return true;
      }).catch(() => false);
      if (!healthy && !this.crashed) {
        this.crashed = true;
        sockets.emit("service:healthcheck:down", { label: this.label, pid: spawnProcess.pid });
      } else if (healthy && this.crashed) {
        this.crashed = false;
        sockets.emit("service:healthcheck:up", { label: this.label, pid: spawnProcess.pid });
      }
      await wait(+(this.health.interval || 1e3));
      this.launchHealthChecker(spawnProcess);
    };
    Service.prototype.respondToProcess = function(pid, message) {
      const process2 = this.pids.find((process3) => process3.pid === pid);
      if (!process2) return console.error(`Pid (${pid}) not found`);
      process2.stdin?.write(`${message.trim()}
`);
      const line = {
        id: v4(),
        raw: `${message.trim()}
`,
        label: this.label,
        msg: `${message.trim()}
`,
        timestamp: Date.now(),
        prompt: true,
        pid
      };
      this.store.push(line);
      sockets.emit("logs:update", [line]);
      return null;
    };
    Service.prototype.terminate = function(pid, forceKill = false) {
      const processFound = this.pids.find((process2) => process2.pid === pid);
      if (!processFound) return console.error(`Pid (${pid}) not found`);
      if (processFound.pid) {
        if (isWindows) process.kill(processFound.pid, "SIGKILL");
        else process.kill(-processFound.pid, forceKill ? "SIGKILL" : "SIGTERM");
      }
      return null;
    };
    Service.prototype.enable = function() {
      this.enabled = true;
      this.sendHasBeenModified();
    };
    Service.prototype.disable = function() {
      this.enabled = false;
      this.sendHasBeenModified();
    };
    Service.prototype.getGlobalEnvs = async function(environmentLabel) {
      const globalEnvironment = await Environment.find(environmentLabel);
      return (label) => {
        const tag = this.extractTag(label);
        if (tag) {
          return globalEnvironment?.envs[tag] ?? "";
        }
        return label;
      };
    };
    Service.prototype.buildEnvs = async function(environmentLabel) {
      const globalEnvironment = await Environment.find(environmentLabel);
      if (!globalEnvironment) return {};
      const extendedEnvironments = await globalEnvironment.getExtendedEnvironments();
      const environmentsKeys = /* @__PURE__ */ new Map();
      const searchEnv = (label, env) => {
        const tag = this.extractTag(label);
        if (tag) {
          return env[tag] ?? "";
        }
        return label;
      };
      extendedEnvironments.map((extendedEnvironment) => {
        Object.keys(this.envs[extendedEnvironment.label] || {}).forEach((key) => {
          const env = this.envs[extendedEnvironment.label][key];
          if (!environmentsKeys.has(key)) {
            for (let i = 0; i < extendedEnvironments.length; i++) {
              let occurrence = searchEnv(env.systemOverride, extendedEnvironments[i].envs) || searchEnv(env.override, extendedEnvironments[i].envs) || searchEnv(env.value, extendedEnvironments[i].envs);
              if (occurrence) occurrence = `${env.prefix || ""}${occurrence || ""}${env.suffix || ""}`;
              const value = occurrence;
              if (value) {
                environmentsKeys.set(key, value);
                return;
              }
            }
          }
        });
      });
      return [...environmentsKeys].reduce((agg, [key, value]) => {
        agg[key] = value;
        return agg;
      }, {});
    };
    Service.prototype.extractTag = function(field) {
      const extractedTag = /{{(.*)}}/gi.exec(field)?.[1]?.trim();
      return extractedTag;
    };
    Service.prototype.parseIncomingCommandDocker = async function(command) {
      let { spawnCmd, spawnArgs = [], spawnOptions = { envs: [] } } = command;
      const isAlive = await execAsync(`docker inspect --format {{.State.Pid}}  ${this.container.name}`, {}).then((pid) => pid.trim() !== "0").catch(() => false);
      const cmd = "docker";
      const args2 = isAlive ? ["exec", this.container.name, spawnCmd, ...spawnArgs] : [
        "run",
        "--rm",
        ...await this.getDockerEnvsArgs(),
        ...await this.getDockerVolumesArgs(),
        ...await this.getDockerPorts(),
        "--network",
        "host",
        "--name",
        this.container.name,
        this.container.name,
        "sh",
        `-c '${spawnCmd} ${spawnArgs.join(" ")}'`
      ];
      const cwd = pathfs.resolve(replaceEnvs(spawnOptions.cwd || this.getRootPath() || "."));
      const options = {
        cwd,
        shell: isWindows ? process.env.ComSpec : "/bin/sh"
      };
      return { cmd, args: args2, options };
    };
    Service.prototype.parseIncomingCommand = async function(command) {
      const { spawnCmd, spawnArgs = [], spawnOptions = { envs: [] } } = command;
      let cmd = spawnCmd?.split(" ")?.[0];
      const argFromCmd = spawnCmd?.split(" ")?.slice(1).join(" ");
      let args2 = [argFromCmd, ...spawnArgs].filter((a) => a);
      const currentAlias = alias[cmd];
      if (currentAlias) {
        cmd = currentAlias?.cmd || cmd;
        args2 = [...currentAlias?.args || [], ...args2];
      }
      const cwd = pathfs.resolve(replaceEnvs(spawnOptions.cwd || this.getRootPath() || "."));
      const options = {
        ...spawnOptions,
        cwd,
        shell: isWindows ? process.env.ComSpec : "/bin/sh",
        env: {
          ...process.env,
          ...await this.buildEnvs(this.Stack.getCurrentEnvironment()?.label)
        }
      };
      if (cmd.match(/[/\\]/g)) {
        cmd = path.resolve(options.cwd.toString(), cmd);
      }
      options.env = { ...process.env, ...options.env };
      return { cmd, args: args2, options };
    };
    function replaceHome(str) {
      return str.startsWith("~") ? pathfs.resolve(os.homedir(), str.replace("~/", "")) : pathfs.resolve(str);
    }
    Service.prototype.getDockerVolumesArgs = async function() {
      const internalVolumeRootPath = replaceHome(this.container.sharedVolume);
      const volumesCmd = this.container.volumes.map((v) => {
        let [external, internal] = v.split(":");
        if (external) external = pathfs.resolve(replaceHome(replaceEnvs(external)));
        if (internal) internal = pathfs.resolve(replaceHome(replaceEnvs(internal)));
        return ["-v", `"${external}:${internal || external}"`];
      });
      volumesCmd.push(...await PromiseB2.map(this.container.ignoreVolumes, async (ignoredVolume) => {
        const volumePath = pathfs.join(internalVolumeRootPath, `ignored-volume-${humanStringToKey(this.label)}`, ignoredVolume);
        if (!existsSync(volumePath)) await mkdir(volumePath, { recursive: true });
        return ["-v", `"${volumePath}:${ignoredVolume}"`];
      }).filter((f) => !!f?.length));
      return volumesCmd.flat(1);
    };
    Service.prototype.getDockerPorts = async function() {
      const portsCmd = [];
      this.container.ports.forEach((port) => {
        portsCmd.push("-p", port);
      });
      return portsCmd.flat(1);
    };
    Service.prototype.getDockerEnvsArgs = async function() {
      const envs = await this.buildEnvs(this.Stack.getCurrentEnvironment()?.label);
      const envCmd = [];
      Object.keys(envs).forEach((key) => {
        envCmd.push("-e", `"${key}=${envs[key]}"`);
      });
      return envCmd;
    };
    Service.prototype.launchDockerBuild = async function({ isMainProcess }) {
      const internalVolumeRootPath = replaceHome(this.container.sharedVolume);
      const dockerFilePath = pathfs.resolve(internalVolumeRootPath, `Dockerfile.${this.container.name}`);
      const dockerContextPath = pathfs.resolve(internalVolumeRootPath, ".empty-context");
      const command = {
        spwanCmd: "docker",
        spawnArgs: ["build", "-f", dockerFilePath, "-t", this.container?.name || "", dockerContextPath],
        spawnOptions: { cwd: internalVolumeRootPath, shell: isWindows ? true : "/bin/sh", env: process.env }
      };
      this.add('<i class="fab fa-docker" title="docker"></i> <i class="fas fa-hard-hat" title="build"></i> Building docker image...', { source: "stdout" }, { pid: null, isMainProcess, command });
      this.add(`<i class="fab fa-docker" title="docker"></i> <i class="fas fa-hard-hat" title="build"></i> ${command.spwanCmd} ${command.spawnArgs?.join(" ")}}`, { source: "stdout" }, { pid: null, isMainProcess, command });
      await new Promise((resolve, reject) => {
        const buildProcess = spawn(command.spwanCmd, command.spawnArgs, command.spawnOptions);
        new CreateInterface({
          input: buildProcess.stdout
        }).on("line", (message) => {
          this.add(`<i class="fab fa-docker" title="docker"></i> <i class="fas fa-hard-hat" title="build"></i> ${message}`, { source: "stdout" }, { isMainProcess, pid: buildProcess.pid, command });
        });
        new CreateInterface({
          input: buildProcess.stderr
        }).on("line", (message) => {
          this.add(`<i class="fab fa-docker" title="docker"></i> <i class="fas fa-hard-hat" title="build"></i> ${message}`, { source: "stdout" }, { isMainProcess, pid: buildProcess.pid, command });
        });
        buildProcess.on("exit", (code) => {
          if (code) {
            this.exited = true;
            this.crashed = true;
            return reject(code);
          }
          return resolve(null);
        });
      });
    };
    Service.prototype.launchDockerBootstrap = async function({ isMainProcess }) {
      const volumesCmd = await this.getDockerVolumesArgs();
      const baseArgs = ["run", "--name", this.container.name, "--init", "--rm", , ...isWindows ? [] : ["--user", this.container.user || `${uid}:${gid}`], "--network", "host", ...volumesCmd];
      if (this.container.bootstrap) {
        const envCmd = await this.getDockerEnvsArgs();
        await PromiseB2.mapSeries(this.container.bootstrap.commands || [], async (command) => {
          await new Promise((resolve, reject) => {
            const entrypoint = command.entrypoint ? ["--entrypoint", `${command.entrypoint}`] : [];
            const userEntrypoint = command.user && !isWindows ? ["--user", `${command.user || this.container.user || `${uid}:${gid}`}`] : [];
            const bootstrapArgs = (
              /**@type {String[]}*/
              [
                ...baseArgs,
                ...entrypoint.flat(1),
                ...userEntrypoint.flat(1),
                ...envCmd.flat(1),
                this.container.name,
                command.cmd
              ].filter((a) => !!a)
            );
            const commandBootstrap = {
              spawnCmd: "docker",
              spawnArgs: bootstrapArgs,
              spawnOptions: { shell: isWindows ? process.env.ComSpec : "/bin/sh" }
            };
            this.add(`<i class="fab fa-docker" title="docker"></i> <i class="fas fa-hourglass-start" title="bootstrap"></i> ${commandBootstrap.spawnCmd} ${commandBootstrap.spawnArgs?.join(" ")}`, { source: "stdout" }, { isMainProcess, pid: null, command: commandBootstrap });
            const bootstrapProcess = spawn(
              commandBootstrap.spawnCmd,
              commandBootstrap.spawnArgs,
              commandBootstrap.spawnOptions
            );
            new CreateInterface({
              input: bootstrapProcess.stdout
            }).on("line", (message) => {
              this.add(`<i class="fab fa-docker" title="docker"></i> <i class="fas fa-hourglass-start" title="bootstrap"></i> ${message}`, { source: "stdout" }, { isMainProcess, pid: bootstrapProcess.pid, command: commandBootstrap });
            });
            new CreateInterface({
              input: bootstrapProcess.stderr
            }).on("line", (message) => {
              this.add(`<i class="fab fa-docker" title="docker"></i> <i class="fas fa-hourglass-start" title="bootstrap"></i> ${message}`, { source: "stderr" }, { isMainProcess, pid: bootstrapProcess.pid, command: commandBootstrap });
            });
            bootstrapProcess.on("exit", (code) => {
              if (code) return reject(code);
              return resolve(null);
            });
          });
        });
      }
    };
    Service.prototype.buildDocker = async function({
      isMainProcess
    }) {
      const internalVolumeRootPath = replaceHome(this.container.sharedVolume);
      const dockerFilePath = pathfs.resolve(internalVolumeRootPath, `Dockerfile.${this.container.name}`);
      const dockerIgnoreFilePath = pathfs.resolve(internalVolumeRootPath, ".dockerignore");
      const dockerContextPath = pathfs.resolve(internalVolumeRootPath, ".empty-context");
      if (!existsSync(internalVolumeRootPath)) await mkdir(internalVolumeRootPath, { recursive: true });
      if (!existsSync(dockerContextPath)) await mkdir(dockerContextPath, { recursive: true });
      await writeFile(dockerFilePath, `${this.container.build || ""}`);
      await writeFile(dockerIgnoreFilePath, "Dockerfile.*".trim(), "utf-8");
      await this.launchDockerBuild({ isMainProcess });
      await this.launchDockerBootstrap({ isMainProcess });
      this.container.customPid = async () => {
        const getPid = () => execAsync(`docker inspect --format {{.State.Pid}}  ${this.container?.name}`, {}).then((a) => a.trim()).catch(() => null);
        let pid = await getPid();
        if (!pid) {
          await wait(1e3);
          pid = await getPid();
        }
        return pid && !Number.isNaN(+pid) ? +pid : null;
      };
    };
    var wait = (ms) => new Promise((resolve) => {
      setTimeout(resolve, ms);
    });
    function killAsync(pid) {
      return new Promise((resolve, reject) => {
        kill(pid, (err, children) => {
          if (err) return reject(err);
          return resolve(children);
        });
      });
    }
    function checkport(_port) {
      return new Promise((resolve) => {
        const s = net.createServer();
        s.once("error", () => {
          s.close();
          resolve(false);
        });
        s.once("listening", () => {
          resolve(true);
          s.close();
        });
        s.listen(_port);
      });
    }
    function extractTag(field) {
      const extractedTag = /{{(.*)}}/gi.exec(field)?.[1]?.trim();
      return extractedTag || "";
    }
    module2.exports = Service;
  }
});

// helpers/exportedHelpers.js
var require_exportedHelpers = __commonJS({
  "helpers/exportedHelpers.js"(exports2, module2) {
    "use strict";
    module2.exports = {
      /**
       * @param {string} str
       * @param {string} search
       */
      searchString(str, search) {
        return str?.toUpperCase()?.includes(search);
      }
    };
  }
});

// helpers/version.js
var require_version = __commonJS({
  "helpers/version.js"(exports2, module2) {
    "use strict";
    var { existsSync, readFileSync } = require("fs");
    var path = require("path");
    var file = {
      version: "0.0.0"
    };
    if (existsSync(path.resolve(__dirname, "./package.json"))) {
      file.version = JSON.parse(readFileSync(path.resolve(__dirname, "./package.json"), { encoding: "utf-8" })).version;
    } else if (existsSync(path.resolve(__dirname, "../package.json"))) {
      file.version = JSON.parse(readFileSync(path.resolve(__dirname, "../package.json"), { encoding: "utf-8" })).version;
    } else if (existsSync(path.resolve(__dirname, "../../../lerna.json"))) {
      file.version = JSON.parse(readFileSync(path.resolve(__dirname, "../../../lerna.json"), { encoding: "utf-8" })).version;
    }
    module2.exports = {
      version: file.version
    };
  }
});

// models/saves.js
var require_saves = __commonJS({
  "models/saves.js"(exports2, module2) {
    "use strict";
    var pathfs = require("path");
    var homedir = require("os").homedir();
    var {
      existsSync,
      mkdirSync,
      writeFileSync,
      readFileSync
    } = require("fs");
    var confDir = pathfs.resolve(homedir, ".stack-monitor");
    function getSave(file, initialData, options = {}) {
      if (!existsSync(confDir)) mkdirSync(confDir);
      const dataConfPath = pathfs.resolve(confDir, file);
      if (!existsSync(dataConfPath)) writeFileSync(dataConfPath, JSON.stringify(initialData), "utf-8");
      const data = JSON.parse(readFileSync(dataConfPath, "utf-8"));
      options?.afterGet?.(data);
      return {
        /** @type {T} */
        data,
        save() {
          options?.beforeSave?.(data);
          writeFileSync(dataConfPath, JSON.stringify(data), "utf-8");
        }
      };
    }
    module2.exports = getSave;
  }
});

// models/stack.js
var require_stack = __commonJS({
  "models/stack.js"(exports2, module2) {
    "use strict";
    var { sockets } = require_src();
    var plugins = require_plugins();
    var PromiseB2 = require("bluebird");
    var Service = require_Service();
    var ports = require_ports();
    var dbs2 = require_dbs();
    var EnvironmentModel = require_Environment();
    var EncryptionKey = require_EncryptionKey();
    var CustomObservable = require_CustomObservable();
    var args2 = require_args();
    var { existsSync, mkdirSync } = require("fs");
    var pathfs = require("path");
    var { default: axios } = require("axios");
    function Stack(stack) {
      this.onServerLauch = new CustomObservable();
      return (async () => {
        this.watchFiles = stack.watchFiles || [];
        this.logParsers = stack.logParsers || [];
        this.monorepo = stack.monorepo || false;
        this.themes = stack.themes || {};
        this.environments = stack.environments ? stack.environments.map((env) => new EnvironmentModel(env)) : [];
        this.documentation = stack.documentation;
        this.services = await PromiseB2.map(stack.services || [], (service) => new Service(
          service,
          /** @type {StackWithPlugins} */
          Stack
        ));
        this.helpers = require_exportedHelpers();
        return this;
      })();
    }
    Stack.currentStack = null;
    Stack.currentWatches = [];
    Stack.currentEnvironment = null;
    Stack.Socket = sockets;
    Stack.plugins = plugins;
    Stack.version = require_version().version || "";
    Stack.url = `http://localhost:${ports.http}`;
    Stack.port = +ports.http;
    Stack.parsers = {
      links: require_link(),
      jsons: require_json(),
      debug: require_debug()
    };
    Stack.helpers = require_exportedHelpers();
    Stack.getSave = require_saves();
    Stack.prototype.enable = async function(servicesLabelSelected) {
      const services = this.getServices();
      await PromiseB2.map(servicesLabelSelected, (serviceConf) => {
        const service = services.find((service2) => serviceConf.label === service2.label);
        if (!service) return null;
        const hasChanged = serviceConf.enabled !== service.enabled;
        if (hasChanged) {
          if (serviceConf.enabled) {
            service.enable();
            return service.launch();
          }
          service.disable();
          return service.kill();
        }
        return null;
      });
    };
    Stack.restart = async function() {
      await PromiseB2.map(this.getEnabledServices(), (service) => service.restart());
    };
    Stack.kill = async function() {
      await PromiseB2.map(this.getEnabledServices(), (service) => service.kill());
    };
    Stack.getCurrentEnvironment = function() {
      return Stack.currentEnvironment;
    };
    Stack.prototype.toStorage = function() {
      return {
        watchFiles: this.watchFiles,
        themes: this.themes,
        services: this.services.map((s) => s.toStorage())
      };
    };
    Stack.getRootPath = () => {
      const rootPath = pathfs.resolve(args2.rootPath, ".stackmonitor");
      if (!existsSync(rootPath)) mkdirSync(rootPath, { recursive: true });
      return rootPath;
    };
    Stack.parse = async function() {
      const dbsRootPath = await dbs2.getDbs("services");
      let services = [];
      let environments = [];
      try {
        services = await PromiseB2.map(dbsRootPath, (id) => Service.load(id, Stack));
        environments = await EnvironmentModel.all();
      } catch (error) {
        console.error(error);
        this.Socket.emit("system:wrongKey");
      }
      return new Stack({
        environments,
        services
      });
    };
    Stack.prototype.launch = async function() {
      await PromiseB2.map(this.getServices(), (microservice) => {
        if (microservice.enabled) {
          return microservice.launch();
        }
        return microservice.kill();
      });
    };
    Stack.getStack = function() {
      return Stack.currentStack;
    };
    Stack.getServices = function() {
      return Stack.currentStack?.services || [];
    };
    Stack.deleteService = async function(label) {
      if (!Stack.currentStack) return;
      const service = this.findService(label);
      Stack.currentStack.services = Stack.currentStack.services.filter((a) => a.label !== label);
      await service.delete();
    };
    Stack.prototype.getServices = function() {
      return Stack.getServices();
    };
    Stack.getAxios = function() {
      return axios.create({ baseURL: this.url });
    };
    Stack.getEnabledServices = function() {
      return Stack.getServices().filter((s) => s.enabled);
    };
    Stack.prototype.getEnabledServices = function() {
      return Stack.getEnabledServices();
    };
    Stack.prototype.exportInApi = function() {
      const res = { ...this };
      res.services = res.services?.map((s) => s.exportInApi());
      return res;
    };
    Stack.findService = function(serviceLabel) {
      return Stack.getServices().filter((s) => s.label === serviceLabel)[0];
    };
    Stack.prototype.findService = function(serviceLabel) {
      return Stack.findService(serviceLabel);
    };
    Stack.selectConf = async function() {
      await EncryptionKey.init();
      if (!EncryptionKey.encryptionKey) {
        await EncryptionKey.saveKey(await EncryptionKey.generateKey());
      }
      Stack.currentStack = await this.parse();
      Stack.currentEnvironment = args2.e || process.env.STACK_MONITOR_DEFAULT_ENVIRONMENT ? Stack.currentStack.environments.find((env) => env.label === args2.e?.toString() || env.label === process.env.STACK_MONITOR_DEFAULT_ENVIRONMENT) || null : Stack.currentStack.environments.find((env) => env.default) || null;
      if (process.env.STACK_MONITOR_SERVICES) {
        process.env.STACK_MONITOR_SERVICES.split(",").forEach((serviceLabel) => {
          const service = Stack.findService(serviceLabel);
          if (service) service.enable();
        });
      }
      return sockets.emit("stack:selectConf");
    };
    Stack.prototype.changeEnvironment = async function(envLabel) {
      const environment = await EnvironmentModel.find(envLabel);
      if (environment) {
        Stack.currentEnvironment = environment;
        Stack.getServices().forEach((service) => {
          if (!service.envs[envLabel]) {
            service.envs[envLabel] = {};
          }
        });
        const enabledServices = Stack.getEnabledServices();
        await Stack.kill();
        enabledServices.forEach((s) => {
          s.enabled = true;
          s.store = [];
        });
        await Stack.getStack()?.launch();
      } else {
        throw new Error("Environment not found");
      }
    };
    Stack.stopWatchers = function() {
      Stack.currentWatches.forEach((currentWatch) => currentWatch.close());
    };
    var pluginsToLoad = (
      /** @type {(keyof typeof plugins)[]} */
      Object.keys(plugins).reduce(
        (p, key) => {
          const plugin = plugins[key];
          if (plugin.export) {
            p[key] = typeof plugin.export === "function" && !/^\s*class\s+/.test(plugin.export.toString()) ? plugin.export(
              /** @type {StackWithPlugins} */
              Stack
            ) : plugin.export;
          }
          return p;
        },
        /** @type {OmitNever<typeof plugins>} */
        {}
      )
    );
    module2.exports = /** @type {StackWithPlugins} */
    Object.assign(Stack, pluginsToLoad);
  }
});

// helpers/console.table.js
var require_console_table = __commonJS({
  "helpers/console.table.js"(exports2, module2) {
    "use strict";
    var { Transform } = require("stream");
    var { Console } = require("console");
    function table(input) {
      const ts = new Transform({ transform(chunk, enc, cb) {
        cb(null, chunk);
      } });
      const logger = new Console({ stdout: ts });
      logger.table(input);
      const table2 = (ts.read() || "").toString();
      let result = "";
      for (const row of table2.split(/[\r\n]+/)) {
        let r = row.replace(/[^┬]*┬/, "\u250C");
        r = r.replace(/^├─*┼/, "\u251C");
        r = r.replace(/│[^│]*/, "");
        r = r.replace(/^└─*┴/, "\u2514");
        r = r.replace(/'/g, " ");
        result += `${r}
`;
      }
      console.log(result);
    }
    module2.exports = table;
  }
});

// helpers/plugins.js
var require_plugins2 = __commonJS({
  "helpers/plugins.js"(exports2, module2) {
    "use strict";
    var plugins = require_plugins();
    var Stack = require_stack();
    var routes = [];
    var forService = [];
    var allPlugins = {};
    Object.keys(plugins).map((key) => plugins[
      /** @type {keyof (typeof plugins)} */
      key
    ]).forEach((plugin) => {
      if (!plugin) return null;
      if (plugin.placements?.includes("service")) forService.push(plugin);
      if (plugin.routes) {
        routes.push(plugin.routes(Stack));
        delete plugin.routes;
      }
      (plugin.placements || []).forEach((p) => {
        const position = typeof p === "string" ? p : p.position;
        if (!position) return null;
        if (!allPlugins[position]) allPlugins[position] = [];
        if (!allPlugins[position].includes(plugin)) {
          allPlugins[position].push({
            ...plugin,
            placements: (plugin.placements || []).filter((_p) => typeof _p !== "string" && _p.position === position)
          });
        }
        return null;
      });
      return plugin;
    });
    module2.exports = {
      forService,
      ...allPlugins,
      routes
    };
  }
});

// routes/plugins.js
var require_plugins3 = __commonJS({
  "routes/plugins.js"(exports2, module2) {
    "use strict";
    var { express } = require_src6();
    var router = express.Router();
    var PromiseB2 = require("bluebird");
    var { findService } = require_stack();
    var Stack = require_stack();
    var plugins = require_plugins2();
    router.get("/services/:service", async (req, res) => {
      const service = findService(req.params.service);
      const services = await PromiseB2.filter(plugins.forService, async (plugin) => plugin.hidden ? !await plugin.hidden(service, Stack, "service") : true);
      services.sort((a, b) => (b?.order || Number.MAX_SAFE_INTEGER) - (a?.order || Number.MAX_SAFE_INTEGER));
      res.send(services);
    });
    router.get("/services", async (req, res) => {
      const services = await PromiseB2.filter(plugins.forService, async (plugin) => plugin.hidden ? !await plugin.hidden(null, Stack, "global") : true);
      res.send(services);
    });
    router.get("/:type", async (req, res) => {
      const services = await PromiseB2.filter(plugins[req.params.type], async (plugin) => plugin.hidden ? !await plugin.hidden(null, Stack, req.params.type) : true);
      services.sort((a, b) => (a.order || 1e3) - (b.order || 1e3));
      res.send(services);
    });
    module2.exports = router;
  }
});

// routes/system.js
var require_system = __commonJS({
  "routes/system.js"(exports2, module2) {
    "use strict";
    var { express } = require_src6();
    var axios = require("axios");
    var router = express.Router();
    var pidusageTree = require("pidusage-tree");
    var os = require("os");
    var Stack = require_stack();
    var { execAsync } = require_exec();
    var args2 = require_args();
    router.get("/:service/infos", async (req, res) => {
      try {
        const service = Stack.findService(req.params.service);
        if (service.container?.name) {
          const data = await execAsync(`docker container stats --format '{ "cpuPerc": "{{.CPUPerc}}", "memPerc": "{{.MemPerc}}" }'  --no-stream --no-trunc   user_api_monorepo`, {}).then((_data) => {
            const data2 = JSON.parse(_data);
            const cpuPerc = data2.cpuPerc.trim().replaceAll("%", "");
            const memPerc = data2.memPerc.trim().replaceAll("%", "");
            return {
              cpu: Number.isNaN(+cpuPerc) ? 0 : +cpuPerc,
              mem: Number.isNaN(+memPerc) ? 0 : +memPerc
            };
          });
          res.json(data);
        } else {
          const pid = service.container?.customPid ? await service.container.customPid({ cmd: null, args: null, pid: null }) : service.pids[0]?.pid;
          res.json(pid ? await getCPU(pid) : {
            cpu: null,
            ram: null
          });
        }
      } catch (e) {
        res.json({ cpu: null, mem: null });
      }
    });
    router.get("/disconnect", async () => {
      process.exit(0);
    });
    router.get("/restart", async (req, res) => {
      res.json({ success: true, message: "Restart in progress..." });
      setTimeout(() => {
        console.log("Restarting application...");
        require("child_process").spawn(process.argv[0], process.argv.slice(1), {
          cwd: args2.initialCwd || process.cwd(),
          detached: true,
          stdio: "inherit"
        }).unref();
        process.exit(0);
      }, 500);
    });
    router.get("/proxy-img", async (req, res) => {
      axios({
        method: "get",
        url: req.query.url,
        responseType: "stream"
      }).then((response) => {
        for (const key in response.headers) {
          if (response.headers.hasOwnProperty(key)) {
            const element = response.headers[key];
            res.header(key, element);
          }
        }
        res.status(response.status);
        response.data.pipe(res);
      }).catch(({ response }) => {
        for (const key in response.headers) {
          if (response.headers.hasOwnProperty(key)) {
            const element = response.headers[key];
            res.header(key, element);
          }
        }
        res.status(response.status);
        response.data.pipe(res);
      });
    });
    async function getCPU(pid) {
      const tree = await pidusageTree(pid).catch(() => null);
      const cpus = [];
      let mem = 0;
      let cpuPerc = 0;
      let totalMem = 0;
      if (tree) {
        Object.keys(tree).forEach((key) => {
          if (tree[key]?.cpu) cpus.push(tree[key].cpu);
          if (tree[key]?.memory) mem += tree[key].memory;
        });
        cpuPerc = cpus.reduce((prev, curr) => prev + curr, 0) / cpus.length;
        totalMem = os.totalmem();
      }
      return {
        cpu: Number.isNaN(cpuPerc) ? 0 : cpuPerc,
        mem: mem / totalMem
      };
    }
    module2.exports = router;
  }
});

// models/myConfs.js
var require_myConfs = __commonJS({
  "models/myConfs.js"(exports2, module2) {
    "use strict";
    var pathfs = require("path");
    var os = require("os");
    var {
      existsSync,
      mkdirSync,
      writeFileSync,
      readFileSync
    } = require("fs");
    var persistencePath = pathfs.resolve(os.homedir(), ".stack-monitor");
    if (!existsSync(persistencePath)) mkdirSync(persistencePath, { recursive: true });
    var confsPath = pathfs.resolve(persistencePath, "confs");
    if (!existsSync(confsPath)) writeFileSync(confsPath, JSON.stringify([]), "utf-8");
    var store = JSON.parse(readFileSync(confsPath, "utf-8"));
    module2.exports = {
      /** @type {string[]} */
      confs: store,
      /** @param {string} conf */
      async add(conf) {
        if (!store.includes(conf)) {
          store.push(conf);
          await writeFileSync(confsPath, JSON.stringify(store), "utf-8");
        }
      },
      /** @param {string} conf */
      async remove(conf) {
        store.splice(store.indexOf(conf), 1);
        await writeFileSync(confsPath, JSON.stringify(store), "utf-8");
      }
    };
  }
});

// routes/stack.js
var require_stack2 = __commonJS({
  "routes/stack.js"(exports2, module2) {
    "use strict";
    var { express } = require_src6();
    var { exec } = require("child_process");
    var open = require("open");
    var commandExists = require("command-exists").sync;
    var { Octokit } = require("fix-esm").require("@octokit/core");
    var { restEndpointMethods } = require("fix-esm").require("@octokit/plugin-rest-endpoint-methods");
    var { sockets } = require_src();
    var Stack = require_stack();
    var myConfs = require_myConfs();
    var router = express.Router();
    var { findService } = Stack;
    var Service = require_Service();
    var Environment = require_Environment();
    var { replaceEnvs } = require_stringTransformer_helper();
    var MyOctokit = Octokit.plugin(restEndpointMethods);
    router.post("/services", async (req, res) => {
      const existingService = Stack.findService(req.body.label);
      if (existingService) {
        await existingService.constructor(req.body, Stack, { isUpdate: true });
        await existingService.save();
        sockets.emit("conf:update", [existingService.label]);
        res.send(existingService);
      } else {
        const service = await new Service(req.body, Stack);
        await service.save();
        Stack.getStack()?.services.push(service);
        sockets.emit("conf:update", [service.label]);
        res.send(service);
      }
    });
    router.put("/:service/duplicate", async (req, res) => {
      const existingService = findService(req.params.service).toStorage();
      const service = await new Service(existingService, Stack);
      service.label = req.body.label;
      await service.save();
      Stack.getStack()?.services.push(service);
      sockets.emit("conf:update");
      res.send(service);
    });
    router.patch("/:service", async (req, res) => {
      const service = findService(req.params.service);
      await service.save();
      Stack.getStack()?.services.push(service);
      sockets.emit("conf:update");
      res.send(service);
    });
    router.get("/export-env", async (req, res) => {
      const service = findService(req.query.service?.toString() || "");
      if (!service) return res.status(404).send("Service not found");
      const command = service.commands?.[+(req.query.commandIndex?.toString() || "0")];
      if (!command) return res.status(404).send("Command not found");
      const envs = await service.buildEnvs(req.query.environment);
      res.send(envs);
    });
    router.delete("/:service", async (req, res) => {
      await Stack.deleteService(req.params.service);
      sockets.emit("conf:update");
      res.send("ok");
    });
    router.get("/has-update", async (req, res) => {
      try {
        const localVersion = `v${require_version().version}`;
        const octokit = new MyOctokit({ auth: process.env.STACK_MONITOR_GH_APIKEY });
        const { data: tags } = await octokit.rest.repos.listTags({ owner: "clabroche", repo: "stack-monitor" });
        const remoteVersion = tags[0]?.name;
        return res.json({
          local: localVersion,
          remote: remoteVersion,
          hasUpdate: localVersion !== remoteVersion
        });
      } catch (error) {
        console.error(error);
        return res.json(null);
      }
    });
    router.get("/configuration", (req, res) => {
      res.json(Stack.getStack()?.exportInApi());
    });
    router.get("/environment", (req, res) => {
      res.json(Stack.getCurrentEnvironment());
    });
    router.patch("/environments/:environmentLabel", async (req, res) => {
      const env = await Stack.getStack()?.environments.find((env2) => env2.label === req.params.environmentLabel);
      if (!env) return res.status(404).send("Environment not found");
      await env.update(req.body);
      await sockets.emit("reloadEnvironments");
      res.json(Stack.getCurrentEnvironment());
    });
    router.delete("/environments/:environmentLabel", async (req, res) => {
      const env = await Stack.getStack()?.environments.find((env2) => env2.label === req.params.environmentLabel);
      if (!env) return res.status(404).send("Environment not found");
      await env.delete();
      await sockets.emit("reloadEnvironments");
      res.json("ok");
    });
    router.get("/additional-themes", (req, res) => {
      const additionalThemes = Stack.getStack()?.themes || {};
      res.json(additionalThemes);
    });
    router.post("/environment", async (req, res) => {
      const { environment } = req.body;
      if (!environment) return res.status(400).send("Provide an environment field in body");
      await Stack.getStack()?.changeEnvironment(environment);
      return res.json(Stack.getCurrentEnvironment());
    });
    router.post("/environment/create", async (req, res) => {
      const { environment } = req.body;
      if (!environment) return res.status(400).send("Provide an environment field in body");
      const newEnvironment = new Environment(environment);
      await newEnvironment.save();
      await sockets.emit("reloadEnvironments");
      return res.json(Stack.getCurrentEnvironment());
    });
    router.get("/environments", async (req, res) => {
      res.json(await Environment.all());
    });
    router.get("/all-confs-path", (req, res) => {
      res.json(myConfs.confs);
    });
    router.post("/select-conf", async (req, res) => {
      const { path } = req.body;
      await Stack.selectConf();
      res.json(path);
    });
    router.post("/delete-conf", async (req, res) => {
      const { path } = req.body;
      await myConfs.remove(path);
      res.json(path);
    });
    router.post("/choose", async (req, res) => {
      const servicesLabelSelected = req.body;
      if (!Array.isArray(servicesLabelSelected)) return res.status(400).send("you should provide an array as body with all service label you want to launch");
      const stack = Stack.getStack();
      if (!stack) return res.status(500).send("Stack not configured");
      await stack.enable(servicesLabelSelected);
      return res.json(stack.getEnabledServices().map((s) => s.exportInApi()));
    });
    router.get("/", (req, res) => {
      const stack = Stack.getStack();
      res.json(stack ? stack.exportInApi() : null);
    });
    router.get("/services", (req, res) => {
      res.json(Stack.getServices().map((s) => s.exportInApi()));
    });
    router.get("/restart-all", async (req, res) => {
      try {
        await Stack.restart();
        res.send({ success: true });
      } catch (error) {
        console.error("Error during restart all:", error);
        const errorMessage = error instanceof Error ? error.message : String(error);
        res.status(500).send({ success: false, error: errorMessage });
      }
    });
    router.get("/:service", (req, res) => {
      const service = findService(req.params.service);
      res.send(service.exportInApi());
    });
    router.get("/:service/open-folder", (req, res) => {
      open(replaceEnvs(req.query.path?.toString() || "."));
      res.send();
    });
    router.get("/:service/restart", async (req, res) => {
      const service = findService(req.params.service);
      await service.restart();
      res.send();
    });
    router.get("/:service/start", async (req, res) => {
      const service = findService(req.params.service);
      await service.launch();
      res.send();
    });
    router.get("/:service/stop", async (req, res) => {
      const service = findService(req.params.service);
      await service.kill();
      res.send();
    });
    module2.exports = router;
  }
});

// routes/fs.js
var require_fs = __commonJS({
  "routes/fs.js"(exports2, module2) {
    "use strict";
    var { express } = require_src6();
    var router = express.Router();
    var pathfs = require("path");
    var PromiseB2 = require("bluebird");
    var os = require("os");
    var { sort } = require("fast-sort");
    var { readdir, readFile, stat } = require("fs/promises");
    router.get("/home-dir", async (req, res) => {
      res.send(os.homedir());
    });
    router.get("/ls", async (req, res) => {
      const path = req.query.path?.toString() || __dirname;
      const dir = await readdir(path);
      const parentDirectory = {
        absolutePath: pathfs.resolve(path, ".."),
        name: "..",
        isDirectory: true
      };
      let dirs = [parentDirectory];
      await PromiseB2.map(dir, async (entry) => {
        try {
          if (entry.charAt(0) === ".") return null;
          const absolutePath = pathfs.resolve(path, entry);
          const entryStat = await stat(absolutePath);
          const entryInfos = {
            absolutePath,
            name: entry,
            isDirectory: entryStat.isDirectory(),
            npmInfos: null,
            isStack: false
          };
          if (entryInfos.isDirectory) {
            entryInfos.npmInfos = await getNpmInfos(entryInfos.absolutePath);
          } else if (pathfs.extname(absolutePath) === ".js") {
            try {
              const stack = require(absolutePath);
              if (Array.isArray(stack) && stack.length && stack[0].label && stack[0].spawnCmd) {
                entryInfos.isStack = true;
              }
            } catch (error) {
              console.error(error);
            }
          }
          dirs.push(entryInfos);
          return null;
        } catch (error) {
          console.error(error);
          return null;
        }
      });
      dirs = sort(dirs).asc((d) => d.name.toUpperCase());
      res.json(dirs);
    });
    async function getNpmInfos(path) {
      const dir = await readdir(path);
      if (dir.includes("package.json")) {
        const packageJSON = JSON.parse(await readFile(pathfs.resolve(path, "package.json"), "utf-8"));
        return {
          path,
          packageJSON,
          version: packageJSON.version,
          name: packageJSON.name,
          author: packageJSON.author
        };
      }
      return null;
    }
    module2.exports = router;
  }
});

// routes/crypto.js
var require_crypto2 = __commonJS({
  "routes/crypto.js"(exports2, module2) {
    "use strict";
    var { express } = require_src6();
    var config = require_EncryptionKey();
    var dbs2 = require_dbs();
    var { encrypt } = require_crypto();
    var fs = require("fs").promises;
    var path = require("path");
    var HTTPError = require_src2();
    var { sockets } = require_src();
    var conflictStorage = require_conflictStorage();
    var router = express.Router();
    router.get("/generate-key", async (req, res) => {
      res.send(await config.generateKey());
    });
    router.post("/encryption-key", async (req, res) => {
      const { key } = req.body;
      await config.saveKey(key);
      return res.send(key);
    });
    router.get("/should-setup", async (req, res) => {
      if (!config.encryptionKey) return res.json(true);
      const envSample = (await dbs2.getDbs("envs"))[0];
      try {
        if (envSample) await dbs2.getDb(`envs/${envSample}`).read();
        return res.json(false);
      } catch (error) {
        console.error(error);
        return res.json(true);
      }
    });
    router.get("/encryption-key", async (req, res) => {
      res.send(config.encryptionKey);
    });
    router.post("/test-encryption-key", async (req, res) => {
      const result = await config.testKey(req.body?.key || config.encryptionKey);
      if (result) return res.send("ok");
      return res.status(400).send(config.encryptionKey);
    });
    router.get("/pending-conflicts", async (req, res) => {
      res.json(conflictStorage.getPendingConflicts());
    });
    router.post("/resolve-conflict", async (req, res) => {
      try {
        const { original, resolution, conflictId } = req.body;
        if (!original || !resolution) {
          throw new HTTPError("Missing original or resolution data", 400);
        }
        const headerMatch = original.match(/<<<<<<< HEAD\r?\n([\s\S]*?)\r?\n=======\r?\n([\s\S]*?)\r?\n>>>>>>>\s?(.*)/);
        if (!headerMatch) {
          throw new HTTPError("Invalid conflict format", 400);
        }
        const branchIdentifier = headerMatch[3];
        const conflict = conflictStorage.getPendingConflicts().find((c) => c.id === conflictId);
        const filePath = conflict?.filePath;
        if (!filePath) {
          console.warn(`No file path found for conflict ID: ${conflictId}`);
        }
        const encryptedResolution = await encrypt(resolution, { additionnalNonce: branchIdentifier });
        let fileWritten = false;
        if (filePath) {
          try {
            const fileExists = await fs.access(filePath).then(() => true).catch(() => false);
            if (fileExists) {
              await fs.writeFile(filePath, encryptedResolution, "utf8");
              console.log(`Conflict resolved and saved to file: ${filePath}`);
              fileWritten = true;
            } else {
              console.error(`Could not resolve conflict: file does not exist at path: ${filePath}`);
            }
          } catch (writeError) {
            const errorMessage = writeError instanceof Error ? writeError.message : String(writeError);
            console.error("Error writing resolved file:", writeError);
            throw new HTTPError(`Failed to write to file: ${errorMessage}`, 500);
          }
        }
        if (conflictId) {
          conflictStorage.removeConflict(conflictId);
        }
        sockets.emit("crypto:conflict-resolved", { conflictId });
        return res.json({
          success: true,
          encryptedResolution,
          filePath,
          fileWritten
        });
      } catch (error) {
        console.error("Error resolving conflict:", error);
        let statusCode = 500;
        let message = "Failed to resolve conflict";
        if (error instanceof Error) {
          message = error.message;
          if (error instanceof HTTPError && typeof error.code === "number") {
            statusCode = error.code;
          }
        }
        return res.status(statusCode).json({
          success: false,
          message
        });
      }
    });
    module2.exports = router;
  }
});

// routes/parsers.js
var require_parsers = __commonJS({
  "routes/parsers.js"(exports2, module2) {
    "use strict";
    var { express } = require_src6();
    var Parser2 = require_Parser();
    var router = express.Router();
    router.post("/", async (req, res) => {
      delete req.body.id;
      const parser2 = new Parser2(req.body);
      await parser2.save();
      res.json(parser2.toStorage());
    });
    router.put("/:id", async (req, res) => {
      const parser2 = await Parser2.find(req.params.id);
      if (!parser2) return res.status(404).send("Parser not found");
      await parser2.update(req.body);
      res.json(parser2.toStorage());
    });
    router.delete("/:id", async (req, res) => {
      const parser2 = await Parser2.find(req.params.id);
      if (!parser2) return res.status(404).send("Parser not found");
      await parser2?.delete();
      res.json("ok");
    });
    router.get("/", async (req, res) => {
      const parsers = await Parser2.all();
      res.json(parsers);
    });
    module2.exports = router;
  }
});

// models/editors.js
var require_editors = __commonJS({
  "models/editors.js"(exports2, module2) {
    "use strict";
    var commandExists = require("command-exists").sync;
    var codeArgsBuilder = ({ link, command, cwd, line, column }) => `${command} ${link ? `--goto ${link}${line ? `:${line}${column ? `:${column}` : ""}` : ""}` : ""} ${cwd}`;
    var intellijArgsBuilder = ({ link, command, cwd, line, column }) => {
      return `${command} ${cwd}  ${column ? `--column ${column}` : ""} ${line ? `--line ${line}` : ""}  ${link}`;
    };
    var editors = {
      code: {
        commands: ["code", "code-insiders"],
        args: codeArgsBuilder
      },
      cursor: {
        commands: ["cursor"],
        args: codeArgsBuilder
      },
      intellij: {
        commands: ["idea"],
        args: intellijArgsBuilder
      },
      androidstudio: {
        commands: ["android-studio"],
        args: intellijArgsBuilder
      },
      sublime: {
        commands: ["subl"],
        args: ({ link, command, cwd, line, column }) => link ? `${command} ${link ? `${link}${line ? `:${line}${column ? `:${column}` : ""}` : ""}` : ""}` : `${command} ${cwd}`
      }
    };
    module2.exports.allEditors = Object.keys(editors);
    module2.exports.availableEditors = module2.exports.allEditors.filter((key) => editors[key].commands.find((command) => commandExists(command)));
    module2.exports.getCommandLine = (editorLabel, { cwd = ".", link = "", line = 0, column = 0 } = {}) => {
      const editor = editors[editorLabel];
      if (!editor) return null;
      const command = editor.commands.find((command2) => commandExists(command2));
      if (command) {
        return editor.args({ cwd, command, line, column, link });
      }
      return;
    };
  }
});

// routes/editors.js
var require_editors2 = __commonJS({
  "routes/editors.js"(exports2, module2) {
    "use strict";
    var { express } = require_src6();
    var { availableEditors, getCommandLine } = require_editors();
    var { replaceEnvs } = require_stringTransformer_helper();
    var Stack = require_stack();
    var { exec } = require("child_process");
    var { findService } = Stack;
    var router = express.Router();
    router.get("/available-editors", (req, res) => {
      res.send(availableEditors);
    });
    router.get("/:service/open-in-vs-code", (req, res) => {
      const service = findService(req.params.service);
      const commandLine = getCommandLine(req.query.editor);
      if (!commandLine) return res.status(400).send("Editor not found");
      exec(commandLine, { cwd: replaceEnvs(service.rootPath) || replaceEnvs(req.query.path?.toString() || "."), env: process.env });
      res.send("ok");
    });
    router.get("/:service/open-link-in-vs-code", (req, res) => {
      const service = findService(req.params.service);
      const commandLine = getCommandLine(req.query.editor, { link: req.query.link?.toString() || "" });
      if (!commandLine) return res.status(400).send("Editor not found");
      const cwd = replaceEnvs(service.rootPath) || replaceEnvs(req.query.path?.toString() || ".");
      exec(commandLine, { cwd, env: process.env });
      res.send({ commandLine, cwd });
    });
    module2.exports = router;
  }
});

// routes/index.js
var require_routes24 = __commonJS({
  "routes/index.js"(exports2, module2) {
    "use strict";
    var { express } = require_src6();
    var router = express.Router();
    var plugins = require_plugins2();
    plugins.routes.forEach((route) => router.use(route));
    router.use("/plugins", require_plugins3());
    router.use("/system", require_system());
    router.use("/stack", require_stack2());
    router.use("/fs", require_fs());
    router.use("/crypto", require_crypto2());
    router.use("/parsers", require_parsers());
    router.use("/editors", require_editors2());
    router.get("/version", async (req, res) => {
      res.send(require_version().version);
    });
    module2.exports = router;
  }
});

// helpers/cpu.js
var require_cpu = __commonJS({
  "helpers/cpu.js"(exports2, module2) {
    "use strict";
    var os = require("os");
    var { fork } = require("child_process");
    var path = require("path");
    var { sockets } = require_src();
    var nbCpus = os.cpus().length;
    var controller = null;
    var child = null;
    controller = new AbortController();
    var { signal } = controller;
    child = fork(path.resolve(__dirname, "./cpuFork.js"), [], { signal });
    child.on("message", (message) => {
      const { ram, cpu } = JSON.parse(message.toString());
      sockets.emit("infos:global", {
        nbCpus,
        memPercentage: ram.memPercentage,
        totalmem: ram.totalmem,
        freemem: ram.freemem,
        cpu
      });
    });
    module2.exports = {
      stopCpu: async () => {
        child?.kill("SIGKILL");
      }
    };
  }
});

// app.js
var require_app = __commonJS({
  "app.js"(exports2, module2) {
    "use strict";
    require("express-async-errors");
    var { express } = require_src6();
    var indexRouter = require_routes24();
    var { stopCpu } = require_cpu();
    var { stopWatchers } = require_stack();
    var app = express();
    app.use("/", indexRouter);
    app.stopWorkers = async () => {
      await stopCpu();
      stopWatchers();
    };
    module2.exports = app;
  }
});

// bin/server.js
var require_server = __commonJS({
  "bin/server.js"(exports2, module2) {
    "use strict";
    var { launch } = require_src6();
    var pathfs = require("path");
    var ports = require_ports();
    var table = require_console_table();
    var args2 = require_args();
    module2.exports = {
      async launch() {
        await launch({
          port: process.env.STACK_MONITOR_HTTP_PORT || 0,
          controllers: () => require_app(),
          socket: true,
          apiPrefix: "/",
          bodyLimit: "100mb",
          noGreetings: true,
          staticController: process.env.NODE_ENV !== "HFBXdZMJxLyJoua28asEaxRixJ6LriR7FnRzX6pwA7pFjZ" ? pathfs.resolve(__dirname, "public") : void 0,
          helmetConf: process.env.NODE_ENV !== "HFBXdZMJxLyJoua28asEaxRixJ6LriR7FnRzX6pwA7pFjZ" ? {
            crossOriginEmbedderPolicy: false,
            crossOriginResourcePolicy: false,
            contentSecurityPolicy: {
              directives: {
                upgradeInsecureRequests: null,
                "frame-src": ["'self'", "clabroche.github.io", "jsoncrack.com"]
              },
              useDefaults: true
            }
          } : null,
          onListening({ server }) {
            const addr = server.address();
            const port = typeof addr === "string" ? addr : addr?.port;
            ports.setHttpPort(+(port || 0));
            if (process.env.NODE_ENV !== "HFBXdZMJxLyJoua28asEaxRixJ6LriR7FnRzX6pwA7pFjZ" && !process.versions.electron) {
              require("open")(`http://localhost:${port}`);
            }
            (() => {
              table([
                { "": "Version", Value: require_version().version, "Overrided By": "-" },
                { "": "Port", Value: ports.http, "Overrided By": "STACK_MONITOR_HTTP_PORT" },
                { "": "Url", Value: `http://localhost:${ports.http}`, "Overrided By": "-" },
                ...args2.ss.length ? [{ "": "Services", Value: args2.ss.join(", "), "Overrided By": "-" }] : []
              ]);
            })();
          }
        });
        await require_stack().selectConf();
      }
    };
  }
});

// bin/www
process.title = "stack-monitor";
var args = require_args();
require("dotenv").config();
(async () => {
  if (args["pull-env"]) {
    await require_stack().selectConf();
    const service = require_stack().findService(args.s);
    if (!service) {
      console.error("Service", args.s, "not found");
      process.exit(1);
    }
    const envs = await service.buildEnvs(args["e"]);
    console.log(Object.keys(envs).reduce((agg, key) => agg += `
${key}=${envs[key]}`, "").trim());
    process.exit(0);
  } else {
    console.log("Root path:", args.rootPath);
    require_server().launch().catch((err) => {
      console.error(err);
      return err;
    });
  }
})();
//# sourceMappingURL=www.js.map