Files
millerson-overlay.nix/packages/aionui/package.nix
Alexander Miroshnichenko 02947027ba
Some checks failed
CI / check (push) Has been cancelled
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.
2026-05-27 13:48:18 +03:00

227 lines
7.2 KiB
Nix
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
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;
};
}