This repository has no description
6.0 kB
161 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 lib.filterAttrs (k: v: k != "hash" && k != "version" && k != "deps" && v != null) p
52 else
53 p;
54
55 cleanSettings = config.settings // {
56 packages = map cleanPackage (config.settings.packages or [ ]);
57 };
58
59 settingsFile = pkgs.writeText "pi-settings.json" (builtins.toJSON cleanSettings);
60 in
61 {
62 _class = "wrapper";
63
64 options = {
65 settings = lib.mkOption {
66 type = lib.types.submodule {
67 freeformType = (pkgs.formats.json { }).type;
68 options.packages = lib.mkOption {
69 type = lib.types.listOf (
70 lib.types.either lib.types.str (
71 lib.types.submodule {
72 freeformType = (pkgs.formats.json { }).type;
73 options.hash = lib.mkOption {
74 type = lib.types.str;
75 description = "Fixed-output hash for fetching this npm package.";
76 };
77 options.source = lib.mkOption {
78 type = lib.types.str;
79 description = "Package source (e.g. npm:package-name).";
80 };
81 options.version = lib.mkOption {
82 type = lib.types.str;
83 description = "Exact version to pin (e.g. 1.0.3). Ensures reproducible fetching.";
84 };
85 options.deps = lib.mkOption {
86 type = lib.types.listOf (lib.types.attrsOf lib.types.anything);
87 default = [ ];
88 description = "Transitive npm dependencies as {source, version, hash} attrsets.";
89 };
90 options.extensions = lib.mkOption {
91 type = lib.types.nullOr (lib.types.listOf lib.types.str);
92 default = null;
93 description = ''
94 Extension filter patterns following pi's native syntax.
95 When null (default), all extensions from the package are loaded.
96 An empty list explicitly disables all extensions.
97 Patterns use prefixes to control inclusion:
98 - `"!pattern"` — exclude matching extensions
99 - `"+pattern"` — force-include (overrides excludes)
100 - `"-pattern"` — force-exclude (overrides includes)
101 - `"path"` — plain path to include
102 '';
103 example = [
104 "!./extensions/resistance.ts"
105 ];
106 };
107 }
108 )
109 );
110 default = [ ];
111 description = "Pi packages list. Object entries may include a `hash` for Nix fetching.";
112 };
113 };
114 default = { };
115 description = "Pi settings.json content.";
116 };
117
118 pluginOverrides = lib.mkOption {
119 type = lib.types.str;
120 default = "";
121 description = ''
122 Shell commands to run after assembling the plugin prefix.
123 The variable `$out` points to the mutable copy.
124 '';
125 };
126
127 pluginPrefix = lib.mkOption {
128 type = lib.types.package;
129 description = "The final npm prefix with all plugins merged and overrides applied.";
130 default = pkgs.runCommand "pi-plugins" { } ''
131 mkdir -p $out
132 ${lib.concatMapStringsSep "\n" (p: "cp -a --no-preserve=mode ${p}/* $out/") fetchedPlugins}
133 ${lib.optionalString (config.pluginOverrides != "") config.pluginOverrides}
134 '';
135 };
136 };
137
138 config.package = lib.mkDefault (
139 self.legacyPackages.${pkgs.system}.llm.pi.overrideAttrs (old: {
140 postInstall = (old.postInstall or "") + ''
141 patch $out/lib/node_modules/@mariozechner/pi-coding-agent/dist/core/settings-manager.js \
142 ${./settings-env.patch}
143 '';
144 })
145 );
146
147 config.extraPackages = [ pkgs.nodejs ];
148
149 config.env.npm_config_prefix = "${config.pluginPrefix}";
150 config.env.PI_SETTINGS_FILE = "${settingsFile}";
151
152 config.meta.maintainers = [
153 {
154 name = "lassulus";
155 github = "lassulus";
156 githubId = 621375;
157 }
158 ];
159 config.meta.platforms = lib.platforms.all;
160 };
161}