add oh-my-pi

This commit is contained in:
2026-05-22 15:42:45 +02:00
parent 65de45f673
commit 419c218ce1
7 changed files with 324 additions and 16 deletions
Generated
+82 -1
View File
@@ -21,6 +21,29 @@
"type": "github"
}
},
"bun2nix": {
"inputs": {
"flake-parts": "flake-parts",
"nixpkgs": [
"nixpkgs"
],
"systems": "systems",
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1778446047,
"narHash": "sha256-oQvcadh2BCkrog+SGrG6YffKJrveYpjj3TdQJWaKhaM=",
"owner": "nix-community",
"repo": "bun2nix",
"rev": "f2bc12af1a6369648aac41041ceeaa0b866599c6",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "bun2nix",
"type": "github"
}
},
"cf": {
"locked": {
"lastModified": 1756852014,
@@ -120,9 +143,30 @@
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"bun2nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1777988971,
"narHash": "sha256-qIoWPDs+0/8JecyYgE3gpKQxW/4bLW/gp45vow9ioCQ=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "0678d8986be1661af6bb555f3489f2fdfc31f6ff",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
"systems": "systems_2"
},
"locked": {
"lastModified": 1731533236,
@@ -435,6 +479,7 @@
"root": {
"inputs": {
"acer-wmi-ext": "acer-wmi-ext",
"bun2nix": "bun2nix",
"claude-code": "claude-code",
"home-manager": "home-manager",
"lanzaboote": "lanzaboote",
@@ -484,6 +529,42 @@
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"bun2nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1775636079,
"narHash": "sha256-pc20NRoMdiar8oPQceQT47UUZMBTiMdUuWrYu2obUP0=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "790751ff7fd3801feeaf96d7dc416a8d581265ba",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
}
},
"root": "root",
+4
View File
@@ -43,6 +43,10 @@
url = "github:sudosubin/nix-skills";
inputs.nixpkgs.follows = "nixpkgs";
};
bun2nix = {
url = "github:nix-community/bun2nix";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixos-hardware, ... }@inputs:
+1
View File
@@ -66,6 +66,7 @@
nodejs_24
codex
claude-code
oh-my-pi
winbox4
amdgpu_top
dua
+2 -1
View File
@@ -12,6 +12,7 @@
ntfsplus,
nix-skills,
nixpkgs-linuxeol,
bun2nix,
...
}:
hardwareConfig: hostConfig:
@@ -31,7 +32,7 @@ nixpkgs.lib.nixosSystem {
claude-code.overlays.default
acer-wmi-ext.overlays.default
nix-skills.overlays.default
];
] ++ (import ../overlays/pkgs.nix { inherit bun2nix; });
nix.settings = {
substituters = [ "https://claude-code.cachix.org" ];
trusted-public-keys = [ "claude-code.cachix.org-1:YeXf2aNu7UTX8Vwrze0za1WEDS+4DuI2kVeWEE4fsRk=" ];
-5
View File
@@ -4,11 +4,6 @@
# Allow unfree packages
nixpkgs.config.allowUnfree = true;
# Overlay different packages on top of nixpkgs
nixpkgs.overlays = [
(import ../../overlays/pkgs.nix)
];
# Ventoy has some blobs making it insecure
nixpkgs.config.permittedInsecurePackages = [
"ventoy-qt5-1.1.12"
+11 -6
View File
@@ -1,9 +1,14 @@
self: super:
{
opencode-claude-auth = super.callPackage ../pkgs/opencode-claude-auth {};
{ bun2nix }:
[
bun2nix.overlays.default
(final: prev: {
oh-my-pi = final.callPackage ../pkgs/oh-my-pi { inherit (final) bun2nix; };
opencode-claude-auth = prev.callPackage ../pkgs/opencode-claude-auth { };
# Build failure 08.05.2026
# https://github.com/NixOS/nixpkgs/issues/513245#issuecomment-4320293674
openldap = super.openldap.overrideAttrs {
doCheck = !super.stdenv.hostPlatform.isi686;
openldap = prev.openldap.overrideAttrs {
doCheck = !prev.stdenv.hostPlatform.isi686;
};
}
})
]
+221
View File
@@ -0,0 +1,221 @@
{ bun2nix, fetchFromGitHub, fetchurl, fetchzip, runCommand, python3, pkgs, stdenv, ... }:
# NOTE: This derivation works around two open bun2nix bugs. Remove the
# workarounds and simplify once they are fixed upstream.
#
# Bug 1 — missing .npm manifest cache files
# https://github.com/nix-community/bun2nix/issues/77
#
# bun's install cache requires two kinds of entries per package:
# - the extracted package directory (e.g. handlebars@4.7.9@@@1/)
# - a hashed .npm manifest file (e.g. 02dd05ab1686ff3a.npm)
# bun2nix only provides the former. bun therefore fetches the manifest from
# the registry during the "Resolving" phase, which fails in the Nix sandbox.
#
# Workaround: pass --offline to bun install. This tells bun to skip manifest
# fetches and trust the lockfile for resolution instead.
#
# Bug 2 — catalog: specifiers still trigger network resolution with --offline
# https://github.com/nix-community/bun2nix/issues/77 (same thread)
#
# bun2nix's bunResolveCatalogRefs rewrites "catalog:" specifiers in
# package.json to the version *range* from the catalog table (e.g. "^1.3.14")
# rather than the exact pinned version from bun.lock's packages section.
# Even with --offline, bun reads the catalog table from bun.lock and tries to
# resolve those ranges, hitting the network.
#
# Workaround: the Python script below pre-processes the source before the
# build. It pins every dep in every workspace package.json to the exact
# version from bun.lock's packages section (so no ranges remain for bun to
# resolve), and strips the catalog/catalogs keys from bun.lock entirely.
#
# bun.lock is JSONC (trailing-comma JSON) so we parse it with Python's stdlib
# json after stripping trailing commas with a regex.
#
# Additionally, oh-my-pi's bun.lock was generated with bun >=1.3.14 which uses
# a different Wyhash seed for cache keys than nixpkgs's bun 1.3.13. Bumping bun
# globally breaks other packages (e.g. opencode), so instead we patch the
# generated wrapper script in postInstall to reference bun 1.3.14 directly.
# Hashes from https://github.com/NixOS/nixpkgs/pull/519796
let
version = "15.2.1";
# bun 1.3.14 — needed for correct cache key hashes; scoped to this package.
bun_1_3_14 = pkgs.bun.overrideAttrs (_: {
version = "1.3.14";
src =
let
sources = {
"aarch64-darwin" = fetchurl {
url = "https://github.com/oven-sh/bun/releases/download/bun-v1.3.14/bun-darwin-aarch64.zip";
hash = "sha256-2LliIYKK1vl6x6wKt+lYcjQa92MAHogD6CZ2UsJlJiA=";
};
"aarch64-linux" = fetchurl {
url = "https://github.com/oven-sh/bun/releases/download/bun-v1.3.14/bun-linux-aarch64.zip";
hash = "sha256-on/7Y6gxA3WDbg1vZorhf6jY0YuIw3yCHGUzGXOhmjs=";
};
"x86_64-darwin" = fetchurl {
url = "https://github.com/oven-sh/bun/releases/download/bun-v1.3.14/bun-darwin-x64-baseline.zip";
hash = "sha256-PjWtb1OXGpg0v55nhuKt9ytfGSHMmpxf3gc9KXKUQHY=";
};
"x86_64-linux" = fetchurl {
url = "https://github.com/oven-sh/bun/releases/download/bun-v1.3.14/bun-linux-x64.zip";
hash = "sha256-lR7iruhV8IWVruxiJSJqKY0/6oOj3NZGXAnLzN9+hI8=";
};
};
in
sources.${stdenv.hostPlatform.system} or (throw "bun 1.3.14 not available for ${stdenv.hostPlatform.system}");
});
src = fetchFromGitHub {
owner = "can1357";
repo = "oh-my-pi";
rev = "v${version}";
hash = "sha256-fztQJrhDG5ZbTlgqoHA96eCgwYm5WIna3mAPlCDWYLM=";
};
# The workspace source for @oh-my-pi/pi-natives has no pre-built .node
# binaries — those only exist in the npm tarball. Fetch it so we can copy
# the platform binaries into packages/natives/native/ before the build.
piNativesTarball = fetchzip {
url = "https://registry.npmjs.org/@oh-my-pi/pi-natives/-/pi-natives-${version}.tgz";
hash = "sha256-mEEnvTNxWFVSs1An61K83sSjUJ5bz4yrluwZvz1+6fg=";
stripRoot = false;
};
srcWithBunNix = runCommand "oh-my-pi-src" {
nativeBuildInputs = [ bun2nix bun_1_3_14 python3 ];
} ''
cp -r ${src} $out
chmod -R u+w $out
# Copy pre-built .node binaries from the npm tarball into the workspace
# source so the runtime can load the native addon without building Rust.
cp ${piNativesTarball}/package/native/*.node $out/packages/natives/native/
bun2nix --lock-file $out/bun.lock --output-file $out/bun.nix
python3 - "$out" << 'EOF'
import sys, re, json, os
root = sys.argv[1]
lock_path = os.path.join(root, "bun.lock")
raw = open(lock_path).read()
lock = json.loads(re.sub(r',(\s*[}\]])', r'\1', raw))
packages = lock.get("packages", {})
catalog = lock.get("catalog", {})
catalogs = lock.get("catalogs", {})
# Build name -> exact resolved version from the packages section.
resolved = {}
for name, entry in packages.items():
if isinstance(entry, list) and entry and isinstance(entry[0], str):
spec = entry[0]
if spec.startswith(name + "@"):
resolved[name] = spec[len(name) + 1:]
def pin(name, spec):
"""Pin a dep specifier to its exact resolved version from bun.lock."""
if not isinstance(spec, str):
return spec
# catalog: specifiers resolve via catalog table then pinned version.
if spec.startswith("catalog:"):
cname = spec[len("catalog:"):]
table = catalog if cname == "" else catalogs.get(cname, {})
rv = resolved.get(name)
cv = table.get(name)
if isinstance(rv, str) and rv.startswith("workspace:"):
return "workspace:*"
if isinstance(rv, str):
return rv
if isinstance(cv, str):
return cv
return spec
# Any npm version range pin to exact resolved version.
if not spec.startswith(("workspace:", "file:", "link:", "git", "http", "/")):
rv = resolved.get(name)
if isinstance(rv, str) and rv.startswith("workspace:"):
return "workspace:*"
if isinstance(rv, str):
return rv
return spec
sections = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]
def rewrite(holder):
for sec in sections:
deps = holder.get(sec)
if isinstance(deps, dict):
for name in list(deps):
deps[name] = pin(name, deps[name])
# Rewrite bun.lock workspaces and drop the catalog tables.
for ws in lock.get("workspaces", {}).values():
rewrite(ws)
lock.pop("catalog", None)
lock.pop("catalogs", None)
open(lock_path, "w").write(json.dumps(lock, indent=2) + "\n")
# Rewrite each workspace package.json (root "" included).
for ws_dir in lock.get("workspaces", {}):
pkg_path = os.path.join(root, ws_dir, "package.json")
if not os.path.exists(pkg_path):
continue
pkg = json.loads(open(pkg_path).read())
rewrite(pkg)
open(pkg_path, "w").write(json.dumps(pkg, indent=2) + "\n")
EOF
'';
in
(bun2nix.writeBunApplication {
pname = "omp";
inherit version;
src = srcWithBunNix;
# oh-my-pi requires bun >=1.3.14 at runtime. writeBunApplication prepends
# pkgs.bun (1.3.13) to PATH in the startup script, so we use an absolute
# path to bun 1.3.14 instead of relying on PATH resolution.
#
# writeBunApplication's installPhase does `cd $out/share/$pname` before
# exec, so $PWD is always the store path. OLDPWD is set by bash's cd to the
# user's original directory. We cd back so omp's process.cwd() is correct,
# and use an absolute path to the entry point so bun resolves modules from
# the store regardless of cwd.
# At this point the wrapper has already done `cd $out/share/omp`, so $PWD
# is the store package dir and OLDPWD is the user's original directory.
# Capture the store dir, cd back to the user's dir so omp's process.cwd()
# is correct, then exec bun with an absolute path so module resolution
# still works from the store.
startScript = ''
_omp_pkg="$PWD"
cd "''${OLDPWD:-$PWD}"
exec ${bun_1_3_14}/bin/bun run "$_omp_pkg/packages/coding-agent/src/cli.ts" "$@"
'';
dontUseBunBuild = true;
dontUseBunCheck = true;
# --offline: workaround for Bug 1 above (missing .npm manifest cache files).
bunInstallFlags = [ "--offline" "--linker=isolated" "--ignore-scripts" ];
# Generate the docs index embedded into the binary at build time.
# The prepack script reads docs/**/*.md and emits docs-index.generated.ts.
postBunNodeModulesInstallPhase = ''
${bun_1_3_14}/bin/bun run packages/coding-agent/scripts/generate-docs-index.ts
'';
bunDeps = bun2nix.fetchBunDeps {
bunNix = "${srcWithBunNix}/bun.nix";
};
meta = {
description = "AI coding agent for the terminal batteries included";
homepage = "https://omp.sh";
mainProgram = "omp";
};
})