Spaces:
Sleeping
Sleeping
| """ | |
| OFF-GRID - Memoria condivisa cloud (open source, Gradio MIT). | |
| Gira su un Hugging Face Space gratuito ed espone un tool MCP che Claude | |
| (anche dall'app mobile, chat normale) chiama per salvare aggiornamenti del | |
| progetto off-grid nella memoria cloud, ANCHE a PC spento. | |
| Gli aggiornamenti vengono salvati in INBOX.md su un HF Dataset privato | |
| (persistente). Quando il PC si riaccende, Claude legge l'INBOX e li integra | |
| nel sistema/dashboard. | |
| Connettore in Claude: Impostazioni > Connettori > URL | |
| https://Ayman666-offgrid-memoria.hf.space/gradio_api/mcp/ | |
| Config via Secrets dello Space: | |
| HF_TOKEN - token Hugging Face ristretto (write solo sul Dataset) | |
| DATASET_REPO - es. "Ayman666/offgrid-memoria-data" | |
| CODICE_SEGRETO - parola segreta che protegge la scrittura | |
| """ | |
| import io | |
| import os | |
| import datetime | |
| from zoneinfo import ZoneInfo | |
| import gradio as gr | |
| from huggingface_hub import HfApi, hf_hub_download | |
| HF_TOKEN = os.environ.get("HF_TOKEN", "") | |
| DATASET_REPO = os.environ.get("DATASET_REPO", "Ayman666/offgrid-memoria-data") | |
| CODICE_SEGRETO = os.environ.get("CODICE_SEGRETO", "") | |
| INBOX_FILE = "INBOX.md" | |
| TZ = ZoneInfo("Europe/Rome") | |
| _api = HfApi(token=HF_TOKEN) if HF_TOKEN else None | |
| def salva_aggiornamento(testo: str, codice: str) -> str: | |
| """Salva un aggiornamento del progetto off-grid nella memoria condivisa cloud di Ayman. | |
| Usa questo strumento quando Ayman, dal telefono, vuole annotare un aggiornamento | |
| (es. una telefonata, una decisione, un appuntamento) che deve restare salvato anche | |
| a PC spento. Richiede la parola segreta di accesso. | |
| Args: | |
| testo (str): Il testo dell'aggiornamento da salvare nella memoria. | |
| codice (str): La parola segreta di accesso (la fornisce Ayman). | |
| Returns: | |
| str: Esito del salvataggio. | |
| """ | |
| if CODICE_SEGRETO and codice.strip() != CODICE_SEGRETO: | |
| return "Accesso negato: parola segreta errata." | |
| if not testo or not testo.strip(): | |
| return "Niente da salvare: il testo è vuoto." | |
| if _api is None: | |
| return "Errore di configurazione: token HF mancante nei Secrets dello Space." | |
| ts = datetime.datetime.now(TZ).strftime("%Y-%m-%d %H:%M") | |
| voce = f"\n## {ts}\n{testo.strip()}\n" | |
| try: | |
| path = hf_hub_download(DATASET_REPO, INBOX_FILE, repo_type="dataset", token=HF_TOKEN) | |
| contenuto = open(path, encoding="utf-8").read() | |
| except Exception: | |
| contenuto = "# INBOX - aggiornamenti da Claude mobile\n\nNote scritte dal telefono. Claude le integra nel sistema quando il PC si riaccende.\n" | |
| nuovo = contenuto + voce | |
| try: | |
| _api.upload_file( | |
| path_or_fileobj=io.BytesIO(nuovo.encode("utf-8")), | |
| path_in_repo=INBOX_FILE, | |
| repo_id=DATASET_REPO, | |
| repo_type="dataset", | |
| commit_message=f"nota mobile {ts}", | |
| ) | |
| except Exception as e: | |
| return f"Errore nel salvataggio: {e}" | |
| return f"Salvato nella memoria ({ts}): {testo.strip()[:60]}" | |
| def leggi_ultimi(codice: str) -> str: | |
| """Legge gli ultimi aggiornamenti salvati nella memoria condivisa cloud di Ayman. | |
| Args: | |
| codice (str): La parola segreta di accesso. | |
| Returns: | |
| str: Gli ultimi aggiornamenti salvati, o un messaggio se vuota. | |
| """ | |
| if CODICE_SEGRETO and codice.strip() != CODICE_SEGRETO: | |
| return "Accesso negato: parola segreta errata." | |
| try: | |
| path = hf_hub_download(DATASET_REPO, INBOX_FILE, repo_type="dataset", token=HF_TOKEN) | |
| contenuto = open(path, encoding="utf-8").read() | |
| return contenuto[-2000:] | |
| except Exception: | |
| return "La memoria è ancora vuota." | |
| with gr.Blocks(title="OFF-GRID Memoria") as demo: | |
| gr.Markdown( | |
| "# OFF-GRID - Memoria condivisa\n" | |
| "Connettore usato da Claude per salvare aggiornamenti nel cloud, anche a PC spento.\n" | |
| "Tool disponibili: **salva_aggiornamento**, **leggi_ultimi** (protetti da parola segreta)." | |
| ) | |
| with gr.Tab("Salva"): | |
| t = gr.Textbox(label="Aggiornamento", lines=4) | |
| c = gr.Textbox(label="Parola segreta", type="password") | |
| b = gr.Button("Salva", variant="primary") | |
| o = gr.Textbox(label="Esito") | |
| b.click(salva_aggiornamento, inputs=[t, c], outputs=o) | |
| with gr.Tab("Leggi"): | |
| c2 = gr.Textbox(label="Parola segreta", type="password") | |
| b2 = gr.Button("Leggi ultimi") | |
| o2 = gr.Markdown() | |
| b2.click(leggi_ultimi, inputs=c2, outputs=o2) | |
| if __name__ == "__main__": | |
| demo.launch(mcp_server=True) | |