cursorflow 1.2.0__py3-none-any.whl
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.
- cursorflow/__init__.py +78 -0
- cursorflow/auto_updater.py +244 -0
- cursorflow/cli.py +408 -0
- cursorflow/core/agent.py +272 -0
- cursorflow/core/auth_handler.py +433 -0
- cursorflow/core/browser_controller.py +534 -0
- cursorflow/core/browser_engine.py +386 -0
- cursorflow/core/css_iterator.py +397 -0
- cursorflow/core/cursor_integration.py +744 -0
- cursorflow/core/cursorflow.py +649 -0
- cursorflow/core/error_correlator.py +322 -0
- cursorflow/core/event_correlator.py +182 -0
- cursorflow/core/file_change_monitor.py +548 -0
- cursorflow/core/log_collector.py +410 -0
- cursorflow/core/log_monitor.py +179 -0
- cursorflow/core/persistent_session.py +910 -0
- cursorflow/core/report_generator.py +282 -0
- cursorflow/log_sources/local_file.py +198 -0
- cursorflow/log_sources/ssh_remote.py +210 -0
- cursorflow/updater.py +512 -0
- cursorflow-1.2.0.dist-info/METADATA +444 -0
- cursorflow-1.2.0.dist-info/RECORD +25 -0
- cursorflow-1.2.0.dist-info/WHEEL +5 -0
- cursorflow-1.2.0.dist-info/entry_points.txt +2 -0
- cursorflow-1.2.0.dist-info/top_level.txt +1 -0
cursorflow/__init__.py
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
"""
|
2
|
+
CursorFlow - AI-guided universal testing framework
|
3
|
+
|
4
|
+
Simple data collection engine that enables Cursor to autonomously test UI
|
5
|
+
and iterate on designs with immediate visual feedback.
|
6
|
+
|
7
|
+
Declarative Actions | Batch Execution | Universal Log Collection | Visual Development
|
8
|
+
"""
|
9
|
+
|
10
|
+
from pathlib import Path
|
11
|
+
|
12
|
+
# Main API - clean and simple
|
13
|
+
from .core.cursorflow import CursorFlow
|
14
|
+
|
15
|
+
# Core components (for advanced usage)
|
16
|
+
from .core.browser_engine import BrowserEngine
|
17
|
+
from .core.log_monitor import LogMonitor
|
18
|
+
from .core.error_correlator import ErrorCorrelator
|
19
|
+
|
20
|
+
def _get_version():
|
21
|
+
"""Get version from git tag or fallback to default"""
|
22
|
+
try:
|
23
|
+
import subprocess
|
24
|
+
result = subprocess.run(
|
25
|
+
['git', 'describe', '--tags', '--exact-match'],
|
26
|
+
capture_output=True,
|
27
|
+
text=True,
|
28
|
+
cwd=Path(__file__).parent.parent
|
29
|
+
)
|
30
|
+
if result.returncode == 0:
|
31
|
+
# Remove 'v' prefix if present
|
32
|
+
return result.stdout.strip().lstrip('v')
|
33
|
+
except Exception:
|
34
|
+
pass
|
35
|
+
|
36
|
+
try:
|
37
|
+
# Try to get latest tag if not on exact tag
|
38
|
+
result = subprocess.run(
|
39
|
+
['git', 'describe', '--tags', '--abbrev=0'],
|
40
|
+
capture_output=True,
|
41
|
+
text=True,
|
42
|
+
cwd=Path(__file__).parent.parent
|
43
|
+
)
|
44
|
+
if result.returncode == 0:
|
45
|
+
tag = result.stdout.strip().lstrip('v')
|
46
|
+
# Add dev suffix if not on exact tag
|
47
|
+
return f"{tag}-dev"
|
48
|
+
except Exception:
|
49
|
+
pass
|
50
|
+
|
51
|
+
# Fallback version
|
52
|
+
return "1.0.0-dev"
|
53
|
+
|
54
|
+
__version__ = _get_version()
|
55
|
+
__author__ = "GeekWarrior Development"
|
56
|
+
|
57
|
+
# Simple public API
|
58
|
+
__all__ = [
|
59
|
+
"CursorFlow", # Main interface for Cursor
|
60
|
+
"BrowserEngine", # Advanced browser control
|
61
|
+
"LogMonitor", # Advanced log monitoring
|
62
|
+
"ErrorCorrelator", # Advanced correlation analysis
|
63
|
+
"check_for_updates", # Update checking
|
64
|
+
"update_cursorflow", # Update management
|
65
|
+
]
|
66
|
+
|
67
|
+
# Update functions (for programmatic access)
|
68
|
+
def check_for_updates(project_dir: str = "."):
|
69
|
+
"""Check for CursorFlow updates"""
|
70
|
+
import asyncio
|
71
|
+
from .updater import check_updates
|
72
|
+
return asyncio.run(check_updates(project_dir))
|
73
|
+
|
74
|
+
def update_cursorflow(project_dir: str = ".", force: bool = False):
|
75
|
+
"""Update CursorFlow package and rules"""
|
76
|
+
import asyncio
|
77
|
+
from .updater import update_cursorflow as _update
|
78
|
+
return asyncio.run(_update(project_dir, force=force))
|
@@ -0,0 +1,244 @@
|
|
1
|
+
"""
|
2
|
+
CursorFlow Auto-Update Integration
|
3
|
+
|
4
|
+
Automatic update checking and notification system for seamless
|
5
|
+
CursorFlow maintenance across projects.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import asyncio
|
9
|
+
import json
|
10
|
+
import time
|
11
|
+
from pathlib import Path
|
12
|
+
from typing import Dict, Optional
|
13
|
+
import logging
|
14
|
+
from .updater import CursorFlowUpdater
|
15
|
+
|
16
|
+
|
17
|
+
class AutoUpdateManager:
|
18
|
+
"""
|
19
|
+
Manages automatic update checking and notifications
|
20
|
+
|
21
|
+
Integrates with CursorFlow initialization to check for updates
|
22
|
+
periodically and notify users of available updates.
|
23
|
+
"""
|
24
|
+
|
25
|
+
def __init__(self, project_dir: str = "."):
|
26
|
+
self.project_dir = Path(project_dir).resolve()
|
27
|
+
self.logger = logging.getLogger(__name__)
|
28
|
+
self.updater = CursorFlowUpdater(str(self.project_dir))
|
29
|
+
|
30
|
+
# Load preferences
|
31
|
+
self.preferences = self._load_preferences()
|
32
|
+
|
33
|
+
async def check_if_update_needed(self) -> Optional[Dict]:
|
34
|
+
"""
|
35
|
+
Check if an update check is needed based on interval
|
36
|
+
|
37
|
+
Returns:
|
38
|
+
Update info if check was performed, None if not needed
|
39
|
+
"""
|
40
|
+
try:
|
41
|
+
# Check if enough time has passed
|
42
|
+
if not self._should_check_for_updates():
|
43
|
+
return None
|
44
|
+
|
45
|
+
# Perform update check
|
46
|
+
update_info = await self.updater.check_for_updates(silent=True)
|
47
|
+
|
48
|
+
# Update last check time
|
49
|
+
self._update_last_check_time()
|
50
|
+
|
51
|
+
return update_info
|
52
|
+
|
53
|
+
except Exception as e:
|
54
|
+
self.logger.error(f"Auto-update check failed: {e}")
|
55
|
+
return None
|
56
|
+
|
57
|
+
async def notify_if_updates_available(self) -> bool:
|
58
|
+
"""
|
59
|
+
Check for updates and notify if available
|
60
|
+
|
61
|
+
Returns:
|
62
|
+
True if updates are available, False otherwise
|
63
|
+
"""
|
64
|
+
update_info = await self.check_if_update_needed()
|
65
|
+
|
66
|
+
if not update_info:
|
67
|
+
return False
|
68
|
+
|
69
|
+
has_updates = (
|
70
|
+
update_info.get("version_update_available", False) or
|
71
|
+
update_info.get("rules_update_available", False) or
|
72
|
+
not update_info.get("dependencies_current", True)
|
73
|
+
)
|
74
|
+
|
75
|
+
if has_updates:
|
76
|
+
self._display_update_notification(update_info)
|
77
|
+
return True
|
78
|
+
|
79
|
+
return False
|
80
|
+
|
81
|
+
def _should_check_for_updates(self) -> bool:
|
82
|
+
"""Check if enough time has passed for update check"""
|
83
|
+
interval_hours = self.preferences.get("check_interval_hours", 24)
|
84
|
+
last_check = self.preferences.get("last_check")
|
85
|
+
|
86
|
+
if not last_check:
|
87
|
+
return True
|
88
|
+
|
89
|
+
try:
|
90
|
+
last_check_time = time.fromisoformat(last_check)
|
91
|
+
current_time = time.time()
|
92
|
+
hours_elapsed = (current_time - last_check_time) / 3600
|
93
|
+
|
94
|
+
return hours_elapsed >= interval_hours
|
95
|
+
except Exception:
|
96
|
+
return True
|
97
|
+
|
98
|
+
def _load_preferences(self) -> Dict:
|
99
|
+
"""Load update preferences"""
|
100
|
+
prefs_file = self.project_dir / ".cursorflow" / "update_preferences.json"
|
101
|
+
|
102
|
+
if prefs_file.exists():
|
103
|
+
try:
|
104
|
+
with open(prefs_file) as f:
|
105
|
+
return json.load(f)
|
106
|
+
except Exception:
|
107
|
+
pass
|
108
|
+
|
109
|
+
# Default preferences
|
110
|
+
return {
|
111
|
+
"check_interval_hours": 24,
|
112
|
+
"auto_update": False,
|
113
|
+
"include_prereleases": False,
|
114
|
+
"backup_before_update": True,
|
115
|
+
"last_check": None
|
116
|
+
}
|
117
|
+
|
118
|
+
def _update_last_check_time(self):
|
119
|
+
"""Update the last check timestamp"""
|
120
|
+
prefs_file = self.project_dir / ".cursorflow" / "update_preferences.json"
|
121
|
+
prefs_file.parent.mkdir(exist_ok=True)
|
122
|
+
|
123
|
+
self.preferences["last_check"] = time.time()
|
124
|
+
|
125
|
+
with open(prefs_file, 'w') as f:
|
126
|
+
json.dump(self.preferences, f, indent=2)
|
127
|
+
|
128
|
+
def _display_update_notification(self, update_info: Dict):
|
129
|
+
"""Display update notification"""
|
130
|
+
print("\n" + "="*60)
|
131
|
+
print("š CursorFlow Updates Available!")
|
132
|
+
print("="*60)
|
133
|
+
|
134
|
+
if update_info.get("version_update_available"):
|
135
|
+
current = update_info.get("current_version", "unknown")
|
136
|
+
latest = update_info.get("latest_version", "unknown")
|
137
|
+
print(f"š¦ Package update: {current} ā {latest}")
|
138
|
+
|
139
|
+
if update_info.get("rules_update_available"):
|
140
|
+
print("š Rules update available")
|
141
|
+
|
142
|
+
if not update_info.get("dependencies_current"):
|
143
|
+
print("š§ Dependency updates available")
|
144
|
+
|
145
|
+
print("\nš” Update commands:")
|
146
|
+
print(" cursorflow update # Update everything")
|
147
|
+
print(" cursorflow check-updates # Check status")
|
148
|
+
print(" cursorflow install-deps # Update dependencies only")
|
149
|
+
|
150
|
+
print("\nāļø To disable these notifications:")
|
151
|
+
print(" Edit .cursorflow/update_preferences.json")
|
152
|
+
print(" Set 'check_interval_hours' to 0")
|
153
|
+
print("="*60)
|
154
|
+
|
155
|
+
|
156
|
+
async def check_for_updates_on_startup(project_dir: str = ".") -> bool:
|
157
|
+
"""
|
158
|
+
Check for updates during CursorFlow startup
|
159
|
+
|
160
|
+
Args:
|
161
|
+
project_dir: Project directory path
|
162
|
+
|
163
|
+
Returns:
|
164
|
+
True if updates are available, False otherwise
|
165
|
+
"""
|
166
|
+
try:
|
167
|
+
manager = AutoUpdateManager(project_dir)
|
168
|
+
return await manager.notify_if_updates_available()
|
169
|
+
except Exception:
|
170
|
+
return False
|
171
|
+
|
172
|
+
|
173
|
+
def integrate_with_cursorflow():
|
174
|
+
"""
|
175
|
+
Integration point for CursorFlow main class
|
176
|
+
|
177
|
+
Add this to CursorFlow.__init__ to enable auto-update checking
|
178
|
+
"""
|
179
|
+
|
180
|
+
# Check for updates asynchronously in background
|
181
|
+
async def background_update_check():
|
182
|
+
try:
|
183
|
+
await check_for_updates_on_startup()
|
184
|
+
except Exception:
|
185
|
+
pass # Silent failure for background task
|
186
|
+
|
187
|
+
# Schedule background check
|
188
|
+
try:
|
189
|
+
loop = asyncio.get_event_loop()
|
190
|
+
if loop.is_running():
|
191
|
+
# If loop is already running, schedule as task
|
192
|
+
loop.create_task(background_update_check())
|
193
|
+
else:
|
194
|
+
# If no loop running, run briefly
|
195
|
+
asyncio.run(background_update_check())
|
196
|
+
except Exception:
|
197
|
+
pass # Silent failure
|
198
|
+
|
199
|
+
|
200
|
+
class UpdateScheduler:
|
201
|
+
"""
|
202
|
+
Schedules periodic update checks during CursorFlow usage
|
203
|
+
"""
|
204
|
+
|
205
|
+
def __init__(self, project_dir: str = "."):
|
206
|
+
self.project_dir = project_dir
|
207
|
+
self.manager = AutoUpdateManager(project_dir)
|
208
|
+
self._check_task = None
|
209
|
+
|
210
|
+
def start_periodic_checking(self, interval_minutes: int = 60):
|
211
|
+
"""Start periodic update checking"""
|
212
|
+
if self._check_task and not self._check_task.done():
|
213
|
+
return # Already running
|
214
|
+
|
215
|
+
async def periodic_check():
|
216
|
+
while True:
|
217
|
+
try:
|
218
|
+
await self.manager.check_if_update_needed()
|
219
|
+
await asyncio.sleep(interval_minutes * 60)
|
220
|
+
except asyncio.CancelledError:
|
221
|
+
break
|
222
|
+
except Exception:
|
223
|
+
await asyncio.sleep(interval_minutes * 60)
|
224
|
+
|
225
|
+
self._check_task = asyncio.create_task(periodic_check())
|
226
|
+
|
227
|
+
def stop_periodic_checking(self):
|
228
|
+
"""Stop periodic update checking"""
|
229
|
+
if self._check_task and not self._check_task.done():
|
230
|
+
self._check_task.cancel()
|
231
|
+
|
232
|
+
|
233
|
+
# Singleton scheduler for global use
|
234
|
+
_global_scheduler = None
|
235
|
+
|
236
|
+
|
237
|
+
def get_update_scheduler(project_dir: str = ".") -> UpdateScheduler:
|
238
|
+
"""Get or create global update scheduler"""
|
239
|
+
global _global_scheduler
|
240
|
+
|
241
|
+
if _global_scheduler is None:
|
242
|
+
_global_scheduler = UpdateScheduler(project_dir)
|
243
|
+
|
244
|
+
return _global_scheduler
|