Some checks failed
CI / check (push) Has been cancelled
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.
227 lines
7.2 KiB
Nix
227 lines
7.2 KiB
Nix
{
|
||
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;
|
||
};
|
||
}
|