diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh index 1519242..eb4cdb5 100644 --- a/.devcontainer/post-create.sh +++ b/.devcontainer/post-create.sh @@ -17,17 +17,10 @@ hatch build # Install this package from local directory. pip install -e . -# Create logs directory if it doesn't exist -mkdir -p logs - -# Create optional data directories -mkdir -p data - # Create .env file if it doesn't exist if [ ! -f .env ]; then echo "📝 Creating .env template..." echo "# Optional: CodeQL database base path" >> .env - echo "CODEQL_DBS_BASE_PATH=$(realpath data)" >> .env echo "⚠️ Please configure the environment or your .env file with required tokens!" fi diff --git a/.dockerignore b/.dockerignore index a815e83..a608da8 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,5 @@ __pycache__ docker/ -logs/**/* .env .venv .direnv diff --git a/.gitignore b/.gitignore index f8b7068..bc7983e 100644 --- a/.gitignore +++ b/.gitignore @@ -13,9 +13,6 @@ __pycache__/ # C extensions *.so -# Logs directory -logs/ - # Distribution / packaging .Python build/ @@ -186,5 +183,3 @@ config.yaml #database *.db -#logs -logs/ \ No newline at end of file diff --git a/README.md b/README.md index 2cafec0..4e2b52c 100644 --- a/README.md +++ b/README.md @@ -150,7 +150,6 @@ if [ ! -f ".env" ]; then fi docker run \ - --volume "$PWD"/logs:/app/logs \ --mount type=bind,src="$PWD"/.env,dst=/app/.env,ro \ ${MY_DATA:+--mount type=bind,src=$MY_DATA,dst=/app/my_data} \ ${MY_MCP_SERVERS:+--mount type=bind,src=$MY_MCP_SERVERS,dst=/app/my_mcp_servers,ro} \ diff --git a/docker/run.sh b/docker/run.sh index 8905bb3..5fec654 100755 --- a/docker/run.sh +++ b/docker/run.sh @@ -16,12 +16,9 @@ # sudo -E ../docker/run.sh -p seclab_taskflow_agent.personalities.assistant 'explain modems to me please' touch -a .env -mkdir -p logs -mkdir -p data docker run -i \ --mount type=bind,src="$PWD",dst=/app \ - -e DATA_DIR=/app/data \ -e GITHUB_PERSONAL_ACCESS_TOKEN="$GITHUB_PERSONAL_ACCESS_TOKEN" \ -e AI_API_TOKEN="$AI_API_TOKEN" \ "ghcr.io/githubsecuritylab/seclab-taskflow-agent" "$@" diff --git a/src/seclab_taskflow_agent/__main__.py b/src/seclab_taskflow_agent/__main__.py index 7f98b11..1072907 100644 --- a/src/seclab_taskflow_agent/__main__.py +++ b/src/seclab_taskflow_agent/__main__.py @@ -33,15 +33,14 @@ from .agent import TaskAgent from .capi import list_tool_call_models, get_AI_token from .available_tools import AvailableTools +from .path_utils import log_file_name load_dotenv(find_dotenv(usecwd=True)) # only model output or help message should go to stdout, everything else goes to log logging.getLogger('').setLevel(logging.NOTSET) -log_dir = pathlib.Path("logs") -log_dir.mkdir(parents=True, exist_ok=True) log_file_handler = RotatingFileHandler( - log_dir.joinpath('task_agent.log'), + log_file_name('task_agent.log'), maxBytes=1024*1024*10, backupCount=10) log_file_handler.setLevel(os.getenv('TASK_AGENT_LOGLEVEL', default='DEBUG')) diff --git a/src/seclab_taskflow_agent/mcp_servers/codeql/client.py b/src/seclab_taskflow_agent/mcp_servers/codeql/client.py index 5ef775d..9a1350c 100644 --- a/src/seclab_taskflow_agent/mcp_servers/codeql/client.py +++ b/src/seclab_taskflow_agent/mcp_servers/codeql/client.py @@ -12,6 +12,7 @@ import os import zipfile import yaml +from seclab_taskflow_agent.path_utils import log_file_name # this is a local fork of https://github.com/riga/jsonrpyc modified for our purposes from . import jsonrpyc @@ -45,8 +46,7 @@ def __init__(self, log_stderr=False): self.server_options = server_options.copy() if log_stderr: - os.makedirs("logs", exist_ok=True) - self.stderr_log = f"logs/codeql_stderr_log.log" + self.stderr_log = log_file_name('codeql_stderr_log.log') self.server_options.append("--log-to-stderr") else: self.stderr_log = os.devnull diff --git a/src/seclab_taskflow_agent/mcp_servers/codeql/mcp_server.py b/src/seclab_taskflow_agent/mcp_servers/codeql/mcp_server.py index 331ac67..b55e7b9 100644 --- a/src/seclab_taskflow_agent/mcp_servers/codeql/mcp_server.py +++ b/src/seclab_taskflow_agent/mcp_servers/codeql/mcp_server.py @@ -2,12 +2,6 @@ # SPDX-License-Identifier: MIT import logging -logging.basicConfig( - level=logging.DEBUG, - format='%(asctime)s - %(levelname)s - %(message)s', - filename='logs/mcp_codeql.log', - filemode='a' -) from .client import run_query, file_from_uri, list_src_files, _debug_log, search_in_src_archive from pydantic import Field #from mcp.server.fastmcp import FastMCP, Context @@ -20,7 +14,14 @@ import re from urllib.parse import urlparse, unquote import zipfile -from seclab_taskflow_agent.path_utils import mcp_data_dir +from seclab_taskflow_agent.path_utils import mcp_data_dir, log_file_name + +logging.basicConfig( + level=logging.DEBUG, + format='%(asctime)s - %(levelname)s - %(message)s', + filename=log_file_name('mcp_codeql.log'), + filemode='a' +) mcp = FastMCP("CodeQL") diff --git a/src/seclab_taskflow_agent/mcp_servers/echo/echo.py b/src/seclab_taskflow_agent/mcp_servers/echo/echo.py index 5667137..eb063ed 100644 --- a/src/seclab_taskflow_agent/mcp_servers/echo/echo.py +++ b/src/seclab_taskflow_agent/mcp_servers/echo/echo.py @@ -2,14 +2,16 @@ # SPDX-License-Identifier: MIT import logging +#from mcp.server.fastmcp import FastMCP +from fastmcp import FastMCP # move to FastMCP 2.0 +from seclab_taskflow_agent.path_utils import log_file_name + logging.basicConfig( level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s', - filename='logs/mcp_echo.log', + filename=log_file_name('mcp_echo.log'), filemode='a' ) -#from mcp.server.fastmcp import FastMCP -from fastmcp import FastMCP # move to FastMCP 2.0 mcp = FastMCP("Echo") diff --git a/src/seclab_taskflow_agent/mcp_servers/logbook/logbook.py b/src/seclab_taskflow_agent/mcp_servers/logbook/logbook.py index 8c8bec0..5115ba4 100644 --- a/src/seclab_taskflow_agent/mcp_servers/logbook/logbook.py +++ b/src/seclab_taskflow_agent/mcp_servers/logbook/logbook.py @@ -2,17 +2,18 @@ # SPDX-License-Identifier: MIT import logging +#from mcp.server.fastmcp import FastMCP +from fastmcp import FastMCP # move to FastMCP 2.0 +import json +from pathlib import Path +from seclab_taskflow_agent.path_utils import mcp_data_dir, log_file_name + logging.basicConfig( level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s', - filename='logs/mcp_logbook.log', + filename=log_file_name('mcp_logbook.log'), filemode='a' ) -#from mcp.server.fastmcp import FastMCP -from fastmcp import FastMCP # move to FastMCP 2.0 -import json -from pathlib import Path -from seclab_taskflow_agent.path_utils import mcp_data_dir mcp = FastMCP("Logbook") diff --git a/src/seclab_taskflow_agent/mcp_servers/memcache/memcache.py b/src/seclab_taskflow_agent/mcp_servers/memcache/memcache.py index f8b706c..86b2029 100644 --- a/src/seclab_taskflow_agent/mcp_servers/memcache/memcache.py +++ b/src/seclab_taskflow_agent/mcp_servers/memcache/memcache.py @@ -2,12 +2,6 @@ # SPDX-License-Identifier: MIT import logging -logging.basicConfig( - level=logging.DEBUG, - format='%(asctime)s - %(levelname)s - %(message)s', - filename='logs/mcp_memcache.log', - filemode='a' -) #from mcp.server.fastmcp import FastMCP from fastmcp import FastMCP # move to FastMCP 2.0 import json @@ -16,7 +10,14 @@ from typing import Any from .memcache_backend.dictionary_file import MemcacheDictionaryFileBackend from .memcache_backend.sqlite import SqliteBackend -from seclab_taskflow_agent.path_utils import mcp_data_dir +from seclab_taskflow_agent.path_utils import mcp_data_dir, log_file_name + +logging.basicConfig( + level=logging.DEBUG, + format='%(asctime)s - %(levelname)s - %(message)s', + filename=log_file_name('mcp_memcache.log'), + filemode='a' +) mcp = FastMCP("Memcache") diff --git a/src/seclab_taskflow_agent/path_utils.py b/src/seclab_taskflow_agent/path_utils.py index 1175191..8d23347 100644 --- a/src/seclab_taskflow_agent/path_utils.py +++ b/src/seclab_taskflow_agent/path_utils.py @@ -31,3 +31,41 @@ def mcp_data_dir(packagename: str, mcpname: str, env_override: str | None) -> Pa p = Path(d).joinpath(packagename).joinpath(mcpname) p.mkdir(parents=True, exist_ok=True) return p + +def log_dir() -> Path: + """ + Get the directory path for storing log files for the seclab-taskflow-agent. + + Returns: + Path: The path to the log directory. + """ + p = os.getenv("LOG_DIR") + if not p: + p = platformdirs.user_log_dir(appname="seclab-taskflow-agent", + appauthor="GitHubSecurityLab", + ensure_exists=True) + return Path(p) + +def log_file(filename: str) -> Path: + """ + Construct the full path to a log file in the user log directory. + + Parameters: + filename (str): The name of the log file. + + Returns: + Path: The full path to the log file in the user log directory. + """ + return log_dir().joinpath(filename) + +def log_file_name(filename: str) -> str: + """ + Construct the full path to a log file in the user log directory. + + Parameters: + filename (str): The name of the log file. + + Returns: + str: The full path to the log file in the user log directory. + """ + return str(log_file(filename)) diff --git a/src/seclab_taskflow_agent/render_utils.py b/src/seclab_taskflow_agent/render_utils.py index cbf0977..fdbe761 100644 --- a/src/seclab_taskflow_agent/render_utils.py +++ b/src/seclab_taskflow_agent/render_utils.py @@ -1,7 +1,16 @@ # SPDX-FileCopyrightText: 2025 GitHub # SPDX-License-Identifier: MIT +import logging import asyncio +from .path_utils import log_file_name + +logging.basicConfig( + level=logging.DEBUG, + format='%(asctime)s - %(levelname)s - %(message)s', + filename=log_file_name('render_stdout.log'), + filemode='a' +) async_output = {} async_output_lock = asyncio.Lock() @@ -31,6 +40,5 @@ async def render_model_output(data: str, data = "** 🤖✏️ Gathering output from async task ... please hold\n" if data: if log: - with open('logs/render_stdout.log', 'a') as f: - f.write(data) + logging.debug(data) print(data, end="", flush=True)