diff --git a/README.md b/README.md index 5a61c05..b3b0208 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,19 @@ These tools are essential for a capable openclaw instance - screen capture, came - **Fresh**: CI keeps tools and skills at latest automatically - **Integrated**: Skills teach your bot how to use each tool +## Pure Nix and Home Manager design + +This repo is meant to be consumable directly from Nix flakes, Darwin, and Home +Manager configs. Packages should stay pinned and reproducible, and optional +Home Manager modules should expose normal `programs.` options instead of +requiring downstream users to hand-write app bundle or launchd glue. + +For macOS app bundles, prefer a Nix package plus Home Manager integration. Home +Manager can expose app bundles from `home.packages` through its Darwin app +targets, and modules can opt into `launchd.agents` when Home Manager should own +startup. Homebrew casks are still useful, but they belong in a nix-darwin +Homebrew configuration, not in these pure Nix package/module definitions. + ## What's included | Tool | What it does | @@ -31,6 +44,7 @@ These tools are essential for a capable openclaw instance - screen capture, came | [**poltergeist**](https://github.com/steipete/poltergeist) | Universal file watcher with auto-rebuild | | [**sag**](https://github.com/steipete/sag) | Command-line ElevenLabs TTS with mac-style flags | | [**imsg**](https://github.com/steipete/imsg) | iMessage/SMS CLI | +| [**CodexBar**](https://github.com/steipete/CodexBar) | macOS menu bar app for Codex, Claude, and other provider usage | ## Usage (as openclaw plugins) @@ -63,6 +77,7 @@ inputs.nix-steipete-tools.packages.aarch64-darwin.camsnap inputs.nix-steipete-tools.packages.aarch64-darwin.discrawl inputs.nix-steipete-tools.packages.aarch64-darwin.peekaboo inputs.nix-steipete-tools.packages.aarch64-darwin.wacrawl +inputs.nix-steipete-tools.packages.aarch64-darwin.codexbar-app # etc. # Linux examples: @@ -73,6 +88,41 @@ inputs.nix-steipete-tools.packages.x86_64-linux.summarize inputs.nix-steipete-tools.packages.x86_64-linux.wacrawl ``` +### Home Manager modules + +Some packages also ship Home Manager modules so they can be enabled directly +without hand-writing install and launchd wiring. For CodexBar: + +```nix +{ + imports = [ inputs.nix-steipete-tools.homeManagerModules.codexbar ]; + + programs.codexbar.enable = true; +} +``` + +That adds the CodexBar app package to `home.packages`. Home Manager can then +expose the app bundle through its Darwin app targets. If you want Home Manager +to own startup too, enable the launchd agent explicitly: + +```nix +{ + programs.codexbar = { + enable = true; + launchd.enable = true; + launchd.keepAlive = false; + }; +} +``` + +### macOS app bundles + +If you only want the raw app package, use the package output directly: + +```nix +inputs.nix-steipete-tools.packages.aarch64-darwin.codexbar-app +``` + ## Skills syncing Skills are vendored from [openclaw/openclaw](https://github.com/openclaw/openclaw) main branch. No pinning - we track latest. diff --git a/flake.nix b/flake.nix index 08241bb..6fb9b3d 100644 --- a/flake.nix +++ b/flake.nix @@ -22,6 +22,7 @@ poltergeist = [ "aarch64-darwin" ]; sag = [ "aarch64-darwin" "x86_64-linux" ]; imsg = [ "aarch64-darwin" ]; + codexbar-app = [ "aarch64-darwin" ]; }; in { packages = forAllSystems (system: @@ -66,8 +67,16 @@ // (lib.optionalAttrs (supports "imsg") { imsg = pkgs.callPackage ./nix/pkgs/imsg.nix {}; }) + // (lib.optionalAttrs (supports "codexbar-app") { + codexbar-app = pkgs.callPackage ./nix/pkgs/codexbar-app.nix {}; + }) ); + homeManagerModules = { + codexbar = import ./nix/modules/home-manager/codexbar.nix { inherit self; }; + default = self.homeManagerModules.codexbar; + }; + checks = forAllSystems (system: self.packages.${system}); }; } diff --git a/nix/modules/home-manager/codexbar.nix b/nix/modules/home-manager/codexbar.nix new file mode 100644 index 0000000..8dfa2c6 --- /dev/null +++ b/nix/modules/home-manager/codexbar.nix @@ -0,0 +1,88 @@ +{ self }: +{ + config, + lib, + pkgs, + ... +}: +let + inherit (lib) mkEnableOption mkIf mkOption types; + cfg = config.programs.codexbar; + system = pkgs.stdenv.hostPlatform.system; + defaultPackage = + if self.packages ? ${system} && self.packages.${system} ? codexbar-app then + self.packages.${system}.codexbar-app + else + null; +in +{ + options.programs.codexbar = { + enable = mkEnableOption "CodexBar macOS menu bar app"; + + package = mkOption { + type = types.nullOr types.package; + default = defaultPackage; + defaultText = lib.literalExpression "inputs.nix-steipete-tools.packages.\${pkgs.system}.codexbar-app"; + description = "CodexBar app package."; + }; + + launchd = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Configure a launchd agent to manage the CodexBar process. + + CodexBar can also manage launch-at-login from inside the app. Enable this + option when you want Home Manager and launchd to own startup instead. + ''; + }; + + keepAlive = mkOption { + type = types.bool; + default = false; + description = "Whether launchd should restart CodexBar after it exits."; + }; + + environmentVariables = mkOption { + type = types.attrsOf types.str; + default = { + PATH = "${config.home.profileDirectory}/bin:/usr/bin:/bin:/usr/sbin:/sbin"; + }; + defaultText = lib.literalExpression '' + { + PATH = "\${config.home.profileDirectory}/bin:/usr/bin:/bin:/usr/sbin:/sbin"; + } + ''; + description = "Environment variables passed to the CodexBar launchd agent."; + }; + }; + }; + + config = mkIf cfg.enable { + assertions = [ + (lib.hm.assertions.assertPlatform "programs.codexbar" pkgs lib.platforms.darwin) + { + assertion = cfg.package != null; + message = "programs.codexbar.package must be set on this platform."; + } + { + assertion = !cfg.launchd.enable || config.launchd.enable; + message = "programs.codexbar.launchd.enable requires launchd.enable."; + } + ]; + + home.packages = lib.mkIf (cfg.package != null) [ cfg.package ]; + + launchd.agents.codexbar = mkIf cfg.launchd.enable { + enable = true; + config = { + ProgramArguments = [ "${cfg.package}/Applications/CodexBar.app/Contents/MacOS/CodexBar" ]; + ProcessType = "Interactive"; + RunAtLoad = true; + KeepAlive = cfg.launchd.keepAlive; + EnvironmentVariables = cfg.launchd.environmentVariables; + }; + }; + }; +} diff --git a/nix/pkgs/codexbar-app.nix b/nix/pkgs/codexbar-app.nix new file mode 100644 index 0000000..c5ab0ef --- /dev/null +++ b/nix/pkgs/codexbar-app.nix @@ -0,0 +1,37 @@ +{ + lib, + stdenvNoCC, + fetchzip, +}: + +stdenvNoCC.mkDerivation { + pname = "codexbar-app"; + version = "0.23"; + + src = fetchzip { + url = "https://github.com/steipete/CodexBar/releases/download/v0.23/CodexBar-0.23.zip"; + hash = "sha256-4LtZZzoP9tofNhy8jVkaopE+aivn0PRBEeJBGpDnbE8="; + stripRoot = false; + }; + + dontUnpack = true; + + installPhase = '' + runHook preInstall + mkdir -p "$out/Applications" + app_path="$(find "$src" -maxdepth 2 -name 'CodexBar.app' -print -quit)" + if [ -z "$app_path" ]; then + echo "CodexBar.app not found in $src" >&2 + exit 1 + fi + cp -R "$app_path" "$out/Applications/CodexBar.app" + runHook postInstall + ''; + + meta = with lib; { + description = "CodexBar macOS menu bar app bundle"; + homepage = "https://github.com/steipete/CodexBar"; + license = licenses.mit; + platforms = [ "aarch64-darwin" ]; + }; +}