diff --git a/flake.nix b/flake.nix index 76b1cc9..80b5e2c 100644 --- a/flake.nix +++ b/flake.nix @@ -27,8 +27,11 @@ }; }; - outputs = { nixpkgs, nixpkgs-unstable, nur, ... }@inputs: + outputs = { self, nixpkgs, nixpkgs-unstable, nur, ... }@inputs: let + openhandsCliVersion = "1.14.0"; + python312SlimDigest = "sha256:804ddf3251a60bbf9c92e73b7566c40428d54d0e79d3428194edf40da6521286"; + mkPkgs = system: import nixpkgs { inherit system; config = { @@ -58,6 +61,69 @@ ]; }; + mkOpenHandsCli = pkgs: pkgs.writeShellApplication { + name = "openhands-cli"; + runtimeInputs = with pkgs; [ docker coreutils ]; + text = '' + set -euo pipefail + + SANDBOX_VOLUMES="''${SANDBOX_VOLUMES:-$PWD:/workspace}" + STATE_DIR="''${OPENHANDS_STATE_DIR:-$HOME/.openhands}" + AGENT_SERVER_IMAGE_REPOSITORY="''${OPENHANDS_AGENT_SERVER_IMAGE_REPOSITORY:-ghcr.io/openhands/agent-server}" + AGENT_SERVER_IMAGE_TAG="''${OPENHANDS_AGENT_SERVER_IMAGE_TAG:-1.15.0-python}" + LLM_MODEL="''${OPENHANDS_LLM_MODEL:-openai/Qwen3-Coder-30B-A3B-Instruct-Q8_0.gguf}" + LLM_BASE_URL="''${OPENHANDS_LLM_BASE_URL:-http://192.168.0.23:8000/v1}" + LLM_API_KEY="''${OPENHANDS_LLM_API_KEY:-local-llm}" + LLM_TIMEOUT="''${OPENHANDS_LLM_TIMEOUT:-300}" + CLI_BASE_IMAGE="''${OPENHANDS_CLI_BASE_IMAGE:-python:3.12-slim@${python312SlimDigest}}" + CLI_VERSION="''${OPENHANDS_CLI_VERSION:-${openhandsCliVersion}}" + CLI_IMAGE="''${OPENHANDS_CLI_IMAGE:-local/openhands-cli:''${CLI_VERSION}}" + CONTAINER_NAME="''${OPENHANDS_CONTAINER_NAME:-openhands-cli-$(date +%Y%m%d%H%M%S)}" + + mkdir -p "$STATE_DIR" + + if ! docker image inspect "$CLI_IMAGE" >/dev/null 2>&1; then + docker build --pull \ + --build-arg BASE_IMAGE="$CLI_BASE_IMAGE" \ + --build-arg OPENHANDS_CLI_VERSION="$CLI_VERSION" \ + -t "$CLI_IMAGE" - <<'EOF' + ARG BASE_IMAGE + FROM ''${BASE_IMAGE} + ARG OPENHANDS_CLI_VERSION + RUN pip install --no-cache-dir uv \ + && uv tool install --python 3.12 "openhands==''${OPENHANDS_CLI_VERSION}" \ + && ln -sf /root/.local/bin/openhands /usr/local/bin/openhands \ + && ln -sf /root/.local/bin/openhands-acp /usr/local/bin/openhands-acp + ENV PATH="/root/.local/bin:''${PATH}" + ENTRYPOINT ["openhands"] + EOF + fi + + tty_flags=() + if [ -t 0 ] && [ -t 1 ]; then + tty_flags=(-it) + fi + + exec docker run "''${tty_flags[@]}" --rm \ + -e AGENT_SERVER_IMAGE_REPOSITORY="$AGENT_SERVER_IMAGE_REPOSITORY" \ + -e AGENT_SERVER_IMAGE_TAG="$AGENT_SERVER_IMAGE_TAG" \ + -e LLM_MODEL="$LLM_MODEL" \ + -e LLM_BASE_URL="$LLM_BASE_URL" \ + -e LLM_API_KEY="$LLM_API_KEY" \ + -e LLM_TIMEOUT="$LLM_TIMEOUT" \ + -e SANDBOX_USER_ID="$(id -u)" \ + -e SANDBOX_VOLUMES="$SANDBOX_VOLUMES" \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v "$PWD":/workspace \ + -v "$STATE_DIR":/root/.openhands \ + -w /workspace \ + --add-host host.docker.internal:host-gateway \ + --name "$CONTAINER_NAME" \ + "$CLI_IMAGE" \ + "$@" + ''; + }; + in { nixosConfigurations = { @@ -85,5 +151,12 @@ age-plugin-yubikey ]; }; + + packages.x86_64-linux.openhands-cli = mkOpenHandsCli (mkPkgs "x86_64-linux"); + + apps.x86_64-linux.openhands-cli = { + type = "app"; + program = "${self.packages.x86_64-linux.openhands-cli}/bin/openhands-cli"; + }; }; } diff --git a/justfile b/justfile index 2869dcb..ad0e9e5 100644 --- a/justfile +++ b/justfile @@ -99,6 +99,11 @@ pkgs: options: @xdg-open https://search.nixos.org/options +# Run OpenHands CLI via flake app (requires a local Docker runtime) +[group('tools')] +openhands-cli *ARGS='': + @nix run .#openhands-cli -- {{ARGS}} + # NixOS-rebuild switch for the current system [group('nixos')] switch: diff --git a/user/modules/utils/dev/default.nix b/user/modules/utils/dev/default.nix index 1c10315..bf7232a 100644 --- a/user/modules/utils/dev/default.nix +++ b/user/modules/utils/dev/default.nix @@ -3,7 +3,6 @@ with lib; let cfg = config.modules.user.utils.dev; - in { options.modules.user.utils.dev = { enable = mkEnableOption "user.utils.dev"; }; config = mkIf cfg.enable {