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