offgrid-memoria / app.py
Ayman666's picture
Upload app.py with huggingface_hub
dc3503c verified
Raw
History Blame Contribute Delete
4.53 kB
"""
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)