@ntlinkmx/nt 0.2.1 → 0.2.2

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 CHANGED
@@ -1,461 +1,473 @@
1
- # nt · CLI del backend Go
2
-
3
- ## Responsabilidad
4
-
5
- Cliente Node/TypeScript dentro de este monorepo para comunicarse por HTTP con el
6
- backend Go de NTLink IA Invoice. Ejecutable **`nt`**, compatible con Windows y
7
- Linux, Node **22.12 o posterior** y npm. No necesita React abierto ni acceso directo
8
- a SQL, certificados, Azure o claves de IA: esas dependencias las gestiona Go.
9
-
10
-
11
- ## Contrato
12
-
13
- ### Instalar y comenzar
14
-
15
- Instala el paquete publicado y úsalo en PowerShell o Bash:
16
-
17
- ```sh
18
- npm install --global @ntlinkmx/nt
19
- nt --version
20
- nt --help
21
- nt mode
22
- nt mode production
23
- nt client info
24
- nt client balance
25
- ```
26
-
27
- `npm ci` compila automáticamente; después de cambiar fuentes usa `npm run build`.
28
- El paquete se publicará como `@ntlinkmx/nt`; instala o actualiza con `npm install --global @ntlinkmx/nt`.
29
- Para preparar el primer lanzamiento, desde la cuenta npm que será dueña del
30
- paquete ejecuta `npm login` y `npm publish` dentro de `cli/`. Es un paso inicial:
31
- npm requiere que el paquete exista antes de activar su trusted publisher.
32
- Después, en npmjs.com abre `Packages` → `@ntlinkmx/nt` → `Settings` →
33
- `Trusted Publisher`; elige GitHub Actions y configura
34
- organización/usuario `NTlink`, repositorio `ntlink-ia-invoice`, workflow
35
- `publish-invoice-cli.yml` y permite `npm publish`. Para las versiones siguientes,
36
- cambia la versión del paquete y ejecuta manualmente el workflow desde la rama
37
- `main` en la pestaña Actions del repositorio. OIDC evita guardar un `NPM_TOKEN`.
38
- El paquete pertenece al scope `@ntlinkmx`.
39
- El workflow tiene que estar primero en `main` para poder seleccionarlo en npm.
40
- Para instalar desde un archivo: ejecuta `npm pack` y luego
41
- `npm install --global ./ntlinkmx-nt-0.2.0.tgz` desde `cli/`.
42
- Para retirar el comando global: `npm uninstall --global @ntlinkmx/nt`.
43
- El perfil predeterminado es `pruebas` y apunta al servidor remoto de pruebas. Las instalaciones existentes conservan su configuración guardada.
44
- El arranque de Go se describe en [desarrollo local](../doc/guides/LOCAL-DEVELOPMENT.md).
45
-
1
+ # nt · CLI del backend Go
2
+
3
+ ## Responsabilidad
4
+
5
+ Cliente Node/TypeScript dentro de este monorepo para comunicarse por HTTP con el
6
+ backend Go de NTLink IA Invoice. Ejecutable **`nt`**, compatible con Windows y
7
+ Linux, Node **22.12 o posterior** y npm. No necesita React abierto ni acceso directo
8
+ a SQL, certificados, Azure o claves de IA: esas dependencias las gestiona Go.
9
+
10
+
11
+ ## Contrato
12
+
13
+ ### Instalar y comenzar
14
+
15
+ Instala el paquete publicado y úsalo en PowerShell o Bash:
16
+
17
+ ```sh
18
+ npm install --global @ntlinkmx/nt
19
+ nt --version
20
+ nt --help
21
+ nt mode
22
+ nt mode production
23
+ nt client info
24
+ nt client balance
25
+ ```
26
+
27
+ `npm ci` compila automáticamente; después de cambiar fuentes usa `npm run build`.
28
+ El paquete se publicará como `@ntlinkmx/nt`; instala o actualiza con `npm install --global @ntlinkmx/nt`.
29
+ Configura una vez Trusted Publisher en npmjs.com: `Packages` -> `@ntlinkmx/nt` ->
30
+ `Settings` -> `Trusted Publisher`; proveedor GitHub Actions, organizaci?n/usuario
31
+ `NTlink`, repositorio `ntlink-ia-invoice`, workflow
32
+ `publish-invoice-cli.yml`, con permiso `npm publish`. El paquete ya existe, as?
33
+ que no hace falta un lanzamiento manual con token. Despu?s ejecuta una vez el
34
+ workflow `Publish Invoice CLI to npm` desde `main` para publicar la versi?n
35
+ pendiente. Para las siguientes versiones, actualiza `version` en `package.json`,
36
+ integra el cambio en `main` y crea/pushea el tag `cli-vX.Y.Z` correspondiente;
37
+ GitHub ejecuta verificaciones y publica autom?ticamente con OIDC. El workflow
38
+ no guarda un `NPM_TOKEN`. `nt --version` lee la versi?n del mismo `package.json`.
39
+ El paquete pertenece al scope `@ntlinkmx`.
40
+ El workflow tiene que estar primero en `main` para poder seleccionarlo en npm.
41
+ Para instalar desde un archivo: ejecuta `npm pack` y luego
42
+ `npm install --global ./ntlinkmx-nt-0.2.0.tgz` desde `cli/`.
43
+ Para retirar el comando global: `npm uninstall --global @ntlinkmx/nt`.
44
+ El perfil predeterminado es `pruebas` y apunta al servidor remoto de pruebas. Las instalaciones existentes conservan su configuración guardada.
45
+ El arranque de Go se describe en [desarrollo local](../doc/guides/LOCAL-DEVELOPMENT.md).
46
+
46
47
  ### Usar en pruebas
47
48
 
48
- Al instalar, el CLI anuncia que queda en modo `pruebas`. La primera operación protegida inicia sesión automáticamente con la cuenta pública de demostración.
49
-
50
- ### Cuenta pública de demostración
51
-
52
- Estas credenciales pertenecen al entorno compartido de pruebas:
53
-
54
- - Correo: `URE180429TM6@ntlink.com.mx`
55
- - Contraseña: `NTPruebas.2021*?`
56
-
57
- Úsalas solo con el perfil `pruebas`. Para autenticar otra cuenta, ejecuta `nt auth login`.
49
+ Al instalar, el CLI anuncia que queda en modo `pruebas`. La primera operación protegida inicia sesión automáticamente con la cuenta pública de demostración.
50
+
51
+ ### Cuenta pública de demostración
52
+
53
+ Estas credenciales pertenecen al entorno compartido de pruebas:
54
+
55
+ - Correo: `URE180429TM6@ntlink.com.mx`
56
+ - Contraseña: `NTPruebas.2021*?`
57
+
58
+ Úsalas solo con el perfil `pruebas`. Para autenticar otra cuenta, ejecuta `nt auth login`.
58
59
 
59
60
  ### Modos y perfiles
60
61
 
61
62
  `nt mode` muestra el modo activo sin imprimir el host. `nt mode pruebas` vuelve al entorno de pruebas. `nt mode production` informa que producción estará disponible próximamente y conserva el modo actual.
62
-
63
- Cada perfil conserva URL, timeout y token independientes. Los nombres de perfiles
64
- son etiquetas locales; no cambian las banderas fiscales del servidor.
65
-
66
- ```sh
67
- nt --profile local config set --base-url http://localhost:8092 --timeout 120
68
- nt --profile pruebas config set --base-url http://pruebas.ntlink.com.mx:8092
69
- nt config set --allow-insecure-http
70
- nt auth login
71
- nt config use pruebas
72
- nt config show
73
- nt config list
74
- nt --profile local health ready
75
- ```
76
-
77
- La URL debe contener protocolo y host/puerto, sin `/api`, usuario, contraseña, query ni fragmento. El origen configurado del perfil `pruebas` está permitido por HTTP para la cuenta compartida del entorno de demostración. Para otros orígenes HTTP, habilita explícitamente `nt config set --allow-insecure-http`; el CLI mostrará una advertencia en cada solicitud. Puedes quitarlo con `nt config set --https-only`.
78
- También existe `config set --url URL`. Cambiar la URL guardada de un perfil borra
79
- su token anterior. Un `--base-url` temporal a otro origen tampoco reutiliza su token.
80
-
81
- | Ajuste | Variable | Opción global |
82
- |---|---|---|
83
- | Perfil | `NTLINK_PROFILE` | `--profile` |
84
- | Origen Go | `NTLINK_BASE_URL` | `--base-url` |
85
- | Bearer | `NTLINK_TOKEN` | `--token` |
86
- | Timeout en segundos | `NTLINK_TIMEOUT` | `--timeout` |
87
-
88
- Prioridad: opción > variable > perfil guardado > valor inicial. No se leen los
89
- tokens ni el archivo del CLI Proxy. La configuración está en
90
- `~/.ntlink-invoice/config.json` (Windows: `%USERPROFILE%\.ntlink-invoice\config.json`);
91
- `NTLINK_INVOICE_CONFIG_DIR` permite cambiar su carpeta.
92
- Los tokens se almacenan localmente sin cifrar; los archivos nuevos usan modo 0600
93
- en POSIX y en Windows heredan las ACL de la carpeta del usuario.
94
- `config show/list` solo muestran el modo y los perfiles; no imprimen el host ni los tokens.
95
-
96
- ### Autenticación
97
-
98
- ```sh
99
- nt auth login
100
- nt auth login --email usuario@example.invalid
101
- nt auth status
102
- nt auth accept-terms
103
- nt auth logout
104
- ```
105
-
106
- El login solicita la contraseña sin mostrarla en terminal. Usa
107
- `POST /api/v2/auth/login` con `email/password` y guarda el campo `token` devuelto
108
- por Membership Go. **Go no expone refresh token**: un 401 requiere iniciar sesión
109
- otra vez. `auth status` consulta el perfil real, no solo la existencia del token.
110
- `logout` borra el token local del perfil; no revoca JWT ya emitidos ni variables
111
- de entorno que sigan definidas.
112
-
113
- Las operaciones genéricas `api` y `native` están deshabilitadas salvo que se pase
114
- `--unsafe-api` antes del comando (por ejemplo, `nt --unsafe-api api GET /api/v2/customer/profile`).
115
- Úsalas solo si conoces el contrato exacto: permiten invocar rutas del backend fuera
116
- de los comandos estables del CLI.
117
-
118
- Para automatizar, usa `NTLINK_PASSWORD` o `--password-stdin` con `--email`:
119
-
120
- ```sh
121
- nt auth login --email usuario@example.invalid --password-stdin
122
- ```
123
-
124
- Conecta stdin desde tu gestor de secretos o tubería. No hay opción `--password`
125
- para evitar dejarla en los argumentos. `auth register registro.json` envía
126
- `email`, `name`, `phone`, `rfc`, `postalCode`; puede crear una cuenta y enviar correo.
127
- `auth verify-code codigo.json` envía `email` y `verificationCode`.
128
-
129
- ### Cuenta, directorios, estadísticas y reportes
130
-
131
- ```sh
132
- nt client info
133
- nt client balance
134
- nt client companies
135
- nt client usage --year 2026
136
- nt client blacklist AAA010101AAA
137
- nt customers list
138
- nt customers get 123
139
- nt customers create cliente.json
140
- nt customers update 123 cliente.json
141
- nt customers delete 123
142
- nt companies list
143
- nt companies get 10
144
- nt companies create emisor.json
145
- nt companies update 10 emisor.json
146
- nt users list
147
- nt users create usuario.json
148
- nt users update usuario@example.invalid usuario.json
149
- nt dashboard daily --range LAST_MONTH
150
- nt dashboard types
151
- nt dashboard status
152
- nt dashboard top-receivers
153
- nt dashboard top-transmitters
154
- nt reports pending-ppd --company 10 --page 0 --size 50
155
- nt reports retentions --from 2026-01-01 --to 2026-01-31
156
- ```
157
-
158
- `users update` recibe el **correo/username** como identificador, conforme a Go.
159
- La autorización de usuarios exige administrador. Crear emisor no instala su CSD.
160
- El CLI envía los JSON sin renombrar campos: los directorios usan los DTO del
161
- [contrato Portal](../backend/doc/modules/Portal.md) y
162
- [frontend/src/api/portal.ts](../frontend/src/api/portal.ts).
163
- Ejemplo sintético de cliente (adapta empresa y datos fiscales antes de guardar):
164
-
165
- ```json
166
- {
167
- "companyId": 10,
168
- "rfc": "AAA010101AAA",
169
- "legalName": "CLIENTE DE EJEMPLO",
170
- "taxRegime": "601",
171
- "postalCode": "06000",
172
- "email": "cliente@example.invalid"
173
- }
174
- ```
175
-
176
- El saldo viene de `/customer/balances`, con canales de portal y web service.
177
- PPD conserva `outstandingBalance` y `balanceSource`; consultar no registra pagos.
178
- Los listados CFDI/reportes/borradores aceptan `--page`, `--size`, `--company`,
179
- `--customer`, `--search`, `--from`, `--to`, `--filter KEY=VALUE` y
180
- `-q KEY=VALUE`. Cada endpoint aplica solo sus filtros admitidos: para borradores,
181
- por ejemplo, usa `--filter estado=sin-timbrar` o `--filter folio=eq:BOR-000000001`.
182
- La paginación del portal comienza en cero y admite hasta 200 filas por solicitud;
183
- no descarga automáticamente las páginas restantes. La salida conserva el sobre
184
- con `content`, `totalElements`, `totalPages`, `page` y `size`.
185
-
186
- ### Catálogos, CFDI y borradores
187
-
188
- ```sh
189
- nt catalogs list
190
- nt catalogs get moneda --id MXN
191
- nt catalogs get ClaveProdServ --description consultoria
192
- nt cfdi list --from 2026-01-01 --to 2026-01-31 --size 100
193
- nt cfdi list --filter uuid=eq:00000000-0000-0000-0000-000000000000
194
- nt cfdi get UUID
195
- nt cfdi status UUID
196
- nt cfdi validate factura.xml
197
- nt cfdi download UUID --format xml --output factura.xml
198
- nt cfdi download UUID --format pdf --output factura.pdf --force
199
- nt cfdi prepare cfdi.json
200
- nt drafts create cfdi.json
201
- nt drafts list --filter estado=sin-timbrar
202
- nt drafts get ID
203
- nt drafts update ID cfdi.json
204
- nt drafts reserve-folio
205
- nt drafts next-folio
206
- ```
207
-
208
- Los nombres de catálogo se normalizan al nombre Go, por ejemplo `moneda` →
209
- `Moneda`. La búsqueda envía `q`; `--id` busca identificador exacto. El listado
210
- incluye los 15 catálogos del portal; los catálogos nativos adicionales están en
211
- `native catalogo`. Que un nombre exista no garantiza que la base tenga sus datos.
212
-
213
- `cfdi prepare` normaliza/recalcula en Go sin guardar ni emitir. `drafts create`
214
- y `update` guardan en SQL. `drafts get` devuelve un sobre con `cfdi`, metadatos y
215
- revisión: para actualizar, envía el **objeto `cfdi` interior**, no todo ese sobre.
216
- `reserve-folio` reserva un folio provisional BOR, no el consecutivo fiscal.
217
- `next-folio` consulta el siguiente consecutivo fiscal de la empresa sin reservarlo;
218
- puede cambiar antes de emitir.
219
- Nómina y complementos viajan en el JSON del portal; el CLI no calcula ISR ni
220
- subsidio. Consulta [emisión](../doc/flows/ISSUANCE.md) y
221
- [nómina](../backend/doc/parity/payroll.md) para el alcance de cada variante.
222
-
223
- Las operaciones explícitas siguientes también tienen comando:
224
-
225
- ```sh
226
- nt cfdi stamp cfdi.json
227
- nt drafts stamp ID cfdi.json
228
- nt cfdi cancel UUID --reason 02
229
- nt cfdi cancel UUID --reason 01 --replacement UUID_SUSTITUTO
230
- nt cfdi email UUID --to facturacion@example.invalid --to archivo@example.invalid
231
- ```
232
-
233
- Estos comandos pueden emitir, cancelar o enviar realmente si el servidor lo
234
- permite. No tienen un modo simulado; nombrar un perfil `pruebas` no garantiza
235
- que su PAC sea sandbox. Las banderas y permisos de Go siguen siendo obligatorios.
236
- Las descargas del portal usan `/api/v2/cfdis/{uuid}/xml|pdf`, distintas de las
237
- rutas de descarga del CLI Proxy, y no sobrescriben archivos sin `--force`.
238
-
239
- ### Chat y análisis de PDF/XML/JSON
240
-
241
- ```sh
242
- nt chat models
243
- nt chat sessions create
244
- nt chat sessions list
245
- nt chat sessions get SESSION_ID
246
- nt chat attach documento.pdf
247
- nt chat send SESSION_ID "Analiza las discrepancias de este documento" --attach documento.pdf
248
- nt chat send SESSION_ID "Consulta mis comprobantes" --provider google --model MODELO_HABILITADO
249
- nt --timeout 360 chat events RUN_ID
250
- nt --timeout 360 chat events RUN_ID --last-event-id 3
251
- nt chat cancel RUN_ID
252
- nt chat sessions delete SESSION_ID
253
- ```
254
-
255
- `send` devuelve `sessionId`, `requestId` y `runId`; consulta sus eventos con
256
- `chat events`. Se pueden adjuntar hasta tres archivos repitiendo `--attach`.
257
- `chat attach` extrae el contenido por sí solo. Go conserva los límites de PDF
258
- (5 MB, 30 páginas) y XML/JSON (1 MB), OCR selectivo y advertencias.
259
- Los proveedores/modelos deben figurar en `chat models`.
260
-
261
- `send --request-id UUID` permite reutilizar un identificador conocido sin crear
262
- una nueva petición de chat; si falta, se genera uno. Los eventos salen como SSE
263
- original (`id`, `event`, `data`), no como un único JSON. Un evento `error` o
264
- `cancelled` es parte del protocolo SSE y debe interpretarlo el consumidor.
265
- Cerrar el CLI o vencer su timeout no cancela la ejecución del servidor;
266
- usa `chat cancel`, recupera la sesión y revisa borradores antes de repetir.
267
- No hay reconexión SSE automática: utiliza `--last-event-id`.
268
- `chat complete solicitud.json` conserva acceso al contrato síncrono de Go.
269
-
270
- Analizar no autoriza guardar: solicita explícitamente la creación de un borrador
271
- cuando quieras persistirlo. El chat puede consultar y preparar borradores; sus
272
- herramientas no timbran, cancelan, cobran ni envían correos. Esas capacidades
273
- pertenecen a los comandos explícitos y rutas nativas correspondientes.
274
-
275
- ### API manual y rutas nativas
276
-
277
- ```sh
278
- nt health live
279
- nt health ready
280
- nt parity
281
- nt --unsafe-api api GET /api/v2/customer/profile
282
- nt --unsafe-api api GET /api/v2/cfdis -q page=0 -q size=20
283
- nt --unsafe-api api POST /api/v2/cfdis/prepare --data cfdi.json
284
- nt --unsafe-api api POST /api/v2/cfdis/validate --raw factura.xml --content-type application/xml
285
- nt native routes
286
- nt native routes --controller Empresa
287
- nt --unsafe-api native empresa --help
288
- nt --unsafe-api native empresa get-empresa -q id=10
289
- nt --unsafe-api native cfdi descargar-acuse --param id=123 --output acuse.xml
290
- nt --unsafe-api native validarxml validar --file archivoXml=factura.xml
291
- ```
292
-
293
- `native` mapea las 87 rutas del registro nativo: catálogos, clientes, empresas,
294
- usuarios, sesión, CSF, reportes, CFDI/REP, retenciones, plantillas, CSD/FIEL/convenios,
295
- entrega y compras según el controlador. Todas conservan el método y path de Go.
296
- `--param KEY=VALUE` sustituye parámetros de ruta, `-q KEY=VALUE` agrega query,
297
- `--data archivo.json` envía JSON, `--raw archivo` envía bytes y
298
- `--file CAMPO=RUTA`/`--field CLAVE=VALOR` construyen multipart.
299
- `--header NOMBRE=VALOR` permite enviar cabeceras adicionales, por ejemplo las
300
- requeridas por contratos con firma; sus valores no se imprimen en verbose.
301
- Las tres formas de cuerpo son excluyentes. `--output` guarda cualquier respuesta
302
- exitosa como bytes; `--stream` muestra SSE original y excluye `--output`.
303
-
304
- Para conocer los campos particulares, abre el módulo del controlador desde
305
- [backend/doc/INDEX.md](../backend/doc/INDEX.md), y consulta los nombres exactos con
306
- `native GRUPO COMANDO --help`. Los comandos nativos siguen nombres derivados de
307
- las acciones originales y sus DTO nativos; no reciben automáticamente los DTO del
308
- portal. Por ejemplo, el `id` nativo de CFDI es el ID SQL, no su UUID.
309
- La tabla siguiente se sincroniza con `npm run routes:sync`; es un inventario de
310
- acceso, no una certificación de operación externa.
311
-
312
- <!-- native-routes:start -->
313
- | Comando (`nt native …`) | HTTP | Ruta Go |
314
- |---|---|---|
315
- | `catalogo get-clave-unidad` | GET | `/api/Catalogo/ClaveUnidad` |
316
- | `catalogo get-clave-unidad-filtro` | GET | `/api/Catalogo/ClaveUnidadFiltro` |
317
- | `catalogo get-clave-unidad-peso` | GET | `/api/Catalogo/ClaveUnidadPeso` |
318
- | `catalogo buscar-estaciones` | GET | `/api/Catalogo/Estaciones` |
319
- | `catalogo buscar-clave-prod-serv-cp` | GET | `/api/Catalogo/ClaveProdServCP` |
320
- | `catalogo buscar-fraccion-arancelaria` | GET | `/api/Catalogo/FraccionArancelaria` |
321
- | `catalogo buscar-prod-serv` | GET | `/api/Catalogo/ClaveProdServ` |
322
- | `catalogo get-estados` | GET | `/api/Catalogo/GetEstados` |
323
- | `catalogo get-paises` | GET | `/api/Catalogo/GetPaises` |
324
- | `catalogo get-localidad` | GET | `/api/Catalogo/GetLocalidad` |
325
- | `catalogo get-municipios` | GET | `/api/Catalogo/GetMunicipios` |
326
- | `catalogo get-cps` | GET | `/api/Catalogo/GetCPs` |
327
- | `catalogo existe-cp` | GET | `/api/Catalogo/ExisteCP` |
328
- | `catalogo get-tasa-cuota` | GET | `/api/Catalogo/TasaCuota` |
329
- | `catalogo get-monedas` | GET | `/api/Catalogo/Monedas` |
330
- | `cfdi timbrar` | POST | `/api/CFDI/Timbrar` |
331
- | `cfdi timbrado-previo` | POST | `/api/CFDI/TimbradoPrevio` |
332
- | `cfdi guardar-plantilla` | POST | `/api/CFDI/Plantilla` |
333
- | `cfdi listar-plantillas` | GET | `/api/CFDI/Plantillas` |
334
- | `cfdi obtener-plantilla` | GET | `/api/CFDI/Plantilla/{id:long}` |
335
- | `cfdi eliminar-plantilla` | POST | `/api/CFDI/Plantilla/{id:long}` |
336
- | `cfdi descargar-xml` | GET | `/api/CFDI/xml/{id:int}` |
337
- | `cfdi verificar-pdf` | HEAD | `/api/CFDI/pdf/{id:int}` |
338
- | `cfdi descargar-pdf` | GET | `/api/CFDI/pdf/{id:int}` |
339
- | `cfdi descargar-acuse` | GET | `/api/CFDI/acuse/{id:int}` |
340
- | `cfdi enviar-correo` | POST | `/api/CFDI/correo/{id:int}` |
341
- | `cfdi enviar-whats-app` | POST | `/api/CFDI/whatsapp/{id:int}` |
342
- | `cfdi descargar-pdf-whats-app` | GET | `/api/CFDI/wa/{token}/{archivo}` |
343
- | `cfdi cancelar` | POST | `/api/CFDI/cancelar/{id:int}` |
344
- | `cliente get-clientes` | GET | `/api/Cliente/GetClientes` |
345
- | `cliente get-by-id` | GET | `/api/Cliente/GetById` |
346
- | `cliente guardar` | POST | `/api/Cliente/Guardar` |
347
- | `config get-session-config` | GET | `/api/Config/session` |
348
- | `empresa get-empresas` | GET | `/api/Empresa/GetEmpresas` |
349
- | `empresa get-sucursales` | GET | `/api/Empresa/GetSucursales` |
350
- | `empresa get-empresa` | GET | `/api/Empresa/GetEmpresa` |
351
- | `empresa get-sucursales-lista` | GET | `/api/Empresa/GetSucursalesLista` |
352
- | `empresa get-sucursal` | GET | `/api/Empresa/GetSucursal` |
353
- | `empresa guardar` | POST | `/api/Empresa/Guardar` |
354
- | `empresa validar-csd` | POST | `/api/Empresa/ValidarCSD` |
355
- | `empresa tiene-csd` | GET | `/api/Empresa/TieneCsd` |
356
- | `empresa subir-fiel` | POST | `/api/Empresa/SubirFiel` |
357
- | `empresa guardar-sucursal` | POST | `/api/Empresa/GuardarSucursal` |
358
- | `empresa parse-constancia` | POST | `/api/Empresa/ParseConstancia` |
359
- | `homedashboard get-stats` | GET | `/api/HomeDashboard/stats` |
360
- | `listanegra consultar-xml` | POST | `/api/ListaNegra/consultar-xml` |
361
- | `pagos config` | GET | `/api/Pagos/Config` |
362
- | `pagos crear-cargo2` | POST | `/api/Pagos/CrearCargo2` |
363
- | `pagos validar-sesion` | GET | `/api/Pagos/ValidarSesion` |
364
- | `pagos webhook` | POST | `/api/Pagos/Webhook` |
365
- | `pagos generar-pre-factura-externa` | POST | `/api/Pagos/GenerarPreFacturaExterna` |
366
- | `renovarfolios obtener-datos-empresa` | GET | `/api/RenovarFolios/ObtenerDatosEmpresa` |
367
- | `renovarfolios renovar` | POST | `/api/RenovarFolios/Renovar` |
368
- | `reporte get-cfdi` | GET | `/api/Reporte/GetCFDI` |
369
- | `reporte lista-timbrado` | GET | `/api/Reporte/lista-timbrado` |
370
- | `reporte reporte-anual` | GET | `/api/Reporte/reporte-anual` |
371
- | `reporte reporte-empresa` | GET | `/api/Reporte/reporte-empresa` |
372
- | `reporte anios` | GET | `/api/Reporte/anios` |
373
- | `retenciones reporte` | GET | `/api/Retenciones/Reporte` |
374
- | `retenciones enviar-correo` | POST | `/api/Retenciones/correo/{id:int}` |
375
- | `retenciones cancelar` | POST | `/api/Retenciones/cancelar/{id:int}` |
376
- | `retenciones descargar-acuse` | GET | `/api/Retenciones/acuse/{id:int}` |
377
- | `retenciones timbrar` | POST | `/api/Retenciones/Timbrar` |
378
- | `retenciones timbrado-previo` | POST | `/api/Retenciones/TimbradoPrevio` |
379
- | `retenciones descargar-xml` | GET | `/api/Retenciones/xml/{id:int}` |
380
- | `retenciones verificar-pdf` | HEAD | `/api/Retenciones/pdf/{id:int}` |
381
- | `retenciones descargar-pdf` | GET | `/api/Retenciones/pdf/{id:int}` |
382
- | `retenciones enviar-whats-app` | POST | `/api/Retenciones/whatsapp/{id:int}` |
383
- | `retenciones descargar-pdf-whats-app` | GET | `/api/Retenciones/wa/{token}/{archivo}` |
384
- | `session login` | POST | `/api/Session/Login` |
385
- | `session generar-codigo` | POST | `/api/Session/GenerarCodigo` |
386
- | `session generar-codigo-correo` | POST | `/api/Session/GenerarCodigoCorreo` |
387
- | `session validar-codigo` | POST | `/api/Session/ValidarCodigo` |
388
- | `session heartbeat` | POST | `/api/Session/Heartbeat` |
389
- | `session logout` | POST | `/api/Session/Logout` |
390
- | `session recuperar-contrasena` | POST | `/api/Session/RecuperarContrasena` |
391
- | `session upload-csf` | POST | `/api/Session/UploadCSF` |
392
- | `session registro` | POST | `/api/Session/Registro` |
393
- | `session cambiar-contrasena` | POST | `/api/Session/CambiarContrasena` |
394
- | `usuarioadmin get-usuarios` | GET | `/api/UsuarioAdmin/GetUsuarios` |
395
- | `usuarioadmin debug-profile` | GET | `/api/UsuarioAdmin/DebugProfile` |
396
- | `usuarioadmin guardar` | POST | `/api/UsuarioAdmin/Guardar` |
397
- | `usuarioadmin editar` | POST | `/api/UsuarioAdmin/Editar` |
398
- | `usuarioadmin cambiar-password` | POST | `/api/UsuarioAdmin/CambiarPassword` |
399
- | `usuarioadmin bloquear` | POST | `/api/UsuarioAdmin/Bloquear/{userId}` |
400
- | `usuarioadmin eliminar` | POST | `/api/UsuarioAdmin/Eliminar/{userId}` |
401
- | `validarxml validar` | POST | `/api/ValidarXML/validar` |
402
- <!-- native-routes:end -->
403
-
404
- ## Flujo y fallos
405
-
406
- CLI → perfil/variables/opciones → HTTP Bearer a Go → permisos y reglas Go →
407
- SQL/servicios según la ruta → JSON o archivo. No copia reglas fiscales al cliente.
408
- Las peticiones anónimas solo se usan en login/registro/verificación, salud/paridad
409
- y rutas nativas marcadas así por el registro; los catálogos del portal sí llevan JWT.
410
- `api --anonymous` permite omitirlo deliberadamente.
411
-
412
- La salida normal es JSON en stdout (`--json` es compatible y opcional). Los
413
- errores van a stderr y terminan con código 1; el éxito usa 0. Ayuda/versión son
414
- texto. `--verbose` imprime método, origen, ruta y estado en stderr; omite tokens,
415
- query y cuerpos. Con `--output`, stdout devuelve `{ "path": "...", "bytes": 123 }`.
416
- En PowerShell, evita convertir XML/PDF a texto por tubería: usa `--output`.
417
-
418
- No se reintenta ninguna petición automáticamente, tampoco después de 401. Un
419
- timeout, 502 o fallo de red durante una escritura puede tener resultado incierto:
420
- consulta su estado antes de repetir. Un timbrado externo y su persistencia SQL
421
- pueden tener éxito parcial; sigue la recuperación documentada en Go.
422
- Los errores HTTP conservan estado y mensaje; `exito:false` nativo también falla.
423
- Go puede devolver 403 por alcance, 409 por conflicto, 501 por módulo pendiente o
424
- 503 por configuración/servicio/catálogo ausente. El CLI no habilita esas funciones.
425
-
426
- ## Reglas y trazabilidad
427
-
428
- | Regla | Implementación y evidencia |
429
- |---|---|
430
- | Token separado por perfil y origen | `src/config.ts`; pruebas de perfiles, override y logout |
431
- | Sin refresh ni repetición automática | `src/http.ts`, `auth login`; pruebas 401/503 y escrituras |
432
- | Descarga exacta y sin sobrescritura implícita | `src/http.ts`; prueba de bytes y `--force` |
433
- | Rutas reales sin inventar equivalentes Proxy | `src/native-routes.json`, `scripts/sync-routes.mjs`; prueba contra registro Go |
434
- | Chat conserva requestId y SSE | `src/cli.ts`; prueba de adjuntos, modelo, eventos y cancelación |
435
-
436
- Fuentes: [Portal](../backend/doc/modules/Portal.md),
437
- [router Go](../backend/internal/api/server.go),
438
- [registro nativo](../backend/internal/legacy/routes.json),
439
- [chat Go](../backend/internal/api/ai.go) y
440
- [capacidades y límites](../doc/FEATURES.md).
441
-
442
- ## Estado y verificación
443
-
444
- Implementación 27/oct/2026. Comprobaciones locales:
445
-
446
- ```sh
447
- npm ci
448
- npm run typecheck
449
- npm test
450
- npm run routes:sync
451
- npm run build
452
- ```
453
-
454
- Las pruebas lanzan el ejecutable real contra un servidor HTTP sintético local;
455
- comprueban comandos, login, perfiles, contratos de transporte, descargas, multipart,
456
- chat y errores. No equivalen a integración Go/SQL/PAC ni prueban timbrados,
457
- cancelaciones, correos, compras o disponibilidad productiva. No se modificó Go.
458
- Las 11 pruebas pasaron en Windows (Node 24) y Ubuntu mediante WSL (Node 22.22.0).
459
- También se consultaron `/health/live` y `/health/ready` del Go real en
460
- `localhost:8092`: estado `ok`, `ready: true`, conexiones main/sat/catalogs `ok`.
461
- Esas consultas no comprueban login real ni escrituras de negocio.
63
+
64
+ Cada perfil conserva URL, timeout y token independientes. Los nombres de perfiles
65
+ son etiquetas locales; no cambian las banderas fiscales del servidor.
66
+
67
+ ```sh
68
+ nt --profile local config set --base-url http://localhost:8092 --timeout 120
69
+ nt --profile pruebas config set --base-url http://pruebas.ntlink.com.mx:8092
70
+ nt config set --allow-insecure-http
71
+ nt auth login
72
+ nt config use pruebas
73
+ nt config show
74
+ nt config list
75
+ nt --profile local health ready
76
+ ```
77
+
78
+ La URL debe contener protocolo y host/puerto, sin `/api`, usuario, contraseña, query ni fragmento. El origen configurado del perfil `pruebas` está permitido por HTTP para la cuenta compartida del entorno de demostración. Para otros orígenes HTTP, habilita explícitamente `nt config set --allow-insecure-http`; el CLI mostrará una advertencia en cada solicitud. Puedes quitarlo con `nt config set --https-only`.
79
+ También existe `config set --url URL`. Cambiar la URL guardada de un perfil borra
80
+ su token anterior. Un `--base-url` temporal a otro origen tampoco reutiliza su token.
81
+
82
+ | Ajuste | Variable | Opción global |
83
+ |---|---|---|
84
+ | Perfil | `NTLINK_PROFILE` | `--profile` |
85
+ | Origen Go | `NTLINK_BASE_URL` | `--base-url` |
86
+ | Bearer | `NTLINK_TOKEN` | `--token` |
87
+ | Timeout en segundos | `NTLINK_TIMEOUT` | `--timeout` |
88
+
89
+ Prioridad: opción > variable > perfil guardado > valor inicial. No se leen los
90
+ tokens ni el archivo del CLI Proxy. La configuración está en
91
+ `~/.ntlink-invoice/config.json` (Windows: `%USERPROFILE%\.ntlink-invoice\config.json`);
92
+ `NTLINK_INVOICE_CONFIG_DIR` permite cambiar su carpeta.
93
+ Los tokens se almacenan localmente sin cifrar; los archivos nuevos usan modo 0600
94
+ en POSIX y en Windows heredan las ACL de la carpeta del usuario.
95
+ `config show/list` solo muestran el modo y los perfiles; no imprimen el host ni los tokens.
96
+
97
+ ### Autenticación
98
+
99
+ ```sh
100
+ nt auth login
101
+ nt auth login --email usuario@example.invalid
102
+ nt auth status
103
+ nt auth accept-terms
104
+ nt auth logout
105
+ ```
106
+
107
+ El login solicita la contraseña sin mostrarla en terminal. Usa `POST /api/v2/auth/login` con `email/password` y guarda el campo `token` devuelto por Membership Go. **Go no expone refresh token**: un 401 requiere iniciar sesión otra vez. Si el perfil contiene un token vencido, el CLI responde 401 y no repite la petición; vuelve a autenticar y comprueba el efecto de una escritura antes de reintentar. `auth status` consulta el perfil real, no solo la existencia del token.
108
+ `logout` borra el token local del perfil; no revoca JWT ya emitidos ni variables
109
+ de entorno que sigan definidas.
110
+
111
+ Las operaciones genéricas `api` y `native` están deshabilitadas salvo que se pase
112
+ `--unsafe-api` antes del comando (por ejemplo, `nt --unsafe-api api GET /api/v2/customer/profile`).
113
+ Úsalas solo si conoces el contrato exacto: permiten invocar rutas del backend fuera
114
+ de los comandos estables del CLI.
115
+
116
+ Para automatizar, usa `NTLINK_PASSWORD` o `--password-stdin` con `--email`:
117
+
118
+ ```sh
119
+ nt auth login --email usuario@example.invalid --password-stdin
120
+ ```
121
+
122
+ Conecta stdin desde tu gestor de secretos o tubería. No hay opción `--password`
123
+ para evitar dejarla en los argumentos. `auth register registro.json` envía
124
+ `email`, `name`, `phone`, `rfc`, `postalCode`; puede crear una cuenta y enviar correo.
125
+ `auth verify-code codigo.json` envía `email` y `verificationCode`.
126
+
127
+ ### Cuenta, directorios, estadísticas y reportes
128
+
129
+ ```sh
130
+ nt client info
131
+ nt client balance
132
+ nt client companies
133
+ nt client usage --year 2026
134
+ nt client blacklist AAA010101AAA
135
+ nt customers list
136
+ nt customers get 123
137
+ nt customers create cliente.json
138
+ nt customers update 123 cliente.json
139
+ nt customers delete 123
140
+ nt companies list
141
+ nt companies get 10
142
+ nt companies create emisor.json
143
+ nt companies update 10 emisor.json
144
+ nt users list
145
+ nt users create usuario.json
146
+ nt users update usuario@example.invalid usuario.json
147
+ nt dashboard daily --range LAST_MONTH
148
+ nt dashboard types
149
+ nt dashboard status
150
+ nt dashboard top-receivers
151
+ nt dashboard top-transmitters
152
+ nt reports pending-ppd --company 10 --page 0 --size 50
153
+ nt reports retentions --from 2026-01-01 --to 2026-01-31
154
+ ```
155
+
156
+ `users update` recibe el **correo/username** como identificador, conforme a Go.
157
+ La autorización de usuarios exige administrador. Crear emisor no instala su CSD.
158
+ El CLI envía los JSON sin renombrar campos: los directorios usan los DTO del
159
+ [contrato Portal](../backend/doc/modules/Portal.md) y
160
+ [frontend/src/api/portal.ts](../frontend/src/api/portal.ts).
161
+ Ejemplo sintético de cliente (adapta empresa y datos fiscales antes de guardar):
162
+
163
+ ```json
164
+ {
165
+ "companyId": 10,
166
+ "rfc": "AAA010101AAA",
167
+ "legalName": "CLIENTE DE EJEMPLO",
168
+ "taxRegime": "601",
169
+ "postalCode": "06000",
170
+ "email": "cliente@example.invalid"
171
+ }
172
+ ```
173
+
174
+ El saldo viene de `/customer/balances`, con canales de portal y web service.
175
+ PPD conserva `outstandingBalance` y `balanceSource`; consultar no registra pagos.
176
+ Los listados CFDI/reportes/borradores aceptan `--page`, `--size`, `--company`,
177
+ `--customer`, `--search`, `--from`, `--to`, `--filter KEY=VALUE` y
178
+ `-q KEY=VALUE`. Cada endpoint aplica solo sus filtros admitidos: para borradores,
179
+ por ejemplo, usa `--filter estado=sin-timbrar` o `--filter folio=eq:BOR-000000001`.
180
+ La paginación del portal comienza en cero y admite hasta 200 filas por solicitud;
181
+ no descarga automáticamente las páginas restantes. La salida conserva el sobre
182
+ con `content`, `totalElements`, `totalPages`, `page` y `size`.
183
+
184
+ ### Catálogos, CFDI y borradores
185
+
186
+ ```sh
187
+ nt catalogs list
188
+ nt catalogs get moneda --id MXN
189
+ nt catalogs get ClaveProdServ --description consultoria
190
+ nt cfdi list --from 2026-01-01 --to 2026-01-31 --size 100
191
+ nt cfdi list --filter uuid=eq:00000000-0000-0000-0000-000000000000
192
+ nt cfdi get UUID
193
+ nt cfdi status UUID
194
+ nt cfdi validate factura.xml
195
+ nt cfdi download UUID --format xml --output factura.xml
196
+ nt cfdi download UUID --format pdf --output factura.pdf --force
197
+ nt cfdi prepare cfdi.json
198
+ nt drafts create cfdi.json
199
+ nt drafts list --filter estado=sin-timbrar
200
+ nt drafts get ID
201
+ nt drafts download ID --format pdf --output PRE-CFDI.pdf
202
+ nt drafts download ID --format xml --output PRE-CFDI.xml
203
+ nt drafts update ID cfdi.json
204
+ nt drafts reserve-folio
205
+ nt drafts next-folio
206
+ ```
207
+
208
+ `nt drafts create cfdi.json` guarda el borrador y descarga automáticamente su PDF
209
+ como `PRE-CFDI-ID.pdf`; la salida conserva `metadata.id` y añade `pdf.path` y
210
+ `pdf.bytes`. Usa `--pdf-output archivo.pdf` para elegir destino, `--no-pdf` para
211
+ omitir la descarga y `--force` para reemplazar el archivo. Si devuelve `pdfError`,
212
+ el borrador ya existe: recupera el PDF con `drafts download`, sin repetir create.
213
+ Después de actualizar, usa `drafts download` para obtener la versión guardada.
214
+ PDF y XML proceden del PRE-CFDI de la tabla; no se timbran ni se suben a S3.
215
+
216
+ Al cambiar el CLI, actualizar también la [skill cfdi](../.agents/skills/cfdi/SKILL.md)
217
+ y sus copias instaladas en la misma entrega.
218
+
219
+
220
+ Los nombres de catálogo se normalizan al nombre Go, por ejemplo `moneda` →
221
+ `Moneda`. La búsqueda envía `q`; `--id` busca identificador exacto. El listado
222
+ incluye los 15 catálogos del portal; los catálogos nativos adicionales están en
223
+ `native catalogo`. Que un nombre exista no garantiza que la base tenga sus datos.
224
+
225
+ `cfdi prepare` normaliza/recalcula en Go sin guardar ni emitir. `drafts create`
226
+ y `update` guardan en SQL. `drafts get` devuelve un sobre con `cfdi`, metadatos y
227
+ revisión: para actualizar, envía el **objeto `cfdi` interior**, no todo ese sobre.
228
+ `reserve-folio` reserva un folio provisional BOR, no el consecutivo fiscal.
229
+ `next-folio` consulta el siguiente consecutivo fiscal de la empresa sin reservarlo;
230
+ puede cambiar antes de emitir.
231
+ Nómina y complementos viajan en el JSON del portal; el CLI no calcula ISR ni
232
+ subsidio. Consulta [emisión](../doc/flows/ISSUANCE.md) y
233
+ [nómina](../backend/doc/parity/payroll.md) para el alcance de cada variante.
234
+
235
+ Las operaciones explícitas siguientes también tienen comando:
236
+
237
+ ```sh
238
+ nt cfdi stamp cfdi.json
239
+ nt drafts stamp ID cfdi.json
240
+ nt cfdi cancel UUID --reason 02
241
+ nt cfdi cancel UUID --reason 01 --replacement UUID_SUSTITUTO
242
+ nt cfdi email UUID --to facturacion@example.invalid --to archivo@example.invalid
243
+ ```
244
+
245
+ Estos comandos pueden emitir, cancelar o enviar realmente si el servidor lo
246
+ permite. No tienen un modo simulado; nombrar un perfil `pruebas` no garantiza
247
+ que su PAC sea sandbox. Las banderas y permisos de Go siguen siendo obligatorios.
248
+ Las descargas del portal usan `/api/v2/cfdis/{uuid}/xml|pdf`, distintas de las
249
+ rutas de descarga del CLI Proxy, y no sobrescriben archivos sin `--force`.
250
+
251
+ ### Chat y análisis de PDF/XML/JSON
252
+
253
+ ```sh
254
+ nt chat models
255
+ nt chat sessions create
256
+ nt chat sessions list
257
+ nt chat sessions get SESSION_ID
258
+ nt chat attach documento.pdf
259
+ nt chat send SESSION_ID "Analiza las discrepancias de este documento" --attach documento.pdf
260
+ nt chat send SESSION_ID "Consulta mis comprobantes" --provider google --model MODELO_HABILITADO
261
+ nt --timeout 360 chat events RUN_ID
262
+ nt --timeout 360 chat events RUN_ID --last-event-id 3
263
+ nt chat cancel RUN_ID
264
+ nt chat sessions delete SESSION_ID
265
+ ```
266
+
267
+ `send` devuelve `sessionId`, `requestId` y `runId`; consulta sus eventos con
268
+ `chat events`. Se pueden adjuntar hasta tres archivos repitiendo `--attach`.
269
+ `chat attach` extrae el contenido por sí solo. Go conserva los límites de PDF
270
+ (5 MB, 30 páginas) y XML/JSON (1 MB), OCR selectivo y advertencias.
271
+ Los proveedores/modelos deben figurar en `chat models`.
272
+
273
+ `send --request-id UUID` permite reutilizar un identificador conocido sin crear
274
+ una nueva petición de chat; si falta, se genera uno. Los eventos salen como SSE
275
+ original (`id`, `event`, `data`), no como un único JSON. Un evento `error` o
276
+ `cancelled` es parte del protocolo SSE y debe interpretarlo el consumidor.
277
+ Cerrar el CLI o vencer su timeout no cancela la ejecución del servidor;
278
+ usa `chat cancel`, recupera la sesión y revisa borradores antes de repetir.
279
+ No hay reconexión SSE automática: utiliza `--last-event-id`.
280
+ `chat complete solicitud.json` conserva acceso al contrato síncrono de Go.
281
+
282
+ Analizar no autoriza guardar: solicita explícitamente la creación de un borrador
283
+ cuando quieras persistirlo. El chat puede consultar y preparar borradores; sus
284
+ herramientas no timbran, cancelan, cobran ni envían correos. Esas capacidades
285
+ pertenecen a los comandos explícitos y rutas nativas correspondientes.
286
+
287
+ ### API manual y rutas nativas
288
+
289
+ ```sh
290
+ nt health live
291
+ nt health ready
292
+ nt parity
293
+ nt --unsafe-api api GET /api/v2/customer/profile
294
+ nt --unsafe-api api GET /api/v2/cfdis -q page=0 -q size=20
295
+ nt --unsafe-api api POST /api/v2/cfdis/prepare --data cfdi.json
296
+ nt --unsafe-api api POST /api/v2/cfdis/validate --raw factura.xml --content-type application/xml
297
+ nt native routes
298
+ nt native routes --controller Empresa
299
+ nt --unsafe-api native empresa --help
300
+ nt --unsafe-api native empresa get-empresa -q id=10
301
+ nt --unsafe-api native cfdi descargar-acuse --param id=123 --output acuse.xml
302
+ nt --unsafe-api native validarxml validar --file archivoXml=factura.xml
303
+ ```
304
+
305
+ `native` mapea las 87 rutas del registro nativo: catálogos, clientes, empresas,
306
+ usuarios, sesión, CSF, reportes, CFDI/REP, retenciones, plantillas, CSD/FIEL/convenios,
307
+ entrega y compras según el controlador. Todas conservan el método y path de Go.
308
+ `--param KEY=VALUE` sustituye parámetros de ruta, `-q KEY=VALUE` agrega query,
309
+ `--data archivo.json` envía JSON, `--raw archivo` envía bytes y
310
+ `--file CAMPO=RUTA`/`--field CLAVE=VALOR` construyen multipart.
311
+ `--header NOMBRE=VALOR` permite enviar cabeceras adicionales, por ejemplo las
312
+ requeridas por contratos con firma; sus valores no se imprimen en verbose.
313
+ Las tres formas de cuerpo son excluyentes. `--output` guarda cualquier respuesta
314
+ exitosa como bytes; `--stream` muestra SSE original y excluye `--output`.
315
+
316
+ Para conocer los campos particulares, abre el módulo del controlador desde
317
+ [backend/doc/INDEX.md](../backend/doc/INDEX.md), y consulta los nombres exactos con
318
+ `native GRUPO COMANDO --help`. Los comandos nativos siguen nombres derivados de
319
+ las acciones originales y sus DTO nativos; no reciben automáticamente los DTO del
320
+ portal. Por ejemplo, el `id` nativo de CFDI es el ID SQL, no su UUID.
321
+ La tabla siguiente se sincroniza con `npm run routes:sync`; es un inventario de
322
+ acceso, no una certificación de operación externa.
323
+
324
+ <!-- native-routes:start -->
325
+ | Comando (`nt native …`) | HTTP | Ruta Go |
326
+ |---|---|---|
327
+ | `catalogo get-clave-unidad` | GET | `/api/Catalogo/ClaveUnidad` |
328
+ | `catalogo get-clave-unidad-filtro` | GET | `/api/Catalogo/ClaveUnidadFiltro` |
329
+ | `catalogo get-clave-unidad-peso` | GET | `/api/Catalogo/ClaveUnidadPeso` |
330
+ | `catalogo buscar-estaciones` | GET | `/api/Catalogo/Estaciones` |
331
+ | `catalogo buscar-clave-prod-serv-cp` | GET | `/api/Catalogo/ClaveProdServCP` |
332
+ | `catalogo buscar-fraccion-arancelaria` | GET | `/api/Catalogo/FraccionArancelaria` |
333
+ | `catalogo buscar-prod-serv` | GET | `/api/Catalogo/ClaveProdServ` |
334
+ | `catalogo get-estados` | GET | `/api/Catalogo/GetEstados` |
335
+ | `catalogo get-paises` | GET | `/api/Catalogo/GetPaises` |
336
+ | `catalogo get-localidad` | GET | `/api/Catalogo/GetLocalidad` |
337
+ | `catalogo get-municipios` | GET | `/api/Catalogo/GetMunicipios` |
338
+ | `catalogo get-cps` | GET | `/api/Catalogo/GetCPs` |
339
+ | `catalogo existe-cp` | GET | `/api/Catalogo/ExisteCP` |
340
+ | `catalogo get-tasa-cuota` | GET | `/api/Catalogo/TasaCuota` |
341
+ | `catalogo get-monedas` | GET | `/api/Catalogo/Monedas` |
342
+ | `cfdi timbrar` | POST | `/api/CFDI/Timbrar` |
343
+ | `cfdi timbrado-previo` | POST | `/api/CFDI/TimbradoPrevio` |
344
+ | `cfdi guardar-plantilla` | POST | `/api/CFDI/Plantilla` |
345
+ | `cfdi listar-plantillas` | GET | `/api/CFDI/Plantillas` |
346
+ | `cfdi obtener-plantilla` | GET | `/api/CFDI/Plantilla/{id:long}` |
347
+ | `cfdi eliminar-plantilla` | POST | `/api/CFDI/Plantilla/{id:long}` |
348
+ | `cfdi descargar-xml` | GET | `/api/CFDI/xml/{id:int}` |
349
+ | `cfdi verificar-pdf` | HEAD | `/api/CFDI/pdf/{id:int}` |
350
+ | `cfdi descargar-pdf` | GET | `/api/CFDI/pdf/{id:int}` |
351
+ | `cfdi descargar-acuse` | GET | `/api/CFDI/acuse/{id:int}` |
352
+ | `cfdi enviar-correo` | POST | `/api/CFDI/correo/{id:int}` |
353
+ | `cfdi enviar-whats-app` | POST | `/api/CFDI/whatsapp/{id:int}` |
354
+ | `cfdi descargar-pdf-whats-app` | GET | `/api/CFDI/wa/{token}/{archivo}` |
355
+ | `cfdi cancelar` | POST | `/api/CFDI/cancelar/{id:int}` |
356
+ | `cliente get-clientes` | GET | `/api/Cliente/GetClientes` |
357
+ | `cliente get-by-id` | GET | `/api/Cliente/GetById` |
358
+ | `cliente guardar` | POST | `/api/Cliente/Guardar` |
359
+ | `config get-session-config` | GET | `/api/Config/session` |
360
+ | `empresa get-empresas` | GET | `/api/Empresa/GetEmpresas` |
361
+ | `empresa get-sucursales` | GET | `/api/Empresa/GetSucursales` |
362
+ | `empresa get-empresa` | GET | `/api/Empresa/GetEmpresa` |
363
+ | `empresa get-sucursales-lista` | GET | `/api/Empresa/GetSucursalesLista` |
364
+ | `empresa get-sucursal` | GET | `/api/Empresa/GetSucursal` |
365
+ | `empresa guardar` | POST | `/api/Empresa/Guardar` |
366
+ | `empresa validar-csd` | POST | `/api/Empresa/ValidarCSD` |
367
+ | `empresa tiene-csd` | GET | `/api/Empresa/TieneCsd` |
368
+ | `empresa subir-fiel` | POST | `/api/Empresa/SubirFiel` |
369
+ | `empresa guardar-sucursal` | POST | `/api/Empresa/GuardarSucursal` |
370
+ | `empresa parse-constancia` | POST | `/api/Empresa/ParseConstancia` |
371
+ | `homedashboard get-stats` | GET | `/api/HomeDashboard/stats` |
372
+ | `listanegra consultar-xml` | POST | `/api/ListaNegra/consultar-xml` |
373
+ | `pagos config` | GET | `/api/Pagos/Config` |
374
+ | `pagos crear-cargo2` | POST | `/api/Pagos/CrearCargo2` |
375
+ | `pagos validar-sesion` | GET | `/api/Pagos/ValidarSesion` |
376
+ | `pagos webhook` | POST | `/api/Pagos/Webhook` |
377
+ | `pagos generar-pre-factura-externa` | POST | `/api/Pagos/GenerarPreFacturaExterna` |
378
+ | `renovarfolios obtener-datos-empresa` | GET | `/api/RenovarFolios/ObtenerDatosEmpresa` |
379
+ | `renovarfolios renovar` | POST | `/api/RenovarFolios/Renovar` |
380
+ | `reporte get-cfdi` | GET | `/api/Reporte/GetCFDI` |
381
+ | `reporte lista-timbrado` | GET | `/api/Reporte/lista-timbrado` |
382
+ | `reporte reporte-anual` | GET | `/api/Reporte/reporte-anual` |
383
+ | `reporte reporte-empresa` | GET | `/api/Reporte/reporte-empresa` |
384
+ | `reporte anios` | GET | `/api/Reporte/anios` |
385
+ | `retenciones reporte` | GET | `/api/Retenciones/Reporte` |
386
+ | `retenciones enviar-correo` | POST | `/api/Retenciones/correo/{id:int}` |
387
+ | `retenciones cancelar` | POST | `/api/Retenciones/cancelar/{id:int}` |
388
+ | `retenciones descargar-acuse` | GET | `/api/Retenciones/acuse/{id:int}` |
389
+ | `retenciones timbrar` | POST | `/api/Retenciones/Timbrar` |
390
+ | `retenciones timbrado-previo` | POST | `/api/Retenciones/TimbradoPrevio` |
391
+ | `retenciones descargar-xml` | GET | `/api/Retenciones/xml/{id:int}` |
392
+ | `retenciones verificar-pdf` | HEAD | `/api/Retenciones/pdf/{id:int}` |
393
+ | `retenciones descargar-pdf` | GET | `/api/Retenciones/pdf/{id:int}` |
394
+ | `retenciones enviar-whats-app` | POST | `/api/Retenciones/whatsapp/{id:int}` |
395
+ | `retenciones descargar-pdf-whats-app` | GET | `/api/Retenciones/wa/{token}/{archivo}` |
396
+ | `session login` | POST | `/api/Session/Login` |
397
+ | `session generar-codigo` | POST | `/api/Session/GenerarCodigo` |
398
+ | `session generar-codigo-correo` | POST | `/api/Session/GenerarCodigoCorreo` |
399
+ | `session validar-codigo` | POST | `/api/Session/ValidarCodigo` |
400
+ | `session heartbeat` | POST | `/api/Session/Heartbeat` |
401
+ | `session logout` | POST | `/api/Session/Logout` |
402
+ | `session recuperar-contrasena` | POST | `/api/Session/RecuperarContrasena` |
403
+ | `session upload-csf` | POST | `/api/Session/UploadCSF` |
404
+ | `session registro` | POST | `/api/Session/Registro` |
405
+ | `session cambiar-contrasena` | POST | `/api/Session/CambiarContrasena` |
406
+ | `usuarioadmin get-usuarios` | GET | `/api/UsuarioAdmin/GetUsuarios` |
407
+ | `usuarioadmin debug-profile` | GET | `/api/UsuarioAdmin/DebugProfile` |
408
+ | `usuarioadmin guardar` | POST | `/api/UsuarioAdmin/Guardar` |
409
+ | `usuarioadmin editar` | POST | `/api/UsuarioAdmin/Editar` |
410
+ | `usuarioadmin cambiar-password` | POST | `/api/UsuarioAdmin/CambiarPassword` |
411
+ | `usuarioadmin bloquear` | POST | `/api/UsuarioAdmin/Bloquear/{userId}` |
412
+ | `usuarioadmin eliminar` | POST | `/api/UsuarioAdmin/Eliminar/{userId}` |
413
+ | `validarxml validar` | POST | `/api/ValidarXML/validar` |
414
+ <!-- native-routes:end -->
415
+
416
+ ## Flujo y fallos
417
+
418
+ CLI → perfil/variables/opciones → HTTP Bearer a Go → permisos y reglas Go →
419
+ SQL/servicios según la ruta → JSON o archivo. No copia reglas fiscales al cliente.
420
+ Las peticiones anónimas solo se usan en login/registro/verificación, salud/paridad
421
+ y rutas nativas marcadas así por el registro; los catálogos del portal sí llevan JWT.
422
+ `api --anonymous` permite omitirlo deliberadamente.
423
+
424
+ La salida normal es JSON en stdout (`--json` es compatible y opcional). Los
425
+ errores van a stderr y terminan con código 1; el éxito usa 0. Ayuda/versión son
426
+ texto. `--verbose` imprime método, origen, ruta y estado en stderr; omite tokens,
427
+ query y cuerpos. Con `--output`, stdout devuelve `{ "path": "...", "bytes": 123 }`.
428
+ En PowerShell, evita convertir XML/PDF a texto por tubería: usa `--output`.
429
+
430
+ No se reintenta ninguna petición automáticamente, tampoco después de 401. Un
431
+ timeout, 502 o fallo de red durante una escritura puede tener resultado incierto:
432
+ consulta su estado antes de repetir. Un timbrado externo y su persistencia SQL
433
+ pueden tener éxito parcial; sigue la recuperación documentada en Go.
434
+ Los errores HTTP conservan estado y mensaje; `exito:false` nativo también falla.
435
+ Go puede devolver 403 por alcance, 409 por conflicto, 501 por módulo pendiente o
436
+ 503 por configuración/servicio/catálogo ausente. El CLI no habilita esas funciones.
437
+
438
+ ## Reglas y trazabilidad
439
+
440
+ | Regla | Implementación y evidencia |
441
+ |---|---|
442
+ | Token separado por perfil y origen | `src/config.ts`; pruebas de perfiles, override y logout |
443
+ | Sin refresh ni repetición automática | `src/http.ts`, `auth login`; pruebas 401/503 y escrituras |
444
+ | Descarga exacta y sin sobrescritura implícita | `src/http.ts`; prueba de bytes y `--force` |
445
+ | Rutas reales sin inventar equivalentes Proxy | `src/native-routes.json`, `scripts/sync-routes.mjs`; prueba contra registro Go |
446
+ | Chat conserva requestId y SSE | `src/cli.ts`; prueba de adjuntos, modelo, eventos y cancelación |
447
+
448
+ Fuentes: [Portal](../backend/doc/modules/Portal.md),
449
+ [router Go](../backend/internal/api/server.go),
450
+ [registro nativo](../backend/internal/legacy/routes.json),
451
+ [chat Go](../backend/internal/api/ai.go) y
452
+ [capacidades y límites](../doc/FEATURES.md).
453
+
454
+ ## Estado y verificación
455
+
456
+ Implementación 27/oct/2026. Comprobaciones locales:
457
+
458
+ ```sh
459
+ npm ci
460
+ npm run typecheck
461
+ npm test
462
+ npm run routes:sync
463
+ npm run build
464
+ ```
465
+
466
+ Las pruebas lanzan el ejecutable real contra un servidor HTTP sintético local;
467
+ comprueban comandos, login, perfiles, contratos de transporte, descargas, multipart,
468
+ chat y errores. No equivalen a integración Go/SQL/PAC ni prueban timbrados,
469
+ cancelaciones, correos, compras o disponibilidad productiva. No se modificó Go.
470
+ Las 11 pruebas pasaron en Windows (Node 24) y Ubuntu mediante WSL (Node 22.22.0).
471
+ También se consultaron `/health/live` y `/health/ready` del Go real en
472
+ `localhost:8092`: estado `ok`, `ready: true`, conexiones main/sat/catalogs `ok`.
473
+ Esas consultas no comprueban login real ni escrituras de negocio.