From 02947027ba4ca34ee6f7bbbd21edf22187e503ed Mon Sep 17 00:00:00 2001 From: Alexander Miroshnichenko Date: Wed, 27 May 2026 13:48:18 +0300 Subject: [PATCH] feat(aionui): Add AionUi AI cowork desktop app (v2.1.4) Build AionUi from source using bun for dependency fetching and electron-vite for TypeScript compilation. Native addons (better-sqlite3, sharp) are compiled against nixpkgs node-gyp. The package uses a fixed-output derivation for bun install, electron-vite for the build, and wraps the result with nixpkgs electron for a fully self-contained runtime. Also includes desktop entry and icon generation. --- README.md | 2 + packages/aionui/default.nix | 5 + packages/aionui/package.nix | 226 ++++++++++++++++++++++++++++++++++++ 3 files changed, 233 insertions(+) create mode 100644 packages/aionui/default.nix create mode 100644 packages/aionui/package.nix diff --git a/README.md b/README.md index 4387de8..8b5ec0a 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ A custom Nix overlay and flake providing additional packages not found in upstre | Package | Description | Category | |---------|-------------|----------| +| `aionui` | Free, open-source, Cowork app with AI Agents | AI Coding Agents | | `container-use` | Containerized environments for coding agents | AI Coding Agents | | `desloppify` | Multi-language codebase health scanner and technical debt tracker for AI agents | AI Coding Agents | | `freebuff` | The world's strongest free coding agent | AI Coding Agents | @@ -106,6 +107,7 @@ nix-overlay/ │ ├── default.nix # Binary-cache-friendly overlay │ └── shared-nixpkgs.nix # Dependency-sharing overlay ├── packages/ # Package definitions +│ ├── aionui/ # AionUi - AI Cowork desktop app │ ├── container-use/ # Containerized environments for coding agents │ ├── default/ # Meta-package listing all packages │ ├── desloppify/ # Codebase health scanner for AI agents diff --git a/packages/aionui/default.nix b/packages/aionui/default.nix new file mode 100644 index 0000000..291b4f4 --- /dev/null +++ b/packages/aionui/default.nix @@ -0,0 +1,5 @@ +{ + pkgs, + ... +}: +pkgs.callPackage ./package.nix { } diff --git a/packages/aionui/package.nix b/packages/aionui/package.nix new file mode 100644 index 0000000..065d4f1 --- /dev/null +++ b/packages/aionui/package.nix @@ -0,0 +1,226 @@ +{ + lib, + stdenv, + fetchFromGitHub, + bun, + nodejs, + node-gyp, + electron, + autoPatchelfHook, + makeWrapper, + copyDesktopItems, + imagemagick, + python3, + cacert, +}: + +let + version = "2.1.4"; + + src = fetchFromGitHub { + owner = "iOfficeAI"; + repo = "AionUi"; + rev = "v${version}"; + hash = "sha256-A7dKGEuo5n+M7D9fJR3TN95q1P7En/ulsTO+ev6SAUY="; + }; + + # Fixed-output derivation: fetch all pure-JS dependencies via bun. + # We skip install scripts because they use #!/usr/bin/env shebangs + # that don't exist in the Nix sandbox. Native addons are built + # separately in the main derivation. + bunDeps = stdenv.mkDerivation { + name = "aionui-bun-deps-${version}"; + inherit src; + nativeBuildInputs = [ + bun + cacert + ]; + + outputHashAlgo = "sha256"; + outputHashMode = "recursive"; + outputHash = "sha256-Fuj55vxDgpx5A4iwnU/5IZKPfaYMH3DeZ1yauuZHn8g="; + + dontPatchShebangs = true; + dontFixup = true; + + SSL_CERT_FILE = "${cacert}/etc/ssl/certs/ca-bundle.crt"; + + buildPhase = '' + export HOME=$TMPDIR + bun install --frozen-lockfile --ignore-scripts + ''; + + installPhase = '' + mkdir -p $out + cp -r node_modules $out/ + cp -r packages $out/ + # Workspace packages reference root configs with relative paths + cp tsconfig.json $out/ + ''; + }; +in + +stdenv.mkDerivation rec { + pname = "aionui"; + inherit version src; + + nativeBuildInputs = [ + bun + nodejs + node-gyp + autoPatchelfHook + makeWrapper + copyDesktopItems + imagemagick + python3 + ]; + + buildInputs = [ + stdenv.cc.cc.lib + ]; + + buildPhase = '' + runHook preBuild + + export HOME=$TMPDIR + + # Use the pre-fetched node_modules from the FOD (symlink farm) + ln -sf ${bunDeps}/node_modules . + + # ── Build better-sqlite3 native addon ────────────────────── + # The FOD output is read-only; build in a temp dir. + echo "Building better-sqlite3 from source..." + BT3_SRC=$TMPDIR/bts3-src + BT3_OUT=$TMPDIR/bts3-out + mkdir -p "$BT3_SRC" "$BT3_OUT" + cp -rL node_modules/better-sqlite3/* "$BT3_SRC/" + chmod -R u+w "$BT3_SRC" + ( + cd "$BT3_SRC" + export npm_config_nodedir=${nodejs} + node-gyp rebuild + mkdir -p "$BT3_OUT/build/Release" + cp build/Release/better_sqlite3.node "$BT3_OUT/build/Release/" 2>/dev/null || true + ) + + # ── Build sharp native addon ────────────────────────────── + echo "Building sharp from source..." + SHARP_SRC=$TMPDIR/sharp-src + SHARP_OUT=$TMPDIR/sharp-out + mkdir -p "$SHARP_SRC" "$SHARP_OUT" + cp -rL node_modules/sharp/* "$SHARP_SRC/" 2>/dev/null || true + if [ -f "$SHARP_SRC/package.json" ]; then + chmod -R u+w "$SHARP_SRC" + ( + cd "$SHARP_SRC" + export npm_config_nodedir=${nodejs} + node-gyp rebuild 2>/dev/null || echo " sharp: build skipped (needs libvips)" + mkdir -p "$SHARP_OUT/build/Release" + cp build/Release/sharp.node "$SHARP_OUT/build/Release/" 2>/dev/null || true + ) + fi + + # ── electron-vite build ─────────────────────────────────── + echo "Running electron-vite build..." + ${nodejs}/bin/node node_modules/.bin/electron-vite build \ + --config packages/desktop/electron.vite.config.ts + + # ── Bundle MCP servers ──────────────────────────────────── + echo "Building MCP servers..." + ${nodejs}/bin/node scripts/build-mcp-servers.js + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + mkdir -p $out/{bin,lib/aionui} + + # ── Built output ────────────────────────────────────────── + cp -r out $out/lib/aionui/ + cp -r public $out/lib/aionui/ + cp package.json $out/lib/aionui/ + + # ── Runtime dependencies ────────────────────────────────── + # Copy node_modules (from the symlink) – the compiled output + # has externalized require() calls for every dependency. + cp -rL node_modules $out/lib/aionui/ + + # Remove prebuilt musl (Alpine) binaries from sharp — they + # are incompatible with glibc and we built from source anyway. + find $out/lib/aionui/node_modules -path "*/sharp-linuxmusl*" -type d -exec rm -rf {} + 2>/dev/null || true + find $out/lib/aionui/node_modules -path "*/sharp-libvips-linuxmusl*" -type d -exec rm -rf {} + 2>/dev/null || true + + # ── Inject compiled native addons ───────────────────────── + # cp -rL from the FOD preserves read-only permissions. + # Make native addon dirs writable before overlaying binaries. + chmod -R u+w $out/lib/aionui/node_modules/better-sqlite3 2>/dev/null || true + chmod -R u+w $out/lib/aionui/node_modules/sharp 2>/dev/null || true + + BT3_OUT=$TMPDIR/bts3-out + if [ -f "$BT3_OUT/build/Release/better_sqlite3.node" ]; then + mkdir -p $out/lib/aionui/node_modules/better-sqlite3/build/Release + cp "$BT3_OUT/build/Release/better_sqlite3.node" \ + $out/lib/aionui/node_modules/better-sqlite3/build/Release/ + fi + + SHARP_OUT=$TMPDIR/sharp-out + if [ -f "$SHARP_OUT/build/Release/sharp.node" ]; then + mkdir -p $out/lib/aionui/node_modules/sharp/build/Release + cp "$SHARP_OUT/build/Release/sharp.node" \ + $out/lib/aionui/node_modules/sharp/build/Release/ + fi + + # ── Desktop entry ───────────────────────────────────────── + mkdir -p $out/share/applications + cat > $out/share/applications/aionui.desktop << EOF + [Desktop Entry] + Name=AionUi + Comment=Free, open-source Cowork app with AI Agents + Exec=$out/bin/aionui + Icon=aionui + Terminal=false + Type=Application + Categories=Office;Utility; + EOF + + mkdir -p $out/share/icons/hicolor/256x256/apps + if [ -f resources/app.png ]; then + convert resources/app.png -resize 256x256 \ + $out/share/icons/hicolor/256x256/apps/aionui.png + fi + + # ── Launcher ────────────────────────────────────────────── + makeWrapper ${electron}/bin/electron $out/bin/aionui \ + --add-flags "$out/lib/aionui" + + runHook postInstall + ''; + + dontStrip = true; + autoPatchelfIgnoreMissingDeps = true; + + desktopItems = [ "aionui.desktop" ]; + + doCheck = false; + + passthru = { + category = "AI Coding Agents"; + updateScript = [ + "nix-update" + "--flake" + ".#aionui" + ]; + }; + + meta = with lib; { + description = "Free, open-source, Cowork app with AI Agents"; + homepage = "https://github.com/iOfficeAI/AionUi"; + changelog = "https://github.com/iOfficeAI/AionUi/releases/tag/v${version}"; + license = licenses.asl20; + sourceProvenance = with sourceTypes; [ fromSource ]; + mainProgram = "aionui"; + platforms = platforms.linux; + }; +}