autoforge-ai 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -1
- package/temp_cleanup.py +148 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "autoforge-ai",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Autonomous coding agent with web UI - build complete apps with AI",
|
|
5
5
|
"license": "AGPL-3.0",
|
|
6
6
|
"bin": {
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"registry.py",
|
|
35
35
|
"rate_limit_utils.py",
|
|
36
36
|
"security.py",
|
|
37
|
+
"temp_cleanup.py",
|
|
37
38
|
"requirements-prod.txt",
|
|
38
39
|
"pyproject.toml",
|
|
39
40
|
".env.example",
|
package/temp_cleanup.py
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Temp Cleanup Module
|
|
3
|
+
===================
|
|
4
|
+
|
|
5
|
+
Cleans up stale temporary files and directories created by AutoForge agents,
|
|
6
|
+
Playwright, Node.js, and other development tools.
|
|
7
|
+
|
|
8
|
+
Called at Maestro (orchestrator) startup to prevent temp folder bloat.
|
|
9
|
+
|
|
10
|
+
Why this exists:
|
|
11
|
+
- Playwright creates browser profiles and artifacts in %TEMP%
|
|
12
|
+
- Node.js creates .node cache files (~7MB each, can accumulate to GBs)
|
|
13
|
+
- MongoDB Memory Server downloads binaries to temp
|
|
14
|
+
- These are never cleaned up automatically
|
|
15
|
+
|
|
16
|
+
When cleanup runs:
|
|
17
|
+
- At Maestro startup (when you click Play or auto-restart after rate limits)
|
|
18
|
+
- Only files/folders older than 1 hour are deleted (safe for running processes)
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import logging
|
|
22
|
+
import shutil
|
|
23
|
+
import tempfile
|
|
24
|
+
import time
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
# Max age in seconds before a temp item is considered stale (1 hour)
|
|
30
|
+
MAX_AGE_SECONDS = 3600
|
|
31
|
+
|
|
32
|
+
# Directory patterns to clean up (glob patterns)
|
|
33
|
+
DIR_PATTERNS = [
|
|
34
|
+
"playwright_firefoxdev_profile-*", # Playwright Firefox profiles
|
|
35
|
+
"playwright-artifacts-*", # Playwright test artifacts
|
|
36
|
+
"playwright-transform-cache", # Playwright transform cache
|
|
37
|
+
"mongodb-memory-server*", # MongoDB Memory Server binaries
|
|
38
|
+
"ng-*", # Angular CLI temp directories
|
|
39
|
+
"scoped_dir*", # Chrome/Chromium temp directories
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
# File patterns to clean up (glob patterns)
|
|
43
|
+
FILE_PATTERNS = [
|
|
44
|
+
".78912*.node", # Node.js native module cache (major space consumer, ~7MB each)
|
|
45
|
+
"claude-*-cwd", # Claude CLI working directory temp files
|
|
46
|
+
"mat-debug-*.log", # Material/Angular debug logs
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def cleanup_stale_temp(max_age_seconds: int = MAX_AGE_SECONDS) -> dict:
|
|
51
|
+
"""
|
|
52
|
+
Clean up stale temporary files and directories.
|
|
53
|
+
|
|
54
|
+
Only deletes items older than max_age_seconds to avoid
|
|
55
|
+
interfering with currently running processes.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
max_age_seconds: Maximum age in seconds before an item is deleted.
|
|
59
|
+
Defaults to 1 hour (3600 seconds).
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
Dictionary with cleanup statistics:
|
|
63
|
+
- dirs_deleted: Number of directories deleted
|
|
64
|
+
- files_deleted: Number of files deleted
|
|
65
|
+
- bytes_freed: Approximate bytes freed
|
|
66
|
+
- errors: List of error messages (for debugging, not fatal)
|
|
67
|
+
"""
|
|
68
|
+
temp_dir = Path(tempfile.gettempdir())
|
|
69
|
+
cutoff_time = time.time() - max_age_seconds
|
|
70
|
+
|
|
71
|
+
stats = {
|
|
72
|
+
"dirs_deleted": 0,
|
|
73
|
+
"files_deleted": 0,
|
|
74
|
+
"bytes_freed": 0,
|
|
75
|
+
"errors": [],
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
# Clean up directories
|
|
79
|
+
for pattern in DIR_PATTERNS:
|
|
80
|
+
for item in temp_dir.glob(pattern):
|
|
81
|
+
if not item.is_dir():
|
|
82
|
+
continue
|
|
83
|
+
try:
|
|
84
|
+
mtime = item.stat().st_mtime
|
|
85
|
+
if mtime < cutoff_time:
|
|
86
|
+
size = _get_dir_size(item)
|
|
87
|
+
shutil.rmtree(item, ignore_errors=True)
|
|
88
|
+
if not item.exists():
|
|
89
|
+
stats["dirs_deleted"] += 1
|
|
90
|
+
stats["bytes_freed"] += size
|
|
91
|
+
logger.debug(f"Deleted temp directory: {item}")
|
|
92
|
+
except Exception as e:
|
|
93
|
+
stats["errors"].append(f"Failed to delete {item}: {e}")
|
|
94
|
+
logger.debug(f"Failed to delete {item}: {e}")
|
|
95
|
+
|
|
96
|
+
# Clean up files
|
|
97
|
+
for pattern in FILE_PATTERNS:
|
|
98
|
+
for item in temp_dir.glob(pattern):
|
|
99
|
+
if not item.is_file():
|
|
100
|
+
continue
|
|
101
|
+
try:
|
|
102
|
+
mtime = item.stat().st_mtime
|
|
103
|
+
if mtime < cutoff_time:
|
|
104
|
+
size = item.stat().st_size
|
|
105
|
+
item.unlink(missing_ok=True)
|
|
106
|
+
if not item.exists():
|
|
107
|
+
stats["files_deleted"] += 1
|
|
108
|
+
stats["bytes_freed"] += size
|
|
109
|
+
logger.debug(f"Deleted temp file: {item}")
|
|
110
|
+
except Exception as e:
|
|
111
|
+
stats["errors"].append(f"Failed to delete {item}: {e}")
|
|
112
|
+
logger.debug(f"Failed to delete {item}: {e}")
|
|
113
|
+
|
|
114
|
+
# Log summary if anything was cleaned
|
|
115
|
+
if stats["dirs_deleted"] > 0 or stats["files_deleted"] > 0:
|
|
116
|
+
mb_freed = stats["bytes_freed"] / (1024 * 1024)
|
|
117
|
+
logger.info(
|
|
118
|
+
f"Temp cleanup: {stats['dirs_deleted']} dirs, "
|
|
119
|
+
f"{stats['files_deleted']} files, {mb_freed:.1f} MB freed"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
return stats
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _get_dir_size(path: Path) -> int:
|
|
126
|
+
"""Get total size of a directory in bytes."""
|
|
127
|
+
total = 0
|
|
128
|
+
try:
|
|
129
|
+
for item in path.rglob("*"):
|
|
130
|
+
if item.is_file():
|
|
131
|
+
try:
|
|
132
|
+
total += item.stat().st_size
|
|
133
|
+
except (OSError, PermissionError):
|
|
134
|
+
pass
|
|
135
|
+
except (OSError, PermissionError):
|
|
136
|
+
pass
|
|
137
|
+
return total
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
if __name__ == "__main__":
|
|
141
|
+
# Allow running directly for testing/manual cleanup
|
|
142
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
143
|
+
print("Running temp cleanup...")
|
|
144
|
+
stats = cleanup_stale_temp()
|
|
145
|
+
mb_freed = stats["bytes_freed"] / (1024 * 1024)
|
|
146
|
+
print(f"Cleanup complete: {stats['dirs_deleted']} dirs, {stats['files_deleted']} files, {mb_freed:.1f} MB freed")
|
|
147
|
+
if stats["errors"]:
|
|
148
|
+
print(f"Errors (non-fatal): {len(stats['errors'])}")
|