1
0
mirror of https://github.com/kmein/niveum synced 2026-03-21 20:31:07 +01:00

19 Commits

Author SHA1 Message Date
fde5e535d6 opencrow: update 2026-03-21 20:15:04 +01:00
99440cbd04 niri: configure for me 2026-03-21 20:14:53 +01:00
1e14296522 spring equinox 2026-03-21 20:14:53 +01:00
cf252fc262 get upstream niri, remove nix-topology 2026-03-21 20:14:53 +01:00
ce2ec87583 meteora: whitelist 2026-03-21 20:14:53 +01:00
6bceb298ad update 2026-03-17 21:06:14 +01:00
df3d15355a ferdium 2026-03-17 21:06:06 +01:00
696ed782b9 update secrets 2026-03-17 21:06:00 +01:00
d3d6a413e6 pi: use from @lassulus 2026-03-17 21:01:30 +01:00
b9a222855e pi: update plugins 2026-03-04 17:59:51 +01:00
f8bb3c04bf pdf-ocr: fix 2026-03-04 17:59:51 +01:00
dce42c7e80 mp3player-write: do not check disk space 2026-03-04 17:59:51 +01:00
5bedd897c9 switch manakish and kabsa 2026-03-04 17:59:51 +01:00
d952ecf17a meteora: host on ful 2026-03-04 17:59:51 +01:00
df36954fed kpaste: reenable 2026-03-04 17:59:51 +01:00
697100f85f cyberlocker-tools: reenable 2026-03-04 17:59:51 +01:00
8eccb752dc getty: always enable autologinOnce 2026-03-04 17:59:51 +01:00
6bb16ff6ed Revert "cuda: build ollama"
This reverts commit fb86f8c7f7.
2026-03-04 17:59:51 +01:00
8fe9e80522 panoptikon: use from new repo 2026-03-04 17:59:51 +01:00
22 changed files with 1024 additions and 192 deletions

View File

@@ -26,8 +26,6 @@
}) })
]; ];
services.getty.autologinOnce = lib.mkForce false;
# to run nspawn in nix sandbox # to run nspawn in nix sandbox
nix.settings = { nix.settings = {
auto-allocate-uids = true; auto-allocate-uids = true;

View File

@@ -5,6 +5,7 @@
... ...
}: }:
let let
niri-config-with = settings: (pkgs.niphas-niri.passthru.configuration.apply settings).wrapper;
commaSep = builtins.concatStringsSep ","; commaSep = builtins.concatStringsSep ",";
xkbOptions = [ xkbOptions = [
@@ -114,7 +115,17 @@ in
if [ -n "$SWAYSOCK" ]; then if [ -n "$SWAYSOCK" ]; then
swaymsg -s $SWAYSOCK 'input * xkb_layout "${defaultLanguage.code},${code}"' swaymsg -s $SWAYSOCK 'input * xkb_layout "${defaultLanguage.code},${code}"'
swaymsg -s $SWAYSOCK 'input * xkb_variant "${defaultLanguage.variant},${variant}"' swaymsg -s $SWAYSOCK 'input * xkb_variant "${defaultLanguage.variant},${variant}"'
swaymsg -s $SWAYSOCK 'input * xkb_options "${lib.concatStringsSep "," xkbOptions}"' swaymsg -s $SWAYSOCK 'input * xkb_options "${commaSep xkbOptions}"'
elif [ -n "$NIRI_SOCKET" ]; then
${lib.getExe pkgs.niphas-niri} msg action load-config-file --path ${
(niri-config-with {
settings.input.keyboard.xkb = {
layout = "${defaultLanguage.code},${code}";
variant = "${defaultLanguage.variant},${variant}";
options = commaSep xkbOptions;
};
}).passthru.configuration."config.kdl".path
}
elif [ -n "$HYPRLAND_INSTANCE_SIGNATURE" ]; then elif [ -n "$HYPRLAND_INSTANCE_SIGNATURE" ]; then
hyprctl keyword input:kb_variant "" # otherwise we end up with an invalid combination for a short while hyprctl keyword input:kb_variant "" # otherwise we end up with an invalid combination for a short while
hyprctl keyword input:kb_layout "${defaultLanguage.code},${code}" hyprctl keyword input:kb_layout "${defaultLanguage.code},${code}"

View File

@@ -71,6 +71,7 @@ in
'') '')
# INTERNET # INTERNET
telegram-desktop telegram-desktop
ferdium
# FILE MANAGERS # FILE MANAGERS
lf lf
# MEDIA # MEDIA
@@ -193,6 +194,8 @@ in
#krebs #krebs
pkgs.nur.repos.mic92.ircsink pkgs.nur.repos.mic92.ircsink
cyberlocker-tools
kpaste
(haskellPackages.ghcWithHoogle (hs: [ (haskellPackages.ghcWithHoogle (hs: [
hs.text hs.text

View File

@@ -8,7 +8,7 @@
# https://danth.github.io/stylix/tricks.html # https://danth.github.io/stylix/tricks.html
stylix.enable = true; stylix.enable = true;
stylix.base16Scheme = "${pkgs.base16-schemes}/share/themes/gruvbox-dark-medium.yaml"; stylix.base16Scheme = "${pkgs.base16-schemes}/share/themes/gruvbox-light-medium.yaml";
stylix.cursor = { stylix.cursor = {
name = "capitaine-cursors-white"; name = "capitaine-cursors-white";

206
flake.lock generated
View File

@@ -137,7 +137,7 @@
}, },
"buildbot-nix": { "buildbot-nix": {
"inputs": { "inputs": {
"flake-parts": "flake-parts_3", "flake-parts": "flake-parts_2",
"hercules-ci-effects": "hercules-ci-effects", "hercules-ci-effects": "hercules-ci-effects",
"nixpkgs": [ "nixpkgs": [
"stockholm", "stockholm",
@@ -219,24 +219,6 @@
} }
}, },
"flake-parts": { "flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1765835352,
"narHash": "sha256-XswHlK/Qtjasvhd1nOa1e8MgZ8GS//jBoTqWtrS1Giw=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "a34fae9c08a15ad73f295041fec82323541400a9",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-parts_2": {
"inputs": { "inputs": {
"nixpkgs-lib": [ "nixpkgs-lib": [
"nur", "nur",
@@ -257,7 +239,7 @@
"type": "github" "type": "github"
} }
}, },
"flake-parts_3": { "flake-parts_2": {
"inputs": { "inputs": {
"nixpkgs-lib": [ "nixpkgs-lib": [
"stockholm", "stockholm",
@@ -279,7 +261,7 @@
"type": "github" "type": "github"
} }
}, },
"flake-parts_4": { "flake-parts_3": {
"inputs": { "inputs": {
"nixpkgs-lib": [ "nixpkgs-lib": [
"stylix", "stylix",
@@ -464,6 +446,24 @@
"type": "github" "type": "github"
} }
}, },
"meteora": {
"inputs": {
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1773473448,
"narHash": "sha256-RpqPNdf0GTcpROKh6Au77cKUbR3MQyYTOMri8KoyGeU=",
"ref": "refs/heads/master",
"rev": "2fdcc62d97623f10e9a041e5c05b141950866551",
"revCount": 1380,
"type": "git",
"url": "ssh://git@github.com/kmein/meteora.git"
},
"original": {
"type": "git",
"url": "ssh://git@github.com/kmein/meteora.git"
}
},
"naersk": { "naersk": {
"inputs": { "inputs": {
"fenix": [ "fenix": [
@@ -489,6 +489,7 @@
}, },
"niphas": { "niphas": {
"inputs": { "inputs": {
"niri": "niri",
"nixpkgs": [ "nixpkgs": [
"nixpkgs-unstable" "nixpkgs-unstable"
], ],
@@ -496,11 +497,11 @@
"wrappers": "wrappers" "wrappers": "wrappers"
}, },
"locked": { "locked": {
"lastModified": 1771601908, "lastModified": 1774116133,
"narHash": "sha256-lqscsSHms5xk8iOOEj0J6XtrIcZp7/TXN4iiQjNeXzM=", "narHash": "sha256-lR6IPtSbw/+xME7TMCHXziis8Lxy6AAkRhRpDuVI33E=",
"ref": "refs/heads/master", "ref": "refs/heads/master",
"rev": "13ee868d5d297fbcfa1370cfff67e5c7f5e3d0aa", "rev": "7dd94c7b96b9f8289e0decdf8d2f75f7b652ef3e",
"revCount": 42, "revCount": 53,
"type": "git", "type": "git",
"url": "https://code.kmein.de/kfm/niphas" "url": "https://code.kmein.de/kfm/niphas"
}, },
@@ -509,6 +510,28 @@
"url": "https://code.kmein.de/kfm/niphas" "url": "https://code.kmein.de/kfm/niphas"
} }
}, },
"niri": {
"inputs": {
"nixpkgs": [
"niphas",
"nixpkgs"
],
"rust-overlay": "rust-overlay"
},
"locked": {
"lastModified": 1773130184,
"narHash": "sha256-3bwx4WqCB06yfQIGB+OgIckOkEDyKxiTD5pOo4Xz2rI=",
"owner": "niri-wm",
"repo": "niri",
"rev": "b07bde3ee82dd73115e6b949e4f3f63695da35ea",
"type": "github"
},
"original": {
"owner": "niri-wm",
"repo": "niri",
"type": "github"
}
},
"nix-index-database": { "nix-index-database": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@@ -529,27 +552,6 @@
"type": "github" "type": "github"
} }
}, },
"nix-topology": {
"inputs": {
"flake-parts": "flake-parts",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1769018862,
"narHash": "sha256-x3eMpPQhZwEDunyaUos084Hx41XwYTi2uHY4Yc4YNlk=",
"owner": "oddlama",
"repo": "nix-topology",
"rev": "a15cac71d3399a4c2d1a3482ae62040a3a0aa07f",
"type": "github"
},
"original": {
"owner": "oddlama",
"repo": "nix-topology",
"type": "github"
}
},
"nix-writers": { "nix-writers": {
"inputs": { "inputs": {
"flake-utils": "flake-utils", "flake-utils": "flake-utils",
@@ -603,21 +605,6 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs-lib": {
"locked": {
"lastModified": 1765674936,
"narHash": "sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"nixpkgs-old": { "nixpkgs-old": {
"locked": { "locked": {
"lastModified": 1682600000, "lastModified": 1682600000,
@@ -651,6 +638,22 @@
} }
}, },
"nixpkgs_2": { "nixpkgs_2": {
"locked": {
"lastModified": 1772479524,
"narHash": "sha256-u7nCaNiMjqvKpE+uZz9hE7pgXXTmm5yvdtFaqzSzUQI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "4215e62dc2cd3bc705b0a423b9719ff6be378a43",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": { "locked": {
"lastModified": 1769598131, "lastModified": 1769598131,
"narHash": "sha256-e7VO/kGLgRMbWtpBqdWl0uFg8Y2XWFMdz0uUJvlML8o=", "narHash": "sha256-e7VO/kGLgRMbWtpBqdWl0uFg8Y2XWFMdz0uUJvlML8o=",
@@ -666,9 +669,25 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs_4": {
"locked": {
"lastModified": 1771369470,
"narHash": "sha256-0NBlEBKkN3lufyvFegY4TYv5mCNHbi5OmBDrzihbBMQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "0182a361324364ae3f436a63005877674cf45efb",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nur": { "nur": {
"inputs": { "inputs": {
"flake-parts": "flake-parts_2", "flake-parts": "flake-parts",
"nixpkgs": [ "nixpkgs": [
"nixpkgs" "nixpkgs"
] ]
@@ -722,11 +741,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1771622489, "lastModified": 1773693388,
"narHash": "sha256-CGAS5ISs+h6GNQwaOLycfbcFRkN0legi/hdDov4Obfk=", "narHash": "sha256-bYppS16wQsnvCpDMVXqUOaYvP2oSqvkzsrYGjJsrt48=",
"owner": "pinpox", "owner": "pinpox",
"repo": "opencrow", "repo": "opencrow",
"rev": "9ec2d17e6c9d45b22b9cca3174b6b1a75758d8f6", "rev": "7f35d518cf0a0a4d5c90fea324425534a947ee33",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -735,6 +754,24 @@
"type": "github" "type": "github"
} }
}, },
"panoptikon": {
"inputs": {
"nixpkgs": "nixpkgs_4"
},
"locked": {
"lastModified": 1771686951,
"narHash": "sha256-+s19xrH0kVVDT48TtpulmuWFMagi/ysjWL+PjKcmF/k=",
"ref": "refs/heads/main",
"rev": "8868eb8736c3395186fa410c040e0000abca56bd",
"revCount": 3,
"type": "git",
"url": "https://code.kmein.de/kfm/panoptikon"
},
"original": {
"type": "git",
"url": "https://code.kmein.de/kfm/panoptikon"
}
},
"retiolum": { "retiolum": {
"locked": { "locked": {
"lastModified": 1756302470, "lastModified": 1756302470,
@@ -759,16 +796,21 @@
"llm-agents": "llm-agents", "llm-agents": "llm-agents",
"menstruation-backend": "menstruation-backend", "menstruation-backend": "menstruation-backend",
"menstruation-telegram": "menstruation-telegram", "menstruation-telegram": "menstruation-telegram",
"meteora": "meteora",
"naersk": "naersk", "naersk": "naersk",
"niphas": "niphas", "niphas": "niphas",
"niri": [
"niphas",
"niri"
],
"nix-index-database": "nix-index-database", "nix-index-database": "nix-index-database",
"nix-topology": "nix-topology",
"nixos-hardware": "nixos-hardware", "nixos-hardware": "nixos-hardware",
"nixpkgs": "nixpkgs_2", "nixpkgs": "nixpkgs_3",
"nixpkgs-old": "nixpkgs-old", "nixpkgs-old": "nixpkgs-old",
"nixpkgs-unstable": "nixpkgs-unstable", "nixpkgs-unstable": "nixpkgs-unstable",
"nur": "nur", "nur": "nur",
"opencrow": "opencrow", "opencrow": "opencrow",
"panoptikon": "panoptikon",
"retiolum": "retiolum", "retiolum": "retiolum",
"scripts": "scripts", "scripts": "scripts",
"stockholm": "stockholm", "stockholm": "stockholm",
@@ -798,6 +840,28 @@
"type": "github" "type": "github"
} }
}, },
"rust-overlay": {
"inputs": {
"nixpkgs": [
"niphas",
"niri",
"nixpkgs"
]
},
"locked": {
"lastModified": 1757989933,
"narHash": "sha256-9cpKYWWPCFhgwQTww8S94rTXgg8Q8ydFv9fXM6I8xQM=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "8249aa3442fb9b45e615a35f39eca2fe5510d7c3",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
}
},
"scripts": { "scripts": {
"inputs": { "inputs": {
"fenix": [ "fenix": [
@@ -853,7 +917,7 @@
"base16-helix": "base16-helix", "base16-helix": "base16-helix",
"base16-vim": "base16-vim", "base16-vim": "base16-vim",
"firefox-gnome-theme": "firefox-gnome-theme", "firefox-gnome-theme": "firefox-gnome-theme",
"flake-parts": "flake-parts_4", "flake-parts": "flake-parts_3",
"gnome-shell": "gnome-shell", "gnome-shell": "gnome-shell",
"nixpkgs": [ "nixpkgs": [
"nixpkgs" "nixpkgs"
@@ -1181,11 +1245,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1770112394, "lastModified": 1772137435,
"narHash": "sha256-H8d0WplmDeuvOM4bsHNt77T6OWiSJMaTP6UG1XyMNxA=", "narHash": "sha256-dqkfxxpIiIs4wdWhT4lfQi1lfA0CgIftPiYGvw0tUOk=",
"owner": "Lassulus", "owner": "Lassulus",
"repo": "wrappers", "repo": "wrappers",
"rev": "23625835eb91d925c498780f5a37442ead1ae8e5", "rev": "4e12f430ae705d9bbb591ca9c51cbccbee050a23",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@@ -14,6 +14,7 @@
niphas.url = "git+https://code.kmein.de/kfm/niphas"; niphas.url = "git+https://code.kmein.de/kfm/niphas";
panoptikon.url = "git+https://code.kmein.de/kfm/panoptikon"; panoptikon.url = "git+https://code.kmein.de/kfm/panoptikon";
nixos-hardware.url = "github:NixOS/nixos-hardware"; nixos-hardware.url = "github:NixOS/nixos-hardware";
niri.url = "github:niri-wm/niri";
nur.url = "github:nix-community/NUR"; nur.url = "github:nix-community/NUR";
retiolum.url = "github:krebs/retiolum"; retiolum.url = "github:krebs/retiolum";
scripts.url = "github:kmein/scripts"; scripts.url = "github:kmein/scripts";
@@ -23,11 +24,11 @@
tinc-graph.url = "github:kmein/tinc-graph"; tinc-graph.url = "github:kmein/tinc-graph";
treefmt-nix.url = "github:numtide/treefmt-nix"; treefmt-nix.url = "github:numtide/treefmt-nix";
voidrice.url = "github:Lukesmithxyz/voidrice"; voidrice.url = "github:Lukesmithxyz/voidrice";
nix-topology.url = "github:oddlama/nix-topology";
wetter.url = "github:4z3/wetter"; wetter.url = "github:4z3/wetter";
wrappers.url = "github:lassulus/wrappers"; wrappers.url = "github:lassulus/wrappers";
llm-agents.url = "github:numtide/llm-agents.nix"; llm-agents.url = "github:numtide/llm-agents.nix";
opencrow.url = "github:pinpox/opencrow"; opencrow.url = "github:pinpox/opencrow";
meteora.url = "git+ssh://git@github.com/kmein/meteora.git";
voidrice.flake = false; voidrice.flake = false;
@@ -53,10 +54,11 @@
autorenkalender.inputs.nixpkgs.follows = "nixpkgs"; autorenkalender.inputs.nixpkgs.follows = "nixpkgs";
home-manager.inputs.nixpkgs.follows = "nixpkgs"; home-manager.inputs.nixpkgs.follows = "nixpkgs";
naersk.inputs.nixpkgs.follows = "nixpkgs"; naersk.inputs.nixpkgs.follows = "nixpkgs";
niri.inputs.nixpkgs.follows = "nixpkgs";
fenix.inputs.nixpkgs.follows = "nixpkgs"; fenix.inputs.nixpkgs.follows = "nixpkgs";
treefmt-nix.inputs.nixpkgs.follows = "nixpkgs"; treefmt-nix.inputs.nixpkgs.follows = "nixpkgs";
nur.inputs.nixpkgs.follows = "nixpkgs"; nur.inputs.nixpkgs.follows = "nixpkgs";
nix-topology.inputs.nixpkgs.follows = "nixpkgs"; niphas.inputs.nixpkgs.follows = "nixpkgs-unstable";
stockholm.inputs.nixpkgs.follows = "nixpkgs"; stockholm.inputs.nixpkgs.follows = "nixpkgs";
menstruation-backend.inputs.nixpkgs.follows = "nixpkgs"; menstruation-backend.inputs.nixpkgs.follows = "nixpkgs";
nix-index-database.inputs.nixpkgs.follows = "nixpkgs"; nix-index-database.inputs.nixpkgs.follows = "nixpkgs";
@@ -64,7 +66,6 @@
stylix.inputs.nixpkgs.follows = "nixpkgs"; stylix.inputs.nixpkgs.follows = "nixpkgs";
tinc-graph.inputs.nixpkgs.follows = "nixpkgs"; tinc-graph.inputs.nixpkgs.follows = "nixpkgs";
wetter.inputs.nixpkgs.follows = "nixpkgs"; wetter.inputs.nixpkgs.follows = "nixpkgs";
niphas.inputs.nixpkgs.follows = "nixpkgs-unstable";
wrappers.inputs.nixpkgs.follows = "nixpkgs"; wrappers.inputs.nixpkgs.follows = "nixpkgs";
opencrow.inputs.nixpkgs.follows = "nixpkgs"; opencrow.inputs.nixpkgs.follows = "nixpkgs";
}; };
@@ -81,8 +82,8 @@
menstruation-telegram, menstruation-telegram,
scripts, scripts,
tinc-graph, tinc-graph,
nix-topology,
llm-agents, llm-agents,
niri,
opencrow, opencrow,
nixpkgs-unstable, nixpkgs-unstable,
nixos-hardware, nixos-hardware,
@@ -96,6 +97,8 @@
stylix, stylix,
voidrice, voidrice,
wetter, wetter,
wrappers,
meteora,
... ...
}: }:
let let
@@ -302,6 +305,7 @@
autorenkalender = autorenkalender.packages.${prev.stdenv.hostPlatform.system}.default; autorenkalender = autorenkalender.packages.${prev.stdenv.hostPlatform.system}.default;
onomap = scripts.packages.${prev.stdenv.hostPlatform.system}.onomap; onomap = scripts.packages.${prev.stdenv.hostPlatform.system}.onomap;
tinc-graph = tinc-graph.packages.${prev.stdenv.hostPlatform.system}.tinc-graph; tinc-graph = tinc-graph.packages.${prev.stdenv.hostPlatform.system}.tinc-graph;
meteora-website = meteora.packages.${prev.stdenv.hostPlatform.system}.website;
# krebs # krebs
brainmelter = prev.callPackage packages/brainmelter.nix { }; brainmelter = prev.callPackage packages/brainmelter.nix { };
@@ -311,7 +315,10 @@
radio-news = prev.callPackage packages/radio-news { }; radio-news = prev.callPackage packages/radio-news { };
untilport = prev.callPackage packages/untilport.nix { }; untilport = prev.callPackage packages/untilport.nix { };
weechat-declarative = prev.callPackage packages/weechat-declarative.nix { }; weechat-declarative = prev.callPackage packages/weechat-declarative.nix { };
pi = prev.callPackage packages/pi.nix { }; pi = prev.callPackage packages/pi {
pkgs = final;
inherit wrappers;
};
# my packages # my packages
betacode = prev.callPackage packages/betacode.nix { }; betacode = prev.callPackage packages/betacode.nix { };
@@ -362,7 +369,7 @@
unicodmenu = prev.callPackage packages/unicodmenu.nix { }; unicodmenu = prev.callPackage packages/unicodmenu.nix { };
vg = prev.callPackage packages/vg.nix { }; vg = prev.callPackage packages/vg.nix { };
vim-kmein = prev.callPackage packages/vim-kmein { }; vim-kmein = prev.callPackage packages/vim-kmein { };
klem = prev.callPackage packages/klem.nix { }; klem = prev.callPackage packages/klem { };
yt-dlp-master = prev.callPackage packages/yt-dlp-master.nix { }; yt-dlp-master = prev.callPackage packages/yt-dlp-master.nix { };
lib = lib // { lib = lib // {
@@ -380,6 +387,7 @@
{ {
nixpkgs.overlays = [ nixpkgs.overlays = [
self.overlays.default self.overlays.default
niri.overlays.default
niphas.overlays.default niphas.overlays.default
panoptikon.overlays.default panoptikon.overlays.default
(final: prev: { (final: prev: {
@@ -390,9 +398,21 @@
user.email = prev.lib.niveum.kieran.email; user.email = prev.lib.niveum.kieran.email;
}; };
}).wrapper; }).wrapper;
niphas-niri =
(prev.niphas-niri.passthru.configuration.apply {
settings.binds = {
"Mod+U".spawn-sh = lib.getExe prev.unicodmenu;
"Mod+P".spawn-sh = lib.getExe prev.rofi-pass-wayland;
"Mod+F12".spawn-sh = lib.getExe (
prev.klem.override {
options = import packages/klem/kmein.nix { pkgs = final; };
}
);
};
}).wrapper;
niphas-editor = prev.niphas-editor.override { niphas-editor = prev.niphas-editor.override {
withCopilot = true; withCopilot = true;
colorscheme = "base16-gruvbox-dark-medium"; colorscheme = "base16-gruvbox-light-medium";
}; };
}) })
]; ];
@@ -410,7 +430,6 @@
} }
agenix.nixosModules.default agenix.nixosModules.default
retiolum.nixosModules.retiolum retiolum.nixosModules.retiolum
nix-topology.nixosModules.default
niphas.nixosModules.nix niphas.nixosModules.nix
niphas.nixosModules.shell niphas.nixosModules.shell
configs/mycelium.nix configs/mycelium.nix
@@ -513,7 +532,7 @@
++ profiles.desktop ++ profiles.desktop
++ [ ++ [
systems/manakish/configuration.nix systems/manakish/configuration.nix
nixos-hardware.nixosModules.lenovo-thinkpad-x230 nixos-hardware.nixosModules.lenovo-thinkpad-x220
]; ];
}; };
kabsa = nixpkgs.lib.nixosSystem { kabsa = nixpkgs.lib.nixosSystem {
@@ -524,7 +543,7 @@
++ profiles.desktop ++ profiles.desktop
++ [ ++ [
systems/kabsa/configuration.nix systems/kabsa/configuration.nix
nixos-hardware.nixosModules.lenovo-thinkpad-x220 nixos-hardware.nixosModules.lenovo-thinkpad-x230
]; ];
}; };
fatteh = nixpkgs.lib.nixosSystem { fatteh = nixpkgs.lib.nixosSystem {
@@ -554,17 +573,10 @@
overlays = [ overlays = [
nur.overlays.default nur.overlays.default
self.overlays.default self.overlays.default
nix-topology.overlays.default
]; ];
}; };
in in
{ {
topology = import nix-topology {
inherit pkgs;
modules = [
{ nixosConfigurations = self.nixosConfigurations; }
];
};
inherit (pkgs) inherit (pkgs)
two56color two56color
avesta avesta

51
packages/klem/kmein.nix Normal file
View File

@@ -0,0 +1,51 @@
{ pkgs, ... }:
let
inherit (pkgs) lib;
in
{
dmenu = "${lib.getExe pkgs.dmenu} -i -p klem";
scripts = {
"p.r paste" = pkgs.writers.writeDash "p.r" ''
${lib.getExe pkgs.curl} -fSs http://p.r --data-binary @- \
| ${lib.getExe' pkgs.coreutils "tail"} --lines=1 \
| ${lib.getExe pkgs.gnused} 's/\\<r\\>/krebsco.de/'
'';
"envs.sh paste" = pkgs.writers.writeDash "envs-host" ''
${lib.getExe pkgs.curl} -F "file=@-" https://envs.sh
'';
# this segfaults
# "envs.sh mirror" = pkgs.writers.writeDash "envs-mirror" ''
# ${pkgs.curl}/bin/curl -F "url=$(${pkgs.coreutils}/bin/cat)" https://envs.sh
# '';
"envs.sh shorten" = pkgs.writers.writeDash "envs-shorten" ''
${lib.getExe pkgs.curl} -F "shorten=$(${lib.getExe' pkgs.coreutils "cat"})" https://envs.sh
'';
"go.r shorten" = pkgs.writers.writeDash "go.r" ''
${lib.getExe pkgs.curl} -fSs http://go.r -F "uri=$(${lib.getExe' pkgs.coreutils "cat"})"
'';
"4d2.org paste" = pkgs.writers.writeDash "4d2-paste" ''
${lib.getExe pkgs.curl} -F "file=@-" https://depot.4d2.org/
'';
"0x0.st shorten" = pkgs.writers.writeDash "0x0.st" ''
${lib.getExe pkgs.curl} -fSs https://0x0.st -F "shorten=$(${lib.getExe' pkgs.coreutils "cat"})"
'';
"rot13" = pkgs.writers.writeDash "rot13" ''
${lib.getExe' pkgs.coreutils "tr"} '[A-Za-z]' '[N-ZA-Mn-za-m]'
'';
"ipa" = pkgs.writers.writeDash "ipa" ''
${lib.getExe pkgs.ipa}
'';
"betacode" = pkgs.writers.writeDash "betacode" ''
${lib.getExe pkgs.betacode}
'';
"curl" = pkgs.writers.writeDash "curl" ''
${lib.getExe pkgs.curl} -fSs "$(${lib.getExe' pkgs.coreutils "cat"})"
'';
ocr = pkgs.writers.writeDash "ocr" ''
${lib.getExe pkgs.tesseract4} -l eng+deu - stdout
'';
emojai = pkgs.writers.writeDash "emojai" ''
${lib.getExe pkgs.curl} https://www.emojai.app/api/generate -X POST -H 'Content-Type: application/json' --data-raw "$(${lib.getExe pkgs.jq} -sR '{emoji:.}')" | ${lib.getExe pkgs.jq} -r .result
'';
};
}

View File

@@ -35,26 +35,6 @@ writers.writeBashBin "mp3player-write" ''
exit 1 exit 1
fi fi
TOTAL_SIZE=0
for f in "''${FILES[@]}"; do
if [ ! -f "$f" ]; then
echo "Warning: File '$f' does not exist, skipping."
continue
fi
FILE_SIZE=$(${lib.getExe' coreutils "stat"} --printf="%s" "$f")
TOTAL_SIZE=$((TOTAL_SIZE + FILE_SIZE / 2))
done
AVAILABLE=$(${lib.getExe' coreutils "df"} --output=avail "$MOUNT_POINT" | ${lib.getExe' coreutils "tail"} -n 1)
AVAILABLE=$((AVAILABLE * 1024))
if [ "$TOTAL_SIZE" -gt "$AVAILABLE" ]; then
echo "Error: Not enough space. Required: $TOTAL_SIZE bytes, Available: $AVAILABLE bytes"
exit 1
fi
echo "Enough space available. Starting conversion..."
sanitize_filename() { sanitize_filename() {
local name local name
name=$(${lib.getExe' coreutils "basename"} "$1") name=$(${lib.getExe' coreutils "basename"} "$1")

View File

@@ -2,7 +2,7 @@
{ {
lib, lib,
writers, writers,
poppler_utils, poppler-utils,
tesseract, tesseract,
coreutils, coreutils,
}: }:
@@ -21,7 +21,7 @@ writers.writeDashBin "pdf-ocr" ''
cd "$tmpdir" cd "$tmpdir"
${lib.getExe' poppler_utils "pdftoppm"} -png "$pdf_path" pdf-ocr ${lib.getExe' poppler-utils "pdftoppm"} -png "$pdf_path" pdf-ocr
for png in pdf-ocr*.png; do for png in pdf-ocr*.png; do
${lib.getExe tesseract} "$png" "$png.txt" 2>/dev/null ${lib.getExe tesseract} "$png" "$png.txt" 2>/dev/null
done done

View File

@@ -1,69 +0,0 @@
{
runCommand,
nodejs,
writeShellApplication,
lib,
jq,
cacert,
pi-llm,
}:
let
# Pre-install pi plugins into a fake npm global prefix
pluginPrefixRaw =
runCommand "pi-plugins-raw"
{
nativeBuildInputs = [
nodejs
cacert
];
outputHashMode = "recursive";
outputHashAlgo = "sha256";
outputHash = "sha256-YrrQ5m8XYKFNR2+dn97GYxKxcWPBndomPZsqKfwD6w0=";
impureEnvVars = [
"http_proxy"
"https_proxy"
];
SSL_CERT_FILE = "${cacert}/etc/ssl/certs/ca-bundle.crt";
}
''
export HOME=$TMPDIR
export npm_config_prefix=$out
npm install -g pi-hooks shitty-extensions
'';
# Remove the resistance extension (annoying terminator quote widget)
pluginPrefix = runCommand "pi-plugins" { } ''
cp -a ${pluginPrefixRaw} $out
chmod -R u+w $out
pkg=$out/lib/node_modules/shitty-extensions/package.json
${lib.getExe jq} '.pi.extensions |= map(select(contains("resistance") | not))' "$pkg" > "$pkg.tmp"
mv "$pkg.tmp" "$pkg"
'';
in
writeShellApplication {
name = "pi";
runtimeInputs = [ nodejs ];
text = ''
set -efu
export npm_config_prefix="${pluginPrefix}"
# Ensure settings.json has our plugins listed
SETTINGS_DIR="''${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}"
SETTINGS_FILE="$SETTINGS_DIR/settings.json"
mkdir -p "$SETTINGS_DIR"
# Add packages to settings if not already present
if [ ! -f "$SETTINGS_FILE" ]; then
echo '{"packages":["npm:pi-hooks","npm:shitty-extensions"]}' > "$SETTINGS_FILE"
else
for pkg in "npm:pi-hooks" "npm:shitty-extensions"; do
if ! grep -q "$pkg" "$SETTINGS_FILE"; then
${lib.getExe jq} --arg p "$pkg" '.packages = ((.packages // []) + [$p] | unique)' "$SETTINGS_FILE" > "$SETTINGS_FILE.tmp"
mv "$SETTINGS_FILE.tmp" "$SETTINGS_FILE"
fi
done
fi
exec ${lib.getExe pi-llm} "$@"
'';
}

70
packages/pi/default.nix Normal file
View File

@@ -0,0 +1,70 @@
{ pkgs, wrappers }:
let
piWrapper = wrappers.lib.wrapModule {
imports = [ ./module.nix ];
inherit pkgs;
settings = {
packages = [
{
source = "npm:pi-hooks";
version = "1.0.3";
hash = "sha256-jU3akfqsWgjvOG+8+Md2qEzkXp48LUaXVncpUMXxy7s=";
deps = [
{
source = "npm:shell-quote";
version = "1.8.3";
hash = "sha256-32QLNUuvjigj1scqLlCVFTfgS3MHm9dBjPk9iVB+IsE=";
}
{
source = "npm:vscode-languageserver-protocol";
version = "3.17.5";
hash = "sha256-dHPrLSFj8/i+oJZE+dgDeJoZXllrZdOUbEFX5YPjzMg=";
deps = [
{
source = "npm:vscode-jsonrpc";
version = "8.2.0";
hash = "sha256-PaRFMcOY8VRQdMtyjjWagi81ufiscXHIR/QvByi5x8s=";
}
{
source = "npm:vscode-languageserver-types";
version = "3.17.5";
hash = "sha256-1nP55/i75RNRvlHFjzLU3PqXpnDruGvGMzaDlMYJysA=";
}
];
}
];
}
{
source = "npm:shitty-extensions";
version = "1.0.9";
hash = "sha256-g26MZ5x4HUcDai4SXPaOEhqgGGqzAI68znnsCbKJv7E=";
extensions = [ "!extensions/resistance.ts" ];
}
];
extensions = [
./questionnaire.ts
];
defaultProvider = "anthropic";
defaultModel = "claude-opus-4-6";
defaultThinkingLevel = "medium";
permissionLevel = "low";
permissionMode = "ask";
permissionConfig.overrides.minimal = [
"nix build *"
"nix eval *"
"nix fmt *"
];
};
pluginOverrides = ''
# Fix keybinding conflicts in extension source
${pkgs.gnused}/bin/sed -i 's/"ctrl+u"/"ctrl+shift+u"/' $out/lib/node_modules/shitty-extensions/extensions/ultrathink.ts
${pkgs.gnused}/bin/sed -i 's/"ctrl+r"/"ctrl+shift+r"/' $out/lib/node_modules/shitty-extensions/extensions/speedreading.ts
# Remove a-nach-b skill
rm -rf $out/lib/node_modules/shitty-extensions/skills/a-nach-b
'';
};
in
piWrapper.wrapper

150
packages/pi/module.nix Normal file
View File

@@ -0,0 +1,150 @@
{
config,
lib,
...
}:
let
pkgs = config.pkgs;
# Extract npm packages with hashes from settings.packages
npmPackages = lib.filter (p: builtins.isAttrs p && p ? hash) (config.settings.packages or [ ]);
# Fetch an npm tarball and unpack it into lib/node_modules/<name>
fetchPlugin =
pkg:
let
source = pkg.source or pkg;
name = lib.removePrefix "npm:" source;
tarball = pkgs.fetchurl {
url = "https://registry.npmjs.org/${name}/-/${name}-${pkg.version}.tgz";
hash = pkg.hash;
};
deps = map fetchPlugin (pkg.deps or [ ]);
in
pkgs.runCommand "pi-plugin-${name}"
{
nativeBuildInputs = [
pkgs.gnutar
pkgs.gzip
];
}
''
mkdir -p $out/lib/node_modules/${name}
tar xzf ${tarball} --strip-components=1 -C $out/lib/node_modules/${name}
${lib.concatMapStringsSep "\n" (
dep:
"mkdir -p $out/lib/node_modules/${name}/node_modules"
+ "\ncp -a ${dep}/lib/node_modules/* $out/lib/node_modules/${name}/node_modules/"
+ "\ncp -a ${dep}/lib/node_modules/* $out/lib/node_modules/"
) deps}
'';
fetchedPlugins = map fetchPlugin npmPackages;
# Strip hash and null-valued attrs from package entries before writing settings.json
cleanPackage =
p:
if builtins.isAttrs p then
lib.filterAttrs (k: v: k != "hash" && k != "version" && k != "deps" && v != null) p
else
p;
cleanSettings = config.settings // {
packages = map cleanPackage (config.settings.packages or [ ]);
};
settingsFile = pkgs.writeText "pi-settings.json" (builtins.toJSON cleanSettings);
in
{
_class = "wrapper";
options = {
settings = lib.mkOption {
type = lib.types.submodule {
freeformType = (pkgs.formats.json { }).type;
options.packages = lib.mkOption {
type = lib.types.listOf (
lib.types.either lib.types.str (
lib.types.submodule {
freeformType = (pkgs.formats.json { }).type;
options.hash = lib.mkOption {
type = lib.types.str;
description = "Fixed-output hash for fetching this npm package.";
};
options.source = lib.mkOption {
type = lib.types.str;
description = "Package source (e.g. npm:package-name).";
};
options.version = lib.mkOption {
type = lib.types.str;
description = "Exact version to pin (e.g. 1.0.3). Ensures reproducible fetching.";
};
options.deps = lib.mkOption {
type = lib.types.listOf (lib.types.attrsOf lib.types.anything);
default = [ ];
description = "Transitive npm dependencies as {source, version, hash} attrsets.";
};
options.extensions = lib.mkOption {
type = lib.types.nullOr (lib.types.listOf lib.types.str);
default = null;
description = ''
Extension filter patterns following pi's native syntax.
When null (default), all extensions from the package are loaded.
An empty list explicitly disables all extensions.
Patterns use prefixes to control inclusion:
- `"!pattern"` exclude matching extensions
- `"+pattern"` force-include (overrides excludes)
- `"-pattern"` force-exclude (overrides includes)
- `"path"` plain path to include
'';
example = [
"!./extensions/resistance.ts"
];
};
}
)
);
default = [ ];
description = "Pi packages list. Object entries may include a `hash` for Nix fetching.";
};
};
default = { };
description = "Pi settings.json content.";
};
pluginOverrides = lib.mkOption {
type = lib.types.str;
default = "";
description = ''
Shell commands to run after assembling the plugin prefix.
The variable `$out` points to the mutable copy.
'';
};
pluginPrefix = lib.mkOption {
type = lib.types.package;
description = "The final npm prefix with all plugins merged and overrides applied.";
default = pkgs.runCommand "pi-plugins" { } ''
mkdir -p $out
${lib.concatMapStringsSep "\n" (p: "cp -a --no-preserve=mode ${p}/* $out/") fetchedPlugins}
${lib.optionalString (config.pluginOverrides != "") config.pluginOverrides}
'';
};
};
config.package = lib.mkDefault pkgs.pi-llm;
config.extraPackages = [ pkgs.nodejs ];
config.env.npm_config_prefix = "${config.pluginPrefix}";
config.env.PI_SETTINGS_FILE = "${settingsFile}";
config.meta.maintainers = [
{
name = "lassulus";
github = "lassulus";
githubId = 621375;
}
];
config.meta.platforms = lib.platforms.all;
}

View File

@@ -0,0 +1,35 @@
#!/usr/bin/env python3
"""Patch pi-hooks permission extension to play peon sounds via pw-play on Linux."""
import re
import sys
ts_file = sys.argv[1]
pw_play = sys.argv[2]
src = open(ts_file).read()
# Use a lambda for replacement to avoid re.sub interpreting backslash sequences
replacement = r"""function playPermissionSound(): void {
const isMac = process.platform === "darwin";
if (isMac) {
exec('afplay /System/Library/Sounds/Funk.aiff 2>/dev/null', (err) => {
if (err) process.stdout.write("\x07");
});
} else {
const n = Math.floor(Math.random() * 4) + 1;
exec(`""" + pw_play + r""" "$HOME/src/sounds/games/Warcraft III/Units/Orc/Peon/PeonWhat${n}.wav"`, () => {});
}
}"""
result = re.sub(
r'function playPermissionSound\(\): void \{.*?\n\}',
lambda m: replacement,
src,
flags=re.DOTALL,
)
if result == src:
print("ERROR: playPermissionSound function not found in", ts_file, file=sys.stderr)
sys.exit(1)
open(ts_file, 'w').write(result)

View File

@@ -0,0 +1,463 @@
/**
* Questionnaire Tool - Ask the user multiple choice questions
*
* Registers a "questionnaire" tool the LLM can call to ask single or multiple
* questions with an interactive TUI. Based on mic92's pi-agent-extensions.
*/
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
import {
Editor,
type EditorTheme,
Key,
matchesKey,
Text,
truncateToWidth,
} from "@mariozechner/pi-tui";
import { Type } from "@sinclair/typebox";
interface QuestionOption {
value: string;
label: string;
description?: string;
}
type RenderOption = QuestionOption & { isOther?: boolean };
interface Question {
id: string;
label: string;
prompt: string;
options: QuestionOption[];
allowOther: boolean;
}
interface Answer {
id: string;
value: string;
label: string;
wasCustom: boolean;
index?: number;
}
interface QuestionnaireResult {
questions: Question[];
answers: Answer[];
cancelled: boolean;
}
const QuestionOptionSchema = Type.Object({
value: Type.String({ description: "The value returned when selected" }),
label: Type.String({ description: "Display label for the option" }),
description: Type.Optional(
Type.String({ description: "Optional description shown below label" }),
),
});
const QuestionSchema = Type.Object({
id: Type.String({ description: "Unique identifier for this question" }),
label: Type.Optional(
Type.String({
description:
"Short contextual label for tab bar, e.g. 'Scope', 'Priority' (defaults to Q1, Q2)",
}),
),
prompt: Type.String({ description: "The full question text to display" }),
options: Type.Array(QuestionOptionSchema, {
description: "Available options to choose from",
}),
allowOther: Type.Optional(
Type.Boolean({
description: "Allow 'Type something' option (default: true)",
}),
),
});
const QuestionnaireParams = Type.Object({
questions: Type.Array(QuestionSchema, {
description: "Questions to ask the user",
}),
});
function errorResult(
message: string,
questions: Question[] = [],
): {
content: { type: "text"; text: string }[];
details: QuestionnaireResult;
} {
return {
content: [{ type: "text", text: message }],
details: { questions, answers: [], cancelled: true },
};
}
export default function questionnaire(pi: ExtensionAPI) {
pi.registerTool({
name: "questionnaire",
label: "Questionnaire",
description:
"Ask the user one or more multiple choice questions. Use for clarifying requirements, getting preferences, confirming decisions, or quizzing. For single questions, shows a simple option list. For multiple questions, shows a tab-based interface.",
parameters: QuestionnaireParams,
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
if (!ctx.hasUI) {
return errorResult(
"Error: UI not available (running in non-interactive mode)",
);
}
if (params.questions.length === 0) {
return errorResult("Error: No questions provided");
}
const questions: Question[] = params.questions.map((q, i) => ({
...q,
label: q.label || `Q${i + 1}`,
allowOther: q.allowOther !== false,
}));
const isMulti = questions.length > 1;
const totalTabs = questions.length + 1;
const result = await ctx.ui.custom<QuestionnaireResult>(
(tui, theme, _kb, done) => {
let currentTab = 0;
let optionIndex = 0;
let inputMode = false;
let inputQuestionId: string | null = null;
let cachedLines: string[] | undefined;
const answers = new Map<string, Answer>();
const editorTheme: EditorTheme = {
borderColor: (s) => theme.fg("accent", s),
selectList: {
selectedPrefix: (t) => theme.fg("accent", t),
selectedText: (t) => theme.fg("accent", t),
description: (t) => theme.fg("muted", t),
scrollInfo: (t) => theme.fg("dim", t),
noMatch: (t) => theme.fg("warning", t),
},
};
const editor = new Editor(tui, editorTheme);
function refresh() {
cachedLines = undefined;
tui.requestRender();
}
function submit(cancelled: boolean) {
done({
questions,
answers: Array.from(answers.values()),
cancelled,
});
}
function currentQuestion(): Question | undefined {
return questions[currentTab];
}
function currentOptions(): RenderOption[] {
const q = currentQuestion();
if (!q) return [];
const opts: RenderOption[] = [...q.options];
if (q.allowOther) {
opts.push({
value: "__other__",
label: "Type something.",
isOther: true,
});
}
return opts;
}
function allAnswered(): boolean {
return questions.every((q) => answers.has(q.id));
}
function advanceAfterAnswer() {
if (!isMulti) {
submit(false);
return;
}
if (currentTab < questions.length - 1) {
currentTab++;
} else {
currentTab = questions.length;
}
optionIndex = 0;
refresh();
}
function saveAnswer(
questionId: string,
value: string,
label: string,
wasCustom: boolean,
index?: number,
) {
answers.set(questionId, {
id: questionId,
value,
label,
wasCustom,
index,
});
}
editor.onSubmit = (value) => {
if (!inputQuestionId) return;
const trimmed = value.trim() || "(no response)";
saveAnswer(inputQuestionId, trimmed, trimmed, true);
inputMode = false;
inputQuestionId = null;
editor.setText("");
advanceAfterAnswer();
};
function handleInput(data: string) {
if (inputMode) {
if (matchesKey(data, Key.escape)) {
inputMode = false;
inputQuestionId = null;
editor.setText("");
refresh();
return;
}
editor.handleInput(data);
refresh();
return;
}
const q = currentQuestion();
const opts = currentOptions();
if (isMulti) {
if (matchesKey(data, Key.tab) || matchesKey(data, Key.right)) {
currentTab = (currentTab + 1) % totalTabs;
optionIndex = 0;
refresh();
return;
}
if (
matchesKey(data, Key.shift("tab")) ||
matchesKey(data, Key.left)
) {
currentTab = (currentTab - 1 + totalTabs) % totalTabs;
optionIndex = 0;
refresh();
return;
}
}
if (currentTab === questions.length) {
if (matchesKey(data, Key.enter) && allAnswered()) {
submit(false);
} else if (matchesKey(data, Key.escape)) {
submit(true);
}
return;
}
if (matchesKey(data, Key.up)) {
optionIndex = Math.max(0, optionIndex - 1);
refresh();
return;
}
if (matchesKey(data, Key.down)) {
optionIndex = Math.min(opts.length - 1, optionIndex + 1);
refresh();
return;
}
if (matchesKey(data, Key.enter) && q) {
const opt = opts[optionIndex];
if (opt.isOther) {
inputMode = true;
inputQuestionId = q.id;
editor.setText("");
refresh();
return;
}
saveAnswer(q.id, opt.value, opt.label, false, optionIndex + 1);
advanceAfterAnswer();
return;
}
if (matchesKey(data, Key.escape)) {
submit(true);
}
}
function render(width: number): string[] {
if (cachedLines) return cachedLines;
const lines: string[] = [];
const q = currentQuestion();
const opts = currentOptions();
const add = (s: string) => lines.push(truncateToWidth(s, width));
add(theme.fg("accent", "─".repeat(width)));
if (isMulti) {
const tabs: string[] = ["← "];
for (let i = 0; i < questions.length; i++) {
const isActive = i === currentTab;
const isAnswered = answers.has(questions[i].id);
const lbl = questions[i].label;
const box = isAnswered ? "■" : "□";
const color = isAnswered ? "success" : "muted";
const text = ` ${box} ${lbl} `;
const styled = isActive
? theme.bg("selectedBg", theme.fg("text", text))
: theme.fg(color, text);
tabs.push(`${styled} `);
}
const canSubmit = allAnswered();
const isSubmitTab = currentTab === questions.length;
const submitText = " ✓ Submit ";
const submitStyled = isSubmitTab
? theme.bg("selectedBg", theme.fg("text", submitText))
: theme.fg(canSubmit ? "success" : "dim", submitText);
tabs.push(`${submitStyled}`);
add(` ${tabs.join("")}`);
lines.push("");
}
function renderOptions() {
for (let i = 0; i < opts.length; i++) {
const opt = opts[i];
const selected = i === optionIndex;
const isOther = opt.isOther === true;
const prefix = selected ? theme.fg("accent", "> ") : " ";
const color = selected ? "accent" : "text";
if (isOther && inputMode) {
add(prefix + theme.fg("accent", `${i + 1}. ${opt.label}`));
} else {
add(prefix + theme.fg(color, `${i + 1}. ${opt.label}`));
}
if (opt.description) {
add(` ${theme.fg("muted", opt.description)}`);
}
}
}
if (inputMode && q) {
add(theme.fg("text", ` ${q.prompt}`));
lines.push("");
renderOptions();
lines.push("");
add(theme.fg("muted", " Your answer:"));
for (const line of editor.render(width - 2)) {
add(` ${line}`);
}
lines.push("");
add(theme.fg("dim", " Enter to submit • Esc to cancel"));
} else if (currentTab === questions.length) {
add(theme.fg("accent", theme.bold(" Ready to submit")));
lines.push("");
for (const question of questions) {
const answer = answers.get(question.id);
if (answer) {
const prefix = answer.wasCustom ? "(wrote) " : "";
add(
`${theme.fg("muted", ` ${question.label}: `)}${theme.fg(
"text",
prefix + answer.label,
)}`,
);
}
}
lines.push("");
if (allAnswered()) {
add(theme.fg("success", " Press Enter to submit"));
} else {
const missing = questions
.filter((q) => !answers.has(q.id))
.map((q) => q.label)
.join(", ");
add(theme.fg("warning", ` Unanswered: ${missing}`));
}
} else if (q) {
add(theme.fg("text", ` ${q.prompt}`));
lines.push("");
renderOptions();
}
lines.push("");
if (!inputMode) {
const help = isMulti
? " Tab/←→ navigate • ↑↓ select • Enter confirm • Esc cancel"
: " ↑↓ navigate • Enter select • Esc cancel";
add(theme.fg("dim", help));
}
add(theme.fg("accent", "─".repeat(width)));
cachedLines = lines;
return lines;
}
return {
render,
invalidate: () => {
cachedLines = undefined;
},
handleInput,
};
},
);
if (result.cancelled) {
return {
content: [{ type: "text", text: "User cancelled the questionnaire" }],
details: result,
};
}
const answerLines = result.answers.map((a) => {
const qLabel = questions.find((q) => q.id === a.id)?.label || a.id;
if (a.wasCustom) {
return `${qLabel}: user wrote: ${a.label}`;
}
return `${qLabel}: user selected: ${a.index}. ${a.label}`;
});
return {
content: [{ type: "text", text: answerLines.join("\n") }],
details: result,
};
},
renderCall(args, theme) {
const qs = (args.questions as Question[]) || [];
const count = qs.length;
const labels = qs.map((q) => q.label || q.id).join(", ");
let text = theme.fg("toolTitle", theme.bold("questionnaire "));
text += theme.fg("muted", `${count} question${count !== 1 ? "s" : ""}`);
if (labels) {
text += theme.fg("dim", ` (${truncateToWidth(labels, 40)})`);
}
return new Text(text, 0, 0);
},
renderResult(result, _options, theme) {
const details = result.details as QuestionnaireResult | undefined;
if (!details) {
const text = result.content[0];
return new Text(text?.type === "text" ? text.text : "", 0, 0);
}
if (details.cancelled) {
return new Text(theme.fg("warning", "Cancelled"), 0, 0);
}
const lines = details.answers.map((a) => {
if (a.wasCustom) {
return `${theme.fg("success", "✓ ")}${theme.fg("accent", a.id)}: ${theme.fg("muted", "(wrote) ")}${a.label}`;
}
const display = a.index ? `${a.index}. ${a.label}` : a.label;
return `${theme.fg("success", "✓ ")}${theme.fg("accent", a.id)}: ${display}`;
});
return new Text(lines.join("\n"), 0, 0);
},
});
}

View File

@@ -0,0 +1,20 @@
--- /tmp/settings-manager.js.orig 2026-02-18 23:29:05.967146474 +0100
+++ /tmp/settings-manager.js 2026-02-18 23:29:12.452952321 +0100
@@ -97,7 +97,16 @@
/** Create a SettingsManager that loads from files */
static create(cwd = process.cwd(), agentDir = getAgentDir()) {
const storage = new FileSettingsStorage(cwd, agentDir);
- return SettingsManager.fromStorage(storage);
+ const manager = SettingsManager.fromStorage(storage);
+ const baseFile = process.env.PI_SETTINGS_FILE;
+ if (baseFile) {
+ try {
+ const baseSettings = JSON.parse(readFileSync(baseFile, "utf-8"));
+ manager.globalSettings = deepMergeSettings(manager.globalSettings, baseSettings);
+ manager.settings = deepMergeSettings(manager.globalSettings, manager.projectSettings);
+ } catch (e) {}
+ }
+ return manager;
}
/** Create a SettingsManager from an arbitrary storage backend */
static fromStorage(storage) {

Submodule secrets updated: 4fa163a68e...a6ec08c247

View File

@@ -1,4 +1,5 @@
secrets/alertmanager-token-reporters.age secrets/alertmanager-token-reporters.age
secrets/anthropic-api-key.age
secrets/brevo-key.age secrets/brevo-key.age
secrets/cifs-credentials-zodiac.age secrets/cifs-credentials-zodiac.age
secrets/copecart-ipn.age secrets/copecart-ipn.age
@@ -54,6 +55,7 @@ secrets/matrix-token-lakai.age
secrets/matrix-token-lakai-env.age secrets/matrix-token-lakai-env.age
secrets/maxmind-license-key.age secrets/maxmind-license-key.age
secrets/mega-password.age secrets/mega-password.age
secrets/meteora-auth.age
secrets/miniflux-api-token.age secrets/miniflux-api-token.age
secrets/miniflux-credentials.age secrets/miniflux-credentials.age
secrets/nextcloud-password-admin.age secrets/nextcloud-password-admin.age
@@ -61,7 +63,11 @@ secrets/nextcloud-password-database.age
secrets/nextcloud-password-fysi.age secrets/nextcloud-password-fysi.age
secrets/nextcloud-password-kieran.age secrets/nextcloud-password-kieran.age
secrets/onlyoffice-jwt-key.age secrets/onlyoffice-jwt-key.age
secrets/openclaw-gateway-token.age
secrets/openclaw-telegram-token.age
secrets/opencrow-gemini-key.age
secrets/opencrow-matrix-token.age secrets/opencrow-matrix-token.age
secrets/opencrow-openrouter-key.age
secrets/opencrow-soul.age secrets/opencrow-soul.age
secrets/openweathermap-api-key.age secrets/openweathermap-api-key.age
secrets/restic.age secrets/restic.age

View File

@@ -1,4 +1,3 @@
{ pkgs, ... }:
{ {
nixpkgs.config = { nixpkgs.config = {
allowUnfree = true; allowUnfree = true;
@@ -22,14 +21,7 @@
}; };
}; };
services.ollama = {
enable = true;
acceleration = "cuda"; # Force it to use the MX150
package = pkgs.ollama-cuda;
};
nix.settings.system-features = [ "cuda" ]; nix.settings.system-features = [ "cuda" ];
nixpkgs.config.cudaCapabilities = [ "6.1" ];
programs.nix-required-mounts = { programs.nix-required-mounts = {
enable = true; enable = true;

View File

@@ -17,6 +17,7 @@
./wallabag.nix ./wallabag.nix
./nethack.nix ./nethack.nix
./opencrow.nix ./opencrow.nix
./meteora.nix
]; ];
niveum.passport = { niveum.passport = {

44
systems/ful/meteora.nix Normal file
View File

@@ -0,0 +1,44 @@
{
config,
lib,
pkgs,
...
}:
let
publicLocations = builtins.listToAttrs (
map (name: lib.nameValuePair name { }) [
"= /index.html"
"= /veroeffentlichungen.html"
"= /lesungen.html"
"= /kolophon.html"
"= /impressum.html"
"= /credo.html"
"= /"
"= /meteora.css"
"= /favicon.ico"
"/img/"
"/fonts/"
]
);
in
{
age.secrets.meteora-auth = {
file = ../../secrets/meteora-auth.age;
owner = "nginx";
};
services.nginx = {
enable = true;
virtualHosts."meteora.xn--kiern-0qa.de" = {
forceSSL = true;
enableACME = true;
root = "${pkgs.meteora-website}";
locations = {
"/" = {
basicAuthFile = config.age.secrets.meteora-auth.path;
};
}
// publicLocations;
};
};
}

View File

@@ -27,6 +27,7 @@
enable = true; enable = true;
package = pkgs.opencrow; package = pkgs.opencrow;
piPackage = pkgs.pi;
extraPackages = [ extraPackages = [
pkgs.pi pkgs.pi