# 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", "<>/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 `<>` se resuelven con el environment de la team (subscriptions las propagan a la GUI). - Secretos: usa `value="pass:"` 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.