From bc75505ca3f04965f2fa71cffd1caa3588a0d26b Mon Sep 17 00:00:00 2001 From: m3tm3re
Date: Tue, 13 Jan 2026 20:26:30 +0100
Subject: [PATCH] feat: add n8n, beads, opencode packages
- Add pkgs/n8n/default.nix (v2.4.1)
- Add pkgs/beads/default.nix (v0.47.1, doCheck=false)
- Add pkgs/opencode/default.nix (v1.1.18)
- Include relax-bun-version-check.patch for opencode
- Register all packages in pkgs/default.nix
---
pkgs/beads/default.nix | 62 ++++++
pkgs/default.nix | 3 +
pkgs/n8n/default.nix | 114 ++++++++++
pkgs/opencode/default.nix | 222 ++++++++++++++++++++
pkgs/opencode/relax-bun-version-check.patch | 28 +++
5 files changed, 429 insertions(+)
create mode 100644 pkgs/beads/default.nix
create mode 100644 pkgs/n8n/default.nix
create mode 100644 pkgs/opencode/default.nix
create mode 100644 pkgs/opencode/relax-bun-version-check.patch
diff --git a/pkgs/beads/default.nix b/pkgs/beads/default.nix
new file mode 100644
index 0000000..15276bf
--- /dev/null
+++ b/pkgs/beads/default.nix
@@ -0,0 +1,62 @@
+{
+ lib,
+ stdenv,
+ buildGoModule,
+ fetchFromGitHub,
+ gitMinimal,
+ installShellFiles,
+ versionCheckHook,
+ writableTmpDirAsHomeHook,
+}:
+buildGoModule (finalAttrs: {
+ pname = "beads";
+ version = "0.47.1";
+
+ src = fetchFromGitHub {
+ owner = "steveyegge";
+ repo = "beads";
+ tag = "v${finalAttrs.version}";
+ hash = "sha256-DwIR/r1TJnpVd/CT1E2OTkAjU7k9/KHbcVwg5zziFVg=";
+ };
+
+ vendorHash = "sha256-pY5m5ODRgqghyELRwwxOr+xlW41gtJWLXaW53GlLaFw=";
+
+ subPackages = ["cmd/bd"];
+
+ ldflags = ["-s" "-w"];
+
+ nativeBuildInputs = [installShellFiles];
+
+ nativeCheckInputs = [gitMinimal writableTmpDirAsHomeHook];
+
+ # Skip security tests on Darwin - they check for /etc/passwd which isn't available in sandbox
+ checkFlags =
+ lib.optionals stdenv.hostPlatform.isDarwin
+ ["-skip=TestCleanupMergeArtifacts_CommandInjectionPrevention"];
+
+ preCheck = ''
+ export PATH="$out/bin:$PATH"
+ '';
+
+ postInstall = lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) ''
+ installShellCompletion --cmd bd \
+ --bash <($out/bin/bd completion bash) \
+ --fish <($out/bin/bd completion fish) \
+ --zsh <($out/bin/bd completion zsh)
+ '';
+
+ nativeInstallCheckInputs = [versionCheckHook writableTmpDirAsHomeHook];
+ versionCheckProgramArg = "version";
+ doInstallCheck = true;
+
+ # Tests require git worktree operations that fail in Nix sandbox
+ doCheck = false;
+
+ meta = {
+ description = "Lightweight memory system for AI coding agents with graph-based issue tracking";
+ homepage = "https://github.com/steveyegge/beads";
+ license = lib.licenses.mit;
+ maintainers = with lib.maintainers; [kedry];
+ mainProgram = "bd";
+ };
+})
diff --git a/pkgs/default.nix b/pkgs/default.nix
index 4b93847..d6a729e 100644
--- a/pkgs/default.nix
+++ b/pkgs/default.nix
@@ -1,11 +1,14 @@
{pkgs, ...}: {
# Custom packages registry
# Each package is defined in its own directory under pkgs/
+ beads = pkgs.callPackage ./beads {};
code2prompt = pkgs.callPackage ./code2prompt {};
hyprpaper-random = pkgs.callPackage ./hyprpaper-random {};
launch-webapp = pkgs.callPackage ./launch-webapp {};
mem0 = pkgs.callPackage ./mem0 {};
msty-studio = pkgs.callPackage ./msty-studio {};
+ n8n = pkgs.callPackage ./n8n {};
+ opencode = pkgs.callPackage ./opencode {};
pomodoro-timer = pkgs.callPackage ./pomodoro-timer {};
rofi-project-opener = pkgs.callPackage ./rofi-project-opener {};
stt-ptt = pkgs.callPackage ./stt-ptt {};
diff --git a/pkgs/n8n/default.nix b/pkgs/n8n/default.nix
new file mode 100644
index 0000000..9d81f5e
--- /dev/null
+++ b/pkgs/n8n/default.nix
@@ -0,0 +1,114 @@
+{
+ stdenv,
+ lib,
+ nixosTests,
+ fetchFromGitHub,
+ nodejs,
+ pnpm_10,
+ fetchPnpmDeps,
+ pnpmConfigHook,
+ python3,
+ node-gyp,
+ cctools,
+ xcbuild,
+ libkrb5,
+ libmongocrypt,
+ libpq,
+ makeWrapper,
+}:
+stdenv.mkDerivation (finalAttrs: {
+ pname = "n8n";
+ version = "2.4.1";
+
+ src = fetchFromGitHub {
+ owner = "n8n-io";
+ repo = "n8n";
+ tag = "n8n@${finalAttrs.version}";
+ hash = "sha256-EQP9ZI8kt30SUYE1+/UUpxQXpavzKqDu8qE24zsNifg=";
+ };
+
+ pnpmDeps = fetchPnpmDeps {
+ inherit (finalAttrs) pname version src;
+ pnpm = pnpm_10;
+ fetcherVersion = 3;
+ hash = "sha256-M/HXRii4FotDC/xIa6DVlY13rCHMq7oZNtPi7hvDXik=";
+ };
+
+ nativeBuildInputs =
+ [
+ pnpmConfigHook
+ pnpm_10
+ python3 # required to build sqlite3 bindings
+ node-gyp # required to build sqlite3 bindings
+ makeWrapper
+ ]
+ ++ lib.optional stdenv.hostPlatform.isDarwin [cctools xcbuild];
+
+ buildInputs = [nodejs libkrb5 libmongocrypt libpq];
+
+ buildPhase = ''
+ runHook preBuild
+
+ pushd node_modules/sqlite3
+ node-gyp rebuild
+ popd
+
+ # TODO: use deploy after resolved https://github.com/pnpm/pnpm/issues/5315
+ pnpm build --filter=n8n
+
+ runHook postBuild
+ '';
+
+ preInstall = ''
+ echo "Removing non-deterministic and unnecessary files"
+
+ find -type d -name .turbo -exec rm -rf {} +
+ rm node_modules/.modules.yaml
+ rm packages/nodes-base/dist/types/nodes.json
+
+ CI=true pnpm --ignore-scripts prune --prod
+ find -type f \( -name "*.ts" -o -name "*.map" \) -exec rm -rf {} +
+ rm -rf node_modules/.pnpm/{typescript*,prettier*}
+ shopt -s globstar
+ # https://github.com/pnpm/pnpm/issues/3645
+ find node_modules packages/**/node_modules -xtype l -delete
+
+ echo "Removed non-deterministic and unnecessary files"
+ '';
+
+ installPhase = ''
+ runHook preInstall
+
+ mkdir -p $out/{bin,lib/n8n}
+ mv {packages,node_modules} $out/lib/n8n
+
+ makeWrapper $out/lib/n8n/packages/cli/bin/n8n $out/bin/n8n \
+ --set N8N_RELEASE_TYPE "stable"
+
+ runHook postInstall
+ '';
+
+ passthru = {
+ tests = nixosTests.n8n;
+ updateScript = ./update.sh;
+ };
+
+ # this package has ~80000 files, these take too long and seem to be unnecessary
+ dontStrip = true;
+ dontPatchELF = true;
+ dontRewriteSymlinks = true;
+
+ meta = {
+ description = "Free and source-available fair-code licensed workflow automation tool";
+ longDescription = ''
+ Free and source-available fair-code licensed workflow automation tool.
+ Easily automate tasks across different services.
+ '';
+ homepage = "https://n8n.io";
+ changelog = "https://github.com/n8n-io/n8n/releases/tag/n8n@${finalAttrs.version}";
+ maintainers = with lib.maintainers; [gepbird AdrienLemaire sweenu];
+ license = lib.licenses.sustainableUse;
+ mainProgram = "n8n";
+ platforms = lib.platforms.unix;
+ };
+})
diff --git a/pkgs/opencode/default.nix b/pkgs/opencode/default.nix
new file mode 100644
index 0000000..a3fe1ee
--- /dev/null
+++ b/pkgs/opencode/default.nix
@@ -0,0 +1,222 @@
+{
+ lib,
+ stdenvNoCC,
+ bun,
+ fetchFromGitHub,
+ fzf,
+ makeBinaryWrapper,
+ models-dev,
+ nix-update-script,
+ ripgrep,
+ testers,
+ installShellFiles,
+ writableTmpDirAsHomeHook,
+}: let
+ pname = "opencode";
+ version = "1.1.18";
+ src = fetchFromGitHub {
+ owner = "anomalyco";
+ repo = "opencode";
+ tag = "v${version}";
+ hash = "sha256-3A4s0FpjZuGB0HGMQVBXfWq+0yHmeIvnEQTSX3amV4I=";
+ };
+
+ node_modules = stdenvNoCC.mkDerivation {
+ pname = "${pname}-node_modules";
+ inherit version src;
+
+ impureEnvVars =
+ lib.fetchers.proxyImpureEnvVars
+ ++ ["GIT_PROXY_COMMAND" "SOCKS_SERVER"];
+
+ nativeBuildInputs = [bun writableTmpDirAsHomeHook];
+
+ dontConfigure = true;
+
+ buildPhase = ''
+ runHook preBuild
+
+ export BUN_INSTALL_CACHE_DIR=$(mktemp -d)
+
+ bun install \
+ --cpu="*" \
+ --filter=./packages/opencode \
+ --force \
+ --frozen-lockfile \
+ --ignore-scripts \
+ --no-progress \
+ --os="*" \
+ --production
+
+ bun run ./nix/scripts/canonicalize-node-modules.ts
+ bun run ./nix/scripts/normalize-bun-binaries.ts
+
+ runHook postBuild
+ '';
+
+ installPhase = ''
+ runHook preInstall
+
+ mkdir -p $out
+ find . -type d -name node_modules -exec cp -R --parents {} $out \;
+
+ runHook postInstall
+ '';
+
+ # NOTE: Required else we get errors that our fixed-output derivation references store paths
+ dontFixup = true;
+
+ outputHash = "sha256-zSco4ORQQOqV3vMPuP+M/q/hBa+MJGnTKIlxgngMA3g=";
+ outputHashAlgo = "sha256";
+ outputHashMode = "recursive";
+ };
+in
+ stdenvNoCC.mkDerivation (finalAttrs: {
+ inherit pname version src node_modules;
+
+ nativeBuildInputs = [
+ bun
+ installShellFiles
+ makeBinaryWrapper
+ models-dev
+ writableTmpDirAsHomeHook
+ ];
+
+ patches = [
+ # NOTE: Relax Bun version check to be a warning instead of an error
+ ./relax-bun-version-check.patch
+ ];
+
+ dontConfigure = true;
+
+ env.MODELS_DEV_API_JSON = "${models-dev}/dist/_api.json";
+ env.OPENCODE_VERSION = finalAttrs.version;
+ env.OPENCODE_CHANNEL = "stable";
+
+ buildPhase = ''
+ runHook preBuild
+
+ # Copy all node_modules including the .bun directory with actual packages
+ cp -r ${finalAttrs.node_modules}/node_modules .
+ cp -r ${finalAttrs.node_modules}/packages .
+
+ (
+ cd packages/opencode
+
+ # Fix symlinks to workspace packages
+ chmod -R u+w ./node_modules
+ mkdir -p ./node_modules/@opencode-ai
+ rm -f ./node_modules/@opencode-ai/{script,sdk,plugin}
+ ln -s $(pwd)/../../packages/script ./node_modules/@opencode-ai/script
+ ln -s $(pwd)/../../packages/sdk/js ./node_modules/@opencode-ai/sdk
+ ln -s $(pwd)/../../packages/plugin ./node_modules/@opencode-ai/plugin
+
+ # Use upstream bundle.ts for Nix-compatible bundling
+ cp ../../nix/bundle.ts ./bundle.ts
+ chmod +x ./bundle.ts
+ bun run ./bundle.ts
+ )
+
+ runHook postBuild
+ '';
+
+ installPhase = ''
+ runHook preInstall
+
+ cd packages/opencode
+ if [ ! -d dist ]; then
+ echo "ERROR: dist directory missing after bundle step"
+ exit 1
+ fi
+
+ mkdir -p $out/lib/opencode
+ cp -r dist $out/lib/opencode/
+ chmod -R u+w $out/lib/opencode/dist
+
+ # Select bundled worker assets deterministically (sorted find output)
+ worker_file=$(find "$out/lib/opencode/dist" -type f \( -path '*/tui/worker.*' -o -name 'worker.*' \) | sort | head -n1)
+ parser_worker_file=$(find "$out/lib/opencode/dist" -type f -name 'parser.worker.*' | sort | head -n1)
+ if [ -z "$worker_file" ]; then
+ echo "ERROR: bundled worker not found"
+ exit 1
+ fi
+
+ main_wasm=$(printf '%s\n' "$out"/lib/opencode/dist/tree-sitter-*.wasm | sort | head -n1)
+ wasm_list=$(find "$out/lib/opencode/dist" -maxdepth 1 -name 'tree-sitter-*.wasm' -print)
+ for patch_file in "$worker_file" "$parser_worker_file"; do
+ [ -z "$patch_file" ] && continue
+ [ ! -f "$patch_file" ] && continue
+ if [ -n "$wasm_list" ] && grep -q 'tree-sitter' "$patch_file"; then
+ # Rewrite wasm references to absolute store paths to avoid runtime resolve failures.
+ bun --bun ../../nix/scripts/patch-wasm.ts "$patch_file" "$main_wasm" $wasm_list
+ fi
+ done
+
+ mkdir -p $out/lib/opencode/node_modules
+ cp -r ../../node_modules/.bun $out/lib/opencode/node_modules/
+ mkdir -p $out/lib/opencode/node_modules/@opentui
+
+ # Generate and install JSON schema
+ mkdir -p $out/share/opencode
+ HOME=$TMPDIR bun --bun script/schema.ts $out/share/opencode/schema.json
+
+ mkdir -p $out/bin
+ makeWrapper ${lib.getExe bun} $out/bin/opencode \
+ --add-flags "run" \
+ --add-flags "$out/lib/opencode/dist/src/index.js" \
+ --prefix PATH : ${lib.makeBinPath [fzf ripgrep]} \
+ --argv0 opencode
+
+ runHook postInstall
+ '';
+
+ postInstall = ''
+ # Add symlinks for platform-specific native modules
+ pkgs=(
+ $out/lib/opencode/node_modules/.bun/@opentui+core-*
+ $out/lib/opencode/node_modules/.bun/@opentui+solid-*
+ $out/lib/opencode/node_modules/.bun/@opentui+core@*
+ $out/lib/opencode/node_modules/.bun/@opentui+solid@*
+ )
+ for pkg in "''${pkgs[@]}"; do
+ if [ -d "$pkg" ]; then
+ pkgName=$(basename "$pkg" | sed 's/@opentui+\([^@]*\)@.*/\1/')
+ ln -sf ../.bun/$(basename "$pkg")/node_modules/@opentui/$pkgName \
+ $out/lib/opencode/node_modules/@opentui/$pkgName
+ fi
+ done
+
+ ${lib.optionalString
+ ((stdenvNoCC.buildPlatform.canExecute stdenvNoCC.hostPlatform)
+ && (stdenvNoCC.hostPlatform.system != "x86_64-darwin")) ''
+ installShellCompletion --cmd opencode \
+ --bash <($out/bin/opencode completion)
+ ''}
+ '';
+
+ passthru = {
+ jsonschema = "${placeholder "out"}/share/opencode/schema.json";
+ tests.version = testers.testVersion {
+ package = finalAttrs.finalPackage;
+ command = "HOME=$(mktemp -d) opencode --version";
+ inherit (finalAttrs) version;
+ };
+ updateScript =
+ nix-update-script {extraArgs = ["--subpackage" "node_modules"];};
+ };
+
+ meta = {
+ description = "AI coding agent built for the terminal";
+ longDescription = ''
+ OpenCode is a terminal-based agent that can build anything.
+ It combines a TypeScript/JavaScript core with a Go-based TUI
+ to provide an interactive AI coding experience.
+ '';
+ homepage = "https://github.com/anomalyco/opencode";
+ license = lib.licenses.mit;
+ maintainers = with lib.maintainers; [delafthi];
+ sourceProvenance = with lib.sourceTypes; [fromSource];
+ platforms = ["aarch64-linux" "x86_64-linux" "aarch64-darwin" "x86_64-darwin"];
+ mainProgram = "opencode";
+ };
+ })
diff --git a/pkgs/opencode/relax-bun-version-check.patch b/pkgs/opencode/relax-bun-version-check.patch
new file mode 100644
index 0000000..5d14f16
--- /dev/null
+++ b/pkgs/opencode/relax-bun-version-check.patch
@@ -0,0 +1,28 @@
+From 0e07ea8225f5667e39c6aa59eea726266f0afab0 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?J=C3=B6rg=20Thalheim?=