diff --git a/apps/rebuild/rebuild.sh b/apps/rebuild/rebuild.sh index d04ed21..0b2ad24 100755 --- a/apps/rebuild/rebuild.sh +++ b/apps/rebuild/rebuild.sh @@ -60,7 +60,7 @@ Rebuild_nixos() { [[ -n "$TARGET_HOST" || -n "$BUILD_HOST" ]] && CMD+=("--ask-sudo-password") # Build config first so we can diff it - local BUILD_CMD=("nixos-rebuild" "build" "--flake" "$FLAKE") + local BUILD_CMD=("nixos-rebuild" "build" "--no-build-output" "--flake" "$FLAKE") [ "$SHOW_TRACE" = 1 ] && BUILD_CMD+=("--show-trace") [ -n "$BUILD_HOST" ] && BUILD_CMD+=("--build-host" "$BUILD_HOST") @@ -101,7 +101,7 @@ Rebuild_home() { # Build config first so we can diff it if [ "$ROLLBACK" = 0 ]; then - local BUILD_CMD=("home-manager" "build" "--flake" "$FLAKE") + local BUILD_CMD=("home-manager" "build" "--no-out-link" "--flake" "$FLAKE") [ "$SHOW_TRACE" = 1 ] && BUILD_CMD+=("--show-trace") _status "Building Home Manager configuration '$FLAKE'..." _status "Executing command: ${BUILD_CMD[*]}" diff --git a/modules/home/hyprland/applications/bitwarden/default.nix b/modules/home/hyprland/applications/bitwarden/default.nix new file mode 100644 index 0000000..bae501f --- /dev/null +++ b/modules/home/hyprland/applications/bitwarden/default.nix @@ -0,0 +1,46 @@ +{ + inputs, + config, + lib, + pkgs, + ... +}: + +let + cfg = config.wayland.windowManager.hyprland; + app = cfg.applications.password-manager.default; + + inherit (lib) mkDefault mkIf; +in +{ + imports = [ ../../../rofi-rbw ]; + + config = mkIf (cfg.enable && app == "rofi-rbw") { + programs.rbw = { + enable = true; + settings = { + # email = "you@example.tld"; # You have to set this in your config + pinentry = mkDefault pkgs.pinentry-gnome3; + lock_timeout = mkDefault 3600; + }; + }; + + programs.rofi-rbw = { + enable = true; + package = mkDefault pkgs.rofi-rbw-wayland; + settings = { + selector = mkDefault "bemenu"; + selector-args = mkDefault "-i -l 20"; + action = mkDefault "copy"; + typing-key-delay = mkDefault 0; + }; + }; + + programs.librewolf = mkIf config.programs.librewolf.enable { + profiles.default.extensions.packages = + with inputs.nur.legacyPackages."${pkgs.stdenv.hostPlatform.system}".repos.rycee.firefox-addons; [ + bitwarden + ]; + }; + }; +} diff --git a/modules/home/hyprland/applications/default.nix b/modules/home/hyprland/applications/default.nix index 8b434be..661d47e 100644 --- a/modules/home/hyprland/applications/default.nix +++ b/modules/home/hyprland/applications/default.nix @@ -56,6 +56,7 @@ let in { imports = [ + ./bitwarden ./bemenu ./dmenu-bluetooth ./dunst-toggle diff --git a/modules/nixos/librechat-oci/default.nix b/modules/nixos/librechat-oci/default.nix index afbdd2b..d35b0ca 100644 --- a/modules/nixos/librechat-oci/default.nix +++ b/modules/nixos/librechat-oci/default.nix @@ -8,67 +8,230 @@ let cfg = config.services.librechat-oci; - image = pkgs.dockerTools.pullImage { - imageName = "ghcr.io/danny-avila/librechat"; - imageDigest = "sha256:a46254938507971e0d4f7ed3f9d116bd9b118f4810b5b75eb716baf575645068"; - hash = "sha256-zevUN6vrs3hymwCGFmk/YXlUzYjN37H+EO5aLxYchyc="; - finalImageName = "ghcr.io/danny-avila/librechat"; - finalImageTag = "v0.8.5"; + defaultImages = { + librechat = pkgs.dockerTools.pullImage { + imageName = "ghcr.io/danny-avila/librechat"; + imageDigest = "sha256:a46254938507971e0d4f7ed3f9d116bd9b118f4810b5b75eb716baf575645068"; + hash = "sha256-zevUN6vrs3hymwCGFmk/YXlUzYjN37H+EO5aLxYchyc="; + finalImageName = "ghcr.io/danny-avila/librechat"; + finalImageTag = "v0.8.5"; + }; + + meilisearch = pkgs.dockerTools.pullImage { + imageName = "getmeili/meilisearch"; + imageDigest = "sha256:b839a48d008d4e67e57f78dcff5b21d5e8b8fa066bacd11f97824d6307abb0dd"; + hash = "sha256-+hypvjFDSdnnWAO4tARTnjNd/6HlF6pMg1L6UUffdYM="; + finalImageName = "getmeili/meilisearch"; + finalImageTag = "v1.44.0"; + }; + + mongodb = pkgs.dockerTools.pullImage { + imageName = "mongo"; + imageDigest = "sha256:098862b1339f031900ca66cf8fef799e616d6324fa41b9a263f2ec899552c1ef"; + hash = "sha256-XuWnvcqAAkAGshdQtnngKOOJP2Bd33FXTbGHTRX3nUc="; + finalImageName = "mongo"; + finalImageTag = "8.0.20"; + }; + + vectordb = pkgs.dockerTools.pullImage { + imageName = "pgvector/pgvector"; + imageDigest = "sha256:8809cfffff0082cf260c9ac752f1dd1afc77f6f0a55c4e6411321e78efc3d9a5"; + hash = "sha256-rc6gQLMzv8UOZVLmWKGUESNIo+iPf5DR7T79AmbzWc4="; + finalImageName = "pgvector/pgvector"; + finalImageTag = "0.8.0-pg15-trixie"; + }; + + ragApi = pkgs.dockerTools.pullImage { + imageName = "registry.librechat.ai/danny-avila/librechat-rag-api-dev-lite"; + imageDigest = "sha256:6dfb6832661ff9c26fa329c823ce266059e33567670a763e9ecb9b566b8daa68"; + hash = "sha256-k8pkEgbqT4NU2+2ZjdRFlfFvMUk/1p+pkysbELh95pM="; + finalImageName = "registry.librechat.ai/danny-avila/librechat-rag-api-dev-lite"; + finalImageTag = "latest"; + }; }; defaultEnv = { + # Server HOST = "0.0.0.0"; - PORT = "3080"; + PORT = toString cfg.port; NO_INDEX = "true"; - DEBUG_LOGGING = "false"; + TRUST_PROXY = "1"; + + # Logging CONSOLE_JSON = "false"; - ALLOW_REGISTRATION = "true"; - ALLOW_EMAIL_LOGIN = "true"; + DEBUG_LOGGING = "true"; + DEBUG_CONSOLE = "false"; + AGENT_DEBUG_LOGGING = "false"; + DEBUG_OPENAI = "false"; + + # Node + NODE_MAX_OLD_SPACE_SIZE = "6144"; + + # Search SEARCH = "true"; MEILI_NO_ANALYTICS = "true"; + + # Moderation + OPENAI_MODERATION = "false"; + BAN_VIOLATIONS = "true"; + BAN_DURATION = "1000 * 60 * 60 * 2"; + BAN_INTERVAL = "20"; + LOGIN_VIOLATION_SCORE = "1"; + REGISTRATION_VIOLATION_SCORE = "1"; + CONCURRENT_VIOLATION_SCORE = "1"; + MESSAGE_VIOLATION_SCORE = "1"; + NON_BROWSER_VIOLATION_SCORE = "20"; + TTS_VIOLATION_SCORE = "0"; + STT_VIOLATION_SCORE = "0"; + FORK_VIOLATION_SCORE = "0"; + IMPORT_VIOLATION_SCORE = "0"; + FILE_UPLOAD_VIOLATION_SCORE = "0"; + LOGIN_MAX = "7"; + LOGIN_WINDOW = "5"; + REGISTER_MAX = "5"; + REGISTER_WINDOW = "60"; + LIMIT_CONCURRENT_MESSAGES = "true"; + CONCURRENT_MESSAGE_MAX = "2"; + LIMIT_MESSAGE_IP = "true"; + MESSAGE_IP_MAX = "40"; + MESSAGE_IP_WINDOW = "1"; + LIMIT_MESSAGE_USER = "false"; + MESSAGE_USER_MAX = "40"; + MESSAGE_USER_WINDOW = "1"; + ILLEGAL_MODEL_REQ_SCORE = "5"; + + # Registration and login + ALLOW_EMAIL_LOGIN = "true"; + ALLOW_REGISTRATION = "false"; + ALLOW_SOCIAL_LOGIN = "false"; + ALLOW_SOCIAL_REGISTRATION = "false"; + ALLOW_PASSWORD_RESET = "false"; + ALLOW_UNVERIFIED_EMAIL_LOGIN = "true"; + SESSION_EXPIRY = "1000 * 60 * 15"; + REFRESH_TOKEN_EXPIRY = "(1000 * 60 * 60 * 24) * 7"; + + # OpenID + OPENID_SCOPE = "openid profile email"; + OPENID_CALLBACK_URL = "/oauth/openid/callback"; + OPENID_AUTO_REDIRECT = "false"; + OPENID_USE_PKCE = "false"; + OPENID_ON_BEHALF_FLOW_USERINFO_SCOPE = "user.read"; + + # OAuth callback URLs + DISCORD_CALLBACK_URL = "/oauth/discord/callback"; + FACEBOOK_CALLBACK_URL = "/oauth/facebook/callback"; + GITHUB_CALLBACK_URL = "/oauth/github/callback"; + GOOGLE_CALLBACK_URL = "/oauth/google/callback"; + APPLE_CALLBACK_URL = "/oauth/apple/callback"; + SAML_CALLBACK_URL = "/oauth/saml/callback"; + + # Entra ID + USE_ENTRA_ID_FOR_PEOPLE_SEARCH = "false"; + ENTRA_ID_INCLUDE_OWNERS_AS_MEMBERS = "false"; + OPENID_GRAPH_SCOPES = "User.Read,People.Read,GroupMember.Read.All"; + + # Shared links + ALLOW_SHARED_LINKS = "true"; + ALLOW_SHARED_LINKS_PUBLIC = "false"; + + # UI + APP_TITLE = "LibreChat"; + HELP_AND_FAQ_URL = "https://librechat.ai"; + + # Flux + FLUX_API_BASE_URL = "https://api.us1.bfl.ai"; + + # Email + EMAIL_PORT = "25"; + EMAIL_FROM = "noreply@librechat.ai"; + + # Azure Blob Storage + AZURE_STORAGE_PUBLIC_ACCESS = "false"; + AZURE_CONTAINER_NAME = "files"; }; + mkImageOption = + name: description: + mkOption { + type = types.package; + default = defaultImages.${name}; + description = description; + }; + inherit (lib) literalExpression mkEnableOption mkIf mkOption mkOverride + optional types ; in { options.services.librechat-oci = { enable = mkEnableOption "LibreChat container with Podman."; - image = mkOption { - type = types.package; - default = image; - description = "The Docker image to use (`pkgs.dockerTools.pullImage`)."; + + images = { + librechat = mkImageOption "librechat" "The LibreChat Docker image (`pkgs.dockerTools.pullImage`)."; + meilisearch = mkImageOption "meilisearch" "The Meilisearch Docker image (`pkgs.dockerTools.pullImage`)."; + mongodb = mkImageOption "mongodb" "The MongoDB Docker image (`pkgs.dockerTools.pullImage`)."; + vectordb = mkImageOption "vectordb" "The pgvector Docker image (`pkgs.dockerTools.pullImage`)."; + ragApi = mkImageOption "ragApi" "The LibreChat RAG API Docker image (`pkgs.dockerTools.pullImage`)."; }; + + configFile = mkOption { + type = types.nullOr types.path; + default = null; + example = literalExpression "./librechat.yaml"; + description = "Path to the `librechat.yaml` configuration file."; + }; + externalUrl = mkOption { type = types.nullOr types.str; default = null; - example = literalExpression "http://${config.networking.domain}"; - description = "Public URL to configure for LibreChat."; + example = literalExpression ''"https://chat.example.com"''; + description = "Public URL to configure for LibreChat (sets DOMAIN_CLIENT and DOMAIN_SERVER)."; }; + port = mkOption { type = types.port; default = 3080; - description = "Which port the LibreChat server listens to."; + description = "Which port the LibreChat server listens on."; }; + + meiliPort = mkOption { + type = types.port; + default = 7700; + description = "Which port Meilisearch listens on."; + }; + + ragPort = mkOption { + type = types.port; + default = 8000; + description = "Which port the RAG API listens on."; + }; + environment = mkOption { default = { }; type = types.attrsOf types.str; description = '' Extra environment variables for LibreChat. - For more details see + These are merged on top of the defaults and can override them. + For secrets use instead. + See . ''; }; + environmentFile = mkOption { - description = "Environment file to be passed to the LibreChat container."; type = types.nullOr types.path; default = null; - example = "config.sops.templates.librechat-env.path"; + example = literalExpression "config.sops.templates.librechat-env.path"; + description = '' + Environment file passed to the LibreChat, Meilisearch, and RAG API + containers. Use this for secrets such as JWT_SECRET, CREDS_KEY, + MEILI_MASTER_KEY, and API keys. + ''; }; }; @@ -89,158 +252,275 @@ in virtualisation.oci-containers.backend = "podman"; - virtualisation.oci-containers.containers."librechat-mongodb" = { - image = "mongo:7.0"; - environment = { - MONGO_INITDB_ROOT_USERNAME = "root"; - MONGO_INITDB_ROOT_PASSWORD = "librechat"; - MONGO_INITDB_DATABASE = "LibreChat"; - }; + virtualisation.oci-containers.containers.librechat-mongodb = { + image = with cfg.images.mongodb; "${imageName}:${imageTag}"; + imageFile = cfg.images.mongodb; + cmd = [ + "mongod" + "--noauth" + ]; volumes = [ "librechat_mongodb_data:/data/db:rw" ]; log-driver = "journald"; extraOptions = [ - "--network=host" + "--network-alias=mongodb" + "--network=librechat_default" + ]; + }; + + virtualisation.oci-containers.containers.librechat-meilisearch = { + image = with cfg.images.meilisearch; "${imageName}:${imageTag}"; + imageFile = cfg.images.meilisearch; + environment = { + MEILI_NO_ANALYTICS = "true"; + MEILI_HOST = "http://meilisearch:${toString cfg.meiliPort}"; + }; + environmentFiles = optional (cfg.environmentFile != null) cfg.environmentFile; + volumes = [ + "librechat_meili_data:/meili_data:rw" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=meilisearch" + "--network=librechat_default" + ]; + }; + + virtualisation.oci-containers.containers.librechat-vectordb = { + image = with cfg.images.vectordb; "${imageName}:${imageTag}"; + imageFile = cfg.images.vectordb; + environment = { + POSTGRES_DB = "mydatabase"; + POSTGRES_USER = "myuser"; + POSTGRES_PASSWORD = "mypassword"; + }; + volumes = [ + "librechat_pgdata:/var/lib/postgresql/data:rw" + ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=vectordb" + "--network=librechat_default" + ]; + }; + + virtualisation.oci-containers.containers.librechat-rag-api = { + image = with cfg.images.ragApi; "${imageName}:${imageTag}"; + imageFile = cfg.images.ragApi; + environment = { + DB_HOST = "vectordb"; + RAG_PORT = toString cfg.ragPort; + }; + environmentFiles = optional (cfg.environmentFile != null) cfg.environmentFile; + dependsOn = [ "librechat-vectordb" ]; + log-driver = "journald"; + extraOptions = [ + "--network-alias=rag_api" + "--network=librechat_default" ]; }; virtualisation.oci-containers.containers.librechat = { - image = toString cfg.image; + image = with cfg.images.librechat; "${imageName}:${imageTag}"; + imageFile = cfg.images.librechat; environment = defaultEnv // { - MONGO_URI = "mongodb://root:librechat@localhost:27017/LibreChat?authSource=admin"; + MONGO_URI = "mongodb://mongodb:27017/LibreChat"; + MEILI_HOST = "http://meilisearch:${toString cfg.meiliPort}"; + RAG_PORT = toString cfg.ragPort; + RAG_API_URL = "http://rag_api:${toString cfg.ragPort}"; + DOMAIN_CLIENT = + if cfg.externalUrl != null then cfg.externalUrl else "http://localhost:${toString cfg.port}"; + DOMAIN_SERVER = + if cfg.externalUrl != null then cfg.externalUrl else "http://localhost:${toString cfg.port}"; } // cfg.environment; + environmentFiles = optional (cfg.environmentFile != null) cfg.environmentFile; volumes = [ - "librechat_data:/app/client/data:rw" "librechat_images:/app/client/public/images:rw" - "librechat_uploads:/app/api/server/files/uploads:rw" + "librechat_uploads:/app/uploads:rw" "librechat_logs:/app/logs:rw" - ]; + ] + ++ optional (cfg.configFile != null) "${cfg.configFile}:/app/librechat.yaml:ro"; + ports = [ - "${toString cfg.port}:${toString cfg.port}" + "0.0.0.0:${toString cfg.port}:${toString cfg.port}/tcp" + ]; + dependsOn = [ + "librechat-mongodb" + "librechat-rag-api" ]; log-driver = "journald"; extraOptions = [ - "--network=host" + "--network-alias=api" + "--network=librechat_default" ]; }; - systemd.services."podman-librechat-mongodb" = { - serviceConfig = { - Restart = mkOverride 90 "always"; - }; + systemd.services.podman-librechat-mongodb = { + serviceConfig.Restart = mkOverride 90 "always"; after = [ + "podman-network-librechat_default.service" "podman-volume-librechat_mongodb_data.service" ]; requires = [ + "podman-network-librechat_default.service" "podman-volume-librechat_mongodb_data.service" ]; - partOf = [ - "podman-compose-librechat-root.target" - ]; - wantedBy = [ - "podman-compose-librechat-root.target" - ]; + partOf = [ "podman-compose-librechat-root.target" ]; + wantedBy = [ "podman-compose-librechat-root.target" ]; }; - systemd.services."podman-librechat" = { - serviceConfig = { - Restart = mkOverride 90 "always"; - }; + systemd.services.podman-librechat-meilisearch = { + serviceConfig.Restart = mkOverride 90 "always"; after = [ - "podman-volume-librechat_data.service" + "podman-network-librechat_default.service" + "podman-volume-librechat_meili_data.service" + ]; + requires = [ + "podman-network-librechat_default.service" + "podman-volume-librechat_meili_data.service" + ]; + partOf = [ "podman-compose-librechat-root.target" ]; + wantedBy = [ "podman-compose-librechat-root.target" ]; + }; + + systemd.services.podman-librechat-vectordb = { + serviceConfig.Restart = mkOverride 90 "always"; + after = [ + "podman-network-librechat_default.service" + "podman-volume-librechat_pgdata.service" + ]; + requires = [ + "podman-network-librechat_default.service" + "podman-volume-librechat_pgdata.service" + ]; + partOf = [ "podman-compose-librechat-root.target" ]; + wantedBy = [ "podman-compose-librechat-root.target" ]; + }; + + systemd.services.podman-librechat-rag-api = { + serviceConfig.Restart = mkOverride 90 "always"; + after = [ + "podman-network-librechat_default.service" + "podman-librechat-vectordb.service" + ]; + requires = [ + "podman-network-librechat_default.service" + "podman-librechat-vectordb.service" + ]; + partOf = [ "podman-compose-librechat-root.target" ]; + wantedBy = [ "podman-compose-librechat-root.target" ]; + }; + + systemd.services.podman-librechat = { + serviceConfig.Restart = mkOverride 90 "always"; + after = [ + "podman-network-librechat_default.service" "podman-volume-librechat_images.service" "podman-volume-librechat_uploads.service" "podman-volume-librechat_logs.service" "podman-librechat-mongodb.service" + "podman-librechat-meilisearch.service" + "podman-librechat-rag-api.service" ]; requires = [ - "podman-volume-librechat_data.service" + "podman-network-librechat_default.service" "podman-volume-librechat_images.service" "podman-volume-librechat_uploads.service" "podman-volume-librechat_logs.service" "podman-librechat-mongodb.service" + "podman-librechat-meilisearch.service" + "podman-librechat-rag-api.service" ]; - partOf = [ - "podman-compose-librechat-root.target" - ]; - wantedBy = [ - "podman-compose-librechat-root.target" - ]; + partOf = [ "podman-compose-librechat-root.target" ]; + wantedBy = [ "podman-compose-librechat-root.target" ]; }; - systemd.services."podman-volume-librechat_data" = { + systemd.services.podman-network-librechat_default = { path = [ pkgs.podman ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; + ExecStop = "podman network rm -f librechat_default"; }; script = '' - podman volume inspect librechat_data || podman volume create librechat_data + podman network inspect librechat_default || podman network create librechat_default ''; partOf = [ "podman-compose-librechat-root.target" ]; wantedBy = [ "podman-compose-librechat-root.target" ]; }; - systemd.services."podman-volume-librechat_images" = { + systemd.services.podman-volume-librechat_mongodb_data = { path = [ pkgs.podman ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; }; - script = '' - podman volume inspect librechat_images || podman volume create librechat_images - ''; + script = "podman volume inspect librechat_mongodb_data || podman volume create librechat_mongodb_data"; partOf = [ "podman-compose-librechat-root.target" ]; wantedBy = [ "podman-compose-librechat-root.target" ]; }; - systemd.services."podman-volume-librechat_uploads" = { + systemd.services.podman-volume-librechat_meili_data = { path = [ pkgs.podman ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; }; - script = '' - podman volume inspect librechat_uploads || podman volume create librechat_uploads - ''; + script = "podman volume inspect librechat_meili_data || podman volume create librechat_meili_data"; partOf = [ "podman-compose-librechat-root.target" ]; wantedBy = [ "podman-compose-librechat-root.target" ]; }; - systemd.services."podman-volume-librechat_logs" = { + systemd.services.podman-volume-librechat_pgdata = { path = [ pkgs.podman ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; }; - script = '' - podman volume inspect librechat_logs || podman volume create librechat_logs - ''; + script = "podman volume inspect librechat_pgdata || podman volume create librechat_pgdata"; partOf = [ "podman-compose-librechat-root.target" ]; wantedBy = [ "podman-compose-librechat-root.target" ]; }; - systemd.services."podman-volume-librechat_mongodb_data" = { + systemd.services.podman-volume-librechat_images = { path = [ pkgs.podman ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; }; - script = '' - podman volume inspect librechat_mongodb_data || podman volume create librechat_mongodb_data - ''; + script = "podman volume inspect librechat_images || podman volume create librechat_images"; partOf = [ "podman-compose-librechat-root.target" ]; wantedBy = [ "podman-compose-librechat-root.target" ]; }; - systemd.targets."podman-compose-librechat-root" = { - unitConfig = { - Description = "Root target generated by compose2nix."; + systemd.services.podman-volume-librechat_uploads = { + path = [ pkgs.podman ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; }; + script = "podman volume inspect librechat_uploads || podman volume create librechat_uploads"; + partOf = [ "podman-compose-librechat-root.target" ]; + wantedBy = [ "podman-compose-librechat-root.target" ]; + }; + + systemd.services.podman-volume-librechat_logs = { + path = [ pkgs.podman ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + script = "podman volume inspect librechat_logs || podman volume create librechat_logs"; + partOf = [ "podman-compose-librechat-root.target" ]; + wantedBy = [ "podman-compose-librechat-root.target" ]; + }; + + systemd.targets.podman-compose-librechat-root = { + unitConfig.Description = "Root target generated by compose2nix."; wantedBy = [ "multi-user.target" ]; }; }; diff --git a/modules/nixos/mcpo/default.nix b/modules/nixos/mcpo/default.nix index 89e0e29..76ad953 100644 --- a/modules/nixos/mcpo/default.nix +++ b/modules/nixos/mcpo/default.nix @@ -28,6 +28,12 @@ in default = null; }; + port = mkOption { + type = types.port; + default = 8000; + description = "Port on which the mcpo service should listen."; + }; + user = mkOption { type = types.str; description = "The user the mcpo service will run as."; @@ -100,7 +106,7 @@ in after = [ "network.target" ]; wantedBy = [ "multi-user.target" ]; serviceConfig = { - ExecStart = "${getExe cfg.package} --config ${configFile}"; + ExecStart = "${getExe cfg.package} --port ${toString cfg.port} --config ${configFile}"; Restart = "on-failure"; User = cfg.user; Group = cfg.group; diff --git a/pkgs/default.nix b/pkgs/default.nix index 28c1f0b..899a133 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -15,6 +15,7 @@ jirafeau-cli = pkgs.callPackage ./jirafeau-cli { }; kicad-mcp = pkgs.callPackage ./kicad-mcp { }; mcpo = pkgs.callPackage ./mcpo { }; + nerdlog = pkgs.callPackage ./nerdlog { }; pass2bw = pkgs.callPackage ./pass2bw { }; pyman = pkgs.callPackage ./pyman { }; quicknote = pkgs.callPackage ./quicknote { }; diff --git a/pkgs/nerdlog/default.nix b/pkgs/nerdlog/default.nix new file mode 100644 index 0000000..b1b59f0 --- /dev/null +++ b/pkgs/nerdlog/default.nix @@ -0,0 +1,83 @@ +{ + lib, + buildGoModule, + fetchFromGitHub, + stdenv, + darwin, + xorg, + pkg-config, + wayland, + libGL, + libxkbcommon, + makeWrapper, +}: + +buildGoModule rec { + pname = "nerdlog"; + version = "1.10.0"; + + src = fetchFromGitHub { + owner = "dimonomid"; + repo = "nerdlog"; + rev = "v${version}"; + hash = "sha256-XlzWNeyd+Ar4ArFcN1wkQ0aod6ckAiIb12odK7cf4+s="; + }; + + vendorHash = "sha256-hvv0dsE1yz85VLaBOE7RWbux8L8kVTihcA1HyyHRYAM="; + + subPackages = [ + "cmd/nerdlog" + "cmd/generate_syslog" + ]; + + doCheck = false; + + nativeBuildInputs = [ + pkg-config + makeWrapper + ]; + + buildInputs = + lib.optionals stdenv.isDarwin [ + darwin.apple_sdk.frameworks.Cocoa + ] + ++ lib.optionals stdenv.isLinux [ + xorg.libX11 + xorg.libXext + xorg.libXfixes + wayland + libGL + libxkbcommon + ]; + + postInstall = lib.optionalString stdenv.isLinux '' + wrapProgram $out/bin/nerdlog \ + --prefix LD_LIBRARY_PATH : ${ + lib.makeLibraryPath [ + xorg.libX11 + xorg.libXext + xorg.libXfixes + wayland + libGL + libxkbcommon + ] + } + ''; + + ldflags = [ + "-s" + "-w" + "-X=github.com/dimonomid/nerdlog/version.version=${version}" + "-X=github.com/dimonomid/nerdlog/version.commit=${src.rev}" + "-X=github.com/dimonomid/nerdlog/version.date=1970-01-01T00:00:00Z" + "-X=github.com/dimonomid/nerdlog/version.builtBy=goreleaser" + ]; + + meta = { + description = "Nerdlog: fast, remote-first, multi-host TUI log viewer with timeline histogram and no central server"; + homepage = "https://github.com/dimonomid/nerdlog"; + changelog = "https://github.com/dimonomid/nerdlog/blob/${src.rev}/CHANGELOG.md"; + license = lib.licenses.bsd2; + mainProgram = "nerdlog"; + }; +} diff --git a/pkgs/pass2bw/convert_csvs.py b/pkgs/pass2bw/convert_csvs.py index 4babb74..c67b842 100644 --- a/pkgs/pass2bw/convert_csvs.py +++ b/pkgs/pass2bw/convert_csvs.py @@ -42,6 +42,14 @@ def process_csv(input_file, output_file): for col, default_val in default_values.items(): if col not in new_row: new_row[col] = default_val + + folder = new_row.get('folder', '') + name = new_row.get('name', '') + if folder.startswith('www') and name: + if not name.startswith('http://') and not name.startswith('https://'): + new_row['login_uri'] = f'https://{name}' + else: + new_row['login_uri'] = name if new_row['notes']: new_row['notes'] = new_row['notes'].replace('\n', ' ').replace('\r', ' ') @@ -49,7 +57,6 @@ def process_csv(input_file, output_file): notes = new_row['notes'] if notes: - # Look for pattern: "login: USERNAME" match = re.search(r'login:\s*(\S+)', notes, re.IGNORECASE) if match: username = match.group(1) diff --git a/pkgs/pass2bw/default.nix b/pkgs/pass2bw/default.nix index 349d8d1..ce42f1f 100644 --- a/pkgs/pass2bw/default.nix +++ b/pkgs/pass2bw/default.nix @@ -2,6 +2,8 @@ stdenv, lib, makeWrapper, + pass, + pass2csv, python3, ... }: @@ -20,13 +22,18 @@ stdenv.mkDerivation rec { installPhase = '' mkdir -p $out/bin - sed -e "s|python ./convert_csvs.py|python $out/bin/convert_csvs.py|" \ - ${src}/${pname}.sh > $out/bin/${pname} + cp ${src}/pass2bw.sh $out/bin/${pname} chmod +x $out/bin/${pname} cp ${src}/convert_csvs.py $out/bin/ wrapProgram $out/bin/${pname} \ - --prefix PATH : ${lib.makeBinPath [ python3 ]} + --prefix PATH : ${ + lib.makeBinPath [ + pass + pass2csv + python3 + ] + } ''; } diff --git a/pkgs/pass2bw/pass2bw.sh b/pkgs/pass2bw/pass2bw.sh index 95cb544..887a881 100644 --- a/pkgs/pass2bw/pass2bw.sh +++ b/pkgs/pass2bw/pass2bw.sh @@ -4,5 +4,5 @@ if [ "$#" -ne 2 ]; then fi pass2csv "$1" /tmp/pass.csv -python ./convert_csvs.py /tmp/pass.csv "$2" +python3 "$(dirname "$0")/convert_csvs.py" /tmp/pass.csv "$2" rm /tmp/pass.csv