From 28f74a745e8ae8aa6fcba0bcd9bf5310a0ff3d31 Mon Sep 17 00:00:00 2001 From: Nate Olsen Date: Sun, 6 Sep 2026 08:34:27 -0700 Subject: [PATCH 1/5] Potential fix for code scanning alert no. 2: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .github/workflows/hass-ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/hass-ci.yml b/.github/workflows/hass-ci.yml index e3b8a7a..a23f10a 100644 --- a/.github/workflows/hass-ci.yml +++ b/.github/workflows/hass-ci.yml @@ -20,6 +20,9 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +permissions: + contents: read + jobs: test: runs-on: ubuntu-latest From 535f0646e9df32fc1648a229f519b300f03ec687 Mon Sep 17 00:00:00 2001 From: Nate Olsen Date: Sun, 6 Sep 2026 08:39:46 -0700 Subject: [PATCH 2/5] resolving hassfest validation error --- custom_components/pfsense/config_flow.py | 7 ++++++- custom_components/pfsense/strings.json | 2 +- custom_components/pfsense/translations/en.json | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/custom_components/pfsense/config_flow.py b/custom_components/pfsense/config_flow.py index ee0db58..5723188 100644 --- a/custom_components/pfsense/config_flow.py +++ b/custom_components/pfsense/config_flow.py @@ -131,7 +131,12 @@ async def async_step_user(self, user_input=None): vol.Optional(CONF_NAME, default=user_input.get(CONF_NAME, "")): str, } ) - return self.async_show_form(step_id="user", data_schema=schema, errors=errors) + return self.async_show_form( + step_id="user", + data_schema=schema, + errors=errors, + description_placeholders={"example_url": "https://pfsense.local:8444"}, + ) async def async_step_import(self, user_input): """Handle YAML import.""" diff --git a/custom_components/pfsense/strings.json b/custom_components/pfsense/strings.json index aa9157d..f3a224d 100644 --- a/custom_components/pfsense/strings.json +++ b/custom_components/pfsense/strings.json @@ -4,7 +4,7 @@ "step": { "user": { "title": "Connect to the pfSense firewall / router", - "description": "Enter the base URL of the pfSense REST API (including the port, e.g. https://pfsense.local:8444) and an API key generated at System › REST API › Keys.", + "description": "Enter the base URL of the pfSense REST API (including the port, e.g. {example_url}) and an API key generated at System › REST API › Keys.", "data": { "url": "URL", "api_key": "API key", diff --git a/custom_components/pfsense/translations/en.json b/custom_components/pfsense/translations/en.json index aa9157d..f3a224d 100644 --- a/custom_components/pfsense/translations/en.json +++ b/custom_components/pfsense/translations/en.json @@ -4,7 +4,7 @@ "step": { "user": { "title": "Connect to the pfSense firewall / router", - "description": "Enter the base URL of the pfSense REST API (including the port, e.g. https://pfsense.local:8444) and an API key generated at System › REST API › Keys.", + "description": "Enter the base URL of the pfSense REST API (including the port, e.g. {example_url}) and an API key generated at System › REST API › Keys.", "data": { "url": "URL", "api_key": "API key", From 10d9a49daffb47a3eda304f3ddde5d0fd35d9cdf Mon Sep 17 00:00:00 2001 From: Nate Olsen Date: Sun, 6 Sep 2026 08:43:17 -0700 Subject: [PATCH 3/5] added permissions to github actions to resolve codeql warnings --- .github/workflows/hacs.yaml | 3 +++ .github/workflows/hassfest.yaml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/hacs.yaml b/.github/workflows/hacs.yaml index c4c0d07..01fad34 100644 --- a/.github/workflows/hacs.yaml +++ b/.github/workflows/hacs.yaml @@ -6,6 +6,9 @@ on: schedule: - cron: "0 0 * * *" +permissions: + contents: read + jobs: validate: runs-on: ubuntu-latest diff --git a/.github/workflows/hassfest.yaml b/.github/workflows/hassfest.yaml index 2fa6d22..ce7e7de 100644 --- a/.github/workflows/hassfest.yaml +++ b/.github/workflows/hassfest.yaml @@ -4,6 +4,9 @@ on: push: pull_request: +permissions: + contents: read + jobs: hassfest: runs-on: ubuntu-latest From e30ce7780c34e90694f5c5cbdbfef482a6dbe89b Mon Sep 17 00:00:00 2001 From: Nate Olsen Date: Sun, 6 Sep 2026 08:46:20 -0700 Subject: [PATCH 4/5] remove ai_engineer --- ai_engineer/dependency_updater.py | 89 ------------------ ai_engineer/issue_resolver.py | 91 ------------------- ai_engineer/release_writer.py | 146 ------------------------------ ai_engineer/repair.py | 99 -------------------- ai_engineer/requirenments.txt | 0 ai_engineer/reviewer.py | 63 ------------- ai_engineer/test_writer.py | 80 ---------------- ai_engineer/triage.py | 70 -------------- 8 files changed, 638 deletions(-) delete mode 100644 ai_engineer/dependency_updater.py delete mode 100644 ai_engineer/issue_resolver.py delete mode 100644 ai_engineer/release_writer.py delete mode 100644 ai_engineer/repair.py delete mode 100644 ai_engineer/requirenments.txt delete mode 100644 ai_engineer/reviewer.py delete mode 100644 ai_engineer/test_writer.py delete mode 100644 ai_engineer/triage.py diff --git a/ai_engineer/dependency_updater.py b/ai_engineer/dependency_updater.py deleted file mode 100644 index 62e2fa5..0000000 --- a/ai_engineer/dependency_updater.py +++ /dev/null @@ -1,89 +0,0 @@ -import os -import re -import requests -from openai import OpenAI - -# 1. Fetch the absolute latest version from PyPI -try: - response = requests.get("https://pypi.org/pypi/FlightRadarAPI/json") - response.raise_for_status() - latest_version = response.json()["info"]["version"] -except Exception as e: - print(f"Failed to fetch PyPI data: {e}") - exit(1) - -# 2. Extract current version from your requirements.txt -current_version = None -try: - with open("requirements.txt", "r") as f: - reqs = f.read() - # Look for FlightRadarAPI==1.3.34 or similar - match = re.search(r"FlightRadarAPI[=<>]+([\d\.]+)", reqs) - if match: - current_version = match.group(1) -except FileNotFoundError: - print("Could not find requirements.txt") - exit(1) - -# 3. Compare versions -if not current_version or current_version == latest_version: - print(f"FlightRadarAPI is already up to date! (Version {latest_version})") - exit(0) - -print(f"🚨 New Version Detected! Updating {current_version} -> {latest_version}") - -# 4. Update the actual files -files_to_update = { - "requirements.txt": f"FlightRadarAPI=={latest_version}", - "requirements_test.txt": f"FlightRadarAPI=={latest_version}", - "custom_components/skyradar_fusion/manifest.json": f"FlightRadarAPI>={latest_version}", -} - -for file_path, replacement_string in files_to_update.items(): - try: - with open(file_path, "r") as f: - content = f.read() - - # Replace the old version string with the new one - new_content = re.sub( - r"FlightRadarAPI[=<>]+[\d\.]+", replacement_string, content - ) - - with open(file_path, "w") as f: - f.write(new_content) - print(f"✅ Updated {file_path}") - except FileNotFoundError: - print(f"⚠️ Could not find {file_path}, skipping.") - -# 5. Let Snoop write the PR description -api_key = os.getenv("OPENROUTER_API_KEY") -if not api_key: - print("Files updated, but no API key found for Snoop. Exiting.") - exit(0) - -client = OpenAI(base_url="https://openrouter.ai/api/v1", api_key=api_key) - -prompt = f""" -You are the AI Staff Engineer for 'SkyRadar Fusion'. Your persona is Snoop Dogg. -You just built a bot that checks the internet for library updates, and it found one! -You automatically bumped the 'FlightRadarAPI' package from version {current_version} to {latest_version} in the manifest and requirements files. - -Write a smooth, professional, yet Snoop-styled Pull Request description explaining that you updated the dependencies. -DO NOT use triple backticks or code blocks. Just output the raw text directly. -""" - -try: - completion = client.chat.completions.create( - model="gpt-4o-mini", - messages=[{"role": "user", "content": prompt}], - ) - pr_body = completion.choices[0].message.content.strip() - - # Save the PR text and title for the GitHub Action to pick up - with open("pr_body.txt", "w") as f: - f.write(pr_body) - with open("pr_title.txt", "w") as f: - f.write(f"⬆️ Bump FlightRadarAPI from {current_version} to {latest_version}") - -except Exception as e: - print(f"Snoop PR generation failed: {e}") diff --git a/ai_engineer/issue_resolver.py b/ai_engineer/issue_resolver.py deleted file mode 100644 index 7509c2c..0000000 --- a/ai_engineer/issue_resolver.py +++ /dev/null @@ -1,91 +0,0 @@ -import os -from openai import OpenAI - -# 1. Grab the issue details from GitHub environment variables -issue_title = os.getenv("ISSUE_TITLE", "") -issue_body = os.getenv("ISSUE_BODY", "") - -if not issue_title or not issue_body: - print("Missing issue context. Exiting.") - exit(0) - -# 2. Point it at the file that needs updating -# For SkyRadar Fusion, this is usually your coordinator or data management file -target_file = "custom_components/skyradar_fusion/coordinator.py" - -try: - with open(target_file, "r") as f: - current_code = f.read() -except FileNotFoundError: - print(f"Could not find target file: {target_file}") - exit(0) - -api_key = os.getenv("OPENROUTER_API_KEY") -if not api_key: - print("API key missing. Exiting.") - exit(0) - -client = OpenAI(base_url="https://openrouter.ai/api/v1", api_key=api_key) - -# 3. Setup the Snoop Coder Prompt -prompt = f""" -You are the Lead AI Software Engineer for 'SkyRadar Fusion'. Your persona is Snoop Dogg. -The user just dropped a new feature request / issue report, and you need to implement it directly into the codebase. - -Here is the Issue Title: -{issue_title} - -Here is the Issue Description: -{issue_body} - -Here is the current Python code for {target_file}: -{current_code} - -Instructions: -1. Carefully implement the requested changes or new API fields into the existing code. -2. Keep the code professional, efficient, and fully compatible with Home Assistant standard practices. -3. Keep all existing functionality completely intact—do not delete unrelated logic! -4. The response MUST contain the COMPLETELY updated python code inside a standard code block. - -IMPORTANT PARSING INSTRUCTIONS: -You must start your response with the exact marker line: -CODE: -followed immediately by the python code block using triple backticks. -""" - -try: - completion = client.chat.completions.create( - model="gpt-4o-mini", - messages=[{"role": "user", "content": prompt}], - ) - - response_text = completion.choices[0].message.content.strip() - - # 4. Parse out the code using our bulletproof trick - lines = response_text.splitlines() - code_lines = [] - is_code = False - - BACKTICKS = "`" * 3 - PY_BACKTICKS = BACKTICKS + "python" - - for line in lines: - if line.startswith("CODE:") or line.startswith(PY_BACKTICKS): - is_code = True - continue - elif is_code: - if line.startswith(BACKTICKS): - is_code = False - else: - code_lines.append(line) - - # 5. Overwrite the file with the AI's new code - if code_lines: - with open(target_file, "w") as f: - f.write("\n".join(code_lines)) - print(f"Successfully implemented feature in {target_file}") - else: - print("Failed to parse code from the AI response.") - -except Exception as e: - print(f"Feature development failed: {e}") diff --git a/ai_engineer/release_writer.py b/ai_engineer/release_writer.py deleted file mode 100644 index 989ec9f..0000000 --- a/ai_engineer/release_writer.py +++ /dev/null @@ -1,146 +0,0 @@ -import os -import re -import time -import requests - -try: - with open("changelog.txt", "r") as f: - changelog = f.read() -except FileNotFoundError: - print("Could not find changelog.txt. Exiting.") - exit(0) - -api_key = os.getenv("OPENROUTER_API_KEY") -if not api_key: - print("No API key found. Exiting.") - exit(0) - -# Detect the project name automatically from the repo path -repo = os.getenv("GITHUB_REPOSITORY") or os.getenv("REPO", "") - -if repo: - repo_name = repo.split("/")[-1] - project_name = repo_name.replace("-", " ").replace("_", " ").title() -else: - project_name = "This Project" -# Anti-markdown-break trick -BACKTICKS = "`" * 3 - -prompt = f""" -You are the AI Release Manager for '{project_name}'. Your persona is Snoop Dogg. -We are dropping a brand new release, and your job is to write the official GitHub Release Notes based on the commit history. - -Here are the commit titles and extended descriptions since the last release: -{changelog} - -CRITICAL INSTRUCTIONS: -1. Even if there is only ONE tiny commit (e.g., "Enhance README"), you must expand it into a full, hype, professional release note. -2. Organize the markdown clearly with these categories (use them even if you have to creatively explain the small changes): - - 🚀 What's New & Fly (The main features or updates) - - 🛠️ Changed & Fixed (Bug fixes, tweaks) - - ⚙️ Under the Hood (Backend, docs, chores) -3. Explain the updates in a smooth, engaging way (Snoop Dogg style, but keep it highly professional). -4. ONLY output the raw Markdown text. DO NOT wrap your response in triple backticks ({BACKTICKS}) or a code block. Just output the raw text directly. -""" - - -def send_request_with_retry(method, url, headers, json_data, timeout, max_retries=5): - """Performs HTTP requests with exponential backoff retries and detailed logs.""" - delay = 1 - for attempt in range(max_retries): - try: - print( - f"Sending {method} request to {url} (Attempt {attempt + 1}/{max_retries})..." - ) - if method == "POST": - response = requests.post( - url, headers=headers, json=json_data, timeout=timeout - ) - elif method == "PATCH": - response = requests.patch( - url, headers=headers, json=json_data, timeout=timeout - ) - else: - raise ValueError(f"Unsupported method: {method}") - - # Check for success status codes - if response.status_code in [200, 201]: - return response - else: - print( - f"Server returned status code {response.status_code}: {response.text}" - ) - except Exception as e: - print(f"Attempt {attempt + 1} failed with error: {e}") - - if attempt < max_retries - 1: - print(f"Retrying in {delay} seconds...") - time.sleep(delay) - delay *= 2 - - raise Exception( - f"Failed to complete {method} request to {url} after {max_retries} attempts." - ) - - -try: - # Direct requests call bypasses any OpenAI library proxy/connection pool bugs - repo = os.getenv("GITHUB_REPOSITORY") or os.getenv("REPO", "") - openrouter_url = "https://openrouter.ai/api/v1/chat/completions" - openrouter_headers = { - "Authorization": f"Bearer {api_key}", - "Content-Type": "application/json", - "HTTP-Referer": f"https://github.com/{repo}" if repo else "https://github.com", - "X-Title": f"{project_name} Release Bot", - } - openrouter_payload = { - "model": "gpt-4o-mini", - "messages": [{"role": "user", "content": prompt}], - } - - # Connect to OpenRouter to write the release notes - api_response = send_request_with_retry( - method="POST", - url=openrouter_url, - headers=openrouter_headers, - json_data=openrouter_payload, - timeout=45.0, - max_retries=5, - ) - - result = api_response.json() - if "choices" not in result or not result["choices"]: - raise Exception(f"Invalid API response structure: {result}") - - release_notes = result["choices"][0]["message"]["content"].strip() - - # Clean up any accidental code block wrappers without breaking Ruff/Markdown - pattern = rf"^{BACKTICKS}(?:markdown)?\n|\n{BACKTICKS}$" - release_notes = re.sub(pattern, "", release_notes).strip() - - # Update GitHub Release - repo = os.getenv("REPO") - release_id = os.getenv("RELEASE_ID") - token = os.getenv("GITHUB_TOKEN") - - github_url = f"https://api.github.com/repos/{repo}/releases/{release_id}" - github_headers = { - "Authorization": f"Bearer {token}", - "Accept": "application/vnd.github.v3+json", - "Content-Type": "application/json", - } - - # Patch the release notes directly onto your GitHub Release page - github_response = send_request_with_retry( - method="PATCH", - url=github_url, - headers=github_headers, - json_data={"body": release_notes}, - timeout=20.0, - max_retries=3, - ) - - print(f"Successfully dropped the new release notes for {project_name}!") - -except Exception as e: - print(f"Release generation failed: {e}") diff --git a/ai_engineer/repair.py b/ai_engineer/repair.py deleted file mode 100644 index 9fc3c98..0000000 --- a/ai_engineer/repair.py +++ /dev/null @@ -1,99 +0,0 @@ -import os -import re -from openai import OpenAI - -# 1. Get the error logs -try: - with open("failed_logs.txt", "r") as f: - logs = f.read()[-3000:] -except FileNotFoundError: - print("No failed_logs.txt found. Exiting.") - exit(0) - -# 2. Extract the broken file path from the Ruff/Pytest logs using Regex -# This looks for lines like "--> ai_engineer/repair.py:21:6" -match = re.search(r"-->\s+([a-zA-Z0-9_/\.]+):", logs) -file_path = match.group(1) if match else None -file_content = "File content could not be loaded." - -# 3. Read the broken code so the AI can actually see it -if file_path: - try: - with open(file_path, "r") as f: - file_content = f.read() - except FileNotFoundError: - print(f"Could not open extracted file path: {file_path}") - -# 4. Initialize AI -api_key = os.getenv("OPENROUTER_API_KEY") -if not api_key: - print("No OPENROUTER_API_KEY found.") - exit(0) - -client = OpenAI(base_url="https://openrouter.ai/api/v1", api_key=api_key) - -# 5. Snoop Dogg Prompt -prompt = f""" -You are the AI Self-Healing Mechanic for 'SkyRadar Fusion'. Your persona is Snoop Dogg. -The CI pipeline just tripped up, but you stay relaxed and fix the engine while it's running. - -The error occurred in this file: {file_path} - -Here is the broken code: -{file_content} - -Here is the error log: -{logs} - -1. Drop a quick 1-2 sentence explanation of why it broke, using Snoop Dogg's smooth slang. Keep it cool. -2. Provide the COMPLETELY FIXED Python code. -3. The fixed code MUST be inside a standard ```python code block. Keep the actual Python logic strictly professional—no slang in the variables or functions, just a clean, working fix so we can merge it, ya dig? - -IMPORTANT: You must start your response with the exact line: -FILEPATH: {file_path} -Then write your Snoop intro. -Then output the fixed code exactly starting with CODE: and then the ```python block. -""" - -# 6. Request Fix -try: - completion = client.chat.completions.create( - model="gpt-4o-mini", - messages=[{"role": "user", "content": prompt}], - ) - - response_text = completion.choices[0].message.content.strip() - - # 7. Print Snoop's explanation to the GitHub Actions terminal so you can read it! - print("\n--- AI MECHANIC REPORT ---") - print(response_text) - print("--------------------------\n") - - # 8. Parse the code out of the response - lines = response_text.splitlines() - target_file = None - code_lines = [] - is_code = False - - for line in lines: - if line.startswith("FILEPATH:"): - target_file = line.replace("FILEPATH:", "").strip() - elif line.startswith("CODE:") or line.startswith("```python"): - is_code = True - continue # skip the marker line - elif is_code: - if line.startswith("```"): - is_code = False # End of block - else: - code_lines.append(line) - - # 9. Apply the fix - if target_file and code_lines: - with open(target_file, "w") as f: - f.write("\n".join(code_lines)) - print(f"Patched {target_file} successfully.") - else: - print("Failed to parse the patched code from the AI response.") - -except Exception as e: - print(f"Repair failed: {e}") diff --git a/ai_engineer/requirenments.txt b/ai_engineer/requirenments.txt deleted file mode 100644 index e69de29..0000000 diff --git a/ai_engineer/reviewer.py b/ai_engineer/reviewer.py deleted file mode 100644 index c535ed2..0000000 --- a/ai_engineer/reviewer.py +++ /dev/null @@ -1,63 +0,0 @@ -import os -import requests -from openai import OpenAI - -try: - with open("pr_diff.txt", "r") as f: - diff_text = f.read() -except FileNotFoundError: - print("No diff found, exiting.") - exit(0) - -if len(diff_text.strip()) < 10: - print("Empty diff, skipping review.") - exit(0) - -api_key = os.getenv("OPENROUTER_API_KEY") -if not api_key: - print("No API key found. Skipping AI review.") - exit(0) - -client = OpenAI(base_url="https://openrouter.ai/api/v1", api_key=api_key) - -prompt = f""" -You are the AI Senior Code Reviewer for 'SkyRadar Fusion'. Your persona is Snoop Dogg. -You are the gatekeeper keeping the codebase fresh, clean, and tight. - -Here is the Pull Request diff: -{diff_text} - -1. Review the code for bugs, efficiency, architecture, and cleanliness. -2. Write your review summary in Snoop Dogg's voice. If it's fly, give it some praise. If it needs work or has security holes, point out the flaws smoothly and tell the dev how to fix it. -3. Any code snippets you suggest must be strictly professional standard Python. - -Sign off your review exactly like this: -**By:** SnoopDogg -**Role:** AI Code Reviewer for SkyRadar Fusion -""" - -completion = client.chat.completions.create( - model="gpt-4o-mini", - messages=[{"role": "user", "content": prompt}], -) - -review_comment = completion.choices[0].message.content.strip() - -if review_comment != "APPROVED": - print("Issues found. Posting comment...") - repo = os.getenv("GITHUB_REPOSITORY") - pr_number = os.getenv("PR_NUMBER") - token = os.getenv("GITHUB_TOKEN") - - url = f"https://api.github.com/repos/{repo}/issues/{pr_number}/comments" - headers = { - "Authorization": f"Bearer {token}", - "Accept": "application/vnd.github.v3+json", - } - requests.post( - url, - headers=headers, - json={"body": f"🔍 **AI Architecture Review:**\n\n{review_comment}"}, - ) -else: - print("Code looks good! No comment needed.") diff --git a/ai_engineer/test_writer.py b/ai_engineer/test_writer.py deleted file mode 100644 index 331092e..0000000 --- a/ai_engineer/test_writer.py +++ /dev/null @@ -1,80 +0,0 @@ -import os -from openai import OpenAI - -try: - with open("merged_diff.txt", "r") as f: - diff_text = f.read() -except FileNotFoundError: - exit(0) - -if len(diff_text.strip()) < 10: - exit(0) - -api_key = os.getenv("OPENROUTER_API_KEY") -if not api_key: - exit(0) - -client = OpenAI( - base_url="https://openrouter.ai/api/v1", - api_key=api_key, -) - -prompt = f""" -You are the AI Quality Assurance Engineer for 'SkyRadar Fusion'. Your persona is Snoop Dogg. -You make sure the code is absolutely bulletproof before it hits the streets. - -Here is the new code diff that needs testing: -{diff_text} - -1. Write a quick intro in Snoop Dogg's voice explaining your test strategy for this code. -2. Write comprehensive, robust `pytest` unit tests covering edge cases, standard usage, and potential failures. -3. The tests MUST be inside a standard python code block (using triple backticks). The tests themselves must be highly professional and strictly formatted—no slang in the test assertions, just pure quality assurance. - -IMPORTANT INSTRUCTIONS: -You must start your response with the exact line: -FILEPATH: tests/test_generated.py -Then write your Snoop intro. -Then output the tests exactly starting with CODE: and then the python code block. - -Sign off your intro exactly like this: -**By:** SnoopDogg -**Role:** AI QA Engineer for SkyRadar Fusion -""" - -try: - completion = client.chat.completions.create( - model="gpt-4o-mini", - messages=[{"role": "user", "content": prompt}], - ) - - lines = completion.choices[0].message.content.strip().splitlines() - file_path = None - code_lines = [] - is_code = False - - # Anti-markdown-break trick - BACKTICKS = "`" * 3 - PY_BACKTICKS = BACKTICKS + "python" - - for line in lines: - if line.startswith("FILEPATH:"): - file_path = line.replace("FILEPATH:", "").strip() - elif line.startswith("CODE:") or line.startswith(PY_BACKTICKS): - is_code = True - continue - elif is_code: - if line.startswith(BACKTICKS): - is_code = False - else: - code_lines.append(line) - - if file_path and code_lines: - os.makedirs(os.path.dirname(file_path), exist_ok=True) - with open(file_path, "w") as f: - f.write("\n".join(code_lines)) - print(f"Wrote test to {file_path}") - else: - print("Failed to extract FILEPATH or CODE from AI response.") - -except Exception as e: - print(f"Test generation failed: {e}") diff --git a/ai_engineer/triage.py b/ai_engineer/triage.py deleted file mode 100644 index e1f16c1..0000000 --- a/ai_engineer/triage.py +++ /dev/null @@ -1,70 +0,0 @@ -import os -import requests -from openai import OpenAI - -issue_title = os.getenv("ISSUE_TITLE", "") -issue_body = os.getenv("ISSUE_BODY", "") -issue_number = os.getenv("ISSUE_NUMBER") -repo = os.getenv("GITHUB_REPOSITORY") -token = os.getenv("GITHUB_TOKEN") - -api_key = os.getenv("OPENROUTER_API_KEY") -if not api_key: - exit(0) - -client = OpenAI(base_url="https://openrouter.ai/api/v1", api_key=api_key) - -prompt = f""" - You are the AI Repository Triage Agent for 'SkyRadar Fusion'. Your persona is Snoop Dogg. - You are chill, smooth, but a highly competent technical maintainer. - - A user just opened the following issue: - Title: {issue_title} - Body: {issue_body} - - 1. Assess the issue and determine the appropriate GitHub labels (e.g., 'bug', 'enhancement', 'question', 'help wanted'). - 2. Write a welcoming response comment to the user in Snoop Dogg's voice. Acknowledge their issue, tell them we got their back, and let them know the team is on it. - 3. IMPORTANT: Do NOT use slang in the actual labels you suggest, only use it in the comment text. - - Sign off your comment exactly like this: - **By:** SnoopDogg - **Role:** AI Triage Agent for SkyRadar Fusion - """ - -try: - completion = client.chat.completions.create( - model="gpt-4o-mini", - messages=[{"role": "user", "content": prompt}], - ) - - lines = completion.choices[0].message.content.strip().splitlines() - labels = [] - comment = "" - - for line in lines: - if line.startswith("LABELS:"): - labels_str = line.replace("LABELS:", "").strip() - labels = [label.strip() for label in labels_str.split(",") if label.strip()] - elif line.startswith("COMMENT:"): - comment = line.replace("COMMENT:", "").strip() - - headers = { - "Authorization": f"Bearer {token}", - "Accept": "application/vnd.github.v3+json", - } - - if labels: - requests.post( - f"https://api.github.com/repos/{repo}/issues/{issue_number}/labels", - headers=headers, - json={"labels": labels}, - ) - if comment: - requests.post( - f"https://api.github.com/repos/{repo}/issues/{issue_number}/comments", - headers=headers, - json={"body": f"?? **AI Triage:**\n\n{comment}"}, - ) - -except Exception as e: - print(f"Failed triage: {e}") From c89fe4d0e18caf667f8cbe5760f7e83298097a97 Mon Sep 17 00:00:00 2001 From: Nate Olsen Date: Sun, 6 Sep 2026 08:53:53 -0700 Subject: [PATCH 5/5] Fix poll crash: build default-gateway set from known keys only GET /api/v2/routing/gateway/default returns `data` with a nested `_links` dict alongside defaultgw4/defaultgw6, so `set(default.values())` in get_gateways_detail() raised "unhashable type: 'dict'" every poll cycle, dropping the integration to its on-disk cache. Build the default-name set from the defaultgw4/defaultgw6 keys instead of every value. Co-Authored-By: Claude Sonnet 5 --- custom_components/pfsense/pypfsense/__init__.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/custom_components/pfsense/pypfsense/__init__.py b/custom_components/pfsense/pypfsense/__init__.py index c06c24a..416b50d 100644 --- a/custom_components/pfsense/pypfsense/__init__.py +++ b/custom_components/pfsense/pypfsense/__init__.py @@ -306,7 +306,13 @@ async def get_gateways_detail(self) -> dict: self._get("/routing/gateways"), self._get("/routing/gateway/default"), ) - default_names = set(default.values()) if isinstance(default, dict) else set() + default_names = set() + if isinstance(default, dict): + default_names = { + default.get("defaultgw4"), + default.get("defaultgw6"), + } + default_names.discard(None) out = {} for gw in gateways or []: name = gw.get("name")