From 8e5d643483fc550e01dc8ea76ffa6b4e7dbbdff4 Mon Sep 17 00:00:00 2001 From: Alessandro <155005371+3clyp50@users.noreply.github.com> Date: Thu, 16 Jul 2026 15:30:37 +0200 Subject: [PATCH] Remove legacy filesystem logs Stop PrintStyle from creating per-process HTML files under /a0/logs and remove the obsolete tracked folder scaffolding. Delete existing legacy log directories during startup migration so self-updated and manually upgraded installations are cleaned automatically. Point plugin debugging guidance to Docker output and add regression coverage for repeatable cleanup. --- .gitignore | 4 ---- AGENTS.md | 1 - docker/AGENTS.md | 2 +- docs/setup/vps-deployment.md | 1 - helpers/migration.py | 3 ++- helpers/migration.py.dox.md | 2 ++ helpers/print_style.py | 42 ++++++--------------------------- helpers/print_style.py.dox.md | 7 +++--- logs/.gitkeep | 0 skills/a0-debug-plugin/SKILL.md | 6 ++--- tests/test_migration_cleanup.py | 16 +++++++++++++ tests/test_print_style.py | 15 ++++-------- 12 files changed, 39 insertions(+), 60 deletions(-) delete mode 100644 logs/.gitkeep create mode 100644 tests/test_migration_cleanup.py diff --git a/.gitignore b/.gitignore index 44e916417..c41f237d2 100644 --- a/.gitignore +++ b/.gitignore @@ -21,10 +21,6 @@ /knowledge/custom/ /instruments/ -# Handle logs directory -logs/** -!logs/**/ - # Handle tmp and usr directory tmp/** !tmp/**/ diff --git a/AGENTS.md b/AGENTS.md index d32f21c51..2ee5811e7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -86,7 +86,6 @@ Intentionally unindexed local or generated roots: | `.conda/`, `.venv/` | Local Python environments. | | `.pytest_cache/`, `__pycache__/` | Generated test and bytecode caches. | | `.vscode/`, `.windsurf/` | Editor-local configuration and assistant metadata. | -| `logs/` | Runtime output. | | `tmp/` | Ignored runtime caches, uploads, and generated work. | | `usr/` | Ignored local user data, settings, plugins, chats, and workdirs. | | `python/` | Generated or legacy runtime mirror; current source is in root modules and tracked source directories. | diff --git a/docker/AGENTS.md b/docker/AGENTS.md index 8fafcaaf5..eef40563a 100644 --- a/docker/AGENTS.md +++ b/docker/AGENTS.md @@ -16,7 +16,7 @@ - Preserve the two-runtime model: the Python 3.12 framework runtime under `/opt/venv-a0` runs the WebUI, APIs, scheduler, framework imports, and plugin hooks; the Python 3.13 agent execution runtime under `/opt/venv` runs agent terminal tasks and user code. - Verify backend imports and plugin hooks with `/opt/venv-a0`; packages installed into `/opt/venv` do not prove framework compatibility. - Do not bake secrets, local `.env` values, or user data into images. -- Keep compose mounts aligned with `usr/`, `logs/`, and other runtime-state expectations. +- Keep compose mounts aligned with `usr/` and other runtime-state expectations. - Image changes that affect GitHub publishing must stay synchronized with `.github/workflows/docker-publish.yml`. ## Work Guidance diff --git a/docs/setup/vps-deployment.md b/docs/setup/vps-deployment.md index 86d15d872..74b03f5d6 100644 --- a/docs/setup/vps-deployment.md +++ b/docs/setup/vps-deployment.md @@ -708,7 +708,6 @@ curl -I https://your-domain.com/login | Environment File | `/opt/a0-instance/.env` | | Memory Storage | `/opt/a0-instance/memory/` | | Work Directory | `/opt/a0-instance/work_dir/` | -| Logs | `/opt/a0-instance/logs/` | | Apache Config (Standard) | `/etc/apache2/sites-available/` | | Apache Config (DirectAdmin) | `/etc/httpd/conf/extra/httpd-includes.conf` | | DirectAdmin SSL Certs | `/usr/local/directadmin/data/users/USER/domains/` | diff --git a/helpers/migration.py b/helpers/migration.py index 4aa609aaa..076712b80 100644 --- a/helpers/migration.py +++ b/helpers/migration.py @@ -130,7 +130,8 @@ def _cleanup_obsolete() -> None: """ to_remove = [ "knowledge/default", - "memory" + "memory", + "logs", ] for path in to_remove: if files.exists(path): diff --git a/helpers/migration.py.dox.md b/helpers/migration.py.dox.md index c548951ab..f95ed1332 100644 --- a/helpers/migration.py.dox.md +++ b/helpers/migration.py.dox.md @@ -24,6 +24,7 @@ - Helper modules own reusable framework APIs and must preserve public callers unless all callers, tests, and docs are updated together. - Update this file whenever public functions, classes, persistence behavior, path/security assumptions, side effects, or cross-module contracts change. +- Startup cleanup removes obsolete `memory`, `knowledge/default`, and legacy `logs` directories; repeated runs are safe. - Observed side-effect areas: filesystem reads, filesystem writes, filesystem deletion, settings/state persistence, secret handling, scheduler state. - Imported dependency areas include: `helpers`, `helpers.print_style`, `json`, `os`. @@ -42,6 +43,7 @@ - Run targeted tests for changed helper behavior; run security regressions for auth, filesystem, WebSocket, tunnel, upload, or secret-handling helpers. - Related tests observed by source search: + - `tests/test_migration_cleanup.py` - `tests/test_browser_agent_regressions.py` - `tests/test_office_canvas_setup.py` - `tests/test_office_document_store.py` diff --git a/helpers/print_style.py b/helpers/print_style.py index ab70f9469..c8d0b86b7 100644 --- a/helpers/print_style.py +++ b/helpers/print_style.py @@ -1,8 +1,10 @@ -import os, webcolors, html +import html import sys -from datetime import datetime from collections.abc import Mapping -from . import files + +import webcolors + +from . import files # Load before strings; helpers.files imports sanitize_string. from .strings import sanitize_string _runtime_module = None @@ -18,7 +20,6 @@ def _get_runtime(): class PrintStyle: last_endline = True - log_file_path = None def __init__(self, bold=False, italic=False, underline=False, font_color="default", background_color="default", padding=False, log_only=False): self.bold = bold @@ -30,14 +31,6 @@ class PrintStyle: self.padding_added = False # Flag to track if padding was added self.log_only = log_only - if PrintStyle.log_file_path is None: - logs_dir = files.get_abs_path("logs") - os.makedirs(logs_dir, exist_ok=True) - log_filename = datetime.now().strftime("log_%Y%m%d_%H%M%S.html") - PrintStyle.log_file_path = os.path.join(logs_dir, log_filename) - with open(PrintStyle.log_file_path, "w", encoding="utf-8", errors="replace") as f: - f.write("
\n")
-
     def _get_rgb_color_code(self, color, is_background=False):
         try:
             if color.startswith("#") and len(color) == 7:
@@ -90,19 +83,8 @@ class PrintStyle:
         if self.padding and not self.padding_added:
             if not self.log_only:
                 print()  # Print an empty line for padding
-            self._log_html("
") self.padding_added = True - def _log_html(self, html): - with open(PrintStyle.log_file_path, "a", encoding="utf-8", errors="replace") as f: # type: ignore[arg-type] - f.write(sanitize_string(html)) - - @staticmethod - def _close_html_log(): - if PrintStyle.log_file_path: - with open(PrintStyle.log_file_path, "a", encoding="utf-8", errors="replace") as f: - f.write("
") - @staticmethod def _format_args(args, sep): if not args: @@ -155,22 +137,16 @@ class PrintStyle: if not PrintStyle.last_endline: if not self.log_only: print() - self._log_html("
") - plain_text, styled_text, html_text = self.get(*args, sep=sep) + _, styled_text, _ = self.get(*args, sep=sep) if not self.log_only: print(styled_text, end=end, flush=flush) - if end.endswith('\n'): - self._log_html(html_text + "
\n") - else: - self._log_html(html_text) PrintStyle.last_endline = end.endswith('\n') def stream(self, *args, sep=' ', flush=True): self._add_padding_if_needed() - plain_text, styled_text, html_text = self.get(*args, sep=sep) + _, styled_text, _ = self.get(*args, sep=sep) if not self.log_only: print(styled_text, end='', flush=flush) - self._log_html(html_text) PrintStyle.last_endline = False def is_last_line_empty(self): @@ -218,7 +194,3 @@ class PrintStyle: def error(*args, sep=' ', end='\n', flush=True): prefixed = PrintStyle._prefixed_args("Error", args) PrintStyle(font_color="red", padding=True).print(*prefixed, sep=sep, end=end, flush=flush) - -# Ensure HTML file is closed properly when the program exits -import atexit -atexit.register(PrintStyle._close_html_log) diff --git a/helpers/print_style.py.dox.md b/helpers/print_style.py.dox.md index 193a9bb26..b0ae5ddf9 100644 --- a/helpers/print_style.py.dox.md +++ b/helpers/print_style.py.dox.md @@ -27,12 +27,13 @@ - Helper modules own reusable framework APIs and must preserve public callers unless all callers, tests, and docs are updated together. - Update this file whenever public functions, classes, persistence behavior, path/security assumptions, side effects, or cross-module contracts change. -- Observed side-effect areas: filesystem reads, WebSocket state, secret handling. -- Imported dependency areas include: `atexit`, `collections.abc`, `datetime`, `html`, `os`, `strings`, `sys`, `webcolors`. +- `PrintStyle` emits sanitized, secret-masked console output and does not create filesystem log files. +- `get()` preserves its plain-text, ANSI-styled, and HTML-styled return values for existing callers. +- Imported dependency areas include: `collections.abc`, `files`, `html`, `strings`, `sys`, `webcolors`. ## Key Concepts -- Important called helpers/classes observed in the source: `atexit.register`, `self._get_rgb_color_code`, `join`, `html.escape.replace`, `sep.join`, `self._format_args`, `sanitize_string`, `self._add_padding_if_needed`, `end.endswith`, `self._log_html`, `sys.stdin.readlines`, `PrintStyle._prefixed_args`, `files.get_abs_path`, `os.makedirs`, `datetime.now.strftime`, `os.path.join`, `f.write`, `self.secrets_mgr.mask_values`, `self._get_styled_text`, `self._get_html_styled_text`. +- Important called helpers/classes observed in the source: `self._get_rgb_color_code`, `html.escape.replace`, `sep.join`, `self._format_args`, `sanitize_string`, `self._add_padding_if_needed`, `end.endswith`, `sys.stdin.readlines`, `PrintStyle._prefixed_args`, `self.secrets_mgr.mask_values`, `self._get_styled_text`, `self._get_html_styled_text`. - Keep request/response, tool, or helper semantics documented here at the same time as source changes. ## Work Guidance diff --git a/logs/.gitkeep b/logs/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/skills/a0-debug-plugin/SKILL.md b/skills/a0-debug-plugin/SKILL.md index a374f597c..28c2d6a04 100644 --- a/skills/a0-debug-plugin/SKILL.md +++ b/skills/a0-debug-plugin/SKILL.md @@ -158,11 +158,11 @@ print('Done') ## 8. Check Agent Zero logs ```bash -# Find recent log files -ls -lt /a0/logs/*.html | head -5 +# Run from the Docker host; replace the name if needed +docker logs --tail 200 a0-instance ``` -Plugin-related errors appear as Python tracebacks mentioning the plugin path. +Plugin-related errors appear in the container output as Python tracebacks mentioning the plugin path. --- diff --git a/tests/test_migration_cleanup.py b/tests/test_migration_cleanup.py new file mode 100644 index 000000000..1d69bbbad --- /dev/null +++ b/tests/test_migration_cleanup.py @@ -0,0 +1,16 @@ +from helpers import files, migration + + +def test_cleanup_obsolete_removes_legacy_logs(tmp_path, monkeypatch): + logs = tmp_path / "logs" + logs.mkdir() + (logs / "old.html").write_text("old log", encoding="utf-8") + keep = tmp_path / "keep.txt" + keep.write_text("keep", encoding="utf-8") + monkeypatch.setattr(files, "_base_dir", str(tmp_path)) + + migration._cleanup_obsolete() + migration._cleanup_obsolete() + + assert not logs.exists() + assert keep.exists() diff --git a/tests/test_print_style.py b/tests/test_print_style.py index 3c85f1755..9a28b7d43 100644 --- a/tests/test_print_style.py +++ b/tests/test_print_style.py @@ -17,16 +17,12 @@ class _PassthroughSecretsManager: @pytest.fixture(autouse=True) def _reset_print_style_state(): - PrintStyle.log_file_path = None PrintStyle.last_endline = True yield - PrintStyle.log_file_path = None PrintStyle.last_endline = True -def test_get_sanitizes_lone_surrogates(tmp_path, monkeypatch): - monkeypatch.setattr("helpers.print_style.files.get_abs_path", lambda _: str(tmp_path)) - +def test_get_sanitizes_lone_surrogates(): style = PrintStyle(log_only=True) style.secrets_mgr = _PassthroughSecretsManager() @@ -37,16 +33,13 @@ def test_get_sanitizes_lone_surrogates(tmp_path, monkeypatch): assert "\ud83d" not in html_text -def test_print_writes_html_log_without_surrogate_crash(tmp_path, monkeypatch): - monkeypatch.setattr("helpers.print_style.files.get_abs_path", lambda _: str(tmp_path)) - - style = PrintStyle(log_only=True) +def test_print_sanitizes_lone_surrogates_without_crash(capsys): + style = PrintStyle() style.secrets_mgr = _PassthroughSecretsManager() style.print("bad \ud83d") - log_path = Path(PrintStyle.log_file_path) - content = log_path.read_text(encoding="utf-8") + content = capsys.readouterr().out assert "bad ?" in content assert "\ud83d" not in content