feat(infra): auto-commit con 88 cambios

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-11 00:16:46 +02:00
parent 6bc97df5c0
commit eb8dbf66a1
126 changed files with 10933 additions and 287 deletions
@@ -0,0 +1,118 @@
---
name: hoppscotch_run_request
kind: function
lang: py
domain: infra
version: "1.0.0"
purity: impure
signature: "def hoppscotch_run_request(method: str, url: str, *, title: str | None = None, headers: dict | None = None, body: str | None = None, body_type: str | None = None, variables: dict | None = None, access_token: str, backend_url: str = \"http://localhost:3170\", record_history: bool = True, timeout_s: float = 30.0, verify_tls: bool = True) -> dict"
description: "Ejecuta una peticion HTTP real (resolviendo placeholders <<var>>/{{var}} con un dict de variables) y la registra en el UserHistory de un Hoppscotch self-hosted via la mutation GraphQL createUserHistory, para que el humano la vea aparecer en vivo en la pestana History de su GUI (subscription userHistoryCreated). La request se ejecuta con las variables resueltas, pero en el History se guarda SIN resolver (con los literales <<var>>) igual que en el editor. resMetadata minimo: statusCode + duration. El access_token va como cookie, no como header Authorization."
tags: [hoppscotch, flow-replay, http]
uses_functions: [build_hoppscotch_collection_py_infra]
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [json, re, requests]
params:
- name: method
desc: "metodo HTTP de la peticion (GET, POST, ...)."
- name: url
desc: "endpoint de la peticion. Puede contener placeholders <<var>> o {{var}} que se resuelven con `variables` antes de ejecutar."
- name: title
desc: "nombre visible de la request en el History. None = derivar de method + path via build_hoppscotch_collection."
- name: headers
desc: "dict name->value de cabeceras. Sus values tambien admiten placeholders <<var>>/{{var}}."
- name: body
desc: "cuerpo de la peticion como texto ya serializado. Admite placeholders. None = sin cuerpo."
- name: body_type
desc: "tipo de cuerpo para el HoppRESTRequest del History: 'json' | 'form' | 'raw' | None."
- name: variables
desc: "dict name->value para resolver los placeholders al EJECUTAR. Una variable que falte deja el literal intacto. None = no se resuelve nada."
- name: access_token
desc: "JWT de sesion (de hoppscotch_login). Viaja en la cookie access_token, NO en el header Authorization. Necesario para registrar en el History."
- name: backend_url
desc: "base del backend Hoppscotch self-host sin barra final. La mutation cuelga de {backend_url}/graphql. Default http://localhost:3170."
- name: record_history
desc: "si True y hay access_token, registra la request ejecutada en el UserHistory via createUserHistory. Default True."
- name: timeout_s
desc: "timeout en segundos de la peticion HTTP ejecutada (y del POST de History). Default 30.0."
- name: verify_tls
desc: "verificacion del certificado TLS de la peticion ejecutada. Default True."
output: "dict. En exito de la ejecucion HTTP: {status: 'ok', status_code: int, duration_ms: int, response_body: str (truncado a 5000 chars), response_headers: dict, recorded: bool, history_id: str|None}. Si la ejecucion fue ok pero el registro de History fallo, status sigue 'ok', recorded False y se anade history_error. Si la ejecucion HTTP falla (RequestException): {status: 'error', error: str, recorded: False}. Nunca lanza por errores de red esperables."
tested: true
tests:
- "test_ejecuta_resolviendo_variables_angle"
- "test_ejecuta_resolviendo_variables_brace"
- "test_record_history_registra_request_sin_resolver"
- "test_record_history_false_no_llama_create_user_history"
- "test_request_exception_status_error"
- "test_variable_faltante_conserva_literal"
test_file_path: "python/functions/infra/hoppscotch_run_request_test.py"
file_path: "python/functions/infra/hoppscotch_run_request.py"
---
## Ejemplo
```python
import sys
sys.path.insert(0, "python/functions")
from infra.hoppscotch_login import hoppscotch_login
from infra.hoppscotch_run_request import hoppscotch_run_request
# 1) Obtener un JWT de sesion (headless, lee el correo de Mailpit).
login = hoppscotch_login("admin@example.com")
assert login["status"] == "ok", login["error"]
token = login["access_token"]
# 2) Ejecutar una request con una variable y dejar rastro en el History de la GUI.
result = hoppscotch_run_request(
"GET",
"<<baseURL>>/api/status",
title="Status",
variables={"baseURL": "https://registry.organic-machine.com"},
access_token=token,
)
print(result["status_code"], result["recorded"], result["history_id"])
# 200 True hist-...
# -> aparece en vivo en la pestana History del Hoppscotch self-host.
```
## Cuando usarla
Cuando el agente ejecuta una consulta HTTP y quiere que el humano la vea en el
History de su GUI Hoppscotch self-hosted, en vivo. La entry aparece via la
subscription `userHistoryCreated` sin que el humano refresque. Util para hacer
auditable/observable lo que el agente prueba: cada `hoppscotch_run_request` deja
en la pestana History la request (con sus variables sin resolver) y su statusCode
+ duracion. Encadena con `hoppscotch_login` para obtener el `access_token`.
## Gotchas
- **El access_token va como cookie, no como header Authorization.** La mutation
`createUserHistory` lee el JWT de la cookie `access_token`. Se manda con
`cookies={"access_token": ...}`. Si expira (~24h), re-loguea con
`hoppscotch_login`.
- **reqData lleva la request SIN resolver.** Lo que se guarda en el History es el
HoppRESTRequest con los placeholders `<<var>>`/`{{var}}` literales, igual que en
el editor de la GUI, para que el humano vea la plantilla con sus variables y no
los valores expandidos. La peticion SI se ejecuta con las variables resueltas.
- **Soporta `<<>>` y `{{}}`.** Hoppscotch usa `<<var>>`; muchas plantillas traen
`{{var}}`. Ambas sintaxis se resuelven al ejecutar. Una variable que falte en
`variables` deja el literal intacto (no rompe).
- **resMetadata minimo: statusCode + duration.** Se envia
`{"statusCode": ..., "duration": ...}`. Si una version del backend exigiera mas
campos, el registro fallaria con `history_error` (la ejecucion HTTP sigue siendo
ok). Ajustar el shape si el self-host lo pide.
- **El body de respuesta se trunca a 5000 chars** en `response_body` del output,
para no devolver payloads enormes. Los `response_headers` van completos.
- **duration_ms viene de `resp.elapsed`,** no de `time.time()`: es la latencia que
midio `requests` para la peticion ejecutada.
- **Degradacion suave del History:** si la ejecucion HTTP fue ok pero el POST de la
mutation falla (transporte, no-JSON, errores GraphQL, sin id), `status` sigue
"ok", `recorded` es False y se anade `history_error` con el detalle.
## Capability growth log
v1.0.0 — version inicial. Ejecucion + registro en UserHistory del self-host;
resolucion de placeholders `<<>>`/`{{}}`.