alegra-data-agent 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +432 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,21 +1,445 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Data Agent Self Service
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> **Cómo se llama cada cosa.** El producto es **Data Agent Self Service** — así aparece en el
|
|
4
|
+
> gateway y en el login. El repositorio es `Alegra-Team/data-agent-selfservice` y el paquete npm
|
|
5
|
+
> es `alegra-data-agent`: son tres registros independientes y no tienen por qué coincidir.
|
|
6
|
+
>
|
|
7
|
+
> El paquete **no se renombra**: su nombre está incrustado en el hook de arranque de cada máquina
|
|
8
|
+
> ya instalada, así que cambiarlo obligaría a migrar a todo el mundo a mano. Lo mismo vale para
|
|
9
|
+
> el `ISSUER`/`AUDIENCE` de los JWT (`gateway/lib/jwt.ts`) — tocarlos invalidaría los tokens ya
|
|
10
|
+
> emitidos.
|
|
11
|
+
|
|
12
|
+
Agente de Data interno de Alegra. Una línea instala las skills + MCPs del equipo BI Data en tu herramienta de IA (Claude Code, Cursor, OpenCode, Antigravity, Codex, Hermes), gated por OAuth `@alegra.com`.
|
|
13
|
+
|
|
14
|
+
> Uso interno de Alegra. No publicar, no promocionar, no contribuciones externas.
|
|
15
|
+
|
|
16
|
+
## Estado
|
|
17
|
+
|
|
18
|
+
- **CLI**: [`alegra-data-agent`](https://www.npmjs.com/package/alegra-data-agent) en npmjs.com.
|
|
19
|
+
- **Gateway en producción**: [https://alegra-data-agent.vercel.app](https://alegra-data-agent.vercel.app).
|
|
20
|
+
- **Healthcheck**: `https://alegra-data-agent.vercel.app/api/health`.
|
|
21
|
+
- **Guía de uso y documentación**: [wiki del agente](https://wiki.alegra.com/doc/alegra-data-agent-DKiPc9cvFS) — empezar, cómo preguntar, qué responde, problemas comunes, [arquitectura y seguridad](https://wiki.alegra.com/doc/arquitectura-y-seguridad-vWfNc90Myy) e [historial de cambios](https://wiki.alegra.com/doc/historial-de-cambios-J8uMHq1sfd). La página de entrada del gateway enlaza a todas.
|
|
22
|
+
|
|
23
|
+
## Compatibilidad
|
|
24
|
+
|
|
25
|
+
| Sistema operativo | ✓ |
|
|
26
|
+
|---|---|
|
|
27
|
+
| macOS (Intel + Apple Silicon) | ✅ |
|
|
28
|
+
| Ubuntu / Debian / Linux | ✅ |
|
|
29
|
+
| WSL (Windows Subsystem for Linux) | ✅ |
|
|
30
|
+
| Windows 10/11 nativo | ✅ |
|
|
31
|
+
|
|
32
|
+
| Cliente AI | ✓ |
|
|
33
|
+
|---|---|
|
|
34
|
+
| [Claude Code](https://claude.com/claude-code) | ✅ |
|
|
35
|
+
| [Cursor](https://cursor.com) | ✅ |
|
|
36
|
+
| [OpenCode](https://opencode.ai) | ✅ |
|
|
37
|
+
| [Antigravity](https://antigravity.dev) | ✅ |
|
|
38
|
+
| [Codex](https://developers.openai.com/codex) | ✅ |
|
|
39
|
+
| [Hermes Agent](https://hermes-agent.nousresearch.com) | ✅ |
|
|
40
|
+
|
|
41
|
+
Si no tenés ningún cliente instalado, el bootstrap te instala Claude Code automáticamente.
|
|
42
|
+
|
|
43
|
+
### Si no usás terminal
|
|
44
|
+
|
|
45
|
+
El comando de arriba es para clientes CLI/IDE. En **claude.ai, Claude Desktop, Cowork y móvil**
|
|
46
|
+
el agente llega sin instalar nada:
|
|
47
|
+
|
|
48
|
+
| | Cómo |
|
|
49
|
+
|---|---|
|
|
50
|
+
| MCPs | Configuración → **Conectores**. En Team/Enterprise el owner los agrega en *Organization settings* y le quedan disponibles a toda Alegra. |
|
|
51
|
+
| Skills | Configuración → **Habilidades** → subir el `.zip`. El owner puede provisionarlas para toda la organización y **aparecen solas**, sin que nadie suba nada. |
|
|
52
|
+
|
|
53
|
+
Los ZIP se generan con `pnpm run pack:web` (uno por skill, en `dist/skills-web/`).
|
|
54
|
+
|
|
55
|
+
Dos cosas que conviene saber:
|
|
56
|
+
|
|
57
|
+
- Un conector de la cuenta se sirve a **todas** las superficies, **Claude Code incluido** — ahí
|
|
58
|
+
aparecen en `/mcp` bajo `claude.ai`. Si ya tenés el MCP como server local, el conector queda
|
|
59
|
+
oculto por URL duplicada; el propio `/mcp` te dice cómo cambiar de uno al otro.
|
|
60
|
+
- Los ZIP subidos **no se auto-actualizan**, y no hay API para subirlos. El CLI sí (manifest
|
|
61
|
+
firmado). Después de un release, `pnpm run pack:web` dice cuáles cambiaron: solo esos se
|
|
62
|
+
vuelven a subir (paso 5 del «Flujo de release»).
|
|
63
|
+
|
|
64
|
+
Detalle y el porqué en [ADR-0010](./docs/adr/0010-superficies-no-code.md).
|
|
4
65
|
|
|
5
66
|
## Instalación
|
|
6
67
|
|
|
68
|
+
**El comando único es:**
|
|
69
|
+
|
|
7
70
|
```sh
|
|
8
71
|
pnpm dlx alegra-data-agent
|
|
9
72
|
```
|
|
10
73
|
|
|
11
|
-
|
|
74
|
+
Funciona en cualquier terminal (macOS Terminal, GNOME Terminal, WSL, PowerShell) y desde la terminal embebida de cualquier cliente AI (Claude Code, Cursor, OpenCode, Antigravity, Codex, Hermes).
|
|
75
|
+
|
|
76
|
+
### Si no tenés Node o pnpm
|
|
77
|
+
|
|
78
|
+
Si al correr `pnpm dlx alegra-data-agent` te dice `pnpm: command not found`, primero corré el bootstrap que te instala Node 22+ y pnpm 11+ automáticamente:
|
|
79
|
+
|
|
80
|
+
**macOS / Linux / WSL** — en cualquier terminal:
|
|
81
|
+
|
|
82
|
+
```sh
|
|
83
|
+
curl -fsSL https://alegra-data-agent.vercel.app/install.sh | bash
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Windows nativo** — en PowerShell (sin admin):
|
|
87
|
+
|
|
88
|
+
```powershell
|
|
89
|
+
irm https://alegra-data-agent.vercel.app/install.ps1 | iex
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
El bootstrap deja Node + pnpm + git + Claude Code listos, y al final corre `pnpm dlx alegra-data-agent` por vos. Después de eso, el comando único es siempre `pnpm dlx alegra-data-agent`.
|
|
93
|
+
|
|
94
|
+
### Después del install
|
|
95
|
+
|
|
96
|
+
1. **Reiniciá tu cliente AI** (Claude Code / Cursor / OpenCode / Antigravity / Codex / Hermes).
|
|
97
|
+
2. **Hacé tu primera pregunta** en español. Ejemplo: *"cuántos nuevos clientes en mayo en Colombia"*.
|
|
98
|
+
3. **La primera vez** que el agente use un servicio (Metabase, Amplitude, Wiki) te va a pedir 1 click con tu cuenta `@alegra.com`. Después no te pide nada más.
|
|
99
|
+
|
|
100
|
+
> El agente nunca te va a pedir API keys, tokens, ni variables de entorno. Todo es OAuth `@alegra.com` automático.
|
|
101
|
+
|
|
102
|
+
> **HubSpot es la excepción: no lo instala el CLI, se habilita como conector de la cuenta.** El
|
|
103
|
+
> servidor de HubSpot no soporta el registro automático que usan los clientes AI, así que una entry
|
|
104
|
+
> local nunca autentica. El conector oficial sí funciona: Configuración → Conectores → HubSpot →
|
|
105
|
+
> Conectar, y queda disponible en Claude Code, Claude Desktop y claude.ai. En Cursor, OpenCode y
|
|
106
|
+
> Codex hoy no hay ruta. Detalle en [`agent/mcps/hubspot/README.md`](./agent/mcps/hubspot/README.md).
|
|
12
107
|
|
|
13
108
|
## Requisitos
|
|
14
109
|
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
110
|
+
- Cuenta Google `@alegra.com` verificada (lo único que sí tenés que tener antes).
|
|
111
|
+
- Node ≥ 22.6.0 — instala el bootstrap si te falta.
|
|
112
|
+
- pnpm ≥ 11.1.2 — instala el bootstrap si te falta.
|
|
113
|
+
- Un cliente AI compatible — el bootstrap instala Claude Code si no detecta ninguno.
|
|
114
|
+
|
|
115
|
+
## Qué se instala
|
|
116
|
+
|
|
117
|
+
| Herramienta | Skills | Configuración MCP |
|
|
118
|
+
|---|---|---|
|
|
119
|
+
| Claude Code | `~/.claude/skills/<n>/` (carpeta) | `~/.claude.json` → `mcpServers` |
|
|
120
|
+
| Cursor | `~/.cursor/skills/<n>/` (carpeta) | `~/.cursor/mcp.json` → `mcpServers` |
|
|
121
|
+
| OpenCode | `~/.config/opencode/skills/<n>/` (carpeta) | `~/.config/opencode/opencode.json` → `mcp` |
|
|
122
|
+
| Antigravity | `~/.gemini/skills/<n>/` (carpeta) | `~/.gemini/config/mcp_config.json` → `mcpServers` |
|
|
123
|
+
| Codex | `~/.agents/skills/<n>/` (carpeta) | `~/.codex/config.toml` → `[mcp_servers.<n>]` |
|
|
124
|
+
|
|
125
|
+
### Acotar los MCPs a una carpeta (opcional)
|
|
126
|
+
|
|
127
|
+
Por defecto los MCPs se instalan a nivel usuario: quedan disponibles en toda sesión, de
|
|
128
|
+
cualquier carpeta. Si en la misma máquina separás contextos —trabajo de Alegra, proyectos
|
|
129
|
+
propios, trabajo de un cliente— podés acotarlos a una sola carpeta:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
mkdir -p ~/.alegra
|
|
133
|
+
echo 'MCP_DIR=~/trabajo/alegra' >> ~/.alegra/agent.conf
|
|
134
|
+
pnpm dlx alegra-data-agent install --yes
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Los MCPs pasan a `~/trabajo/alegra/.mcp.json`, que Claude Code resuelve subiendo desde el
|
|
138
|
+
directorio actual: **cascadea a todo lo que esté dentro de esa carpeta y es invisible fuera
|
|
139
|
+
de ella**. Se pre-aprueban solos, así que no hay que aceptarlos repo por repo.
|
|
140
|
+
|
|
141
|
+
También funciona como env var (`ALEGRA_DATA_AGENT_MCP_DIR`, le gana al archivo), pero el
|
|
142
|
+
archivo es lo recomendado: el hook de auto-update corre en un shell no interactivo y no ve
|
|
143
|
+
lo que exportes en tu `.bashrc`, así que reinstalaría a nivel usuario.
|
|
144
|
+
|
|
145
|
+
**Qué clientes lo respetan.** Claude Code y Cursor pueden acotar un MCP a una carpeta, así
|
|
146
|
+
que con `MCP_DIR` los MCPs van solo a esos dos. OpenCode, Hermes, Codex y Antigravity no
|
|
147
|
+
pueden: OpenCode busca su config de proyecto solo hasta la raíz del repo git, y Hermes guarda
|
|
148
|
+
los MCPs por perfil. Ahí los MCPs quedarían globales, así que no se instalan y se retiran los
|
|
149
|
+
que ya tuvieran. Las skills sí se instalan en todos. Para consultar datos de Alegra, usá
|
|
150
|
+
Claude Code o Cursor dentro de la carpeta.
|
|
151
|
+
|
|
152
|
+
**Se corrige solo.** Cada arranque compara dónde debería estar cada MCP con dónde está y
|
|
153
|
+
retira lo que sobra, siempre que sea nuestro: una copia en `~/.claude.json` de antes de
|
|
154
|
+
configurar `MCP_DIR`, la de una carpeta vieja si lo cambiaste o lo sacaste, o la de un cliente
|
|
155
|
+
que no corresponde. Un server tuyo con el mismo nombre y otra configuración no se toca.
|
|
156
|
+
`alegra-data-agent status` te dice en qué archivo quedaron y avisa si ve algo fuera de lugar.
|
|
157
|
+
|
|
158
|
+
Detalle completo en [ADR-0012](docs/adr/0012-mcp-scope-por-workspace.md).
|
|
159
|
+
|
|
160
|
+
### Dónde quedan los archivos que genera el agente
|
|
161
|
+
|
|
162
|
+
Cuando pedís un CSV, un Excel o un gráfico, el agente lo guarda en `alegra-data/` dentro de
|
|
163
|
+
la carpeta donde abriste la sesión, y te dice la ruta al terminar (en WSL, también la de
|
|
164
|
+
Windows). Nunca en los temporales del cliente. Para que vaya siempre a la misma carpeta:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
mkdir -p ~/.alegra
|
|
168
|
+
echo "OUTPUT_DIR=$HOME/Documentos/alegra-data" >> ~/.alegra/agent.conf
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Lo lee el agente, no el CLI, así que aplica desde la siguiente pregunta, sin reinstalar. En
|
|
172
|
+
claude.ai, Desktop y Cowork el archivo aparece para descargar en el chat.
|
|
173
|
+
|
|
174
|
+
El CLI:
|
|
175
|
+
|
|
176
|
+
1. Verifica auth contra el gateway (OAuth Google `hd=alegra.com`).
|
|
177
|
+
2. Descarga el manifest firmado Ed25519 y verifica firma antes de tocar disco.
|
|
178
|
+
3. Verifica SHA-256 por archivo antes de escribirlo.
|
|
179
|
+
4. Escribe atómicamente (tmp + rename) los configs JSON y respalda lo que ya existía (`*.bak-<ts>`).
|
|
180
|
+
5. Registra en `~/.config/alegra-data-agent/lock.json` qué quedó instalado por target.
|
|
181
|
+
6. Configura **auto-update en cada cliente instalado** (los 5, no solo Claude Code): Claude Code y Codex vía hook `SessionStart`, Cursor vía `workspaceOpen`, Antigravity vía `SessionStart` (Gemini), OpenCode vía plugin. Al abrir el cliente, el contenido se actualiza solo.
|
|
182
|
+
|
|
183
|
+
## Comandos
|
|
184
|
+
|
|
185
|
+
```sh
|
|
186
|
+
pnpm dlx alegra-data-agent # instala / actualiza todo (default)
|
|
187
|
+
pnpm dlx alegra-data-agent --target=claude-code,cursor
|
|
188
|
+
pnpm dlx alegra-data-agent --only=alegra-sales,alegra-growth
|
|
189
|
+
pnpm dlx alegra-data-agent --dry-run
|
|
190
|
+
|
|
191
|
+
pnpm dlx alegra-data-agent update # re-descarga lo que cambió
|
|
192
|
+
pnpm dlx alegra-data-agent status # estado actual + drift
|
|
193
|
+
pnpm dlx alegra-data-agent list # catálogo del gateway
|
|
194
|
+
pnpm dlx alegra-data-agent uninstall
|
|
195
|
+
|
|
196
|
+
pnpm dlx alegra-data-agent login
|
|
197
|
+
pnpm dlx alegra-data-agent logout
|
|
198
|
+
pnpm dlx alegra-data-agent whoami
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Ayuda completa: `pnpm dlx alegra-data-agent help`.
|
|
202
|
+
|
|
203
|
+
> El CLI siempre termina ejecutando la última versión publicada: si pnpm sirve una anterior (cooldown de 24h de pnpm 11), el comando detecta la nueva en el registry y se re-ejecuta solo con ella (self-bounce).
|
|
204
|
+
|
|
205
|
+
## Skills
|
|
206
|
+
|
|
207
|
+
16 skills. Cada equipo dueño mantiene la suya; BI Data mantiene la infraestructura y las skills horizontales.
|
|
208
|
+
|
|
209
|
+
| Skill | Dueño | Tipo |
|
|
210
|
+
|---|---|---|
|
|
211
|
+
| `alegra-bi` (router) | BI Data | Router |
|
|
212
|
+
| `alegra-strategic` | Strategic | Dominio |
|
|
213
|
+
| `alegra-product` | Producto | Dominio |
|
|
214
|
+
| `alegra-sales` | Sales | Dominio |
|
|
215
|
+
| `alegra-accountant` | Programa Contadores | Dominio |
|
|
216
|
+
| `alegra-cx` | Customer Experience | Dominio |
|
|
217
|
+
| `alegra-growth` | Growth | Dominio |
|
|
218
|
+
| `alegra-alanube` | Alanube | Dominio |
|
|
219
|
+
| `alegra-collection` | Collection | Dominio |
|
|
220
|
+
| `alegra-sr` | Strategic Relationships | Dominio |
|
|
221
|
+
| `alegra-board` | BI Data | Dominio |
|
|
222
|
+
| `dashboard-auditor` | BI Data | Horizontal (review) |
|
|
223
|
+
| `dataviz-reviewer` | BI Data | Horizontal (review) |
|
|
224
|
+
| `presentation-coach` | BI Data | Horizontal (coach) |
|
|
225
|
+
| `launch-measurement` | BI Data | Horizontal (proceso) |
|
|
226
|
+
| `agent-onboarding` | BI Data | Soporte |
|
|
227
|
+
|
|
228
|
+
Arquitectura: [`docs/skills-architecture.md`](./docs/skills-architecture.md). Contrato de ejecución (MBQL por defecto, SQL nativo de fallback, acceso MCP-first sin tokens): [`agent/skills/SKILL_STANDARD.md`](./agent/skills/SKILL_STANDARD.md) §3.1.
|
|
229
|
+
|
|
230
|
+
### Cómo responde el agente
|
|
231
|
+
|
|
232
|
+
Tres reglas transversales que heredan todas las skills de dominio (detalle en [`agent/skills/SKILL_STANDARD.md`](./agent/skills/SKILL_STANDARD.md)):
|
|
233
|
+
|
|
234
|
+
- **Pregunta la población, asume los defaults.** Si falta el período, el país o el segmento, elige el más útil y lo avisa en una línea. Si no está claro **qué se cuenta** — "nuevos usuarios" puede ser signups (se registraron) o new logos (empezaron a pagar), que difieren en un orden de magnitud — pregunta antes de consultar. El vocabulario de poblaciones vive en el router (`alegra-bi`, sección «Vocabulario»).
|
|
235
|
+
- **La respuesta dice de dónde sale el número.** `New logos (empezaron a pagar · fact_customers_mrr) | 71`, no `Nuevos | 71`. Una cifra sin procedencia no se puede auditar, y las mismas palabras nombran poblaciones distintas según la tabla.
|
|
236
|
+
- **Una pregunta = una consulta.** Dos si hace falta una semilla o un delta. MBQL por defecto; cuando MBQL no lo expresa (CTEs, subqueries, window functions) va SQL nativo por `execute_sql` en una llamada, en vez de partirlo en varias.
|
|
237
|
+
|
|
238
|
+
**Fuentes de verdad, cuando dos tablas dan números distintos:** `dm_strategic.fact_customers_mrr` manda para todo lo que sea quién paga, quién empezó y quién dejó de pagar. `dm_sales.fact_closed_deals` es su vista comercial (quién cerró, contra qué meta), conectada por `id_company`. Para eventos de uso de producto, `amplitude_events_enriched` en Metabase — no el MCP de Amplitude, no las columnas `*_m` de retención.
|
|
239
|
+
|
|
240
|
+
## MCPs
|
|
241
|
+
|
|
242
|
+
Los remotos van vía OAuth — el cliente abre el flow en el primer uso, **cero tokens manuales, cero env vars con secretos**. `salesforce` es la excepción: MCP stdio local del vendor, auth por la Salesforce CLI (`sf org login web --set-default`, requiere Node ≥ 22), solo lectura, para casos puntuales fuera del mirror de Metabase. No se autentica desde `/mcp` — soporte en el Caso 11 de `agent-onboarding`.
|
|
243
|
+
|
|
244
|
+
| MCP | Backend | Transporte | Auth |
|
|
245
|
+
|---|---|---|---|
|
|
246
|
+
| `amplitude` | Amplitude SaaS | remoto (http) | OAuth |
|
|
247
|
+
| `hubspot` | HubSpot SaaS | conector de cuenta | OAuth — **no se instala**: se habilita en Configuración → Conectores ([por qué](./agent/mcps/hubspot/README.md)) |
|
|
248
|
+
| `metabase` | Metabase self-hosted | remoto (http) | OAuth corporativo |
|
|
249
|
+
| `salesforce` | Salesforce DX (solo lectura) | stdio local | Salesforce CLI (`sf org login web`) — no por `/mcp` |
|
|
250
|
+
| `wiki-alegra` | Wiki self-hosted | remoto (http) | OAuth corporativo |
|
|
251
|
+
|
|
252
|
+
Llave universal para joins: `id_company`. Detalles en [`docs/joining-data-sources.md`](./docs/joining-data-sources.md).
|
|
253
|
+
|
|
254
|
+
## Para mantenedores
|
|
255
|
+
|
|
256
|
+
Skills viven en `agent/skills/<name>/SKILL.md` (+ anexos). MCPs en `agent/mcps/<name>/config.example.json`. El manifest firmado se regenera cuando esos paths cambian — **hoy en local**, porque Actions está apagado (ver «Release sin Actions» más abajo).
|
|
257
|
+
|
|
258
|
+
### Flujo de release
|
|
259
|
+
|
|
260
|
+
> Los pasos 1 y 2 describen el flujo **con CI**. Mientras Actions siga apagado, la validación y la firma van en local: ver «Release sin Actions» justo abajo.
|
|
261
|
+
|
|
262
|
+
1. PR `<rama>-changes` o `feature/* → testing` modificando `agent/skills/**` o `agent/mcps/**` (CI valida standards, scan y frescura del manifest).
|
|
263
|
+
2. PR `testing → main`: el workflow `sign-manifest.yml` se dispara en el PR (toca esos paths), regenera el manifest, lo escanea, lo firma con `ALEGRA_SIGNING_KEY` y commitea el manifest firmado de vuelta al branch del PR vía la REST API de GitHub (queda Verified, server-side). Al mergear, `main` ya tiene el manifest correcto.
|
|
264
|
+
3. Gateway en Vercel se redespliega manualmente con `pnpm run deploy` desde `gateway/` (corre `prepare:skills`, que verifica firma y sha256, y después `vercel --prod`; nunca `vercel --prod` a secas). La integración Vercel ↔ GitHub no está habilitada: el proyecto de Vercel vive en su propio team y no depende de la organización de GitHub.
|
|
265
|
+
4. Próximo `update` de cualquier alegrito trae los cambios — sin republicar el CLI a npm.
|
|
266
|
+
5. **Superficies no-code (claude.ai, Desktop, Cowork).** Los pasos anteriores no llegan ahí: esos
|
|
267
|
+
ZIP se suben a mano y no hay API para automatizarlo (ADR-0010). `pnpm run pack:web` lista
|
|
268
|
+
exactamente cuáles cambiaron respecto de lo que ya está en la organización. Se suben esos, y
|
|
269
|
+
después `pnpm run pack:web -- --mark-published <skills>` registra lo subido en
|
|
270
|
+
`agent/web-published.json`, que se commitea. Sin este paso, quien usa Desktop sigue con las
|
|
271
|
+
definiciones viejas sin saberlo.
|
|
272
|
+
|
|
273
|
+
### Release sin Actions
|
|
274
|
+
|
|
275
|
+
Con los workflows apagados, los dos pasos que hacía el CI se hacen en local. Requieren la clave
|
|
276
|
+
privada de firma (gestor del equipo) y el token de npm.
|
|
277
|
+
|
|
278
|
+
```sh
|
|
279
|
+
# 1. Validar, escanear, regenerar y FIRMAR el manifest
|
|
280
|
+
ALEGRA_SIGNING_KEY="$(cat /ruta/a/la/clave)" pnpm run manifest
|
|
281
|
+
|
|
282
|
+
# 2. Verificar que el manifest quedó firmado y válido antes de commitear
|
|
283
|
+
node --experimental-strip-types -e "import('./src/signature.ts').then(async m => {
|
|
284
|
+
const man = JSON.parse((await import('node:fs')).readFileSync('agent/skills/index.json','utf-8'));
|
|
285
|
+
const r = m.verifyManifestSignature(man);
|
|
286
|
+
console.log(r.ok ? 'firma válida — ' + man.signature.keyId : 'INVÁLIDA: ' + r.reason);
|
|
287
|
+
})"
|
|
288
|
+
|
|
289
|
+
# 3. Publicar a npm: DESPUÉS del merge a main, del deploy del gateway verificado en producción
|
|
290
|
+
# con una sesión real (manifest nuevo, firma válida, update instala) y del tag. Lección de la 0.5.0.
|
|
291
|
+
pnpm run prepublishOnly && cp .github/README-npm.md README.md && pnpm publish --access public --no-git-checks
|
|
292
|
+
git checkout README.md # devolver el README completo al repo
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
> `--no-git-checks` es obligatorio acá, no opcional: el swap del paso anterior deja el working tree
|
|
296
|
+
> "sucio" a propósito (es exactamente lo que hace `publish.yml` en `Swap to minimal README for npm
|
|
297
|
+
> tarball` + `--no-git-checks` en su paso `Publish`). Sin el flag, `pnpm publish` rechaza con
|
|
298
|
+
> `ERR_PNPM_GIT_UNCLEAN` — no es una falla real, es el propio swap documentado arriba.
|
|
299
|
+
|
|
300
|
+
El manifest firmado se commitea en el PR, en vez de que el bot lo commitee al branch. Todo lo
|
|
301
|
+
demás del flujo no cambia.
|
|
302
|
+
|
|
303
|
+
**Después de publicar, la wiki.** Copiar la entrada nueva del `CHANGELOG.md` **arriba** de la
|
|
304
|
+
página hija [Historial de cambios](https://wiki.alegra.com/doc/historial-de-cambios-J8uMHq1sfd) y
|
|
305
|
+
actualizar la línea **Estado** del home de la wiki (versión, skills, fecha). El home no lleva
|
|
306
|
+
historial: solo el enlace a esa página, para que no crezca con cada release. El CHANGELOG del repo
|
|
307
|
+
es la fuente; la wiki lo copia, nunca al revés.
|
|
308
|
+
|
|
309
|
+
### Publicación de nueva versión del CLI
|
|
310
|
+
|
|
311
|
+
1. Bump `version` en `package.json` y `gateway/package.json` + entrada en `CHANGELOG.md` + regenerar el manifest con **`pnpm run manifest`** (el pipeline completo: `validate` → `scan` → `generate` → `sign`; el `productVersion` sale de package.json). Todo dentro del mismo PR del release.
|
|
312
|
+
2. Tras el merge a main, tag sobre el merge commit: `git tag -s vX.Y.Z && git push origin vX.Y.Z`.
|
|
313
|
+
3. **Con Actions apagado este paso no ocurre:** el tag no publica nada y el publish se hace a mano (ver «Release sin Actions»). Cuando Actions vuelva, el workflow `publish.yml` valida que `package.json` coincida con el tag, corre los gates (validate → scan → tests) y publica: build → swap a `.github/README-npm.md` (README mínimo del tarball) → `pnpm publish --access public`. Requiere el secret `NPM_TOKEN` configurado.
|
|
314
|
+
|
|
315
|
+
> ⚠️ **Nunca corras `manifest:generate` solo.** Borra el bloque `signature` de
|
|
316
|
+
> `agent/skills/index.json`, y `verifyManifestSignature` es hard-fail: un manifest sin firma hace
|
|
317
|
+
> que el CLI rechace **toda** instalación y update, no solo la skill que tocaste. Siempre
|
|
318
|
+
> `pnpm run manifest`, que firma al final. `tests/security/manifest-committed.test.mjs` lo caza en
|
|
319
|
+
> local si se te pasa.
|
|
320
|
+
|
|
321
|
+
**Si el release sale mal:** mover el dist-tag `latest` hacia atrás **no revierte a nadie** — el
|
|
322
|
+
hook resuelve por rango semver y los tags no participan. El procedimiento real está en
|
|
323
|
+
[`docs/rollback.md`](./docs/rollback.md).
|
|
324
|
+
|
|
325
|
+
### Local
|
|
326
|
+
|
|
327
|
+
```sh
|
|
328
|
+
pnpm run manifest:scan # buscar prompt-injection / credenciales
|
|
329
|
+
pnpm run manifest:generate # regenera agent/skills/index.json
|
|
330
|
+
pnpm run manifest:sign # firma (requiere ALEGRA_SIGNING_KEY)
|
|
331
|
+
pnpm run manifest # los cuatro en orden (validate → scan → generate → sign)
|
|
332
|
+
|
|
333
|
+
pnpm install
|
|
334
|
+
pnpm typecheck
|
|
335
|
+
pnpm build # → dist/
|
|
336
|
+
|
|
337
|
+
pnpm test # Fase 1: SOLO valida el schema de los evals — no
|
|
338
|
+
# ejecuta ningún agente
|
|
339
|
+
pnpm test:security # la suite real: firma, paths, clientes, drift de MCPs
|
|
340
|
+
pnpm test:all # ambos
|
|
341
|
+
|
|
342
|
+
pnpm run eval:run -- --local # Fase 2: ejecuta los evals de verdad contra las skills
|
|
343
|
+
# del repo. Cuesta tokens y tarda horas (116 casos); para
|
|
344
|
+
# un caso puntual: --only <nombre-del-caso>
|
|
345
|
+
|
|
346
|
+
pnpm run verify:clients # E2E: instala en un HOME temporal con 5 de los 6 clientes
|
|
347
|
+
# simulados (Hermes aún no está cubierto) y verifica rutas, shapes de MCP, hooks
|
|
348
|
+
# e idempotencia (requiere sesión y red)
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
> **`test:all` en verde no dice que las skills respondan bien.** La Fase 1 solo lee los YAML;
|
|
352
|
+
> si tardó un segundo, no ejecutó nada. Medir routing, tablas y números es `eval:run`: corre cada
|
|
353
|
+
> caso aislado (sin el repo ni la memoria del dev a la vista) y consulta Metabase de verdad.
|
|
354
|
+
> Detalle en [`docs/evals.md`](./docs/evals.md).
|
|
355
|
+
|
|
356
|
+
El grueso de la verificación corre en local, no en CI: la organización está en plan Free y el
|
|
357
|
+
pool de minutos de Actions es compartido. `pre-commit install && pre-commit install --hook-type
|
|
358
|
+
pre-push` deja los mismos gates enganchados a los commits.
|
|
359
|
+
|
|
360
|
+
### Keypair de firma
|
|
361
|
+
|
|
362
|
+
```sh
|
|
363
|
+
pnpm run signing-key:generate # escribe la privada a un archivo 600; NO la imprime
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
La pública va en `TRUSTED_KEYS` (`src/signature.ts`); la privada, **en dos lugares**: el gestor
|
|
367
|
+
de contraseñas del equipo y el secret `ALEGRA_SIGNING_KEY` del repo. Un secret de GitHub no se
|
|
368
|
+
puede leer: si es la única copia, se pierde en cuanto haga falta usarla en otro lado — pasó con
|
|
369
|
+
la clave v1 y obligó a rotar.
|
|
370
|
+
|
|
371
|
+
El CLI acepta **varias claves a la vez**, que es lo que permite rotar sin ventana de rotura: con
|
|
372
|
+
una sola, el CLI nuevo rechaza el manifest viejo *y* el viejo rechaza el nuevo. Procedimiento
|
|
373
|
+
completo en [SECURITY.md](SECURITY.md#custodia-y-rotación-de-la-clave-de-firma).
|
|
374
|
+
|
|
375
|
+
## Estructura del repo
|
|
376
|
+
|
|
377
|
+
```
|
|
378
|
+
data-agent-selfservice/
|
|
379
|
+
├── src/ # CLI TypeScript (publicado a npm como dist/)
|
|
380
|
+
├── scripts/ # generate-manifest, sign-manifest, scan-skills, generate-signing-key, verify-clients
|
|
381
|
+
├── agent/
|
|
382
|
+
│ ├── skills/ # SKILL.md + anexos, agrupados por dueño
|
|
383
|
+
│ └── mcps/ # config.example.json por MCP
|
|
384
|
+
├── gateway/ # Next.js en Vercel — OAuth + manifest firmado + skills
|
|
385
|
+
├── docs/ # ADRs + guías
|
|
386
|
+
├── tests/ # evals + security tests
|
|
387
|
+
├── install.sh # bootstrap para macOS/Linux/WSL (servido por gateway)
|
|
388
|
+
└── install.ps1 # bootstrap para Windows (servido por gateway)
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
## Troubleshooting
|
|
392
|
+
|
|
393
|
+
| Síntoma | Probable causa | Fix |
|
|
394
|
+
|---|---|---|
|
|
395
|
+
| `pnpm: command not found` | pnpm no instalado | `corepack enable && corepack prepare pnpm@11.1.2 --activate` |
|
|
396
|
+
| `Node.js >= 22.6.0` requerido | Node viejo | `nvm install 22` o reinstala desde nodejs.org |
|
|
397
|
+
| `Solo cuentas @alegra.com` | OAuth con cuenta personal | Login con tu cuenta corporativa |
|
|
398
|
+
| El cliente no ve los MCPs | Cambio de config no recargado | Reinicia el cliente |
|
|
399
|
+
| `keyId no reconocido` | El CLI no conoce la clave con la que se firmó el manifest (rotación en curso y CLI viejo) | `pnpm dlx alegra-data-agent@latest`. Durante una rotación el CLI acepta la clave nueva y la anterior, así que esto solo aparece en versiones muy atrasadas |
|
|
400
|
+
| Una versión recién publicada no se instala / no actualiza | Cooldown de pnpm 11 (`minimumReleaseAge`, ~24h) sobre versiones de <24h | Desde 0.2.2 el CLI hace self-bounce: aunque pnpm sirva una versión anterior, el comando detecta la última en el registry y se re-ejecuta con ella solo. El self-update (hook) y el bootstrap además corren con `--config.minimumReleaseAge=0`. Para forzarlo a mano: `pnpm dlx alegra-data-agent@<versión>` (pin exacto) |
|
|
401
|
+
|
|
402
|
+
Si nada funciona: la skill `agent-onboarding` (instalada por defecto) tiene una guía paso a paso de troubleshooting que el modelo puede correr contigo.
|
|
403
|
+
|
|
404
|
+
## Branching
|
|
405
|
+
|
|
406
|
+
`<nombre>-changes → testing → main`. Cada quien trabaja en su rama personal (`jhon-changes`,
|
|
407
|
+
`luis-changes`…) y llega por PR: primero a `testing`, después a `main`. Nada entra directo a
|
|
408
|
+
`main`, y **el PR a `main` lo aprueba y mergea otra persona** — nadie mergea el suyo. Detalle en
|
|
409
|
+
[CONTRIBUTING.md](CONTRIBUTING.md).
|
|
410
|
+
|
|
411
|
+
Cambios a `agent/skills/**` o `agent/mcps/**` disparan el workflow de firma en el PR hacia `main`
|
|
412
|
+
(el manifest firmado se commitea al branch del PR antes del merge). Cambios a `scripts/**.mjs`
|
|
413
|
+
requieren review humano: no autoejecutan la firma, para que un PR a los scripts no pueda
|
|
414
|
+
exfiltrar la clave.
|
|
415
|
+
|
|
416
|
+
> En plan Free los repos privados no tienen rulesets, así que esto es **un acuerdo del equipo,
|
|
417
|
+
> no un candado técnico**. Lo mismo vale para los commits firmados. Ver
|
|
418
|
+
> [Cómo trabajamos en GitHub (Alegra-Team)](https://wiki.alegra.com/doc/como-trabajamos-en-github-alegra-team-2WuRXpYEmg).
|
|
419
|
+
|
|
420
|
+
## CI / Workflows
|
|
421
|
+
|
|
422
|
+
> ⚠️ **Hoy Actions está deshabilitado en este repo.** La organización agotó su presupuesto
|
|
423
|
+
> (`the job was not started because recent account payments have failed or your spending limit
|
|
424
|
+
> needs to be increased`), así que ningún workflow puede correr y dejarlos activos solo llenaría
|
|
425
|
+
> los PRs de checks rojos que no dicen nada del código. **Mientras dure, todo es local**: la
|
|
426
|
+
> validación con pre-commit/pre-push, y la firma del manifest y la publicación a npm a mano —
|
|
427
|
+
> ver «Release sin Actions» abajo. Para reactivarlos cuando haya presupuesto:
|
|
428
|
+
> `gh api -X PUT /repos/Alegra-Team/data-agent-selfservice/actions/permissions -F enabled=true`.
|
|
429
|
+
|
|
430
|
+
Cuatro workflows, y solo uno corre por PR. El pool de minutos de Actions es compartido entre
|
|
431
|
+
todos los repos de la organización (plan Free), así que en GitHub queda únicamente lo que no se
|
|
432
|
+
puede garantizar en la máquina del autor; el resto vive en `.pre-commit-config.yaml`.
|
|
433
|
+
|
|
434
|
+
| Workflow | Cuándo dispara | Qué hace |
|
|
435
|
+
|---|---|---|
|
|
436
|
+
| `pr.yml` | PRs a `testing` y a `main` (`paths-ignore` docs-only) | **Un solo job**: gates pnpm-only, standards de skills/MCPs (strict), scan de secretos y prompt injection, frescura del manifest, typecheck y tests. El typecheck del gateway solo si el PR toca `gateway/` |
|
|
437
|
+
| `sign-manifest.yml` | PRs a `main` que tocan `agent/skills/**` o `agent/mcps/**` | Regenera + escanea + firma el manifest y lo commitea al branch del PR vía REST API de GitHub, no con `git push`: así queda firmado server-side y Verified. Necesita el secret `ALEGRA_SIGNING_KEY` |
|
|
438
|
+
| `security.yml` | Schedule semanal + `workflow_dispatch` | Gitleaks + OSV + Trivy + pnpm audit. CodeQL solo bajo dispatch explícito (es el job más caro). No corre por push ni por PR |
|
|
439
|
+
| `publish.yml` | Tag `v*.*.*` | Valida versión == tag + gates + publica el CLI a npmjs.com. Necesita el secret `NPM_TOKEN` |
|
|
18
440
|
|
|
19
|
-
|
|
441
|
+
Por qué un solo job y no varios: GitHub factura por job con un mínimo de un minuto, así que
|
|
442
|
+
partir la validación en cuatro costaba cuatro minutos aunque dos de ellos fueran un `grep` de
|
|
443
|
+
tres segundos.
|
|
20
444
|
|
|
21
|
-
|
|
445
|
+
Dependabot apunta a `testing` como cualquier otro cambio, no directo a main).
|