# Capability group: `hoppscotch`

Operar una instancia **self-hosted de Hoppscotch** (consola de APIs, alternativa open-source a
Postman) desde el registry, vía su **API GraphQL**. El agente crea/edita requests, colecciones y
environments por la API; el humano los ve **en vivo** en su GUI (subscriptions = hot-reload real).
Las requests viven en la base de datos del self-host (Postgres), compartida entre el agente y la GUI.

Este es el **flujo canónico**. El antiguo modo "archivo `.json` local" (funciones
`parse_*` / `run_*` / `add_hoppscotch_request`) **fue eliminado**: escribía un `.json` en disco que
NO subía al workspace, así que el humano no lo veía en la GUI. No lo reintroduzcas.

## Stack self-host

Vive en `projects/web_scraping/hoppscotch/selfhost/` (docker compose: AIO + Postgres + mailpit).

| Servicio | URL | Para qué |
|---|---|---|
| App (cliente) | `http://localhost:3009` | la GUI donde el humano usa las colecciones (instalable como PWA) |
| Admin dashboard | `http://localhost:3100` | gestión (usuarios, config) |
| Backend GraphQL | `http://localhost:3170/graphql` | la API que usan las funciones |
| Mailpit | `http://localhost:8025` | captura el magic link del login (SMTP de pruebas, sin correo real) |

Levantar: `cd selfhost && docker compose up -d`. Team de trabajo: **"registry"**. Cuenta: `admin@example.com`.

## Funciones

| ID | Firma corta | Qué hace |
|---|---|---|
| `hoppscotch_login_py_infra` | `(email, *, backend_url, mailpit_url) -> {access_token,...}` | login por magic link headless (lee el link de mailpit) → JWT |
| `hoppscotch_create_request_py_infra` | `(collection_id, method, url, *, title, headers, body, body_type, team_id, access_token) -> dict` | crea una request en una colección de la team |
| `hoppscotch_update_request_py_infra` | `(request_id, method, url, *, title, headers, body, body_type, access_token) -> dict` | actualiza una request |
| `hoppscotch_delete_request_py_infra` | `(request_id, *, access_token) -> dict` | borra una request |
| `hoppscotch_list_requests_py_infra` | `(collection_id, *, access_token) -> {requests:[...]}` | lista las requests de una colección |
| `hoppscotch_set_environment_py_infra` | `(team_id, name, variables, *, access_token) -> dict` | crea/actualiza (idempotente) el environment de la team; resuelve secretos `pass:` |
| `build_hoppscotch_collection_py_infra` | `(calls, *, name, request_names) -> dict` | **helper interno** de create/update: serializa call specs al formato HoppRESTRequest. NO para escribir `.json` a mano |
| `pass_get_secret_py_infra` | `(path, *, line) -> {value}` | lee un secreto de `pass` (lo consume `set_environment` para no hardcodear keys) |

`access_token` se pasa como **cookie**, no header `Authorization`. Caduca a 24h → re-login con `hoppscotch_login`.

## Ejemplo canónico (end-to-end)

```python
import sys, os
sys.path.insert(0, os.path.join(os.path.expanduser("~/fn_registry"), "python", "functions"))
from infra.hoppscotch_login import hoppscotch_login
from infra.hoppscotch_create_request import hoppscotch_create_request
from infra.hoppscotch_set_environment import hoppscotch_set_environment

TEAM = "cmq8kn0v500030xls1nvminjy"          # team "registry"
COLL = "cmq8knppc00040xlskt4ist27"          # colección registry_api (de hoppscotch_list/DB)

tok = hoppscotch_login("admin@example.com")["access_token"]

# 1. Variables del workspace (secreto resuelto desde pass, no hardcodeado)
hoppscotch_set_environment(TEAM, "registry", [
    {"key": "baseURL", "value": "https://registry.organic-machine.com", "secret": False},
    {"key": "api_key", "value": "pass:apis/registry", "secret": True},   # pass: -> pass_get_secret
], access_token=tok)

# 2. Crear una request → aparece EN VIVO en la GUI del humano (subscriptions)
hoppscotch_create_request(
    COLL, "GET", "<<baseURL>>/api/status",
    title="status", headers={"Accept": "application/json"},
    team_id=TEAM, access_token=tok,
)
```

## Fronteras (qué NO cubre)

- **No es modo archivo**: no escribe colecciones `.json` locales como fuente. Las requests viven en el
  Postgres del self-host. (Los `.json` en `collections/` son solo respaldo/semilla importable.)
- **No automatiza la GUI**: opera por la API; la GUI la mira el humano.
- **No gestiona usuarios/teams del dashboard**: eso es el admin dashboard (`:3100`).
- **No ejecuta los scripts pre/post-request JS** de Hoppscotch.

## Gotchas

- `access_token` como **cookie** (`cookies={"access_token": tok}`), no `Authorization`. 24h de vida.
- `createRequestInCollection` de esta instancia **exige `team_id`** en el input (no solo el collectionID).
- Variables `<<var>>` se resuelven con el environment de la team (subscriptions las propagan a la GUI).
- Secretos: usa `value="pass:<ruta>"` en `set_environment` → se resuelve de `pass`, nunca se hardcodea
  ni se logea en crudo.
- El secreto viaja en claro al backend local por GraphQL — es local (`127.0.0.1`), aceptable.
