signalpilot-ai-internal 0.4.10__py3-none-any.whl → 0.7.6__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.
- signalpilot_ai_internal/_version.py +1 -1
- signalpilot_ai_internal/cache_service.py +152 -1
- signalpilot_ai_internal/file_scanner_service.py +1395 -0
- signalpilot_ai_internal/handlers.py +478 -2
- signalpilot_ai_internal/html_export_template/README.md +23 -0
- signalpilot_ai_internal/html_export_template/conf.json +12 -0
- signalpilot_ai_internal/html_export_template/index.html.j2 +140 -0
- {signalpilot_ai_internal-0.4.10.data → signalpilot_ai_internal-0.7.6.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/package.json +3 -2
- {signalpilot_ai_internal-0.4.10.data → signalpilot_ai_internal-0.7.6.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/schemas/signalpilot-ai-internal/package.json.orig +2 -1
- signalpilot_ai_internal-0.7.6.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/490.b4ccb9601c8112407c5d.js +1 -0
- signalpilot_ai_internal-0.7.6.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/741.dc49867fafb03ea2ba4d.js +1 -0
- signalpilot_ai_internal-0.7.6.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/839.ed04fa601a43e8dd24d1.js +1 -0
- signalpilot_ai_internal-0.7.6.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/898.4e9edb7f224152c1dcb4.js +2 -0
- signalpilot_ai_internal-0.7.6.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/remoteEntry.ee8951353b00c13b8070.js +1 -0
- {signalpilot_ai_internal-0.4.10.data → signalpilot_ai_internal-0.7.6.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/static/third-party-licenses.json +6 -6
- {signalpilot_ai_internal-0.4.10.dist-info → signalpilot_ai_internal-0.7.6.dist-info}/METADATA +3 -1
- signalpilot_ai_internal-0.7.6.dist-info/RECORD +49 -0
- signalpilot_ai_internal-0.4.10.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/104.04e170724f369fcbaf19.js +0 -2
- signalpilot_ai_internal-0.4.10.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/104.04e170724f369fcbaf19.js.LICENSE.txt +0 -24
- signalpilot_ai_internal-0.4.10.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/188.8de71ad111d5f3bd39c8.js +0 -1
- signalpilot_ai_internal-0.4.10.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/280.35d8c8b68815702a5238.js +0 -2
- signalpilot_ai_internal-0.4.10.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/606.90aaaae46b73dc3c08fb.js +0 -1
- signalpilot_ai_internal-0.4.10.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/839.460965ed98350008ee29.js +0 -1
- signalpilot_ai_internal-0.4.10.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/remoteEntry.1b0f690a6fcd55d60a1c.js +0 -1
- signalpilot_ai_internal-0.4.10.dist-info/RECORD +0 -47
- {signalpilot_ai_internal-0.4.10.data → signalpilot_ai_internal-0.7.6.data}/data/etc/jupyter/jupyter_server_config.d/signalpilot_ai.json +0 -0
- {signalpilot_ai_internal-0.4.10.data → signalpilot_ai_internal-0.7.6.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/install.json +0 -0
- {signalpilot_ai_internal-0.4.10.data → signalpilot_ai_internal-0.7.6.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/schemas/signalpilot-ai-internal/plugin.json +0 -0
- {signalpilot_ai_internal-0.4.10.data → signalpilot_ai_internal-0.7.6.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/static/122.e2dadf63dc64d7b5f1ee.js +0 -0
- {signalpilot_ai_internal-0.4.10.data → signalpilot_ai_internal-0.7.6.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/static/220.328403b5545f268b95c6.js +0 -0
- {signalpilot_ai_internal-0.4.10.data → signalpilot_ai_internal-0.7.6.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/static/262.726e1da31a50868cb297.js +0 -0
- {signalpilot_ai_internal-0.4.10.data → signalpilot_ai_internal-0.7.6.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/static/353.72484b768a04f89bd3dd.js +0 -0
- {signalpilot_ai_internal-0.4.10.data → signalpilot_ai_internal-0.7.6.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/static/364.dbec4c2dc12e7b050dcc.js +0 -0
- {signalpilot_ai_internal-0.4.10.data → signalpilot_ai_internal-0.7.6.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/static/384.fa432bdb7fb6b1c95ad6.js +0 -0
- {signalpilot_ai_internal-0.4.10.data → signalpilot_ai_internal-0.7.6.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/static/439.37e271d7a80336daabe2.js +0 -0
- {signalpilot_ai_internal-0.4.10.data → signalpilot_ai_internal-0.7.6.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/static/476.9b4f05a99f5003f82094.js +0 -0
- {signalpilot_ai_internal-0.4.10.data → signalpilot_ai_internal-0.7.6.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/static/481.73c7a9290b7d35a8b9c1.js +0 -0
- {signalpilot_ai_internal-0.4.10.data → signalpilot_ai_internal-0.7.6.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/static/512.b58fc0093d080b8ee61c.js +0 -0
- {signalpilot_ai_internal-0.4.10.data → signalpilot_ai_internal-0.7.6.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/static/553.b4042a795c91d9ff71ef.js +0 -0
- {signalpilot_ai_internal-0.4.10.data → signalpilot_ai_internal-0.7.6.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/static/553.b4042a795c91d9ff71ef.js.LICENSE.txt +0 -0
- {signalpilot_ai_internal-0.4.10.data → signalpilot_ai_internal-0.7.6.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/static/635.9720593ee20b768da3ca.js +0 -0
- {signalpilot_ai_internal-0.4.10.data → signalpilot_ai_internal-0.7.6.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/static/713.8e6edc9a965bdd578ca7.js +0 -0
- {signalpilot_ai_internal-0.4.10.data → signalpilot_ai_internal-0.7.6.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/static/742.91e7b516c8699eea3373.js +0 -0
- {signalpilot_ai_internal-0.4.10.data → signalpilot_ai_internal-0.7.6.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/static/785.3aa564fc148b37d1d719.js +0 -0
- {signalpilot_ai_internal-0.4.10.data → signalpilot_ai_internal-0.7.6.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/static/888.34054db17bcf6e87ec95.js +0 -0
- /signalpilot_ai_internal-0.4.10.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/280.35d8c8b68815702a5238.js.LICENSE.txt → /signalpilot_ai_internal-0.7.6.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/898.4e9edb7f224152c1dcb4.js.LICENSE.txt +0 -0
- {signalpilot_ai_internal-0.4.10.data → signalpilot_ai_internal-0.7.6.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/static/style.js +0 -0
- {signalpilot_ai_internal-0.4.10.dist-info → signalpilot_ai_internal-0.7.6.dist-info}/WHEEL +0 -0
- {signalpilot_ai_internal-0.4.10.dist-info → signalpilot_ai_internal-0.7.6.dist-info}/licenses/LICENSE +0 -0
|
@@ -3,6 +3,7 @@ Persistent caching service for SignalPilot AI.
|
|
|
3
3
|
Handles OS-specific cache directory management and robust file operations.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
+
import hashlib
|
|
6
7
|
import json
|
|
7
8
|
import os
|
|
8
9
|
import platform
|
|
@@ -12,7 +13,7 @@ import threading
|
|
|
12
13
|
import time
|
|
13
14
|
import uuid
|
|
14
15
|
from pathlib import Path
|
|
15
|
-
from typing import Any, Dict, Optional
|
|
16
|
+
from typing import Any, Dict, List, Optional
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
class CacheDirectoryManager:
|
|
@@ -537,8 +538,150 @@ class PersistentCacheService:
|
|
|
537
538
|
return info
|
|
538
539
|
|
|
539
540
|
|
|
541
|
+
class FileScanCacheManager:
|
|
542
|
+
"""Dedicated cache manager for file scanning operations with individual file caching"""
|
|
543
|
+
|
|
544
|
+
def __init__(self):
|
|
545
|
+
self.cache_dir = CacheDirectoryManager.find_usable_cache_directory()
|
|
546
|
+
self.file_scans_dir = None
|
|
547
|
+
self.scanned_directories_file = None
|
|
548
|
+
self._lock = threading.RLock()
|
|
549
|
+
|
|
550
|
+
if self.cache_dir:
|
|
551
|
+
self.file_scans_dir = self.cache_dir / "file_scans"
|
|
552
|
+
self.scanned_directories_file = self.file_scans_dir / "scanned_directories.json"
|
|
553
|
+
|
|
554
|
+
# Initialize file scans directory
|
|
555
|
+
try:
|
|
556
|
+
self.file_scans_dir.mkdir(parents=True, exist_ok=True)
|
|
557
|
+
print(f"File scan cache directory initialized: {self.file_scans_dir}")
|
|
558
|
+
except Exception as e:
|
|
559
|
+
print(f"ERROR: Failed to create file scans directory: {e}")
|
|
560
|
+
self.file_scans_dir = None
|
|
561
|
+
self.scanned_directories_file = None
|
|
562
|
+
else:
|
|
563
|
+
print("WARNING: File scan cache manager running without persistent storage!")
|
|
564
|
+
|
|
565
|
+
def _get_file_cache_path(self, file_path: str) -> Path:
|
|
566
|
+
"""Generate cache file path using MD5 hash of absolute file path"""
|
|
567
|
+
if not self.file_scans_dir:
|
|
568
|
+
raise ValueError("File scans directory not available")
|
|
569
|
+
|
|
570
|
+
# Create hash from absolute file path
|
|
571
|
+
file_hash = hashlib.md5(file_path.encode()).hexdigest()
|
|
572
|
+
return self.file_scans_dir / f"{file_hash}.json"
|
|
573
|
+
|
|
574
|
+
def is_available(self) -> bool:
|
|
575
|
+
"""Check if file scan cache is available"""
|
|
576
|
+
return self.file_scans_dir is not None and self.file_scans_dir.exists()
|
|
577
|
+
|
|
578
|
+
def get_file_entry(self, file_path: str) -> Optional[Dict[str, Any]]:
|
|
579
|
+
"""Read individual file cache entry"""
|
|
580
|
+
if not self.is_available():
|
|
581
|
+
return None
|
|
582
|
+
|
|
583
|
+
try:
|
|
584
|
+
cache_path = self._get_file_cache_path(file_path)
|
|
585
|
+
return RobustFileOperations.safe_read_json(cache_path, None)
|
|
586
|
+
except Exception as e:
|
|
587
|
+
print(f"Error reading file cache for {file_path}: {e}")
|
|
588
|
+
return None
|
|
589
|
+
|
|
590
|
+
def set_file_entry(self, file_path: str, entry: Dict[str, Any]) -> bool:
|
|
591
|
+
"""Write individual file cache entry"""
|
|
592
|
+
if not self.is_available():
|
|
593
|
+
return False
|
|
594
|
+
|
|
595
|
+
try:
|
|
596
|
+
cache_path = self._get_file_cache_path(file_path)
|
|
597
|
+
return RobustFileOperations.safe_write_json(cache_path, entry)
|
|
598
|
+
except Exception as e:
|
|
599
|
+
print(f"Error writing file cache for {file_path}: {e}")
|
|
600
|
+
return False
|
|
601
|
+
|
|
602
|
+
def delete_file_entry(self, file_path: str) -> bool:
|
|
603
|
+
"""Delete individual file cache entry"""
|
|
604
|
+
if not self.is_available():
|
|
605
|
+
return False
|
|
606
|
+
|
|
607
|
+
try:
|
|
608
|
+
cache_path = self._get_file_cache_path(file_path)
|
|
609
|
+
if cache_path.exists():
|
|
610
|
+
cache_path.unlink()
|
|
611
|
+
return True
|
|
612
|
+
return True # File doesn't exist, consider it deleted
|
|
613
|
+
except Exception as e:
|
|
614
|
+
print(f"Error deleting file cache for {file_path}: {e}")
|
|
615
|
+
return False
|
|
616
|
+
|
|
617
|
+
def get_scanned_directories(self) -> List[Dict[str, Any]]:
|
|
618
|
+
"""Read scanned directories list"""
|
|
619
|
+
if not self.scanned_directories_file:
|
|
620
|
+
return []
|
|
621
|
+
|
|
622
|
+
try:
|
|
623
|
+
return RobustFileOperations.safe_read_json(self.scanned_directories_file, [])
|
|
624
|
+
except Exception as e:
|
|
625
|
+
print(f"Error reading scanned directories: {e}")
|
|
626
|
+
return []
|
|
627
|
+
|
|
628
|
+
def set_scanned_directories(self, directories: List[Dict[str, Any]]) -> bool:
|
|
629
|
+
"""Write scanned directories list"""
|
|
630
|
+
if not self.scanned_directories_file:
|
|
631
|
+
return False
|
|
632
|
+
|
|
633
|
+
try:
|
|
634
|
+
return RobustFileOperations.safe_write_json(self.scanned_directories_file, directories)
|
|
635
|
+
except Exception as e:
|
|
636
|
+
print(f"Error writing scanned directories: {e}")
|
|
637
|
+
return False
|
|
638
|
+
|
|
639
|
+
def clear_all_file_entries(self) -> bool:
|
|
640
|
+
"""Clear all cached file entries"""
|
|
641
|
+
if not self.is_available():
|
|
642
|
+
return False
|
|
643
|
+
|
|
644
|
+
try:
|
|
645
|
+
# Remove all .json files except scanned_directories.json
|
|
646
|
+
for cache_file in self.file_scans_dir.glob("*.json"):
|
|
647
|
+
if cache_file.name != "scanned_directories.json":
|
|
648
|
+
cache_file.unlink()
|
|
649
|
+
return True
|
|
650
|
+
except Exception as e:
|
|
651
|
+
print(f"Error clearing file entries: {e}")
|
|
652
|
+
return False
|
|
653
|
+
|
|
654
|
+
def get_cache_stats(self) -> Dict[str, Any]:
|
|
655
|
+
"""Return cache statistics"""
|
|
656
|
+
stats = {
|
|
657
|
+
"available": self.is_available(),
|
|
658
|
+
"file_scans_directory": str(self.file_scans_dir) if self.file_scans_dir else None,
|
|
659
|
+
"total_file_entries": 0,
|
|
660
|
+
"total_cache_size": 0,
|
|
661
|
+
"scanned_directories_count": 0
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
if self.is_available():
|
|
665
|
+
try:
|
|
666
|
+
# Count file entries (excluding scanned_directories.json)
|
|
667
|
+
file_entries = list(self.file_scans_dir.glob("*.json"))
|
|
668
|
+
file_entries = [f for f in file_entries if f.name != "scanned_directories.json"]
|
|
669
|
+
stats["total_file_entries"] = len(file_entries)
|
|
670
|
+
stats["total_cache_size"] = sum(f.stat().st_size for f in file_entries if f.exists())
|
|
671
|
+
|
|
672
|
+
# Count scanned directories
|
|
673
|
+
directories = self.get_scanned_directories()
|
|
674
|
+
stats["scanned_directories_count"] = len(directories)
|
|
675
|
+
|
|
676
|
+
except Exception as e:
|
|
677
|
+
stats["error"] = str(e)
|
|
678
|
+
|
|
679
|
+
return stats
|
|
680
|
+
|
|
681
|
+
|
|
540
682
|
# Global cache service instance
|
|
541
683
|
_cache_service = None
|
|
684
|
+
_file_scan_cache_manager = None
|
|
542
685
|
|
|
543
686
|
|
|
544
687
|
def get_cache_service() -> PersistentCacheService:
|
|
@@ -547,3 +690,11 @@ def get_cache_service() -> PersistentCacheService:
|
|
|
547
690
|
if _cache_service is None:
|
|
548
691
|
_cache_service = PersistentCacheService()
|
|
549
692
|
return _cache_service
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
def get_file_scan_cache_manager() -> FileScanCacheManager:
|
|
696
|
+
"""Get the global file scan cache manager instance"""
|
|
697
|
+
global _file_scan_cache_manager
|
|
698
|
+
if _file_scan_cache_manager is None:
|
|
699
|
+
_file_scan_cache_manager = FileScanCacheManager()
|
|
700
|
+
return _file_scan_cache_manager
|