From ee7d66f47ef788cc1ed2977b013c31a2b32f09e7 Mon Sep 17 00:00:00 2001 From: juanaquas Date: Sun, 1 Mar 2026 00:40:02 -0600 Subject: [PATCH 1/7] Add GitHub Actions workflow for Python package with Conda This workflow automates the building and testing of a Python package using Conda on push events. It sets up Python, installs dependencies, lints the code with flake8, and runs tests with pytest. Signed-off-by: juanaquas --- .github/workflows/python-package-conda.yml | 34 ++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/workflows/python-package-conda.yml diff --git a/.github/workflows/python-package-conda.yml b/.github/workflows/python-package-conda.yml new file mode 100644 index 0000000..f358604 --- /dev/null +++ b/.github/workflows/python-package-conda.yml @@ -0,0 +1,34 @@ +name: Python Package using Conda + +on: [push] + +jobs: + build-linux: + runs-on: ubuntu-latest + strategy: + max-parallel: 5 + + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: '3.10' + - name: Add conda to system path + run: | + # $CONDA is an environment variable pointing to the root of the miniconda directory + echo $CONDA/bin >> $GITHUB_PATH + - name: Install dependencies + run: | + conda env update --file environment.yml --name base + - name: Lint with flake8 + run: | + conda install flake8 + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + conda install pytest + pytest From 9204d8820fdeea7955f27998e377e944ceb92c6a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Mar 2026 07:37:35 +0000 Subject: [PATCH 2/7] Initial plan From 3dfea336dfe066913ba73230d2907282dc6206a8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Mar 2026 07:52:13 +0000 Subject: [PATCH 3/7] Handle optional OpenAI dependency gracefully Co-authored-by: juanaquas <264702634+juanaquas@users.noreply.github.com> --- clients/python/hancock_client.py | 8 +++++++- hancock_agent.py | 19 ++++++++++++++----- hancock_constants.py | 9 +++++++++ 3 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 hancock_constants.py diff --git a/clients/python/hancock_client.py b/clients/python/hancock_client.py index 83a209d..7b486e9 100644 --- a/clients/python/hancock_client.py +++ b/clients/python/hancock_client.py @@ -19,7 +19,12 @@ import os from typing import Optional -from openai import OpenAI +from hancock_constants import OPENAI_IMPORT_ERROR_MSG, require_openai + +try: + from openai import OpenAI +except ImportError: # allow import; require_openai() enforces dependency in constructor + OpenAI = None # type: ignore # ── Models ────────────────────────────────────────────────────────────────── MODELS: dict[str, str] = { @@ -83,6 +88,7 @@ def __init__( coder_model: str = "qwen-coder", base_url: str = "https://integrate.api.nvidia.com/v1", ): + require_openai(OpenAI) key = api_key or os.environ.get("NVIDIA_API_KEY") if not key: raise ValueError( diff --git a/hancock_agent.py b/hancock_agent.py index 6561f71..49e97e0 100644 --- a/hancock_agent.py +++ b/hancock_agent.py @@ -28,17 +28,19 @@ export NVIDIA_API_KEY="nvapi-..." or pass --api-key "nvapi-..." """ +from __future__ import annotations import argparse import hmac import json import os import sys import readline # noqa: F401 — enables arrow-key history in CLI +from hancock_constants import OPENAI_IMPORT_ERROR_MSG, require_openai try: from openai import OpenAI -except ImportError: - sys.exit("Run: .venv/bin/pip install openai flask") +except ImportError: # allow import without OpenAI; client factories enforce requirement at runtime + OpenAI = None # type: ignore # ── Hancock identity ────────────────────────────────────────────────────────── PENTEST_SYSTEM = """You are Hancock, an elite penetration tester and offensive security specialist built by CyberViser. @@ -246,21 +248,28 @@ def make_ollama_client() -> OpenAI: """Returns an OpenAI-compatible client pointed at the local Ollama server.""" + require_openai(OpenAI) return OpenAI(base_url=OLLAMA_BASE_URL, api_key="ollama") def make_client(api_key: str) -> OpenAI: """Returns an OpenAI-compatible client pointed at NVIDIA NIM (legacy).""" + require_openai(OpenAI) return OpenAI(base_url=NIM_BASE_URL, api_key=api_key) def make_openai_client() -> OpenAI | None: - """Returns an OpenAI client if credentials are available, else None.""" + """Returns an OpenAI client if credentials are available, else None. + + Unlike the Ollama/NIM factories, OpenAI is a best-effort fallback, so the + absence of the dependency simply disables this path. + """ + if OpenAI is None: + return None key = os.getenv("OPENAI_API_KEY", "") - org = os.getenv("OPENAI_ORG_ID", "") if not key or key.startswith("sk-your"): return None - return OpenAI(api_key=key, organization=org or None) + return OpenAI(api_key=key, organization=os.getenv("OPENAI_ORG_ID") or None) def chat(client: OpenAI, history: list[dict], model: str, stream: bool = True, diff --git a/hancock_constants.py b/hancock_constants.py new file mode 100644 index 0000000..5cd249a --- /dev/null +++ b/hancock_constants.py @@ -0,0 +1,9 @@ +"""Shared constants for Hancock modules.""" + +OPENAI_IMPORT_ERROR_MSG = "OpenAI client not installed. Run: pip install openai" + + +def require_openai(openai_cls): + """Raise ImportError when the OpenAI dependency is missing.""" + if openai_cls is None: + raise ImportError(OPENAI_IMPORT_ERROR_MSG) From 258fe1c810c1f59284e666740ef62789515d2619 Mon Sep 17 00:00:00 2001 From: juanaquas Date: Sun, 1 Mar 2026 02:10:38 -0600 Subject: [PATCH 4/7] Update clients/python/hancock_client.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: juanaquas --- clients/python/hancock_client.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/clients/python/hancock_client.py b/clients/python/hancock_client.py index 7b486e9..a8c4129 100644 --- a/clients/python/hancock_client.py +++ b/clients/python/hancock_client.py @@ -19,7 +19,26 @@ import os from typing import Optional -from hancock_constants import OPENAI_IMPORT_ERROR_MSG, require_openai + +try: + from hancock_constants import OPENAI_IMPORT_ERROR_MSG, require_openai +except ImportError: + # Fallback definitions when hancock_constants is not available (e.g., in installed package). + OPENAI_IMPORT_ERROR_MSG = ( + "The 'openai' package is required to use HancockClient. " + "Install it with 'pip install openai' and ensure NVIDIA_API_KEY is set." + ) + + def require_openai() -> None: + """ + Ensure that the optional 'openai' dependency is available. + + This mirrors the behavior of the helper from hancock_constants: it defers + the ImportError until runtime (e.g., in the constructor) and provides a + clear error message when the dependency is missing. + """ + if OpenAI is None: # type: ignore[name-defined] + raise ImportError(OPENAI_IMPORT_ERROR_MSG) try: from openai import OpenAI From 58652032ace543e4a3b7723a66affbca958781d5 Mon Sep 17 00:00:00 2001 From: juanaquas Date: Sun, 1 Mar 2026 02:13:51 -0600 Subject: [PATCH 5/7] Update clients/python/hancock_client.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: juanaquas --- clients/python/hancock_client.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/clients/python/hancock_client.py b/clients/python/hancock_client.py index a8c4129..4ae97e6 100644 --- a/clients/python/hancock_client.py +++ b/clients/python/hancock_client.py @@ -29,15 +29,20 @@ "Install it with 'pip install openai' and ensure NVIDIA_API_KEY is set." ) - def require_openai() -> None: + def require_openai(client_cls: Optional[object] = None) -> None: """ Ensure that the optional 'openai' dependency is available. This mirrors the behavior of the helper from hancock_constants: it defers the ImportError until runtime (e.g., in the constructor) and provides a clear error message when the dependency is missing. + + The optional client_cls argument is accepted for compatibility with call + sites that invoke require_openai(OpenAI); if not provided, the module-level + OpenAI symbol is used. """ - if OpenAI is None: # type: ignore[name-defined] + client = client_cls if client_cls is not None else OpenAI # type: ignore[name-defined] + if client is None: raise ImportError(OPENAI_IMPORT_ERROR_MSG) try: From 24201310704e2a4550a98a5ba9bd697412f6701b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Mar 2026 08:41:07 +0000 Subject: [PATCH 6/7] Initial plan From 843d9406dca36fab9a075a37010ccb332903b82d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Mar 2026 08:44:17 +0000 Subject: [PATCH 7/7] chore: add autopilot cli flags Co-authored-by: juanaquas <264702634+juanaquas@users.noreply.github.com> --- hancock_agent.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hancock_agent.py b/hancock_agent.py index 49e97e0..90340f1 100644 --- a/hancock_agent.py +++ b/hancock_agent.py @@ -973,6 +973,9 @@ def main(): parser.add_argument("--model", default=None, help="Model ID (overrides backend default)") parser.add_argument("--server", action="store_true", help="Run as REST API server") parser.add_argument("--port", type=int, default=int(os.getenv("HANCOCK_PORT", "5000"))) + # Compatibility / no-op flags for autopilot runners + parser.add_argument("--max-autopilot-continues", type=int, default=None, help="Optional autopilot hint (ignored by Hancock)") + parser.add_argument("--allow-all", action="store_true", help="Optional autopilot hint (ignored by Hancock)") args = parser.parse_args() backend = os.getenv("HANCOCK_LLM_BACKEND", "ollama").lower()