Spaces:
Running
Running
| from __future__ import annotations | |
| import logging | |
| import json | |
| import os | |
| import platform | |
| import time | |
| import uuid | |
| from pathlib import Path | |
| from typing import Any | |
| ROOT = Path(__file__).resolve().parents[1] | |
| DATA_DIR = ROOT / 'data' | |
| TRACKER_PATH = DATA_DIR / 'copy_tracker.json' | |
| DISABLE_MARKER = '.friday_disabled' | |
| COPY_ID_FILE = '.friday_copy_id.json' | |
| def _now_text() -> str: | |
| return time.strftime('%Y-%m-%d %H:%M:%S') | |
| def _load() -> dict[str, Any]: | |
| try: | |
| if TRACKER_PATH.exists(): | |
| data = json.loads(TRACKER_PATH.read_text(encoding='utf-8')) | |
| if isinstance(data, dict): | |
| data.setdefault('events', []) | |
| return data | |
| except Exception as _friday_exc: | |
| logging.getLogger(__name__).debug('suppressed optional failure', exc_info=_friday_exc) | |
| return {'events': []} | |
| def _save(data: dict[str, Any]) -> None: | |
| try: | |
| DATA_DIR.mkdir(parents=True, exist_ok=True) | |
| TRACKER_PATH.write_text(json.dumps(data, indent=2), encoding='utf-8') | |
| except Exception as _friday_exc: | |
| logging.getLogger(__name__).debug('suppressed optional failure', exc_info=_friday_exc) | |
| def record_copy(target: str | os.PathLike[str], copy_type: str, result: dict[str, Any] | None=None) -> dict[str, Any]: | |
| target_path = Path(target).resolve() | |
| copy_id = uuid.uuid4().hex[:12].upper() | |
| event = {'id': copy_id, 'type': str(copy_type or 'unknown'), 'status': 'active', 'target': str(target_path), 'device_name': platform.node() or 'unknown-device', 'date': _now_text(), 'timestamp': time.time(), 'result': result or {}} | |
| data = _load() | |
| data.setdefault('events', []).append(event) | |
| _save(data) | |
| try: | |
| target_path.mkdir(parents=True, exist_ok=True) | |
| (target_path / COPY_ID_FILE).write_text(json.dumps(event, indent=2), encoding='utf-8') | |
| except Exception as _friday_exc: | |
| logging.getLogger(__name__).debug('suppressed optional failure', exc_info=_friday_exc) | |
| try: | |
| from core.gui_bridge import update as gui_update | |
| gui_update(last_copy_event=event) | |
| except Exception as _friday_exc: | |
| logging.getLogger(__name__).debug('suppressed optional failure', exc_info=_friday_exc) | |
| return event | |
| def _already_tracked(target: Path) -> bool: | |
| target_text = str(target.resolve()).lower() | |
| for event in list_copies(): | |
| if str(event.get('target', '')).lower() == target_text: | |
| return True | |
| return False | |
| def scan_removable_copies() -> list[dict[str, Any]]: | |
| found: list[dict[str, Any]] = [] | |
| if os.name != 'nt': | |
| return found | |
| try: | |
| import ctypes | |
| get_drive_type = ctypes.windll.kernel32.GetDriveTypeW | |
| for letter in 'DEFGHIJKLMNOPQRSTUVWXYZ': | |
| drive = Path(f'{letter}:\\') | |
| if get_drive_type(str(drive)) != 2: | |
| continue | |
| for candidate in (drive / 'FRIDAY', drive / 'FRIDAY_Copy'): | |
| if not candidate.exists(): | |
| continue | |
| if not ((candidate / '.portable_friday').exists() or (candidate / COPY_ID_FILE).exists()): | |
| continue | |
| if _already_tracked(candidate): | |
| continue | |
| found.append(record_copy(candidate, 'detected_removable', {'detected': True})) | |
| except Exception as _friday_exc: | |
| logging.getLogger(__name__).debug('suppressed optional failure', exc_info=_friday_exc) | |
| return found | |
| def watch_copy_tracker(speak=None, interval: float=30.0) -> None: | |
| last_notice = 0.0 | |
| while True: | |
| time.sleep(interval) | |
| try: | |
| found = scan_removable_copies() | |
| if found and speak and (time.time() - last_notice > 60.0): | |
| last_notice = time.time() | |
| speak(f"JARVIS copy detected on removable storage. Copy id {found[-1].get('id')}.") | |
| except Exception as _friday_exc: | |
| logging.getLogger(__name__).debug('suppressed optional failure', exc_info=_friday_exc) | |
| def list_copies() -> list[dict[str, Any]]: | |
| events = _load().get('events', []) | |
| return events if isinstance(events, list) else [] | |
| def revoke_copy(copy_id_or_target: str) -> tuple[bool, str]: | |
| needle = (copy_id_or_target or '').strip() | |
| if not needle: | |
| return (False, 'No copy id or path provided.') | |
| data = _load() | |
| events = data.get('events', []) | |
| if not isinstance(events, list): | |
| events = [] | |
| selected = None | |
| for event in events: | |
| if not isinstance(event, dict): | |
| continue | |
| if str(event.get('id', '')).lower() == needle.lower() or needle.lower() in str(event.get('target', '')).lower(): | |
| selected = event | |
| break | |
| if selected is None: | |
| return (False, 'I could not find that copy in the tracker.') | |
| target = Path(str(selected.get('target') or '')) | |
| try: | |
| target.mkdir(parents=True, exist_ok=True) | |
| (target / DISABLE_MARKER).write_text(json.dumps({'disabled_at': _now_text(), 'copy_id': selected.get('id'), 'reason': 'Revoked by owner from source system.'}, indent=2), encoding='utf-8') | |
| selected['status'] = 'revoked' | |
| selected['revoked_at'] = _now_text() | |
| _save(data) | |
| return (True, f"Copy {selected.get('id')} has been locked. It will refuse to start from {target}.") | |
| except Exception as _friday_exc: | |
| logging.getLogger(__name__).debug('suppressed optional failure', exc_info=_friday_exc) | |
| return (False, f'I found the copy, but could not write the lock marker: {_friday_exc}') | |
| def disabled_reason(root: Path | None=None) -> str: | |
| base = root or ROOT | |
| marker = base / DISABLE_MARKER | |
| if not marker.exists(): | |
| return '' | |
| try: | |
| data = json.loads(marker.read_text(encoding='utf-8')) | |
| return str(data.get('reason') or 'This JARVIS copy has been disabled by the owner.') | |
| except Exception as _friday_exc: | |
| logging.getLogger(__name__).debug('suppressed optional failure', exc_info=_friday_exc) | |
| return 'This JARVIS copy has been disabled by the owner.' | |