atagen

roundabout

public
Star0
Watch0
052a1a49371c39019ad2acebf23b58977d2fa829
1
ssh://brewery@pls.lobotomise.me:33/atagen/roundabout.git
nix/nixos-module.nixNix106 lines3.3 KB
1
self:
2
{
3
  config,
4
  lib,
5
  pkgs,
6
  ...
7
}:
8
9
let
10
  cfg = config.programs.roundabout;
11
  toml = pkgs.formats.toml { };
12
  plugin = pkgs.callPackage ./plugin.nix { roundabout = cfg.package; };
13
in
14
{
15
  options.programs.roundabout = {
16
    enable = lib.mkEnableOption "roundabout, build admission for agents";
17
18
    package = lib.mkOption {
19
      type = lib.types.package;
20
      default = self.packages.${pkgs.stdenv.hostPlatform.system}.roundabout;
21
      defaultText = lib.literalExpression "roundabout.packages.\${system}.roundabout";
22
      description = "the roundabout package";
23
    };
24
25
    socketActivation = lib.mkOption {
26
      type = lib.types.bool;
27
      default = true;
28
      description = ''
29
        let a systemd user socket own `$XDG_RUNTIME_DIR/roundabout.sock` and start the
30
        daemon on demand. when disabled, the first client spawns daemon instead
31
      '';
32
    };
33
34
    settings = lib.mkOption {
35
      inherit (toml) type;
36
      default = { };
37
      example = lib.literalExpression ''
38
        {
39
          budget.ram = "16GiB";
40
          enforce.unwrapped = "wrap";
41
          artifacts.stop = "block";
42
        }
43
      '';
44
      description = ''
45
        written to `/etc/roundabout/config.toml`, which anyone without a
46
        `~/.config/roundabout/config.toml` of their own gets. see readme
47
      '';
48
    };
49
50
    claude = {
51
      hooks = lib.mkOption {
52
        type = lib.types.attrs;
53
        readOnly = true;
54
        default = import ./hooks.nix {
55
          inherit lib;
56
          roundabout = cfg.package;
57
        };
58
        defaultText = lib.literalMD "roundabout's hooks, keyed by event";
59
        description = ''
60
          hooks in `settings.json` shape, for whatever writes your claude config.
61
          lists concatenate under the json format type, so they sit alongside
62
          hooks you already have
63
        '';
64
      };
65
66
      skill = lib.mkOption {
67
        type = lib.types.path;
68
        readOnly = true;
69
        default = "${plugin}/plugins/roundabout/skills/roundabout";
70
        defaultText = lib.literalMD "the roundabout skill directory";
71
        description = "the skill directory, for configs that manage `~/.claude/skills` themselves";
72
      };
73
74
      plugin = lib.mkOption {
75
        type = lib.types.package;
76
        readOnly = true;
77
        default = plugin;
78
        defaultText = lib.literalMD "the roundabout plugin marketplace";
79
        description = "hooks and skill as a marketplace, for `claude plugin marketplace add`";
80
      };
81
    };
82
  };
83
84
  config = lib.mkIf cfg.enable {
85
    environment.systemPackages = [ cfg.package ];
86
87
    # a real file rather than the usual /etc/static symlink, so a rebuild
88
    # renames over it where the daemon's inotify watch can see
89
    environment.etc."roundabout/config.toml" = lib.mkIf (cfg.settings != { }) {
90
      source = toml.generate "roundabout-config.toml" cfg.settings;
91
      mode = "0444";
92
    };
93
94
    systemd.user.sockets.roundabout = lib.mkIf cfg.socketActivation {
95
      description = "roundabout build admission socket";
96
      wantedBy = [ "sockets.target" ];
97
      listenStreams = [ "%t/roundabout.sock" ];
98
      socketConfig.SocketMode = "0600";
99
    };
100
101
    systemd.user.services.roundabout = lib.mkIf cfg.socketActivation {
102
      description = "roundabout build admission daemon";
103
      serviceConfig.ExecStart = "${lib.getExe cfg.package} daemon";
104
    };
105
  };
106
}