fintecia 0.0.0-stage → 1.0.0
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 +134 -2
- package/dist/auth/firebaseConfig.d.ts +40 -0
- package/dist/auth/firebaseConfig.js +54 -0
- package/dist/auth/firebaseConfig.js.map +1 -0
- package/dist/auth/session.d.ts +20 -0
- package/dist/auth/session.js +25 -0
- package/dist/auth/session.js.map +1 -0
- package/dist/fiel/decode.d.ts +10 -0
- package/dist/fiel/decode.js +17 -0
- package/dist/fiel/decode.js.map +1 -0
- package/dist/fiel/fiel.d.ts +35 -0
- package/dist/fiel/fiel.js +39 -0
- package/dist/fiel/fiel.js.map +1 -0
- package/dist/fiel/store.d.ts +33 -0
- package/dist/fiel/store.js +61 -0
- package/dist/fiel/store.js.map +1 -0
- package/dist/fin/read.d.ts +19 -0
- package/dist/fin/read.js +54 -0
- package/dist/fin/read.js.map +1 -0
- package/dist/glossary.d.ts +17 -0
- package/dist/glossary.js +160 -0
- package/dist/glossary.js.map +1 -0
- package/dist/handlers.d.ts +38 -0
- package/dist/handlers.js +302 -0
- package/dist/handlers.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +121 -0
- package/dist/index.js.map +1 -0
- package/dist/keychain/keychain.d.ts +14 -0
- package/dist/keychain/keychain.js +150 -0
- package/dist/keychain/keychain.js.map +1 -0
- package/dist/onboard/cli.d.ts +1 -0
- package/dist/onboard/cli.js +13 -0
- package/dist/onboard/cli.js.map +1 -0
- package/dist/onboard/server.d.ts +8 -0
- package/dist/onboard/server.js +395 -0
- package/dist/onboard/server.js.map +1 -0
- package/dist/public/lookup.d.ts +35 -0
- package/dist/public/lookup.js +45 -0
- package/dist/public/lookup.js.map +1 -0
- package/dist/public/verify.d.ts +37 -0
- package/dist/public/verify.js +61 -0
- package/dist/public/verify.js.map +1 -0
- package/dist/sat/artifacts.d.ts +33 -0
- package/dist/sat/artifacts.js +69 -0
- package/dist/sat/artifacts.js.map +1 -0
- package/dist/sat/descarga.d.ts +44 -0
- package/dist/sat/descarga.js +108 -0
- package/dist/sat/descarga.js.map +1 -0
- package/dist/sat/fielProvider.d.ts +28 -0
- package/dist/sat/fielProvider.js +22 -0
- package/dist/sat/fielProvider.js.map +1 -0
- package/dist/sat/jobs.d.ts +47 -0
- package/dist/sat/jobs.js +40 -0
- package/dist/sat/jobs.js.map +1 -0
- package/dist/sat/keychainFielProvider.d.ts +17 -0
- package/dist/sat/keychainFielProvider.js +36 -0
- package/dist/sat/keychainFielProvider.js.map +1 -0
- package/dist/sat/parse.d.ts +33 -0
- package/dist/sat/parse.js +55 -0
- package/dist/sat/parse.js.map +1 -0
- package/dist/sat/upload.d.ts +42 -0
- package/dist/sat/upload.js +67 -0
- package/dist/sat/upload.js.map +1 -0
- package/dist/telemetry/client.d.ts +32 -0
- package/dist/telemetry/client.js +53 -0
- package/dist/telemetry/client.js.map +1 -0
- package/dist/telemetry/installId.d.ts +1 -0
- package/dist/telemetry/installId.js +36 -0
- package/dist/telemetry/installId.js.map +1 -0
- package/dist/tools.d.ts +24 -0
- package/dist/tools.js +189 -0
- package/dist/tools.js.map +1 -0
- package/package.json +64 -4
package/README.md
CHANGED
|
@@ -1,3 +1,135 @@
|
|
|
1
|
-
#
|
|
1
|
+
# fintecia
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/Fintecia/fintecia-mcp/actions/workflows/ci.yml)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://nodejs.org)
|
|
6
|
+
|
|
7
|
+
Servidor MCP **local** de [Fintecia](https://fintecia.com.mx/?utm_source=mcp&utm_medium=referral&utm_campaign=fintecia_mcp&utm_content=readme) para agentes de IA
|
|
8
|
+
(Claude, Cursor, ChatGPT, OpenCode y cualquier cliente MCP). Le da a tu agente acceso a
|
|
9
|
+
tus facturas (CFDI), conciliación bancaria, riesgo fiscal (69-B / art. 69) y
|
|
10
|
+
deducibilidad — descargando tus facturas directamente del SAT con tu **e-firma local**,
|
|
11
|
+
que **nunca sale de tu máquina**.
|
|
12
|
+
|
|
13
|
+
## Qué puede hacer tu agente
|
|
14
|
+
|
|
15
|
+
- **Descargar CFDIs del SAT** (emitidos / recibidos) por periodo, con tu e-firma local.
|
|
16
|
+
- **Consultar tus finanzas** ya calculadas por Fintecia (balances, impuestos, conciliación).
|
|
17
|
+
- **Verificar el riesgo fiscal** de cualquier RFC (listas 69-B y art. 69) y el estatus de una factura.
|
|
18
|
+
- **Evaluar la deducibilidad** de tus gastos, con la evidencia de cada veredicto.
|
|
19
|
+
|
|
20
|
+
## Estado del proyecto
|
|
21
|
+
|
|
22
|
+
Esta es una **primera versión (0.1.0)**. Lo disponible hoy:
|
|
23
|
+
|
|
24
|
+
- ✅ Descarga de CFDI del SAT con e-firma local + materialización en Fintecia.
|
|
25
|
+
- ✅ Consultas materializadas: `fintecia_financials` (estados NIF B-3/B-6),
|
|
26
|
+
`fintecia_tax_summary`, `fintecia_risk_report`/`_check`, `fintecia_deductibility`.
|
|
27
|
+
- ✅ `fintecia_reconciliation` (estado de conciliación banco↔CFDI, **solo lectura**).
|
|
28
|
+
|
|
29
|
+
**Pendiente (próximas versiones):**
|
|
30
|
+
|
|
31
|
+
- ⏳ **Carga del PDF de estado de cuenta bancario** para conciliar contra CFDI
|
|
32
|
+
(la reconciliación hoy solo reporta datos ya importados; no acepta el PDF todavía).
|
|
33
|
+
|
|
34
|
+
## Requisitos
|
|
35
|
+
|
|
36
|
+
- **Node.js 20 o superior.**
|
|
37
|
+
- **Cuenta de Fintecia** para las herramientas `fintecia_*` (login con Google).
|
|
38
|
+
- **e-firma** (`.cer` + `.key` + contraseña) solo si querés descargar CFDIs del SAT.
|
|
39
|
+
Sin e-firma, las consultas públicas y las de tu cuenta siguen funcionando.
|
|
40
|
+
|
|
41
|
+
## Instalación
|
|
42
|
+
|
|
43
|
+
### 1. Instalar el servidor
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm install -g fintecia
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Esto deja el binario **`fintecia-mcp`** en tu `PATH` (apuntando a `dist/index.js`,
|
|
50
|
+
definido en `package.json` `bin`). No hace falta clonar el repo ni compilar.
|
|
51
|
+
|
|
52
|
+
### 2. Registrar el servidor en tu cliente MCP
|
|
53
|
+
|
|
54
|
+
Agregá una entrada `fintecia` apuntando al binario `fintecia-mcp`. En **Claude
|
|
55
|
+
Desktop** (`claude_desktop_config.json`):
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"mcpServers": {
|
|
60
|
+
"fintecia": {
|
|
61
|
+
"command": "fintecia-mcp",
|
|
62
|
+
"args": []
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Cursor** usa el mismo formato en `.cursor/mcp.json`; otros clientes (OpenCode,
|
|
69
|
+
Windsurf, etc.) aceptan una estructura equivalente. Consultá la documentación de tu
|
|
70
|
+
cliente para la sintaxis exacta.
|
|
71
|
+
|
|
72
|
+
### 3. Primer uso: login + e-firma
|
|
73
|
+
|
|
74
|
+
1. Pedile a tu agente que ejecute la herramienta **`fintecia_login`**.
|
|
75
|
+
2. Abrí en tu navegador la URL local que devuelve.
|
|
76
|
+
3. Iniciá sesión con Google y cargá tu e-firma (`.cer` + `.key` + contraseña).
|
|
77
|
+
4. Listo: tu agente queda autenticado y vinculado a tu RFC.
|
|
78
|
+
|
|
79
|
+
## Herramientas
|
|
80
|
+
|
|
81
|
+
### Públicas del SAT (sin cuenta, sin e-firma)
|
|
82
|
+
|
|
83
|
+
| Herramienta | Qué hace |
|
|
84
|
+
|---|---|
|
|
85
|
+
| `sat_efos_check` | ¿Un RFC está en la lista 69-B (EFOS, operaciones presuntamente inexistentes)? |
|
|
86
|
+
| `sat_debts_check` | ¿Un RFC tiene créditos fiscales firmes (art. 69)? |
|
|
87
|
+
| `cfdi_verify` | Estatus de una factura (vigente / cancelado) + Validación EFOS. |
|
|
88
|
+
|
|
89
|
+
### Descarga del SAT (e-firma local)
|
|
90
|
+
|
|
91
|
+
| Herramienta | Qué hace |
|
|
92
|
+
|---|---|
|
|
93
|
+
| `fintecia_efirmas` | Lista las e-firmas registradas en esta máquina. |
|
|
94
|
+
| `cfdi_download` | Descarga CFDIs del SAT por periodo (async → devuelve un `jobId`). |
|
|
95
|
+
| `cfdi_download_status` | Consulta el estado de una descarga en curso. |
|
|
96
|
+
| `cfdi_sync_status` | Qué tan al día está tu información local. |
|
|
97
|
+
|
|
98
|
+
### Fintecia (requiere login)
|
|
99
|
+
|
|
100
|
+
| Herramienta | Qué hace |
|
|
101
|
+
|---|---|
|
|
102
|
+
| `fintecia_profile` | Tu usuario y los contribuyentes a los que estás vinculado. |
|
|
103
|
+
| `fintecia_financials` | Balances y razones materializados — usá esto para responder "cuánto". |
|
|
104
|
+
| `fintecia_tax_summary` | Resumen de ISR/IVA por periodo. |
|
|
105
|
+
| `fintecia_reconciliation` | Estado de la conciliación banco ↔ CFDI. |
|
|
106
|
+
| `fintecia_transactions` | Ingresos/gastos (dato crudo, solo para inspección). |
|
|
107
|
+
| `fintecia_cfdi` | Buscar / ver facturas (dato crudo, solo para inspección). |
|
|
108
|
+
| `fintecia_risk_report` | Cruce de tus contrapartes contra las listas 69-B / 69. |
|
|
109
|
+
| `fintecia_risk_check` | Ficha de riesgo de un proveedor (RFC puntual). |
|
|
110
|
+
| `fintecia_deductibility` | Veredicto de deducibilidad por gasto, con evidencia. |
|
|
111
|
+
|
|
112
|
+
> **El agente razona, Fintecia calcula.** Para responder "cuánto" usá siempre las
|
|
113
|
+
> herramientas materializadas (`fintecia_financials`, `fintecia_tax_summary`); las
|
|
114
|
+
> herramientas `_transactions` / `_cfdi` son solo para inspeccionar qué hay detrás de
|
|
115
|
+
> un número, nunca para re-sumarlo.
|
|
116
|
+
|
|
117
|
+
### Glosario de campos (estática, sin login)
|
|
118
|
+
|
|
119
|
+
| Herramienta | Qué hace |
|
|
120
|
+
|---|---|
|
|
121
|
+
| `fintecia_glosario` | Diccionario de campos de las tools `fintecia_*` (money objects, NIF B-3/B-6, impuestos vs ISR prepagado, IVA en el balance). Estática y sin auth — devuelve el glosario para interpretar `fintecia_financials`. |
|
|
122
|
+
|
|
123
|
+
## Seguridad y privacidad
|
|
124
|
+
|
|
125
|
+
- Tu **e-firma nunca sale de tu máquina**: se guarda en el keychain del sistema
|
|
126
|
+
(macOS Keychain / Windows Credential Manager / Linux libsecret). Fintecia solo
|
|
127
|
+
recibe los CFDI.
|
|
128
|
+
- El servidor es **local** (corre por stdio, en tu máquina) y **sin secretos**:
|
|
129
|
+
la autenticación es OAuth de *loopback* — tu navegador hace el login y el token
|
|
130
|
+
queda en memoria, no en disco.
|
|
131
|
+
- Todo acceso a tus datos pasa por tu sesión autenticada.
|
|
132
|
+
|
|
133
|
+
## Licencia
|
|
134
|
+
|
|
135
|
+
MIT
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firebase Web config (public). The MCP is a public client: these values are
|
|
3
|
+
* meant to be embedded (the Web API key is not a secret). Overridable via env
|
|
4
|
+
* for staging/local emulator.
|
|
5
|
+
*
|
|
6
|
+
* `FINTECIA_ENV` selects the backend environment. Unlike the Flutter app's
|
|
7
|
+
* `Env` (which defaults to "local" for dev), the MCP is distributed to end
|
|
8
|
+
* users, so it defaults to "production": when local, the onboarding page
|
|
9
|
+
* connects the Auth SDK to the Auth emulator at 127.0.0.1:9099 (same as
|
|
10
|
+
* `lib/main.dart` `useAuthEmulator`).
|
|
11
|
+
*/
|
|
12
|
+
export declare const FIREBASE_ENV: string;
|
|
13
|
+
export declare const isLocal: boolean;
|
|
14
|
+
export declare const AUTH_EMULATOR_URL: string;
|
|
15
|
+
/**
|
|
16
|
+
* Fintecia backend base URL (no trailing slash), env-aware.
|
|
17
|
+
*
|
|
18
|
+
* The MCP talks to the backend over plain `fetch` (NOT the Firebase Functions
|
|
19
|
+
* SDK), so it must know the full base URL. `FINTECIA_API_URL` wins when set;
|
|
20
|
+
* otherwise the URL is derived from `FINTECIA_ENV`:
|
|
21
|
+
* - local → Hosting emulator (http://127.0.0.1:5000). The client calls
|
|
22
|
+
* `/api/v1/*` paths, which are Hosting rewrites (firebase.json);
|
|
23
|
+
* the Functions emulator (5001) serves functions BY NAME, so it
|
|
24
|
+
* does NOT serve the `/api/v1/*` rewrite paths.
|
|
25
|
+
* - staging → fintecia-staging.web.app
|
|
26
|
+
* - production → app.fintecia.com.mx
|
|
27
|
+
*
|
|
28
|
+
* L22 bug: previously `FINTECIA_API_URL` was read raw with NO default, so a
|
|
29
|
+
* prod run failed with "uploadBaseUrl no configurado (definí FINTECIA_API_URL)".
|
|
30
|
+
*/
|
|
31
|
+
export declare function resolveApiBaseUrl(env: string, explicit?: string, localOverride?: string): string;
|
|
32
|
+
export declare const API_BASE_URL: string;
|
|
33
|
+
export declare const FIREBASE_WEB_CONFIG: {
|
|
34
|
+
apiKey: string;
|
|
35
|
+
authDomain: string;
|
|
36
|
+
projectId: string;
|
|
37
|
+
appId: string;
|
|
38
|
+
storageBucket: string;
|
|
39
|
+
messagingSenderId: string;
|
|
40
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firebase Web config (public). The MCP is a public client: these values are
|
|
3
|
+
* meant to be embedded (the Web API key is not a secret). Overridable via env
|
|
4
|
+
* for staging/local emulator.
|
|
5
|
+
*
|
|
6
|
+
* `FINTECIA_ENV` selects the backend environment. Unlike the Flutter app's
|
|
7
|
+
* `Env` (which defaults to "local" for dev), the MCP is distributed to end
|
|
8
|
+
* users, so it defaults to "production": when local, the onboarding page
|
|
9
|
+
* connects the Auth SDK to the Auth emulator at 127.0.0.1:9099 (same as
|
|
10
|
+
* `lib/main.dart` `useAuthEmulator`).
|
|
11
|
+
*/
|
|
12
|
+
export const FIREBASE_ENV = process.env.FINTECIA_ENV ?? 'production';
|
|
13
|
+
export const isLocal = FIREBASE_ENV === 'local';
|
|
14
|
+
export const AUTH_EMULATOR_URL = process.env.FIREBASE_AUTH_EMULATOR ?? 'http://127.0.0.1:9099';
|
|
15
|
+
/**
|
|
16
|
+
* Fintecia backend base URL (no trailing slash), env-aware.
|
|
17
|
+
*
|
|
18
|
+
* The MCP talks to the backend over plain `fetch` (NOT the Firebase Functions
|
|
19
|
+
* SDK), so it must know the full base URL. `FINTECIA_API_URL` wins when set;
|
|
20
|
+
* otherwise the URL is derived from `FINTECIA_ENV`:
|
|
21
|
+
* - local → Hosting emulator (http://127.0.0.1:5000). The client calls
|
|
22
|
+
* `/api/v1/*` paths, which are Hosting rewrites (firebase.json);
|
|
23
|
+
* the Functions emulator (5001) serves functions BY NAME, so it
|
|
24
|
+
* does NOT serve the `/api/v1/*` rewrite paths.
|
|
25
|
+
* - staging → fintecia-staging.web.app
|
|
26
|
+
* - production → app.fintecia.com.mx
|
|
27
|
+
*
|
|
28
|
+
* L22 bug: previously `FINTECIA_API_URL` was read raw with NO default, so a
|
|
29
|
+
* prod run failed with "uploadBaseUrl no configurado (definí FINTECIA_API_URL)".
|
|
30
|
+
*/
|
|
31
|
+
export function resolveApiBaseUrl(env, explicit, localOverride) {
|
|
32
|
+
const trimmed = explicit?.trim();
|
|
33
|
+
if (trimmed)
|
|
34
|
+
return trimmed.replace(/\/+$/, '');
|
|
35
|
+
switch (env) {
|
|
36
|
+
case 'local':
|
|
37
|
+
return (localOverride?.trim() ||
|
|
38
|
+
'http://127.0.0.1:5000');
|
|
39
|
+
case 'staging':
|
|
40
|
+
return 'https://fintecia-staging.web.app';
|
|
41
|
+
default: // production and any unknown value → prod (fail safe to the real API)
|
|
42
|
+
return 'https://app.fintecia.com.mx';
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export const API_BASE_URL = resolveApiBaseUrl(FIREBASE_ENV, process.env.FINTECIA_API_URL, process.env.FINTECIA_LOCAL_API_URL);
|
|
46
|
+
export const FIREBASE_WEB_CONFIG = {
|
|
47
|
+
apiKey: process.env.FIREBASE_API_KEY ?? 'AIzaSyAt1DR0jI8h9GqaPo8kaSwVNL_4Hb3IhL8',
|
|
48
|
+
authDomain: process.env.FIREBASE_AUTH_DOMAIN ?? 'app.fintecia.com.mx',
|
|
49
|
+
projectId: process.env.FIREBASE_PROJECT_ID ?? 'fintecia',
|
|
50
|
+
appId: process.env.FIREBASE_APP_ID ?? '1:608545925207:web:68758d816a6085dce39b12',
|
|
51
|
+
storageBucket: process.env.FIREBASE_STORAGE_BUCKET ?? 'fintecia.firebasestorage.app',
|
|
52
|
+
messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID ?? '608545925207',
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=firebaseConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"firebaseConfig.js","sourceRoot":"","sources":["../../src/auth/firebaseConfig.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,YAAY,CAAC;AACrE,MAAM,CAAC,MAAM,OAAO,GAAG,YAAY,KAAK,OAAO,CAAC;AAChD,MAAM,CAAC,MAAM,iBAAiB,GAC5B,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,uBAAuB,CAAC;AAEhE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,iBAAiB,CAC/B,GAAW,EACX,QAAiB,EACjB,aAAsB;IAEtB,MAAM,OAAO,GAAG,QAAQ,EAAE,IAAI,EAAE,CAAC;IACjC,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAChD,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,OAAO;YACV,OAAO,CACL,aAAa,EAAE,IAAI,EAAE;gBACrB,uBAAuB,CACxB,CAAC;QACJ,KAAK,SAAS;YACZ,OAAO,kCAAkC,CAAC;QAC5C,SAAS,sEAAsE;YAC7E,OAAO,6BAA6B,CAAC;IACzC,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,iBAAiB,CAC3C,YAAY,EACZ,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAC5B,OAAO,CAAC,GAAG,CAAC,sBAAsB,CACnC,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,yCAAyC;IACjF,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,qBAAqB;IACrE,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,UAAU;IACxD,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,2CAA2C;IACjF,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,8BAA8B;IACpF,iBAAiB,EAAE,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,cAAc;CAC9E,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth session (loopback OAuth result). The local web page performs the
|
|
3
|
+
* Firebase sign-in (Google + MFA) and POSTs the tokens to the local server,
|
|
4
|
+
* which stores them here for the process lifetime.
|
|
5
|
+
*
|
|
6
|
+
* In-memory only for now: the MCP is a stdio process that lives while the
|
|
7
|
+
* agent talks to it; the ID token is refreshed via the refresh token. Disk
|
|
8
|
+
* persistence of the refresh token (surviving MCP restarts) is a follow-up.
|
|
9
|
+
*/
|
|
10
|
+
export interface AuthSession {
|
|
11
|
+
idToken: string;
|
|
12
|
+
refreshToken: string;
|
|
13
|
+
}
|
|
14
|
+
export declare class SessionStore {
|
|
15
|
+
private session;
|
|
16
|
+
set(session: AuthSession): void;
|
|
17
|
+
get(): AuthSession | null;
|
|
18
|
+
clear(): void;
|
|
19
|
+
get idToken(): string | null;
|
|
20
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth session (loopback OAuth result). The local web page performs the
|
|
3
|
+
* Firebase sign-in (Google + MFA) and POSTs the tokens to the local server,
|
|
4
|
+
* which stores them here for the process lifetime.
|
|
5
|
+
*
|
|
6
|
+
* In-memory only for now: the MCP is a stdio process that lives while the
|
|
7
|
+
* agent talks to it; the ID token is refreshed via the refresh token. Disk
|
|
8
|
+
* persistence of the refresh token (surviving MCP restarts) is a follow-up.
|
|
9
|
+
*/
|
|
10
|
+
export class SessionStore {
|
|
11
|
+
session = null;
|
|
12
|
+
set(session) {
|
|
13
|
+
this.session = session;
|
|
14
|
+
}
|
|
15
|
+
get() {
|
|
16
|
+
return this.session;
|
|
17
|
+
}
|
|
18
|
+
clear() {
|
|
19
|
+
this.session = null;
|
|
20
|
+
}
|
|
21
|
+
get idToken() {
|
|
22
|
+
return this.session?.idToken ?? null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/auth/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,MAAM,OAAO,YAAY;IACf,OAAO,GAAuB,IAAI,CAAC;IAE3C,GAAG,CAAC,OAAoB;QACtB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,GAAG;QACD,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACtB,CAAC;IAED,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,OAAO,EAAE,OAAO,IAAI,IAAI,CAAC;IACvC,CAAC;CACF"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decode a base64 file (sent by the browser) into the string format nodecfdi's
|
|
3
|
+
* `Fiel.create` expects. The browser reads the .cer/.key as base64 (binary-safe
|
|
4
|
+
* — `readAsDataURL`), unlike `readAsText` which corrupts DER binaries.
|
|
5
|
+
*
|
|
6
|
+
* - PEM → the UTF-8 text as-is.
|
|
7
|
+
* - DER cert → base64 (matches Fintecia's `cerContents` in ws-auth.ts).
|
|
8
|
+
* - DER key → latin1/binary string (matches `keyBinary` in ws-auth.ts).
|
|
9
|
+
*/
|
|
10
|
+
export declare function decodeFileForFiel(b64: string, kind: 'cert' | 'key'): string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decode a base64 file (sent by the browser) into the string format nodecfdi's
|
|
3
|
+
* `Fiel.create` expects. The browser reads the .cer/.key as base64 (binary-safe
|
|
4
|
+
* — `readAsDataURL`), unlike `readAsText` which corrupts DER binaries.
|
|
5
|
+
*
|
|
6
|
+
* - PEM → the UTF-8 text as-is.
|
|
7
|
+
* - DER cert → base64 (matches Fintecia's `cerContents` in ws-auth.ts).
|
|
8
|
+
* - DER key → latin1/binary string (matches `keyBinary` in ws-auth.ts).
|
|
9
|
+
*/
|
|
10
|
+
export function decodeFileForFiel(b64, kind) {
|
|
11
|
+
const buf = Buffer.from(b64, 'base64');
|
|
12
|
+
const text = buf.toString('utf8');
|
|
13
|
+
if (text.includes('-----BEGIN'))
|
|
14
|
+
return text;
|
|
15
|
+
return kind === 'cert' ? buf.toString('base64') : buf.toString('latin1');
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=decode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decode.js","sourceRoot":"","sources":["../../src/fiel/decode.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAW,EAAE,IAAoB;IACjE,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7C,OAAO,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAC3E,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Fiel } from "@nodecfdi/sat-ws-descarga-masiva";
|
|
2
|
+
/**
|
|
3
|
+
* FIEL inspection — thin wrapper over nodecfdi's `Fiel` (the same library the
|
|
4
|
+
* Fintecia backend uses in `functions/src/sat/ws-auth.ts`).
|
|
5
|
+
*
|
|
6
|
+
* The RFC lives in the X.509 `x500UniqueIdentifier` (OID 2.5.4.45), NOT the CN.
|
|
7
|
+
* nodecfdi's `Fiel.create(...).getRfc()` extracts it consistently with Fintecia,
|
|
8
|
+
* so we do NOT re-implement certificate parsing here.
|
|
9
|
+
*
|
|
10
|
+
* The private key (`keyPem`) and password are never logged, never persisted
|
|
11
|
+
* (the password is re-entered per signing session), and never leave the machine.
|
|
12
|
+
*/
|
|
13
|
+
export interface FielInfo {
|
|
14
|
+
rfc: string;
|
|
15
|
+
serial: string;
|
|
16
|
+
pem: string;
|
|
17
|
+
/** Legal name (razón social / nombre) from the certificate's CN, if present. */
|
|
18
|
+
razonSocial: string | null;
|
|
19
|
+
isValid: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface InspectedFiel {
|
|
22
|
+
fiel: Fiel;
|
|
23
|
+
info: FielInfo;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Build a nodecfdi Fiel from cert + key + passPhrase and extract its public
|
|
27
|
+
* identity (RFC, serial). Throws when the FIEL is invalid or expired.
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* Extract the holder's legal name (razón social / nombre) from the subject's
|
|
31
|
+
* CN. The RFC lives in `x500UniqueIdentifier` (OID 2.5.4.45), NOT the CN —
|
|
32
|
+
* the CN carries the name. Returns null when the CN is absent.
|
|
33
|
+
*/
|
|
34
|
+
export declare function extractRazonSocial(certPem: string): string | null;
|
|
35
|
+
export declare function inspectFiel(certPem: string, keyPem: string, passPhrase: string): InspectedFiel;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { X509Certificate } from "node:crypto";
|
|
2
|
+
import { Fiel } from "@nodecfdi/sat-ws-descarga-masiva";
|
|
3
|
+
/**
|
|
4
|
+
* Build a nodecfdi Fiel from cert + key + passPhrase and extract its public
|
|
5
|
+
* identity (RFC, serial). Throws when the FIEL is invalid or expired.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Extract the holder's legal name (razón social / nombre) from the subject's
|
|
9
|
+
* CN. The RFC lives in `x500UniqueIdentifier` (OID 2.5.4.45), NOT the CN —
|
|
10
|
+
* the CN carries the name. Returns null when the CN is absent.
|
|
11
|
+
*/
|
|
12
|
+
export function extractRazonSocial(certPem) {
|
|
13
|
+
try {
|
|
14
|
+
const subject = new X509Certificate(certPem).subject;
|
|
15
|
+
const m = subject.match(/^CN=([^\n]+)/m);
|
|
16
|
+
return m ? m[1].trim() : null;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function inspectFiel(certPem, keyPem, passPhrase) {
|
|
23
|
+
const fiel = Fiel.create(certPem, keyPem, passPhrase);
|
|
24
|
+
if (!fiel.isValid()) {
|
|
25
|
+
throw new Error("FIEL inválida o vencida");
|
|
26
|
+
}
|
|
27
|
+
const pem = fiel.getCertificatePemContents();
|
|
28
|
+
return {
|
|
29
|
+
fiel,
|
|
30
|
+
info: {
|
|
31
|
+
rfc: fiel.getRfc(),
|
|
32
|
+
serial: fiel.getCertificateSerial(),
|
|
33
|
+
pem,
|
|
34
|
+
razonSocial: extractRazonSocial(pem),
|
|
35
|
+
isValid: true,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=fiel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fiel.js","sourceRoot":"","sources":["../../src/fiel/fiel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,kCAAkC,CAAC;AA4BxD;;;GAGG;AACH;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,eAAe,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC;QACrD,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QACzC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,OAAe,EACf,MAAc,EACd,UAAkB;IAElB,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;IAEtD,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,CAAC,yBAAyB,EAAE,CAAC;IAE7C,OAAO;QACL,IAAI;QACJ,IAAI,EAAE;YACJ,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE;YAClB,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE;YACnC,GAAG;YACH,WAAW,EAAE,kBAAkB,CAAC,GAAG,CAAC;YACpC,OAAO,EAAE,IAAI;SACd;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FIEL storage — multi-taxpayer, keyed by RFC.
|
|
3
|
+
*
|
|
4
|
+
* Security model (S2): the .key is the private material and NEVER leaves the
|
|
5
|
+
* machine nor is logged. The stored `.key` PEM is already password-encrypted
|
|
6
|
+
* with the user's FIEL password; the password itself is held ONLY in the OS
|
|
7
|
+
* keychain (never in the file store, never logged, never uploaded). The .cer
|
|
8
|
+
* is public.
|
|
9
|
+
*
|
|
10
|
+
* FileFielStore is the cross-platform base and the Linux fallback. The OS
|
|
11
|
+
* keychain (macOS Keychain / Windows DPAPI / Linux libsecret) is a follow-up
|
|
12
|
+
* refinement behind the same FielStore port (T1b).
|
|
13
|
+
*/
|
|
14
|
+
export interface FielCredentials {
|
|
15
|
+
rfc: string;
|
|
16
|
+
certPem: string;
|
|
17
|
+
keyPem: string;
|
|
18
|
+
}
|
|
19
|
+
export interface FielStore {
|
|
20
|
+
store(fiel: FielCredentials): Promise<void>;
|
|
21
|
+
load(rfc: string): Promise<FielCredentials | null>;
|
|
22
|
+
list(): Promise<string[]>;
|
|
23
|
+
remove(rfc: string): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
export declare class FileFielStore implements FielStore {
|
|
26
|
+
private readonly baseDir;
|
|
27
|
+
constructor(baseDir?: string);
|
|
28
|
+
private dirFor;
|
|
29
|
+
store(fiel: FielCredentials): Promise<void>;
|
|
30
|
+
load(rfc: string): Promise<FielCredentials | null>;
|
|
31
|
+
list(): Promise<string[]>;
|
|
32
|
+
remove(rfc: string): Promise<void>;
|
|
33
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
const CERT_FILE = "certificate.cer";
|
|
5
|
+
const KEY_FILE = "private.key";
|
|
6
|
+
function sanitizeRfc(rfc) {
|
|
7
|
+
// RFC is alphanumeric; defensive guard against path traversal.
|
|
8
|
+
if (!/^[A-ZÑ&0-9]{12,13}$/.test(rfc)) {
|
|
9
|
+
throw new Error(`invalid RFC for storage: ${rfc}`);
|
|
10
|
+
}
|
|
11
|
+
return rfc;
|
|
12
|
+
}
|
|
13
|
+
export class FileFielStore {
|
|
14
|
+
baseDir;
|
|
15
|
+
constructor(baseDir = defaultBaseDir()) {
|
|
16
|
+
this.baseDir = baseDir;
|
|
17
|
+
}
|
|
18
|
+
dirFor(rfc) {
|
|
19
|
+
return path.join(this.baseDir, sanitizeRfc(rfc));
|
|
20
|
+
}
|
|
21
|
+
async store(fiel) {
|
|
22
|
+
const dir = this.dirFor(fiel.rfc);
|
|
23
|
+
await fs.mkdir(dir, { recursive: true });
|
|
24
|
+
// 0600: owner-only — the .key must not be world-readable.
|
|
25
|
+
await fs.writeFile(path.join(dir, CERT_FILE), fiel.certPem, { mode: 0o600 });
|
|
26
|
+
await fs.writeFile(path.join(dir, KEY_FILE), fiel.keyPem, { mode: 0o600 });
|
|
27
|
+
}
|
|
28
|
+
async load(rfc) {
|
|
29
|
+
const dir = this.dirFor(rfc);
|
|
30
|
+
try {
|
|
31
|
+
const [certPem, keyPem] = await Promise.all([
|
|
32
|
+
fs.readFile(path.join(dir, CERT_FILE), "utf8"),
|
|
33
|
+
fs.readFile(path.join(dir, KEY_FILE), "utf8"),
|
|
34
|
+
]);
|
|
35
|
+
return { rfc, certPem, keyPem };
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async list() {
|
|
42
|
+
try {
|
|
43
|
+
const entries = await fs.readdir(this.baseDir, { withFileTypes: true });
|
|
44
|
+
return entries
|
|
45
|
+
.filter((e) => e.isDirectory())
|
|
46
|
+
.map((e) => e.name)
|
|
47
|
+
.filter((name) => /^[A-ZÑ&0-9]{12,13}$/.test(name))
|
|
48
|
+
.sort();
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async remove(rfc) {
|
|
55
|
+
await fs.rm(this.dirFor(rfc), { recursive: true, force: true });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function defaultBaseDir() {
|
|
59
|
+
return path.join(os.homedir(), ".fintecia", "fiel");
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/fiel/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read client for the Fintecia MCP read endpoints. The MCP queries the
|
|
3
|
+
* backend for MATERIALIZED data (the backend computes; the client reads).
|
|
4
|
+
* Each call is `POST /api/v1/mcp/<resource>` with `{ taxpayerRfc, ... }` and
|
|
5
|
+
* a Bearer ID token from the loopback session.
|
|
6
|
+
*/
|
|
7
|
+
export interface ReadClientConfig {
|
|
8
|
+
baseUrl: string;
|
|
9
|
+
getAuthToken: () => Promise<string | null>;
|
|
10
|
+
fetchFn?: typeof fetch;
|
|
11
|
+
}
|
|
12
|
+
export declare class ReadClient {
|
|
13
|
+
private readonly config;
|
|
14
|
+
constructor(config: ReadClientConfig);
|
|
15
|
+
private fetch;
|
|
16
|
+
read(path: string, taxpayerRfc: string, extra?: Record<string, unknown>): Promise<unknown>;
|
|
17
|
+
/** Read an endpoint that needs NO taxpayerRfc (e.g. profile, by uid). */
|
|
18
|
+
readRaw(path: string): Promise<unknown>;
|
|
19
|
+
}
|
package/dist/fin/read.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read client for the Fintecia MCP read endpoints. The MCP queries the
|
|
3
|
+
* backend for MATERIALIZED data (the backend computes; the client reads).
|
|
4
|
+
* Each call is `POST /api/v1/mcp/<resource>` with `{ taxpayerRfc, ... }` and
|
|
5
|
+
* a Bearer ID token from the loopback session.
|
|
6
|
+
*/
|
|
7
|
+
export class ReadClient {
|
|
8
|
+
config;
|
|
9
|
+
constructor(config) {
|
|
10
|
+
this.config = config;
|
|
11
|
+
}
|
|
12
|
+
fetch(input, init) {
|
|
13
|
+
const fn = this.config.fetchFn ?? fetch;
|
|
14
|
+
return fn(input, init);
|
|
15
|
+
}
|
|
16
|
+
async read(path, taxpayerRfc, extra = {}) {
|
|
17
|
+
const idToken = await this.config.getAuthToken();
|
|
18
|
+
if (!idToken) {
|
|
19
|
+
throw new Error("no autenticado (fintecia_login)");
|
|
20
|
+
}
|
|
21
|
+
const res = await this.fetch(`${this.config.baseUrl}${path}`, {
|
|
22
|
+
method: "POST",
|
|
23
|
+
headers: {
|
|
24
|
+
"content-type": "application/json",
|
|
25
|
+
authorization: `Bearer ${idToken}`,
|
|
26
|
+
},
|
|
27
|
+
body: JSON.stringify({ taxpayerRfc, ...extra }),
|
|
28
|
+
});
|
|
29
|
+
if (!res.ok) {
|
|
30
|
+
throw new Error(`read ${path} failed: ${res.status}`);
|
|
31
|
+
}
|
|
32
|
+
return res.json();
|
|
33
|
+
}
|
|
34
|
+
/** Read an endpoint that needs NO taxpayerRfc (e.g. profile, by uid). */
|
|
35
|
+
async readRaw(path) {
|
|
36
|
+
const idToken = await this.config.getAuthToken();
|
|
37
|
+
if (!idToken) {
|
|
38
|
+
throw new Error("no autenticado (fintecia_login)");
|
|
39
|
+
}
|
|
40
|
+
const res = await this.fetch(`${this.config.baseUrl}${path}`, {
|
|
41
|
+
method: "POST",
|
|
42
|
+
headers: {
|
|
43
|
+
"content-type": "application/json",
|
|
44
|
+
authorization: `Bearer ${idToken}`,
|
|
45
|
+
},
|
|
46
|
+
body: JSON.stringify({}),
|
|
47
|
+
});
|
|
48
|
+
if (!res.ok) {
|
|
49
|
+
throw new Error(`read ${path} failed: ${res.status}`);
|
|
50
|
+
}
|
|
51
|
+
return res.json();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=read.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/fin/read.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,MAAM,OAAO,UAAU;IACQ;IAA7B,YAA6B,MAAwB;QAAxB,WAAM,GAAN,MAAM,CAAkB;IAAG,CAAC;IAEjD,KAAK,CAAC,KAAa,EAAE,IAAkB;QAC7C,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,KAAK,CAAC;QACxC,OAAO,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,IAAI,CACR,IAAY,EACZ,WAAmB,EACnB,QAAiC,EAAE;QAEnC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;QACjD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACrD,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,GAAG,IAAI,EAAE,EAAE;YAC5D,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,UAAU,OAAO,EAAE;aACnC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,WAAW,EAAE,GAAG,KAAK,EAAE,CAAC;SAChD,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,QAAQ,IAAI,YAAY,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QACxD,CAAC;QACD,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;IACpB,CAAC;IAED,yEAAyE;IACzE,KAAK,CAAC,OAAO,CAAC,IAAY;QACxB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;QACjD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACrD,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,GAAG,IAAI,EAAE,EAAE;YAC5D,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,UAAU,OAAO,EAAE;aACnC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;SACzB,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,QAAQ,IAAI,YAAY,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QACxD,CAAC;QACD,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;IACpB,CAAC;CACF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Field glossary for the Fintecia MCP tools.
|
|
3
|
+
*
|
|
4
|
+
* The agent cannot reliably deduce accounting-semantics fields (taxes vs
|
|
5
|
+
* prepaid ISR, IVA acreditable vs trasladado, preTaxIncome vs netIncome),
|
|
6
|
+
* so this dictionary documents them. It is served by the static
|
|
7
|
+
* `fintecia_glosario` tool — no auth, no backend round-trip — so the agent
|
|
8
|
+
* can load it during onboarding, in parallel with login + e-firma.
|
|
9
|
+
*/
|
|
10
|
+
export interface GlossaryEntry {
|
|
11
|
+
/** "summary" | "incomeStatement" | "balanceSheet" | "principio" */
|
|
12
|
+
section: string;
|
|
13
|
+
key: string;
|
|
14
|
+
/** Short meaning in the domain language (neutral Spanish). */
|
|
15
|
+
meaning: string;
|
|
16
|
+
}
|
|
17
|
+
export declare const FINANCIALS_GLOSSARY: GlossaryEntry[];
|