mirror of
https://github.com/kmein/niveum
synced 2026-03-18 02:51:08 +01:00
Compare commits
19 Commits
3216cd19f5
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 6bceb298ad | |||
| df3d15355a | |||
| 696ed782b9 | |||
| d3d6a413e6 | |||
| b9a222855e | |||
| f8bb3c04bf | |||
| dce42c7e80 | |||
| 5bedd897c9 | |||
| d952ecf17a | |||
| df36954fed | |||
| 697100f85f | |||
| 8eccb752dc | |||
| 6bb16ff6ed | |||
| 8fe9e80522 | |||
| 314848d877 | |||
| 377b338979 | |||
| 26231912cf | |||
| a5532f1e9d | |||
| d878b73ab5 |
@@ -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;
|
||||||
|
|||||||
@@ -265,9 +265,9 @@ in
|
|||||||
exec-once = [
|
exec-once = [
|
||||||
(lib.getExe pkgs.ashell)
|
(lib.getExe pkgs.ashell)
|
||||||
"hyprctl dispatch exec \"[workspace special:${language.obsidian} silent] obsidian\""
|
"hyprctl dispatch exec \"[workspace special:${language.obsidian} silent] obsidian\""
|
||||||
"${lib.getExe' pkgs.wl-clipboard "wl-paste"} -t text --watch ${lib.getExe pkgs.clipman} store"
|
(lib.getExe pkgs.niphas-clipboard-watcher)
|
||||||
(lib.getExe pkgs.hyprsunset)
|
(lib.getExe pkgs.niphas-redshift)
|
||||||
(lib.getExe pkgs.hyprpaper)
|
(lib.getExe pkgs.niphas-set-wallpaper)
|
||||||
];
|
];
|
||||||
|
|
||||||
device = [
|
device = [
|
||||||
@@ -341,7 +341,7 @@ in
|
|||||||
",XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"
|
",XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"
|
||||||
",XF86MonBrightnessUp, exec, brightnessctl -e4 -n2 set 5%+"
|
",XF86MonBrightnessUp, exec, brightnessctl -e4 -n2 set 5%+"
|
||||||
",XF86MonBrightnessDown, exec, brightnessctl -e4 -n2 set 5%-"
|
",XF86MonBrightnessDown, exec, brightnessctl -e4 -n2 set 5%-"
|
||||||
", Print, exec, ${lib.getExe pkgs.niphas-screenshot} -m region --clipboard-only"
|
", Print, exec, ${lib.getExe pkgs.niphas-screenshot}"
|
||||||
];
|
];
|
||||||
bindl = [
|
bindl = [
|
||||||
", XF86AudioNext, exec, playerctl next"
|
", XF86AudioNext, exec, playerctl next"
|
||||||
@@ -355,7 +355,7 @@ in
|
|||||||
"${mod} SHIFT, R, exit,"
|
"${mod} SHIFT, R, exit,"
|
||||||
"${mod}, t, exec, ${lib.getExe pkgs.niphas-file-browser}"
|
"${mod}, t, exec, ${lib.getExe pkgs.niphas-file-browser}"
|
||||||
"${mod}, Y, exec, ${lib.getExe pkgs.niphas-web-browser}"
|
"${mod}, Y, exec, ${lib.getExe pkgs.niphas-web-browser}"
|
||||||
"${mod}, Q, exec, ${lib.getExe pkgs.clipman} pick --tool=rofi"
|
"${mod}, Q, exec, ${lib.getExe pkgs.niphas-clipman}"
|
||||||
"${mod}, u, exec, ${lib.getExe pkgs.unicodmenu}"
|
"${mod}, u, exec, ${lib.getExe pkgs.unicodmenu}"
|
||||||
"${mod}, p, exec, ${lib.getExe pkgs.rofi-pass-wayland}"
|
"${mod}, p, exec, ${lib.getExe pkgs.rofi-pass-wayland}"
|
||||||
"${mod} SHIFT, Z, togglefloating,"
|
"${mod} SHIFT, Z, togglefloating,"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
86
flake.lock
generated
86
flake.lock
generated
@@ -464,6 +464,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": [
|
||||||
@@ -496,11 +514,11 @@
|
|||||||
"wrappers": "wrappers"
|
"wrappers": "wrappers"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1770756688,
|
"lastModified": 1773416451,
|
||||||
"narHash": "sha256-raCwOTt5xT7J1ysxdGrmBva6OVrvjf47EgVLi5B5R5o=",
|
"narHash": "sha256-EZbY8AT7fmqmjW/IjYKMI3XFgDkdNqcJH7uMnzUAnMs=",
|
||||||
"ref": "refs/heads/master",
|
"ref": "refs/heads/master",
|
||||||
"rev": "86bf2150a7cabd225149f35c0ff57576af6ded44",
|
"rev": "cd556263286754a37ac7d3a4a71b8699923ea37c",
|
||||||
"revCount": 38,
|
"revCount": 45,
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://code.kmein.de/kfm/niphas"
|
"url": "https://code.kmein.de/kfm/niphas"
|
||||||
},
|
},
|
||||||
@@ -651,6 +669,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,6 +700,22 @@
|
|||||||
"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_2",
|
||||||
@@ -722,11 +772,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1771337567,
|
"lastModified": 1773693388,
|
||||||
"narHash": "sha256-DiH3hGzC7z6qv39zuFCrMrKDTKdtsN+z/OorsbBdJaQ=",
|
"narHash": "sha256-bYppS16wQsnvCpDMVXqUOaYvP2oSqvkzsrYGjJsrt48=",
|
||||||
"owner": "pinpox",
|
"owner": "pinpox",
|
||||||
"repo": "opencrow",
|
"repo": "opencrow",
|
||||||
"rev": "8da71c7d1d04e27ffb5aade99f67e591d52c30c4",
|
"rev": "7f35d518cf0a0a4d5c90fea324425534a947ee33",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -735,6 +785,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 +827,18 @@
|
|||||||
"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",
|
||||||
"nix-index-database": "nix-index-database",
|
"nix-index-database": "nix-index-database",
|
||||||
"nix-topology": "nix-topology",
|
"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",
|
||||||
|
|||||||
23
flake.nix
23
flake.nix
@@ -12,6 +12,7 @@
|
|||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
|
||||||
nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
|
nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
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";
|
||||||
nixos-hardware.url = "github:NixOS/nixos-hardware";
|
nixos-hardware.url = "github:NixOS/nixos-hardware";
|
||||||
nur.url = "github:nix-community/NUR";
|
nur.url = "github:nix-community/NUR";
|
||||||
retiolum.url = "github:krebs/retiolum";
|
retiolum.url = "github:krebs/retiolum";
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
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;
|
||||||
|
|
||||||
@@ -90,10 +92,13 @@
|
|||||||
autorenkalender,
|
autorenkalender,
|
||||||
telebots,
|
telebots,
|
||||||
stockholm,
|
stockholm,
|
||||||
|
panoptikon,
|
||||||
nix-index-database,
|
nix-index-database,
|
||||||
stylix,
|
stylix,
|
||||||
voidrice,
|
voidrice,
|
||||||
wetter,
|
wetter,
|
||||||
|
wrappers,
|
||||||
|
meteora,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
@@ -193,7 +198,6 @@
|
|||||||
nixosModules = {
|
nixosModules = {
|
||||||
moodle-dl = import modules/moodle-dl.nix;
|
moodle-dl = import modules/moodle-dl.nix;
|
||||||
passport = import modules/passport.nix;
|
passport = import modules/passport.nix;
|
||||||
panoptikon = import modules/panoptikon.nix;
|
|
||||||
power-action = import modules/power-action.nix;
|
power-action = import modules/power-action.nix;
|
||||||
system-dependent = import modules/system-dependent.nix;
|
system-dependent = import modules/system-dependent.nix;
|
||||||
telegram-bot = import modules/telegram-bot.nix;
|
telegram-bot = import modules/telegram-bot.nix;
|
||||||
@@ -301,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 { };
|
||||||
@@ -310,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 { };
|
||||||
@@ -369,10 +377,6 @@
|
|||||||
inherit lib;
|
inherit lib;
|
||||||
pkgs = final;
|
pkgs = final;
|
||||||
};
|
};
|
||||||
panoptikon = import lib/panoptikon.nix {
|
|
||||||
inherit lib;
|
|
||||||
pkgs = final;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -384,6 +388,7 @@
|
|||||||
nixpkgs.overlays = [
|
nixpkgs.overlays = [
|
||||||
self.overlays.default
|
self.overlays.default
|
||||||
niphas.overlays.default
|
niphas.overlays.default
|
||||||
|
panoptikon.overlays.default
|
||||||
(final: prev: {
|
(final: prev: {
|
||||||
niphas-git =
|
niphas-git =
|
||||||
(prev.niphas-git.passthru.configuration.apply {
|
(prev.niphas-git.passthru.configuration.apply {
|
||||||
@@ -448,7 +453,7 @@
|
|||||||
++ profiles.server
|
++ profiles.server
|
||||||
++ [
|
++ [
|
||||||
systems/ful/configuration.nix
|
systems/ful/configuration.nix
|
||||||
self.nixosModules.panoptikon
|
panoptikon.nixosModules.default
|
||||||
self.nixosModules.go-webring
|
self.nixosModules.go-webring
|
||||||
stockholm.nixosModules.reaktor2
|
stockholm.nixosModules.reaktor2
|
||||||
opencrow.nixosModules.default
|
opencrow.nixosModules.default
|
||||||
@@ -515,7 +520,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 {
|
||||||
@@ -526,7 +531,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 {
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
{
|
|
||||||
pkgs,
|
|
||||||
lib,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
{
|
|
||||||
# watcher scripts
|
|
||||||
url =
|
|
||||||
address:
|
|
||||||
pkgs.writers.writeDash "watch-url" ''
|
|
||||||
${pkgs.curl}/bin/curl -sSL ${lib.escapeShellArg address} \
|
|
||||||
| ${pkgs.python3Packages.html2text}/bin/html2text --decode-errors=ignore
|
|
||||||
'';
|
|
||||||
urlSelector =
|
|
||||||
selector: address:
|
|
||||||
pkgs.writers.writeDash "watch-url-selector" ''
|
|
||||||
${pkgs.curl}/bin/curl -sSL ${lib.escapeShellArg address} \
|
|
||||||
| ${pkgs.htmlq}/bin/htmlq ${lib.escapeShellArg selector} \
|
|
||||||
| ${pkgs.python3Packages.html2text}/bin/html2text
|
|
||||||
'';
|
|
||||||
urlJSON =
|
|
||||||
{
|
|
||||||
jqScript ? ".",
|
|
||||||
}:
|
|
||||||
address:
|
|
||||||
pkgs.writers.writeDash "watch-url-json" ''
|
|
||||||
${pkgs.curl}/bin/curl -sSL ${lib.escapeShellArg address} | ${pkgs.jq}/bin/jq -f ${pkgs.writeText "script.jq" jqScript}
|
|
||||||
'';
|
|
||||||
|
|
||||||
# reporter scripts
|
|
||||||
kpaste-irc =
|
|
||||||
{
|
|
||||||
target,
|
|
||||||
retiolumLink ? false,
|
|
||||||
server ? "irc.r",
|
|
||||||
messagePrefix ? "change detected: ",
|
|
||||||
nick ? ''"$PANOPTIKON_WATCHER"-watcher'',
|
|
||||||
}:
|
|
||||||
pkgs.writers.writeDash "kpaste-irc-reporter" ''
|
|
||||||
KPASTE_CONTENT_TYPE=text/plain ${pkgs.kpaste}/bin/kpaste \
|
|
||||||
| ${pkgs.gnused}/bin/sed -n "${if retiolumLink then "2" else "3"}s/^/${messagePrefix}/p" \
|
|
||||||
| ${pkgs.nur.repos.mic92.ircsink}/bin/ircsink \
|
|
||||||
--nick ${nick} \
|
|
||||||
--server ${server} \
|
|
||||||
--target ${target}
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
{
|
|
||||||
config,
|
|
||||||
lib,
|
|
||||||
pkgs,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
{
|
|
||||||
options.services.panoptikon = {
|
|
||||||
enable = lib.mkEnableOption "Generic command output / website watcher";
|
|
||||||
watchers = lib.mkOption {
|
|
||||||
type = lib.types.attrsOf (
|
|
||||||
lib.types.submodule (watcher: {
|
|
||||||
options = {
|
|
||||||
script = lib.mkOption {
|
|
||||||
type = lib.types.path;
|
|
||||||
description = ''
|
|
||||||
A script whose stdout is to be watched.
|
|
||||||
'';
|
|
||||||
example = ''
|
|
||||||
pkgs.writers.writeDash "github-meta" '''
|
|
||||||
''${pkgs.curl}/bin/curl -sSL https://api.github.com/meta | ''${pkgs.jq}/bin/jq
|
|
||||||
'''
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
frequency = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
description = ''
|
|
||||||
How often to run the script. See systemd.time(7) for more information about the format.
|
|
||||||
'';
|
|
||||||
example = "*:0/3";
|
|
||||||
default = "daily";
|
|
||||||
};
|
|
||||||
loadCredential = lib.mkOption {
|
|
||||||
type = lib.types.listOf lib.types.str;
|
|
||||||
description = ''
|
|
||||||
This can be used to pass secrets to the systemd service without adding them to the nix store.
|
|
||||||
'';
|
|
||||||
default = [ ];
|
|
||||||
};
|
|
||||||
reporters = lib.mkOption {
|
|
||||||
type = lib.types.listOf lib.types.path;
|
|
||||||
description = ''
|
|
||||||
A list of scripts that take the diff (if any) via stdin and report it (e.g. to IRC, Telegram or Prometheus). The name of the watcher will be in the $PANOPTIKON_WATCHER environment variable.
|
|
||||||
'';
|
|
||||||
example = ''
|
|
||||||
[
|
|
||||||
(pkgs.writers.writeDash "telegram-reporter" '''
|
|
||||||
''${pkgs.curl}/bin/curl -X POST https://api.telegram.org/bot''${TOKEN}/sendMessage \
|
|
||||||
-d chat_id=123456 \
|
|
||||||
-d text="$(cat)"
|
|
||||||
''')
|
|
||||||
(pkgs.writers.writeDash "notify" '''
|
|
||||||
''${pkgs.libnotify}/bin/notify-send "$PANOPTIKON_WATCHER has changed."
|
|
||||||
''')
|
|
||||||
]
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
config = { };
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config =
|
|
||||||
let
|
|
||||||
cfg = config.services.panoptikon;
|
|
||||||
in
|
|
||||||
lib.mkIf cfg.enable {
|
|
||||||
users.extraUsers.panoptikon = {
|
|
||||||
isSystemUser = true;
|
|
||||||
createHome = true;
|
|
||||||
home = "/var/lib/panoptikon";
|
|
||||||
group = "panoptikon";
|
|
||||||
};
|
|
||||||
|
|
||||||
users.extraGroups.panoptikon = { };
|
|
||||||
|
|
||||||
systemd.timers = lib.attrsets.mapAttrs' (
|
|
||||||
watcherName: _:
|
|
||||||
lib.nameValuePair "panoptikon-${watcherName}" {
|
|
||||||
timerConfig.RandomizedDelaySec = toString (60 * 60);
|
|
||||||
}
|
|
||||||
) cfg.watchers;
|
|
||||||
|
|
||||||
systemd.services = lib.attrsets.mapAttrs' (
|
|
||||||
watcherName: watcherOptions:
|
|
||||||
lib.nameValuePair "panoptikon-${watcherName}" {
|
|
||||||
enable = true;
|
|
||||||
startAt = watcherOptions.frequency;
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "oneshot";
|
|
||||||
User = "panoptikon";
|
|
||||||
Group = "panoptikon";
|
|
||||||
WorkingDirectory = "/var/lib/panoptikon";
|
|
||||||
RestartSec = toString (60 * 60);
|
|
||||||
Restart = "on-failure";
|
|
||||||
LoadCredential = watcherOptions.loadCredential;
|
|
||||||
};
|
|
||||||
unitConfig = {
|
|
||||||
StartLimitIntervalSec = "300";
|
|
||||||
StartLimitBurst = "5";
|
|
||||||
};
|
|
||||||
environment.PANOPTIKON_WATCHER = watcherName;
|
|
||||||
wants = [ "network-online.target" ];
|
|
||||||
script = ''
|
|
||||||
set -fux
|
|
||||||
${watcherOptions.script} > ${lib.escapeShellArg watcherName}
|
|
||||||
diff_output=$(${pkgs.diffutils}/bin/diff --new-file ${
|
|
||||||
lib.escapeShellArg (watcherName + ".old")
|
|
||||||
} ${lib.escapeShellArg watcherName} || :)
|
|
||||||
if [ -n "$diff_output" ]
|
|
||||||
then
|
|
||||||
${lib.strings.concatMapStringsSep "\n" (
|
|
||||||
reporter: ''echo "$diff_output" | ${reporter} || :''
|
|
||||||
) watcherOptions.reporters}
|
|
||||||
fi
|
|
||||||
mv ${lib.escapeShellArg watcherName} ${lib.escapeShellArg (watcherName + ".old")}
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
) cfg.watchers;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -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")
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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-ruCukrbH89/uZHhxYSmrcs5NUi2ZG8nuwTEuJKHP2rw=";
|
|
||||||
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
70
packages/pi/default.nix
Normal 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
150
packages/pi/module.nix
Normal 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;
|
||||||
|
}
|
||||||
35
packages/pi/patch-permission-sound.py
Normal file
35
packages/pi/patch-permission-sound.py
Normal 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)
|
||||||
463
packages/pi/questionnaire.ts
Normal file
463
packages/pi/questionnaire.ts
Normal 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);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
20
packages/pi/settings-env.patch
Normal file
20
packages/pi/settings-env.patch
Normal 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) {
|
||||||
2
secrets
2
secrets
Submodule secrets updated: 94998bbbe0...a6ec08c247
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
./wallabag.nix
|
./wallabag.nix
|
||||||
./nethack.nix
|
./nethack.nix
|
||||||
./opencrow.nix
|
./opencrow.nix
|
||||||
|
./meteora.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
niveum.passport = {
|
niveum.passport = {
|
||||||
|
|||||||
18
systems/ful/meteora.nix
Normal file
18
systems/ful/meteora.nix
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{ config, pkgs, ... }: {
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -48,8 +48,8 @@
|
|||||||
NIX_REMOTE = "daemon";
|
NIX_REMOTE = "daemon";
|
||||||
|
|
||||||
PI_PERMISSION_LEVEL = "high";
|
PI_PERMISSION_LEVEL = "high";
|
||||||
OPENCROW_MATRIX_HOMESERVER = "https://matrix.4d2.org";
|
OPENCROW_MATRIX_HOMESERVER = "https://matrix.org";
|
||||||
OPENCROW_MATRIX_USER_ID = "@fable:4d2.org";
|
OPENCROW_MATRIX_USER_ID = "@fable_ai:matrix.org";
|
||||||
OPENCROW_SOUL_FILE = "/run/opencrow/SOUL.md";
|
OPENCROW_SOUL_FILE = "/run/opencrow/SOUL.md";
|
||||||
OPENCROW_HEARTBEAT_INTERVAL = "2h";
|
OPENCROW_HEARTBEAT_INTERVAL = "2h";
|
||||||
|
|
||||||
|
|||||||
@@ -5,42 +5,23 @@
|
|||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
irc-xxx = pkgs.lib.panoptikon.kpaste-irc {
|
irc-xxx = pkgs.panoptikonReporters.kpaste-irc {
|
||||||
target = lib.escapeShellArg "#xxx";
|
target = lib.escapeShellArg "#xxx";
|
||||||
retiolumLink = true;
|
retiolumLink = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
matrix =
|
matrix-kmein = pkgs.panoptikonReporters.matrix {
|
||||||
{
|
homeserver = "matrix.4d2.org";
|
||||||
server ? "matrix.4d2.org",
|
roomId = lib.escapeShellArg "!zlwCuPiCNMSxDviFzA:4d2.org";
|
||||||
target,
|
tokenPath = config.age.secrets.matrix-token-lakai.path;
|
||||||
}:
|
};
|
||||||
pkgs.writers.writeDash "matrix-reporter" ''
|
|
||||||
export RAW_MESSAGE="$(cat)"
|
|
||||||
export MESSAGE=$(printf '<b>%s</b><br><pre>%s</pre>' "$PANOPTIKON_WATCHER" "$RAW_MESSAGE")
|
|
||||||
export MATRIX_TOKEN="$(cat ${config.age.secrets.matrix-token-lakai.path})"
|
|
||||||
export JSON_PAYLOAD=$(${pkgs.jq}/bin/jq -n --arg msgtype "m.text" --arg body "$RAW_MESSAGE" --arg formattedBody "$MESSAGE" '{msgtype: $msgtype, body: $body, format: "org.matrix.custom.html", formatted_body: $formattedBody}')
|
|
||||||
${pkgs.curl}/bin/curl -X POST "https://${server}/_matrix/client/r0/rooms/${target}/send/m.room.message" \
|
|
||||||
-d "$JSON_PAYLOAD" \
|
|
||||||
-H "Authorization: Bearer $MATRIX_TOKEN" \
|
|
||||||
-H "Content-Type: application/json"
|
|
||||||
'';
|
|
||||||
|
|
||||||
matrix-kmein = matrix { target = "!zlwCuPiCNMSxDviFzA:4d2.org"; };
|
telegram-kmein = pkgs.panoptikonReporters.telegram {
|
||||||
|
tokenPath = config.age.secrets.telegram-token-kmein.path;
|
||||||
|
chatId = "-1001796440545";
|
||||||
|
};
|
||||||
|
|
||||||
telegram-kmein =
|
irc-kmein = pkgs.panoptikonReporters.kpaste-irc {
|
||||||
let
|
|
||||||
chatId = "-1001796440545";
|
|
||||||
in
|
|
||||||
pkgs.writers.writeDash "telegram-fulltext" ''
|
|
||||||
export TOKEN="$(cat "$CREDENTIALS_DIRECTORY/token")"
|
|
||||||
${pkgs.curl}/bin/curl -X POST "https://api.telegram.org/bot''${TOKEN}/sendMessage" \
|
|
||||||
-d chat_id=${chatId} \
|
|
||||||
-d text="$(cat)" \
|
|
||||||
| ${pkgs.jq}/bin/jq -e .ok
|
|
||||||
'';
|
|
||||||
|
|
||||||
irc-kmein = pkgs.lib.panoptikon.kpaste-irc {
|
|
||||||
messagePrefix = "$PANOPTIKON_WATCHER: ";
|
messagePrefix = "$PANOPTIKON_WATCHER: ";
|
||||||
target = "kmein";
|
target = "kmein";
|
||||||
nick = "panoptikon-kmein";
|
nick = "panoptikon-kmein";
|
||||||
@@ -48,7 +29,12 @@ let
|
|||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
age.secrets.telegram-token-kmein.file = ../../secrets/telegram-token-kmein.age;
|
age.secrets.telegram-token-kmein = {
|
||||||
|
file = ../../secrets/telegram-token-kmein.age;
|
||||||
|
owner = "panoptikon";
|
||||||
|
group = "panoptikon";
|
||||||
|
mode = "400";
|
||||||
|
};
|
||||||
age.secrets.matrix-token-lakai = {
|
age.secrets.matrix-token-lakai = {
|
||||||
file = ../../secrets/matrix-token-lakai.age;
|
file = ../../secrets/matrix-token-lakai.age;
|
||||||
owner = "panoptikon";
|
owner = "panoptikon";
|
||||||
@@ -60,7 +46,7 @@ in
|
|||||||
enable = true;
|
enable = true;
|
||||||
watchers = {
|
watchers = {
|
||||||
"github-meta" = {
|
"github-meta" = {
|
||||||
script = pkgs.lib.panoptikon.urlJSON {
|
script = pkgs.panoptikonWatchers.json {
|
||||||
jqScript = ''
|
jqScript = ''
|
||||||
{
|
{
|
||||||
ssh_key_fingerprints: .ssh_key_fingerprints,
|
ssh_key_fingerprints: .ssh_key_fingerprints,
|
||||||
@@ -70,83 +56,71 @@ in
|
|||||||
} "https://api.github.com/meta";
|
} "https://api.github.com/meta";
|
||||||
reporters = [ irc-xxx ];
|
reporters = [ irc-xxx ];
|
||||||
};
|
};
|
||||||
lammla = {
|
|
||||||
script = pkgs.lib.panoptikon.url "http://lammla.info/index.php?reihe=30";
|
|
||||||
reporters = [ matrix-kmein ];
|
|
||||||
};
|
|
||||||
kratylos = {
|
|
||||||
script = pkgs.lib.panoptikon.url "https://kratylos.reichert-online.org/current_issue/KRATYLOS";
|
|
||||||
reporters = [ matrix-kmein ];
|
|
||||||
};
|
|
||||||
kobudo-tesshinkan = {
|
kobudo-tesshinkan = {
|
||||||
script = pkgs.lib.panoptikon.url "https://kobudo-tesshinkan.eu/index.php/de/termine-berichte/lehrgaenge/";
|
script = pkgs.panoptikonWatchers.html "https://kobudo-tesshinkan.eu/index.php/de/termine-berichte/lehrgaenge/";
|
||||||
reporters = [
|
reporters = [
|
||||||
telegram-kmein
|
telegram-kmein
|
||||||
matrix-kmein
|
matrix-kmein
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
zeno-free = {
|
|
||||||
script = pkgs.lib.panoptikon.urlSelector ".zenoCOMain" "http://www.zeno.org/Lesesaal/M/E-Books";
|
|
||||||
reporters = [ matrix-kmein ];
|
|
||||||
};
|
|
||||||
carolinawelslau = {
|
carolinawelslau = {
|
||||||
script = pkgs.lib.panoptikon.urlSelector "#main" "https://carolinawelslau.de/";
|
script = pkgs.panoptikonWatchers.htmlSelector "#main" "https://carolinawelslau.de/";
|
||||||
reporters = [ matrix-kmein ];
|
reporters = [ matrix-kmein ];
|
||||||
};
|
};
|
||||||
humboldt-preis = {
|
humboldt-preis = {
|
||||||
script = pkgs.lib.panoptikon.urlSelector "#content-core" "https://www.hu-berlin.de/de/ueberblick/menschen/ehrungen/humboldtpreis";
|
script = pkgs.panoptikonWatchers.htmlSelector "#content-core" "https://www.hu-berlin.de/de/ueberblick/menschen/ehrungen/humboldtpreis";
|
||||||
reporters = [ matrix-kmein ];
|
reporters = [ matrix-kmein ];
|
||||||
};
|
};
|
||||||
lisalittmann = {
|
lisalittmann = {
|
||||||
script = pkgs.lib.panoptikon.urlSelector "#site-content" "https://lisalittmann.de/";
|
script = pkgs.panoptikonWatchers.htmlSelector "#site-content" "https://lisalittmann.de/";
|
||||||
reporters = [ matrix-kmein ];
|
reporters = [ matrix-kmein ];
|
||||||
};
|
};
|
||||||
lisalittmann-archive = {
|
lisalittmann-archive = {
|
||||||
script = pkgs.lib.panoptikon.urlSelector "#site-content" "https://lisalittmann.de/archive/";
|
script = pkgs.panoptikonWatchers.htmlSelector "#site-content" "https://lisalittmann.de/archive/";
|
||||||
reporters = [ matrix-kmein ];
|
reporters = [ matrix-kmein ];
|
||||||
};
|
};
|
||||||
lisalittmann-projects = {
|
lisalittmann-projects = {
|
||||||
script = pkgs.lib.panoptikon.urlSelector "#site-content" "https://lisalittmann.de/projects/";
|
script = pkgs.panoptikonWatchers.htmlSelector "#site-content" "https://lisalittmann.de/projects/";
|
||||||
reporters = [ matrix-kmein ];
|
reporters = [ matrix-kmein ];
|
||||||
};
|
};
|
||||||
tatort = {
|
tatort = {
|
||||||
script = pkgs.lib.panoptikon.urlSelector ".linklist" "https://www.daserste.de/unterhaltung/krimi/tatort/sendung/index.html";
|
script = pkgs.panoptikonWatchers.htmlSelector ".linklist" "https://www.daserste.de/unterhaltung/krimi/tatort/sendung/index.html";
|
||||||
reporters = [ matrix-kmein ];
|
reporters = [ matrix-kmein ];
|
||||||
};
|
};
|
||||||
warpgrid-idiomarium = {
|
warpgrid-idiomarium = {
|
||||||
script = pkgs.lib.panoptikon.urlSelector "#site-content" "https://warpgrid.de/idiomarium/";
|
script = pkgs.panoptikonWatchers.htmlSelector "#site-content" "https://warpgrid.de/idiomarium/";
|
||||||
reporters = [ matrix-kmein ];
|
reporters = [ matrix-kmein ];
|
||||||
};
|
};
|
||||||
warpgrid-futurism = {
|
warpgrid-futurism = {
|
||||||
script = pkgs.lib.panoptikon.urlSelector "#site-content" "https://warpgrid.de/futurism/";
|
script = pkgs.panoptikonWatchers.htmlSelector "#site-content" "https://warpgrid.de/futurism/";
|
||||||
reporters = [ matrix-kmein ];
|
reporters = [ matrix-kmein ];
|
||||||
};
|
};
|
||||||
warpgrid-imagiary = {
|
warpgrid-imagiary = {
|
||||||
script = pkgs.lib.panoptikon.urlSelector "#site-content" "https://warpgrid.de/imagiary/";
|
script = pkgs.panoptikonWatchers.htmlSelector "#site-content" "https://warpgrid.de/imagiary/";
|
||||||
reporters = [ matrix-kmein ];
|
reporters = [ matrix-kmein ];
|
||||||
};
|
};
|
||||||
warpgrid-alchemy = {
|
warpgrid-alchemy = {
|
||||||
script = pkgs.lib.panoptikon.urlSelector "#site-content" "https://warpgrid.de/alchemy/";
|
script = pkgs.panoptikonWatchers.htmlSelector "#site-content" "https://warpgrid.de/alchemy/";
|
||||||
reporters = [ matrix-kmein ];
|
reporters = [ matrix-kmein ];
|
||||||
};
|
};
|
||||||
indogermanische-forschungen = {
|
indogermanische-forschungen = {
|
||||||
script = pkgs.lib.panoptikon.urlSelector "#latestIssue" "https://www.degruyter.com/journal/key/INDO/html";
|
script = pkgs.panoptikonWatchers.htmlSelector "#latestIssue" "https://www.degruyter.com/journal/key/INDO/html";
|
||||||
reporters = [ matrix-kmein ];
|
reporters = [ matrix-kmein ];
|
||||||
};
|
};
|
||||||
ig-neuigkeiten = {
|
ig-neuigkeiten = {
|
||||||
script = pkgs.lib.panoptikon.urlSelector "[itemprop=articleBody]" "https://www.indogermanistik.org/aktuelles/neuigkeiten.html";
|
script = pkgs.panoptikonWatchers.htmlSelector "[itemprop=articleBody]" "https://www.indogermanistik.org/aktuelles/neuigkeiten.html";
|
||||||
reporters = [ matrix-kmein ];
|
reporters = [ matrix-kmein ];
|
||||||
};
|
};
|
||||||
ig-tagungen = {
|
ig-tagungen = {
|
||||||
script = pkgs.lib.panoptikon.urlSelector "[itemprop=articleBody]" "https://www.indogermanistik.org/tagungen/tagungen-der-ig.html";
|
script = pkgs.panoptikonWatchers.htmlSelector "[itemprop=articleBody]" "https://www.indogermanistik.org/tagungen/tagungen-der-ig.html";
|
||||||
reporters = [ matrix-kmein ];
|
reporters = [ matrix-kmein ];
|
||||||
};
|
};
|
||||||
fu-distant = {
|
fu-distant = {
|
||||||
script = pkgs.lib.panoptikon.urlSelector "#current_events" "https://www.geschkult.fu-berlin.de/en/e/ma-distant/Termine/index.html";
|
script = pkgs.panoptikonWatchers.htmlSelector "#current_events" "https://www.geschkult.fu-berlin.de/en/e/ma-distant/Termine/index.html";
|
||||||
reporters = [ matrix-kmein ];
|
reporters = [ matrix-kmein ];
|
||||||
};
|
};
|
||||||
fu-aegyptologie = {
|
fu-aegyptologie = {
|
||||||
script = pkgs.lib.panoptikon.urlSelector "#current_events" "https://www.geschkult.fu-berlin.de/e/aegyptologie/termine/index.html";
|
script = pkgs.panoptikonWatchers.htmlSelector "#current_events" "https://www.geschkult.fu-berlin.de/e/aegyptologie/termine/index.html";
|
||||||
reporters = [ matrix-kmein ];
|
reporters = [ matrix-kmein ];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -34,7 +34,6 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
swapDevices = [ ];
|
swapDevices = [ ];
|
||||||
zramSwap.enable = true;
|
|
||||||
|
|
||||||
nix.settings.max-jobs = lib.mkDefault 2;
|
nix.settings.max-jobs = lib.mkDefault 2;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
swapDevices = [ ];
|
swapDevices = [ ];
|
||||||
zramSwap.enable = true;
|
zramSwap.enable = false;
|
||||||
|
|
||||||
powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
|
powerManagement.cpuFreqGovernor = lib.mkDefault "powersave";
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user