feat(browser): auto-commit con 178 cambios

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-20 18:22:23 +02:00
parent 7d100e7f3e
commit 763e06c127
178 changed files with 19917 additions and 317 deletions
+105
View File
@@ -0,0 +1,105 @@
---
name: add_event_dav
kind: pipeline
lang: py
domain: pipelines
version: "1.0.0"
purity: impure
signature: "def add_event_dav(summary: str, start: str, end: str = '', *, location: str = '', description: str = '', all_day: bool = False, rrule: str = '', alarm_minutes: int = 0, uid: str = '', base_url: str = DEFAULT_BASE_URL, username: str = DEFAULT_USERNAME, collection_path: str = DEFAULT_COLLECTION, secret_path: str = 'dav/xandikos-enmanuel', timeout_s: float = 20.0, verify_tls: bool = True) -> dict"
description: "One-shot que anade UN evento al calendario CalDAV de Enmanuel (Xandikos self-hosted) en una sola llamada. Compone build_vevent (componer el VCALENDAR), extract_or_make_uid (UID si falta), pass_get_secret (resolver la contrasena DAV desde pass) y caldav_put_event (HTTP PUT). Impuro: escritura remota real. Idempotente por UID. La contrasena nunca se logea ni aparece en el resultado. Defaults apuntan al calendario de Enmanuel."
tags: [dav, caldav, calendar, event, pipelines]
params:
- name: summary
desc: "titulo del evento (-> SUMMARY). Obligatorio."
- name: start
desc: "fecha/hora de inicio, p.ej. '2026-06-20T17:00' (naive local), con sufijo 'Z' para UTC, o '2026-06-20' para all_day. Obligatorio."
- name: end
desc: "fecha/hora de fin. Si vacio y no es all_day, se deriva +1h del start; si all_day, el dia siguiente."
- name: location
desc: "lugar del evento (-> LOCATION)."
- name: description
desc: "descripcion del evento (-> DESCRIPTION)."
- name: all_day
desc: "bool. Si True, evento de dia completo (DTSTART;VALUE=DATE)."
- name: rrule
desc: "regla de recurrencia RRULE, p.ej. 'FREQ=WEEKLY;BYDAY=MO'."
- name: alarm_minutes
desc: "int. Si > 0, anade un recordatorio (VALARM display) N minutos antes."
- name: uid
desc: "UID explicito del evento. Si vacio, se sintetiza determinista del VCALENDAR (re-subir el mismo evento sobrescribe = idempotente)."
- name: base_url
desc: "URL base del servidor DAV. Default = Xandikos de Enmanuel."
- name: username
desc: "usuario para HTTP Basic auth. Default 'enmanuel'."
- name: collection_path
desc: "ruta de la coleccion CalDAV destino. Default '/enmanuel/calendars/calendar/'."
- name: secret_path
desc: "ruta del secreto en pass con la contrasena DAV. Default 'dav/xandikos-enmanuel'."
- name: timeout_s
desc: "timeout del PUT en segundos. Default 20.0."
- name: verify_tls
desc: "si True (default) verifica el certificado TLS. No desactivar salvo entornos de prueba."
output: "dict. En exito: {status: 'ok', http_status: int, uid: str, url: str}. En error (sin lanzar): {status: 'error', error: str, uid: str|None, http_status: int|None}. La contrasena nunca aparece en el resultado."
uses_functions: [build_vevent_py_core, extract_or_make_uid_py_infra, pass_get_secret_py_infra, caldav_put_event_py_infra]
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [os, sys, argparse, json]
tested: false
tests: []
test_file_path: ""
file_path: "python/functions/pipelines/add_event_dav.py"
---
## Ejemplo
```bash
# Anadir un evento con hora, lugar y recordatorio (UID sintetico determinista):
./fn run add_event_dav --summary "Cita dentista" --start 2026-06-20T17:00 \
--end 2026-06-20T18:00 --location "Clinica" --alarm-minutes 30
# {"status": "ok", "http_status": 201, "uid": "evt-<md5>", "url": "https://dav-.../enmanuel/calendars/calendar/evt-<md5>.ics"}
# Evento de dia completo recurrente:
./fn run add_event_dav --summary "Cumpleanos" --start 2026-06-20 --all-day \
--rrule "FREQ=YEARLY"
```
```python
import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from pipelines.add_event_dav import add_event_dav
res = add_event_dav(
"Reunion equipo", "2026-06-22T09:00", "2026-06-22T10:00",
location="Sala A", description="Sprint review", alarm_minutes=15,
)
print(res["status"], res["uid"]) # 'ok' evt-...
```
## Cuando usarla
Cuando quieras anadir un evento al calendario de Enmanuel sin orquestar a mano
los pasos (componer el iCal, resolver el secreto, hacer el PUT). Es la operacion
one-shot del grupo `dav` para CalDAV. Para subir un `.ics` entero con N eventos
usa `import_ics_to_caldav_py_pipelines`; para un solo evento parametrizado, esta.
Pasa `uid` explicito si quieres controlar/actualizar un evento concreto; dejalo
vacio para crear uno nuevo con UID derivado del contenido.
## Gotchas
- **Accion con efecto real (impura)**: hace un HTTP PUT que escribe en el
calendario remoto de Enmanuel. No es un dry-run. Verifica `start`/`end` antes
de lanzar; un PUT con datos erroneos crea el evento igualmente.
- **Idempotente por UID**: el nombre del recurso es `<uid>.ics`. Re-subir el
mismo UID SOBRESCRIBE el evento existente (no duplica). Con `uid` vacio el UID
es determinista (md5 de summary+start): re-lanzar el mismo evento exacto pisa
el anterior; cambiar summary o start crea un recurso nuevo.
- **Secreto desde `pass`, nunca hardcode**: la contrasena se resuelve con
`pass_get_secret('dav/xandikos-enmanuel')` y NUNCA se logea ni se incluye en el
dict de retorno. Si `pass` no esta instalado o la entry no existe, devuelve
`{status:'error', error:'pass: ...'}` sin lanzar y sin hacer el PUT.
- **`verify_tls=True` por defecto**: no uses `--no-verify-tls` salvo en pruebas
controladas. El servidor de Enmanuel tiene certificado valido.
- **ValueError de build_vevent**: si falta `summary` o `start`, el pipeline lo
captura y devuelve `{status:'error'}` (no propaga la excepcion).