Imported as a linear vendor commit so forge rebases do not replay the upstream root tree. git-subtree-dir: third_party/pi git-subtree-split: 71023aec1546d6d677a65669c5665c0927ce0dd0
181 lines
5.3 KiB
Nix
181 lines
5.3 KiB
Nix
{
|
|
autoPatchelfHook,
|
|
fd,
|
|
fetchurl,
|
|
importNpmLock,
|
|
lib,
|
|
libxcb,
|
|
makeWrapper,
|
|
nodejs_22,
|
|
ripgrep,
|
|
source,
|
|
stdenv,
|
|
wl-clipboard,
|
|
xclip,
|
|
}:
|
|
|
|
let
|
|
nodejs = nodejs_22;
|
|
packageJson = lib.importJSON (source + "/packages/coding-agent/package.json");
|
|
# Lockfile root used by the pi.dev installer. It pins the coding agent's
|
|
# runtime dependency tree and is kept in sync with package-lock.json by
|
|
# `npm run check`.
|
|
installLock = source + "/packages/coding-agent/install-lock";
|
|
modelCatalogPin = lib.importJSON ./model-catalog.json;
|
|
modelCatalog = fetchurl {
|
|
name = "pi-model-catalog.json";
|
|
# The typed catalog is the representation whose bytes the revision hashes.
|
|
url = "https://pi.dev/api/models/revisions/${modelCatalogPin.revision}?types=chat,image,classifier";
|
|
sha256 = lib.removePrefix "sha256-" modelCatalogPin.revision;
|
|
};
|
|
|
|
workspacePackages = stdenv.mkDerivation {
|
|
pname = "pi-workspace-packages";
|
|
inherit (packageJson) version;
|
|
src = source;
|
|
|
|
npmDeps = importNpmLock { npmRoot = source; };
|
|
npmRebuildFlags = [ "--ignore-scripts" ];
|
|
|
|
nativeBuildInputs = [
|
|
nodejs
|
|
importNpmLock.npmConfigHook
|
|
];
|
|
|
|
buildPhase = ''
|
|
runHook preBuild
|
|
node packages/ai/scripts/hydrate-model-catalog.ts ${modelCatalog}
|
|
npm run build:offline
|
|
runHook postBuild
|
|
'';
|
|
|
|
installPhase = ''
|
|
runHook preInstall
|
|
|
|
pack_package() {
|
|
local package_dir="$1"
|
|
local output_name="$2"
|
|
local tarball
|
|
|
|
tarball="$(cd "$package_dir" && npm pack --ignore-scripts --silent --pack-destination "$TMPDIR")"
|
|
mv "$TMPDIR/$tarball" "$out/$output_name.tgz"
|
|
}
|
|
|
|
mkdir -p "$out"
|
|
pack_package packages/telemetry telemetry
|
|
pack_package packages/ai ai
|
|
pack_package packages/tui tui
|
|
pack_package packages/agent agent
|
|
pack_package packages/codemode codemode
|
|
pack_package packages/mcp mcp
|
|
pack_package packages/coding-agent coding-agent
|
|
|
|
runHook postInstall
|
|
'';
|
|
};
|
|
|
|
npmDeps = importNpmLock {
|
|
npmRoot = installLock;
|
|
# The install lock points internal packages at registry releases. Replace
|
|
# them with the packages built from this checkout.
|
|
packageSourceOverrides = {
|
|
"node_modules/@earendil-works/pi-agent-core" = workspacePackages + "/agent.tgz";
|
|
"node_modules/@earendil-works/pi-ai" = workspacePackages + "/ai.tgz";
|
|
"node_modules/@earendil-works/pi-codemode" = workspacePackages + "/codemode.tgz";
|
|
"node_modules/@earendil-works/pi-coding-agent" = workspacePackages + "/coding-agent.tgz";
|
|
"node_modules/@earendil-works/pi-mcp" = workspacePackages + "/mcp.tgz";
|
|
"node_modules/@earendil-works/pi-telemetry" = workspacePackages + "/telemetry.tgz";
|
|
"node_modules/@earendil-works/pi-tui" = workspacePackages + "/tui.tgz";
|
|
};
|
|
};
|
|
in
|
|
stdenv.mkDerivation {
|
|
pname = "pi";
|
|
inherit (packageJson) version;
|
|
src = installLock;
|
|
inherit npmDeps;
|
|
|
|
npmRebuildFlags = [ "--ignore-scripts" ];
|
|
|
|
nativeBuildInputs = [
|
|
nodejs
|
|
importNpmLock.npmConfigHook
|
|
makeWrapper
|
|
]
|
|
++ lib.optionals stdenv.hostPlatform.isLinux [ autoPatchelfHook ];
|
|
|
|
buildInputs = [ nodejs ] ++ lib.optionals stdenv.hostPlatform.isLinux [
|
|
stdenv.cc.cc.lib
|
|
libxcb
|
|
];
|
|
|
|
dontBuild = true;
|
|
dontStrip = true;
|
|
|
|
installPhase = ''
|
|
runHook preInstall
|
|
|
|
mkdir -p "$out/lib/pi" "$out/bin"
|
|
cp -R . "$out/lib/pi"
|
|
|
|
# The wrapper runs Pi with an absolute Node path. Node is only on PATH so Pi can
|
|
# find npm for package installs; append it so that shells started by the
|
|
# bash tool keep the user's node/npm (e.g. from mise or nvm). fd, ripgrep,
|
|
# and the clipboard tools are Pi's own dependencies and take precedence.
|
|
makeWrapper ${nodejs}/bin/node "$out/bin/pi" \
|
|
--add-flags "$out/lib/pi/node_modules/@earendil-works/pi-coding-agent/dist/bundle/cli.js" \
|
|
--suffix PATH : ${lib.makeBinPath [ nodejs ]} \
|
|
--prefix PATH : ${
|
|
lib.makeBinPath (
|
|
[
|
|
fd
|
|
ripgrep
|
|
]
|
|
++ lib.optionals stdenv.hostPlatform.isLinux [
|
|
wl-clipboard
|
|
xclip
|
|
]
|
|
)
|
|
}
|
|
|
|
runHook postInstall
|
|
'';
|
|
|
|
doInstallCheck = true;
|
|
installCheckPhase = ''
|
|
runHook preInstallCheck
|
|
test "$("$out/bin/pi" --version)" = "${packageJson.version}"
|
|
# Load host-platform TUI helpers directly so missing native dependencies
|
|
# fail the build rather than silently disabling clipboard support.
|
|
${nodejs}/bin/node -e \
|
|
"const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const dir = '$out/lib/pi/node_modules/@earendil-works/pi-tui/native/' + process.platform + '/prebuilds/' + process.platform + '-' + process.arch;
|
|
if (fs.existsSync(dir)) {
|
|
for (const file of fs.readdirSync(dir)) {
|
|
if (file.endsWith('.node')) require(path.join(dir, file));
|
|
}
|
|
}"
|
|
${nodejs}/bin/node -e \
|
|
"require('$out/lib/pi/node_modules/@silvia-odwyer/photon-node')"
|
|
runHook postInstallCheck
|
|
'';
|
|
|
|
meta = {
|
|
description = packageJson.description;
|
|
homepage = "https://pi.dev";
|
|
license = lib.licenses.mit;
|
|
mainProgram = "pi";
|
|
platforms = [
|
|
"aarch64-darwin"
|
|
"aarch64-linux"
|
|
"x86_64-darwin"
|
|
"x86_64-linux"
|
|
];
|
|
sourceProvenance = with lib.sourceTypes; [
|
|
fromSource
|
|
binaryNativeCode
|
|
];
|
|
};
|
|
}
|