forked from
lass.pds.lassul.us/superconfig
This repository has no description
6.4 kB
170 lines
1{ self, ... }:
2{
3 flake.wrapperModules.pi =
4 {
5 config,
6 lib,
7 ...
8 }:
9 let
10 pkgs = config.pkgs;
11
12 # Extract npm packages with hashes from settings.packages
13 npmPackages = lib.filter (p: builtins.isAttrs p && p ? hash) (config.settings.packages or [ ]);
14
15 # Fetch an npm tarball and unpack it into lib/node_modules/<name>
16 fetchPlugin =
17 pkg:
18 let
19 source = pkg.source or pkg;
20 name = lib.removePrefix "npm:" source;
21 tarball = pkgs.fetchurl {
22 url = "https://registry.npmjs.org/${name}/-/${name}-${pkg.version}.tgz";
23 hash = pkg.hash;
24 };
25 deps = map fetchPlugin (pkg.deps or [ ]);
26 in
27 pkgs.runCommand "pi-plugin-${name}"
28 {
29 nativeBuildInputs = [
30 pkgs.gnutar
31 pkgs.gzip
32 ];
33 }
34 ''
35 mkdir -p $out/lib/node_modules/${name}
36 tar xzf ${tarball} --strip-components=1 -C $out/lib/node_modules/${name}
37 ${lib.concatMapStringsSep "\n" (
38 dep:
39 "mkdir -p $out/lib/node_modules/${name}/node_modules"
40 + "\ncp -a ${dep}/lib/node_modules/* $out/lib/node_modules/${name}/node_modules/"
41 + "\ncp -a ${dep}/lib/node_modules/* $out/lib/node_modules/"
42 ) deps}
43 '';
44
45 fetchedPlugins = map fetchPlugin npmPackages;
46
47 # Strip hash and null-valued attrs from package entries before writing settings.json
48 cleanPackage =
49 p:
50 if builtins.isAttrs p then
51 let
52 cleaned = lib.filterAttrs (k: v: k != "hash" && k != "version" && k != "deps" && v != null) p;
53 # Embed version into source spec so pi treats it as pinned (e.g. npm:pi-hooks@1.0.3)
54 withVersion =
55 if p ? version && p ? source && !(lib.hasInfix "@" (lib.removePrefix "npm:" p.source)) then
56 cleaned // { source = "${p.source}@${p.version}"; }
57 else
58 cleaned;
59 in
60 withVersion
61 else
62 p;
63
64 cleanSettings = config.settings // {
65 packages = map cleanPackage (config.settings.packages or [ ]);
66 };
67
68 settingsFile = pkgs.writeText "pi-settings.json" (builtins.toJSON cleanSettings);
69 in
70 {
71 _class = "wrapper";
72
73 options = {
74 settings = lib.mkOption {
75 type = lib.types.submodule {
76 freeformType = (pkgs.formats.json { }).type;
77 options.packages = lib.mkOption {
78 type = lib.types.listOf (
79 lib.types.either lib.types.str (
80 lib.types.submodule {
81 freeformType = (pkgs.formats.json { }).type;
82 options.hash = lib.mkOption {
83 type = lib.types.str;
84 description = "Fixed-output hash for fetching this npm package.";
85 };
86 options.source = lib.mkOption {
87 type = lib.types.str;
88 description = "Package source (e.g. npm:package-name).";
89 };
90 options.version = lib.mkOption {
91 type = lib.types.str;
92 description = "Exact version to pin (e.g. 1.0.3). Ensures reproducible fetching.";
93 };
94 options.deps = lib.mkOption {
95 type = lib.types.listOf (lib.types.attrsOf lib.types.anything);
96 default = [ ];
97 description = "Transitive npm dependencies as {source, version, hash} attrsets.";
98 };
99 options.extensions = lib.mkOption {
100 type = lib.types.nullOr (lib.types.listOf lib.types.str);
101 default = null;
102 description = ''
103 Extension filter patterns following pi's native syntax.
104 When null (default), all extensions from the package are loaded.
105 An empty list explicitly disables all extensions.
106 Patterns use prefixes to control inclusion:
107 - `"!pattern"` — exclude matching extensions
108 - `"+pattern"` — force-include (overrides excludes)
109 - `"-pattern"` — force-exclude (overrides includes)
110 - `"path"` — plain path to include
111 '';
112 example = [
113 "!./extensions/resistance.ts"
114 ];
115 };
116 }
117 )
118 );
119 default = [ ];
120 description = "Pi packages list. Object entries may include a `hash` for Nix fetching.";
121 };
122 };
123 default = { };
124 description = "Pi settings.json content.";
125 };
126
127 pluginOverrides = lib.mkOption {
128 type = lib.types.str;
129 default = "";
130 description = ''
131 Shell commands to run after assembling the plugin prefix.
132 The variable `$out` points to the mutable copy.
133 '';
134 };
135
136 pluginPrefix = lib.mkOption {
137 type = lib.types.package;
138 description = "The final npm prefix with all plugins merged and overrides applied.";
139 default = pkgs.runCommand "pi-plugins" { } ''
140 mkdir -p $out
141 ${lib.concatMapStringsSep "\n" (p: "cp -a --no-preserve=mode ${p}/* $out/") fetchedPlugins}
142 ${lib.optionalString (config.pluginOverrides != "") config.pluginOverrides}
143 '';
144 };
145 };
146
147 config.package = lib.mkDefault (
148 self.legacyPackages.${pkgs.system}.llm.pi.overrideAttrs (old: {
149 postInstall = (old.postInstall or "") + ''
150 patch $out/lib/node_modules/@mariozechner/pi-coding-agent/dist/core/settings-manager.js \
151 ${./settings-env.patch}
152 '';
153 })
154 );
155
156 config.extraPackages = [ pkgs.nodejs ];
157
158 config.env.npm_config_prefix = "${config.pluginPrefix}";
159 config.env.PI_SETTINGS_FILE = "${settingsFile}";
160
161 config.meta.maintainers = [
162 {
163 name = "lassulus";
164 github = "lassulus";
165 githubId = 621375;
166 }
167 ];
168 config.meta.platforms = lib.platforms.all;
169 };
170}