@dozimple/abap-adt 1.0.1 → 1.2.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/CHANGELOG.md +76 -0
- package/README.es.md +13 -9
- package/README.md +13 -9
- package/dist/core/catalog.en.js +4 -0
- package/dist/core/catalog.js +4 -0
- package/dist/core/concurrency.js +13 -0
- package/dist/core/connection.js +41 -0
- package/dist/core/errors.js +14 -0
- package/dist/core/packages.js +26 -0
- package/dist/core/registry.js +80 -7
- package/dist/core/transport.js +7 -0
- package/dist/tools/core/dumps.js +133 -10
- package/dist/tools/core/enhancements.js +135 -0
- package/dist/tools/core/package_contents.js +3 -14
- package/dist/tools/core/revert_source.js +111 -0
- package/dist/tools/core/run_atc.js +9 -4
- package/dist/tools/core/run_unit_tests.js +4 -1
- package/dist/tools/core/sap_notes.js +148 -0
- package/dist/tools/core/source_search.js +188 -0
- package/dist/tools/core/sql_query.js +18 -2
- package/dist/tools/core/syntax_check.js +17 -2
- package/dist/tools/core/transport_diff.js +11 -13
- package/dist/tools/core/where_used.js +6 -1
- package/dist/tools/local/sap_systems.js +4 -0
- package/docs/TOOLS.md +155 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,82 @@ Formato basado en [Keep a Changelog](https://keepachangelog.com/es-ES/1.1.0/). V
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [1.2.0] - 2026-09-23
|
|
8
|
+
|
|
9
|
+
Cuatro tools para diagnóstico y soporte: dumps agrupados por periodo, búsqueda de texto en el código, estado de notas SAP y ampliaciones/BAdI. Todas verificadas en vivo en un NW 7.50.
|
|
10
|
+
|
|
11
|
+
### Añadido
|
|
12
|
+
- **`enhancements`: ampliaciones y BAdI.** Tres preguntas en una tool, por SQL de diccionario (7.50 y S/4):
|
|
13
|
+
implementaciones de una BAdI, nuevas y clásicas, activas o no, con las propias (Z/Y) primero; ampliaciones de código
|
|
14
|
+
implementadas dentro de un programa, clase o grupo de funciones, avisando de las que **sustituyen** código estándar
|
|
15
|
+
(overwrite); y todas las implementaciones de un espacio de nombres por tipo, con el total real. Lo que cambia el
|
|
16
|
+
comportamiento de un estándar sin tocar su código, que es lo primero a mirar en un incidente. Verificado en vivo en
|
|
17
|
+
un 7.50: 1.070 implementaciones Z (566 de código, 501 de BAdI).
|
|
18
|
+
- **`sap_notes`: estado de notas SAP en el sistema, como en SNOTE**, sin portal ni S-user: si la nota está descargada,
|
|
19
|
+
estado de implementación y de tratamiento, versión, componente y título (español si existe). Por notas concretas
|
|
20
|
+
(«¿está la 2198647 en PRD?») o filtrando por estado y/o prefijo de componente, con el total real. Distingue «no
|
|
21
|
+
descargada» de «no implementada». Los códigos de estado no son valores fijos del diccionario: su significado sale de
|
|
22
|
+
las constantes de SAP (`IF_SCWN_NA_CONSTANTS`), verificadas en un 7.50. En sistemas con datos productivos no se
|
|
23
|
+
muestra quién trató la nota. Sustituto seguro del MCP de notas de SAP, que usa APIs privadas y el S-user.
|
|
24
|
+
- **`source_search`: buscar texto en el código de un paquete, una orden o una lista de objetos.** Texto literal o
|
|
25
|
+
expresión regular (máx. 200 caracteres), sin distinguir mayúsculas; devuelve objeto, include, línea y la línea
|
|
26
|
+
encontrada, con salida estructurada. Lee cada fuente (programas, clases con sus includes locales, interfaces,
|
|
27
|
+
módulos e includes propios de los grupos de funciones, CDS) con 4 lecturas en paralelo, progreso y cancelación;
|
|
28
|
+
exige un alcance, tiene tope de objetos (150 por defecto, ~0,8 s por objeto medido en 7.50) y lista aparte los
|
|
29
|
+
objetos que no pudo leer. Existe porque ADT no tiene búsqueda de texto en NW 7.50 (`textsearch` da 404 y no está
|
|
30
|
+
en el discovery), y llega donde `where_used` no: verificado en vivo, `where_used` no encontró ningún uso de una
|
|
31
|
+
clase de excepción y `source_search` encontró 49 en 6 objetos del mismo paquete.
|
|
32
|
+
- **`dumps(group_by, days)`: los errores más frecuentes de un periodo.** Cuenta los dumps de los últimos N días (hasta
|
|
33
|
+
90) agrupados por error, programa, error+programa, usuario o día, con primer y último caso, usuarios distintos
|
|
34
|
+
afectados y dónde terminó el más reciente (include y línea); salida estructurada. Los grupos salen de la cabecera
|
|
35
|
+
de ST22 (`SNAP_BEG.FLIST`, solo metadatos: `SNAP`, que guarda valores de variables, sigue vetada), porque el feed
|
|
36
|
+
ADT solo trae los dumps más recientes: en un 7.50 real devolvió 5 con miles en ST22, y su parámetro `from` no
|
|
37
|
+
amplía la ventana. En sistemas con datos productivos no se agrupa por usuario (columna personal); el número de
|
|
38
|
+
usuarios afectados sí se da. Cierra el hueco anotado el 11-09.
|
|
39
|
+
|
|
40
|
+
### Corregido
|
|
41
|
+
- `package_contents` listaba objetos **borrados** que TADIR conserva con `DELFLAG = 'X'`; ahora no aparecen.
|
|
42
|
+
|
|
43
|
+
## [1.1.0] - 2026-09-22
|
|
44
|
+
|
|
45
|
+
Tool nueva y contrato MCP ampliado (salida estructurada, progreso, cancelación) más resiliencia de conexión: el sprint 1 y parte del 2 del plan de mejoras.
|
|
46
|
+
|
|
47
|
+
### Añadido
|
|
48
|
+
- **Progreso y cancelación** (sprint 2 del plan): las tools largas informan de su avance (`transport_diff` objeto a
|
|
49
|
+
objeto; `run_atc`, `where_used` y `run_unit_tests` por fase) y el cliente lo recibe como notificaciones de progreso
|
|
50
|
+
MCP si las pidió. La cancelación del cliente se respeta **entre pasos**: el paso en curso termina (una llamada ADT
|
|
51
|
+
no se puede abortar) y el siguiente ya no empieza. Una petición cancelada termina como `CANCELLED`, un tipo propio
|
|
52
|
+
que no cuenta como fallo del servidor ni abre el circuito.
|
|
53
|
+
- **Salida estructurada** (`outputSchema` / `structuredContent` de MCP), sprint 2 del plan: una tool puede declarar
|
|
54
|
+
`output` y devolver los mismos datos del texto de forma tipada, para que el cliente no tenga que interpretar la
|
|
55
|
+
prosa. El registro exige que toda respuesta no errónea de esas tools la traiga (si falta es error del servidor,
|
|
56
|
+
no un éxito a medias) y el SDK la valida contra el esquema antes de responder. Primeras tools: `sql_query` (filas,
|
|
57
|
+
columnas, valores hasta 500, `truncated`, avisos) y `syntax_check` (errores, avisos y mensajes con línea y
|
|
58
|
+
severidad). `docs/TOOLS.md` documenta el esquema de salida de cada una.
|
|
59
|
+
- **`revert_source`: volver a una versión anterior con confirmación.** Tras un `write_source` cuya activación falló,
|
|
60
|
+
el objeto queda con un borrador inactivo encima de la versión activa; la tool vuelve a escribir la versión elegida
|
|
61
|
+
(`active`: la última activa; `previous`: la anterior a la activa; `N`: una del historial de `object_versions`)
|
|
62
|
+
pasando por la misma vista previa, huella, bloqueo y orden que cualquier escritura. Nunca revierte por su cuenta:
|
|
63
|
+
un rollback automático que pisara una versión sin preguntar sería peor que dejar el objeto inactivo.
|
|
64
|
+
- **Resiliencia de conexión** (sprint 1 del plan de mejoras):
|
|
65
|
+
- **Sesión caducada renovada en las lecturas.** Si una tool de lectura recibe un token CSRF rechazado o un 401 en
|
|
66
|
+
una sesión que ya había entrado, el servidor descarta el cliente, vuelve a entrar y repite la lectura una vez;
|
|
67
|
+
la respuesta lo anota. Nunca en escrituras ni ejecuciones: el bloqueo se perdió con la sesión y un reintento
|
|
68
|
+
podría escribir dos veces. Una contraseña rechazada sigue fallando a la primera y sigue olvidándose.
|
|
69
|
+
- **Circuit breaker por sistema.** Tres fallos de red en un minuto abren el circuito de ESE sistema durante un
|
|
70
|
+
minuto: toda tool responde al instante «no se vuelve a intentar durante N s» en vez de esperar 120 s por llamada.
|
|
71
|
+
Los demás sistemas no se ven afectados; `sap_systems(check=true)` lo cierra y reintenta. Solo cuentan los fallos
|
|
72
|
+
de red reales de la librería, no los tiempos agotados propios.
|
|
73
|
+
- **Tiempo máximo por tool** (`timeoutMs`, por defecto 60 s; ATC y diff de orden 180 s, where-used y ABAP Unit
|
|
74
|
+
120 s). Al vencer, error `NETWORK` con el tiempo y la sugerencia de acotar; no se reintenta.
|
|
75
|
+
|
|
76
|
+
### Cambiado
|
|
77
|
+
- Insignia de **OpenSSF Best Practices (Passing)** en el README: el proyecto cumple los 67 criterios del nivel
|
|
78
|
+
Passing, incluidas las sugerencias, con la ficha pública en https://www.bestpractices.dev/projects/14759.
|
|
79
|
+
- El job de publicación usa Node 24, que ya trae npm >= 11.5.1: se quita la instalación global de npm, que no se
|
|
80
|
+
puede fijar por hash. Un paso comprueba la versión y falla antes de publicar si no la cumple. El build sigue en
|
|
81
|
+
Node 22, la versión mínima que soporta el servidor.
|
|
82
|
+
|
|
7
83
|
## [1.0.1] - 2026-09-22
|
|
8
84
|
|
|
9
85
|
Versión de mantenimiento: seguimiento de la auditoría de seguridad, procedimiento de commit y pruebas por propiedades.
|
package/README.es.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
[English](README.md) · **Español**
|
|
6
6
|
|
|
7
|
-
[](../../actions/workflows/ci.yml)    
|
|
7
|
+
[](../../actions/workflows/ci.yml) [](https://www.bestpractices.dev/projects/14759)    
|
|
8
8
|
|
|
9
9
|
**IA que trabaja en tu SAP con las reglas de un consultor senior.**
|
|
10
10
|
|
|
@@ -56,12 +56,12 @@ El agente elige las tools, las encadena y responde con evidencia. Cada respuesta
|
|
|
56
56
|
| Grupo | Para qué | Tools |
|
|
57
57
|
|---|---|---|
|
|
58
58
|
| [Revisión de código y pases](#g-revision) | Saber qué cambia de verdad una orden y qué puede romper, antes de liberarla. | 5 |
|
|
59
|
-
| [Calidad, ATC y remediación](#g-calidad) | Encontrar, entender y corregir hallazgos con la sintaxis y las correcciones reales de SAP. |
|
|
60
|
-
| [Exploración del repositorio](#g-exploracion) | Leer y entender cualquier objeto ABAP y sus relaciones, en ECC y en S/4HANA. |
|
|
59
|
+
| [Calidad, ATC y remediación](#g-calidad) | Encontrar, entender y corregir hallazgos con la sintaxis y las correcciones reales de SAP. | 6 |
|
|
60
|
+
| [Exploración del repositorio](#g-exploracion) | Leer y entender cualquier objeto ABAP y sus relaciones, en ECC y en S/4HANA. | 11 |
|
|
61
61
|
| [Consulta de datos](#g-datos) | Preguntar a las tablas con ABAP SQL, de solo lectura y sin tocar material de credenciales. | 2 |
|
|
62
62
|
| [Diagnóstico de incidentes](#g-diagnostico) | Reunir en una conversación lo que antes exigía ST22, SM37, SLG1 y /IWFND/ERROR_LOG. | 4 |
|
|
63
63
|
| [Documentación SAP](#g-documentacion) | Responder con la documentación oficial y comprobar qué sintaxis existe en cada release. | 7 |
|
|
64
|
-
| [Escritura controlada](#g-escritura) | Guardar cambios solo en desarrollo, en la orden correcta y con la sintaxis verificada antes. |
|
|
64
|
+
| [Escritura controlada](#g-escritura) | Guardar cambios solo en desarrollo, en la orden correcta y con la sintaxis verificada antes. | 5 |
|
|
65
65
|
| [DoZimple Transport Risk](#g-transport-risk) | Decidir si un pase entero puede ir a calidad o productivo, con el porqué en lenguaje de negocio. | 7 |
|
|
66
66
|
| [Operación y crecimiento](#g-operacion) | Ver qué funciona en cada sistema y decidir con datos cuál es la siguiente tool. | 4 |
|
|
67
67
|
<!-- groups:end -->
|
|
@@ -122,6 +122,7 @@ la **[referencia completa](docs/TOOLS.md)**.
|
|
|
122
122
|
| [`run_atc`](docs/TOOLS.md#calidad) | **Ejecutar ATC.** Ejecuta el ATC sobre un objeto o una orden de transporte y lista los hallazgos numerados (prioridad, línea, check, mensaje), con los totales P1/P2/P3 que da SAP. | lectura |
|
|
123
123
|
| [`atc_quickfix`](docs/TOOLS.md#calidad) | **Correcciones propuestas por SAP.** Correcciones que SAP ofrece (las mismas de Ctrl+1 en Eclipse) para un hallazgo ATC o una línea: crear símbolo de texto, extraer constante, etc. | lectura |
|
|
124
124
|
| [`api_release_state`](docs/TOOLS.md#calidad) | **¿Está liberada esta API? ¿Cuál es su sucesor?** Estado de liberación de un objeto SAP (clase, FM/BAPI, tabla, CDS…) por contrato C0–C4 y su sucesor liberado, leído del propio sistema. | lectura |
|
|
125
|
+
| [`sap_notes`](docs/TOOLS.md#calidad) | **Notas SAP en el sistema (SNOTE).** Estado de notas SAP en este sistema, como en SNOTE: si está descargada, estado de implementación (completa, incompleta, versión anterior, se puede implementar, obsoleta, no se puede implementar), estado de tratamiento, versión, componente y título. | lectura |
|
|
125
126
|
| [`syntax_check`](docs/TOOLS.md#calidad) | **Chequeo de sintaxis SAP.** Chequeo de sintaxis real de SAP (no abaplint). | lectura |
|
|
126
127
|
| [`run_unit_tests`](docs/TOOLS.md#calidad) | **Ejecutar ABAP Unit.** Ejecuta los tests ABAP Unit de una clase o programa y devuelve el resultado por método, con el detalle de cada fallo. | ejecuta (DEV) |
|
|
127
128
|
|
|
@@ -135,12 +136,14 @@ la **[referencia completa](docs/TOOLS.md)**.
|
|
|
135
136
|
| [`search_objects`](docs/TOOLS.md#exploracion) | **Buscar objetos ABAP.** Busca objetos del repositorio por nombre (admite * como comodín). | lectura |
|
|
136
137
|
| [`get_source`](docs/TOOLS.md#exploracion) | **Leer fuente ABAP.** Lee la fuente de cualquier objeto: programa, include, clase (y sus includes), interfaz, módulo de función (sin necesidad de saber el grupo), CDS, tabla/estructura, etc. | lectura |
|
|
137
138
|
| [`where_used`](docs/TOOLS.md#exploracion) | **Dónde se usa.** Lista de uso (where-used) de un objeto: quién lo referencia, con paquete y responsable. | lectura |
|
|
139
|
+
| [`source_search`](docs/TOOLS.md#exploracion) | **Buscar texto en el código.** Busca un texto (o una expresión regular) en el código fuente de un paquete (con subpaquetes), de una orden de transporte o de una lista de objetos, y devuelve objeto, include, línea y la línea encontrada. | lectura |
|
|
138
140
|
| [`object_versions`](docs/TOOLS.md#exploracion) | **Versiones de un objeto.** Historial de versiones de un objeto (fecha, autor, orden). | lectura |
|
|
139
141
|
| [`package_contents`](docs/TOOLS.md#exploracion) | **Contenido de un paquete.** Objetos de un paquete de desarrollo agrupados por tipo, con sus subpaquetes (TADIR/TDEVC, cualquier release). | lectura |
|
|
140
142
|
| [`ddic_type_info`](docs/TOOLS.md#exploracion) | **Elemento de datos, dominio o tipo tabla.** Definición de un tipo DDIC: elemento de datos (dominio, tipo, longitud, textos), dominio (tipo, longitud, valores fijos, tabla de valores) o tipo tabla (tipo de línea, clave). | lectura |
|
|
141
143
|
| [`transaction_info`](docs/TOOLS.md#exploracion) | **Qué ejecuta una transacción.** Programa, dynpro y parámetros de una transacción (TSTC/TSTCP), con su texto. | lectura |
|
|
142
144
|
| [`function_modules`](docs/TOOLS.md#exploracion) | **Módulos de función de un grupo.** Lista los módulos de función de un grupo de funciones, con su texto y si son RFC o de actualización. | lectura |
|
|
143
145
|
| [`text_elements`](docs/TOOLS.md#exploracion) | **Símbolos de texto y textos de selección.** Lee los símbolos de texto (TEXT-001…), textos de selección o encabezados de un programa, clase o grupo de funciones. | lectura |
|
|
146
|
+
| [`enhancements`](docs/TOOLS.md#exploracion) | **Ampliaciones y BAdI.** Qué ampliaciones hay y dónde. badi=NOMBRE: implementaciones de esa BAdI, nuevas y clásicas, activas o no. program + program_type (PROG, CLAS, FUGR): ampliaciones de código (puntos y secciones implícitos o explícitos) implementadas dentro de ese programa, clase o grupo — lo que cambia el comportamiento de un estándar sin tocar su código. prefix (p. ej. Z): implementaciones de ampliación de ese espacio de nombres, por tipo. | lectura |
|
|
144
147
|
|
|
145
148
|
<a id="g-datos"></a>
|
|
146
149
|
### Consulta de datos
|
|
@@ -187,6 +190,7 @@ la **[referencia completa](docs/TOOLS.md)**.
|
|
|
187
190
|
| Tool | Qué hace | Acceso |
|
|
188
191
|
|---|---|---|
|
|
189
192
|
| [`write_source`](docs/TOOLS.md#escritura) | **Guardar fuente en SAP.** Sustituye la fuente COMPLETA de un objeto existente (o de un include de clase), en la orden indicada. | escribe (DEV autorizado) |
|
|
193
|
+
| [`revert_source`](docs/TOOLS.md#escritura) | **Volver a una versión anterior.** Deshace un cambio escribiendo de nuevo una versión anterior del objeto: la última activa (para limpiar un borrador inactivo tras un write_source cuya activación falló), la anterior a la activa, o una concreta del historial de object_versions. | escribe (DEV autorizado) |
|
|
190
194
|
| [`activate`](docs/TOOLS.md#escritura) | **Activar objeto.** Activa un objeto y devuelve los mensajes de SAP tal cual (errores con línea, avisos, objetos que quedan inactivos). | escribe (DEV autorizado) |
|
|
191
195
|
| [`write_text_elements`](docs/TOOLS.md#escritura) | **Crear o cambiar símbolos de texto.** Añade o modifica símbolos de texto (o textos de selección) de un programa/clase/grupo, fusionando con los existentes: no borra los que no se mencionan. | escribe (DEV autorizado) |
|
|
192
196
|
| [`create_transport`](docs/TOOLS.md#escritura) | **Crear orden de transporte.** Crea una orden workbench para el paquete de un objeto, ANTES de la primera edición, para que el cambio caiga en la orden del ticket y no en una tarea reutilizada. | escribe (DEV autorizado) |
|
|
@@ -291,7 +295,7 @@ Modelo de amenazas con STRIDE y OWASP Top 10 para aplicaciones LLM: **[docs/THRE
|
|
|
291
295
|
Con la versión exacta y sin scripts de instalación, igual que las dependencias del propio proyecto:
|
|
292
296
|
|
|
293
297
|
```sh
|
|
294
|
-
npm install -g --ignore-scripts @dozimple/abap-adt@1.0
|
|
298
|
+
npm install -g --ignore-scripts @dozimple/abap-adt@1.2.0
|
|
295
299
|
PKG="$(npm root -g)/@dozimple/abap-adt"
|
|
296
300
|
mkdir -p ~/.config/abap-adt-dozimple && chmod 700 ~/.config/abap-adt-dozimple
|
|
297
301
|
cp "$PKG/config/systems.example.json" ~/.config/abap-adt-dozimple/systems.json # sistemas, roles y permisos
|
|
@@ -307,9 +311,9 @@ Registro en el cliente MCP:
|
|
|
307
311
|
```
|
|
308
312
|
|
|
309
313
|
Cada versión se publica desde el CI con [procedencia de npm](https://docs.npmjs.com/generating-provenance-statements):
|
|
310
|
-
`npm view @dozimple/abap-adt@1.0
|
|
314
|
+
`npm view @dozimple/abap-adt@1.2.0 dist.attestations` muestra la atestación, y la release de GitHub incluye el
|
|
311
315
|
paquete, su bundle de Sigstore (`.sigstore.json`), el mismo bundle como procedencia in-toto (`.intoto.jsonl`)
|
|
312
|
-
y el SBOM. Para comprobarlo: `gh attestation verify dozimple-abap-adt-1.0.
|
|
316
|
+
y el SBOM. Para comprobarlo: `gh attestation verify dozimple-abap-adt-1.2.0.tgz --repo <owner>/abap-adt-dozimple`.
|
|
313
317
|
|
|
314
318
|
### Desde el código fuente
|
|
315
319
|
|
|
@@ -343,7 +347,7 @@ abap-adt-doZimple se construye sobre el trabajo de otros, y lo reconoce: cada to
|
|
|
343
347
|
<!-- credits:start -->
|
|
344
348
|
| Proyecto | Autor / titular | Licencia | Tipo | Usado en |
|
|
345
349
|
|---|---|---|---|---|
|
|
346
|
-
| [abap-adt-api](https://github.com/marcellourbani/abap-adt-api) | Marcello Urbani | MIT | dependencia | todas (núcleo), `transport_diff`, `transport_contents`, `inactive_objects`, `edit_preflight`, `run_atc` y
|
|
350
|
+
| [abap-adt-api](https://github.com/marcellourbani/abap-adt-api) | Marcello Urbani | MIT | dependencia | todas (núcleo), `transport_diff`, `transport_contents`, `inactive_objects`, `edit_preflight`, `run_atc` y 26 más |
|
|
347
351
|
| [Model Context Protocol TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk) | Model Context Protocol | MIT | dependencia | todas (núcleo) |
|
|
348
352
|
| [mcp-sap-docs](https://github.com/marianfoo/mcp-sap-docs) | Marian Zeis (marianfoo) | Apache-2.0 | dependencia | `abap_feature_matrix`, `docs_search`, `docs_fetch`, `clean_core_objects`, `clean_core_object`, `abap_lint` y 1 más |
|
|
349
353
|
| [abaplint](https://github.com/abaplint/abaplint) | Lars Hvam y contribuidores | MIT | dependencia | `abap_lint` |
|
|
@@ -358,7 +362,7 @@ abap-adt-doZimple se construye sobre el trabajo de otros, y lo reconoce: cada to
|
|
|
358
362
|
| [ABAP Remote FS (vscode_abap_remote_fs)](https://github.com/marcellourbani/vscode_abap_remote_fs) | Marcello Urbani | MIT | idea | `syntax_check` |
|
|
359
363
|
| [mcp-abap-adt](https://github.com/mario-andreschak/mcp-abap-adt) | mario-andreschak | MIT | idea | `search_objects`, `get_source`, `package_contents`, `ddic_type_info`, `transaction_info`, `table_contents` |
|
|
360
364
|
| [ARC-1](https://github.com/arc-mcp/arc-1) | arc-mcp (Marian Zeis y contribuidores) | MIT | idea | `transport_diff`, `atc_quickfix`, `gateway_errors` |
|
|
361
|
-
| [vibing-steampunk](https://github.com/oisee/vibing-steampunk) | oisee y contribuidores | MIT | idea | `co_change`, `api_release_state`, `jobs`, `application_log` |
|
|
365
|
+
| [vibing-steampunk](https://github.com/oisee/vibing-steampunk) | oisee y contribuidores | MIT | idea | `co_change`, `api_release_state`, `source_search`, `enhancements`, `jobs`, `application_log` |
|
|
362
366
|
| [ABAP Accelerator for Amazon Q Developer](https://github.com/aws-solutions-library-samples/guidance-for-deploying-sap-abap-accelerator-for-amazon-q-developer) | AWS Solutions Library Samples | MIT-0 | idea | todas (núcleo), `usage_stats` |
|
|
363
367
|
| [An O(ND) Difference Algorithm and Its Variations (1986)](https://doi.org/10.1007/BF01840446) | Eugene W. Myers | algoritmo publicado | algoritmo | `transport_diff`, `atc_quickfix` |
|
|
364
368
|
<!-- credits:end -->
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
**English** · [Español](README.es.md)
|
|
6
6
|
|
|
7
|
-
[](../../actions/workflows/ci.yml)    
|
|
7
|
+
[](../../actions/workflows/ci.yml) [](https://www.bestpractices.dev/projects/14759)    
|
|
8
8
|
|
|
9
9
|
**AI that works on your SAP system with the rules of a senior consultant.**
|
|
10
10
|
|
|
@@ -57,12 +57,12 @@ and **a failure is never presented as an empty result or as success**.
|
|
|
57
57
|
| Group | What for | Tools |
|
|
58
58
|
|---|---|---|
|
|
59
59
|
| [Code review and transports](#g-revision) | Know what a transport really changes and what it may break, before releasing it. | 5 |
|
|
60
|
-
| [Quality, ATC and remediation](#g-calidad) | Find, understand and fix findings with SAP's real syntax check and quick fixes. |
|
|
61
|
-
| [Repository exploration](#g-exploracion) | Read and understand any ABAP object and its relations, on ECC and S/4HANA. |
|
|
60
|
+
| [Quality, ATC and remediation](#g-calidad) | Find, understand and fix findings with SAP's real syntax check and quick fixes. | 6 |
|
|
61
|
+
| [Repository exploration](#g-exploracion) | Read and understand any ABAP object and its relations, on ECC and S/4HANA. | 11 |
|
|
62
62
|
| [Data queries](#g-datos) | Query tables with ABAP SQL, read-only, with sensitive and personal data protected. | 2 |
|
|
63
63
|
| [Incident diagnosis](#g-diagnostico) | One conversation for what used to take ST22, SM37, SLG1 and /IWFND/ERROR_LOG. | 4 |
|
|
64
64
|
| [SAP documentation](#g-documentacion) | Answer from official documentation and check which syntax exists in each release. | 7 |
|
|
65
|
-
| [Controlled writes](#g-escritura) | Save changes only in development, in the right transport, previewed and confirmed by a human. |
|
|
65
|
+
| [Controlled writes](#g-escritura) | Save changes only in development, in the right transport, previewed and confirmed by a human. | 5 |
|
|
66
66
|
| [DoZimple Transport Risk](#g-transport-risk) | Decide whether a whole release can go to QA or production, with the why in business terms. | 7 |
|
|
67
67
|
| [Operations and growth](#g-operacion) | See what works on each system and decide the next tool with data. | 4 |
|
|
68
68
|
<!-- groups:end -->
|
|
@@ -123,6 +123,7 @@ Summary per group; each tool's details — parameters, types, defaults, requirem
|
|
|
123
123
|
| [`run_atc`](docs/TOOLS.md#calidad) | **Run ATC.** Runs the ABAP Test Cockpit on an object or a transport and lists numbered findings (priority, line, check, message) with SAP's P1/P2/P3 totals. | read |
|
|
124
124
|
| [`atc_quickfix`](docs/TOOLS.md#calidad) | **SAP-proposed fixes.** The fixes SAP offers (the same as Ctrl+1 in Eclipse) for an ATC finding or a line: create text symbol, extract constant, etc. | read |
|
|
125
125
|
| [`api_release_state`](docs/TOOLS.md#calidad) | **Is this API released? What is its successor?** Release state of an SAP object (class, function module/BAPI, table, CDS…) by contract C0–C4 and its released successor, read from the system itself. | read |
|
|
126
|
+
| [`sap_notes`](docs/TOOLS.md#calidad) | **SAP notes in the system (SNOTE).** Whether a note is downloaded in this system, its implementation and processing status, version, component and title, per note or filtered by status/component; no SAP portal or S-user needed. | read |
|
|
126
127
|
| [`syntax_check`](docs/TOOLS.md#calidad) | **SAP syntax check.** SAP's real syntax check (not abaplint), also on code not saved yet. | read |
|
|
127
128
|
| [`run_unit_tests`](docs/TOOLS.md#calidad) | **Run ABAP Unit.** Runs the ABAP Unit tests of a class or program (harmless and short only) and returns the result per method, with each failure in detail. | executes (DEV) |
|
|
128
129
|
|
|
@@ -136,12 +137,14 @@ Summary per group; each tool's details — parameters, types, defaults, requirem
|
|
|
136
137
|
| [`search_objects`](docs/TOOLS.md#exploracion) | **Search ABAP objects.** Searches repository objects by name (supports * wildcards). | read |
|
|
137
138
|
| [`get_source`](docs/TOOLS.md#exploracion) | **Read ABAP source.** Reads the source of any object: program, include, class (and its includes), interface, function module (without knowing its group), CDS, table/structure, etc. | read |
|
|
138
139
|
| [`where_used`](docs/TOOLS.md#exploracion) | **Where used.** Where-used list of an object: who references it, with package and owner. | read |
|
|
140
|
+
| [`source_search`](docs/TOOLS.md#exploracion) | **Search text in source code.** Searches a text or regex in the source of a package (with subpackages), a transport or a list of objects, and returns object, include, line and the matching line; reaches what where-used misses (dynamic calls, literals, Z code outside the reference index). | read |
|
|
139
141
|
| [`object_versions`](docs/TOOLS.md#exploracion) | **Object versions.** Version history of an object (date, author, transport). | read |
|
|
140
142
|
| [`package_contents`](docs/TOOLS.md#exploracion) | **Package contents.** Objects of a development package grouped by type, with subpackages (TADIR/TDEVC, any release). | read |
|
|
141
143
|
| [`ddic_type_info`](docs/TOOLS.md#exploracion) | **Data element, domain or table type.** Definition of a DDIC type: data element (domain, type, length, texts), domain (type, length, fixed values, value table) or table type (line type, key). | read |
|
|
142
144
|
| [`transaction_info`](docs/TOOLS.md#exploracion) | **What a transaction runs.** Program, screen and parameters of a transaction (TSTC/TSTCP), with its text. | read |
|
|
143
145
|
| [`function_modules`](docs/TOOLS.md#exploracion) | **Function modules of a group.** Lists the function modules of a function group with their text and whether they are RFC or update modules; given a module, finds its group and siblings. Works with /XXX/ namespaces. | read |
|
|
144
146
|
| [`text_elements`](docs/TOOLS.md#exploracion) | **Text symbols and selection texts.** Reads the text symbols (TEXT-001…), selection texts or headings of a program, class or function group. | read |
|
|
147
|
+
| [`enhancements`](docs/TOOLS.md#exploracion) | **Enhancements and BAdIs.** Implementations of a BAdI (new and classic, active or not), source code enhancements implemented inside a program, class or function group (flagging those that overwrite standard code), or all enhancement implementations of a namespace by type. | read |
|
|
145
148
|
|
|
146
149
|
<a id="g-datos"></a>
|
|
147
150
|
### Data queries
|
|
@@ -188,6 +191,7 @@ Summary per group; each tool's details — parameters, types, defaults, requirem
|
|
|
188
191
|
| Tool | What it does | Access |
|
|
189
192
|
|---|---|---|
|
|
190
193
|
| [`write_source`](docs/TOOLS.md#escritura) | **Save source to SAP.** Replaces the FULL source of an existing object (or class include) in the given transport, after a preview with syntax check and diff and a human confirmation. | writes (authorized DEV) |
|
|
194
|
+
| [`revert_source`](docs/TOOLS.md#escritura) | **Revert to an earlier version.** Writes back an earlier version of an object (the last active one, the one before it, or a numbered one from object_versions) through the same preview, fingerprint, lock and transport as write_source; never reverts on its own. | writes (authorized DEV) |
|
|
191
195
|
| [`activate`](docs/TOOLS.md#escritura) | **Activate object.** Activates an object and returns SAP's messages as they are (errors with line, warnings, objects left inactive). | writes (authorized DEV) |
|
|
192
196
|
| [`write_text_elements`](docs/TOOLS.md#escritura) | **Create or change text symbols.** Adds or changes text symbols (or selection texts) of a program/class/group, merging with the existing ones: nothing not mentioned is deleted. | writes (authorized DEV) |
|
|
193
197
|
| [`create_transport`](docs/TOOLS.md#escritura) | **Create transport request.** Creates a workbench request for an object's package BEFORE the first edit, so the change lands in the ticket's transport and not in a reused task. | writes (authorized DEV) |
|
|
@@ -291,7 +295,7 @@ Designed to pass a Security and Basis review without exceptions. Details: **[SEC
|
|
|
291
295
|
Pinned to an exact version, without install scripts, like the project's own dependencies:
|
|
292
296
|
|
|
293
297
|
```sh
|
|
294
|
-
npm install -g --ignore-scripts @dozimple/abap-adt@1.0
|
|
298
|
+
npm install -g --ignore-scripts @dozimple/abap-adt@1.2.0
|
|
295
299
|
PKG="$(npm root -g)/@dozimple/abap-adt"
|
|
296
300
|
mkdir -p ~/.config/abap-adt-dozimple && chmod 700 ~/.config/abap-adt-dozimple
|
|
297
301
|
cp "$PKG/config/systems.example.json" ~/.config/abap-adt-dozimple/systems.json # systems, roles and permissions
|
|
@@ -307,9 +311,9 @@ MCP client registration:
|
|
|
307
311
|
```
|
|
308
312
|
|
|
309
313
|
Every release is published from CI with [npm provenance](https://docs.npmjs.com/generating-provenance-statements):
|
|
310
|
-
`npm view @dozimple/abap-adt@1.0
|
|
314
|
+
`npm view @dozimple/abap-adt@1.2.0 dist.attestations` shows the attestation, and the GitHub release carries the
|
|
311
315
|
tarball, its Sigstore bundle (`.sigstore.json`), the same bundle as in-toto provenance (`.intoto.jsonl`) and the
|
|
312
|
-
SBOM. To check it: `gh attestation verify dozimple-abap-adt-1.0.
|
|
316
|
+
SBOM. To check it: `gh attestation verify dozimple-abap-adt-1.2.0.tgz --repo <owner>/abap-adt-dozimple`.
|
|
313
317
|
|
|
314
318
|
### From source
|
|
315
319
|
|
|
@@ -344,7 +348,7 @@ abap-adt-doZimple is built on other people's work, and says so: each tool lists
|
|
|
344
348
|
<!-- credits:start -->
|
|
345
349
|
| Project | Author / holder | License | Type | Used in |
|
|
346
350
|
|---|---|---|---|---|
|
|
347
|
-
| [abap-adt-api](https://github.com/marcellourbani/abap-adt-api) | Marcello Urbani | MIT | dependency | all (core), `transport_diff`, `transport_contents`, `inactive_objects`, `edit_preflight`, `run_atc` and
|
|
351
|
+
| [abap-adt-api](https://github.com/marcellourbani/abap-adt-api) | Marcello Urbani | MIT | dependency | all (core), `transport_diff`, `transport_contents`, `inactive_objects`, `edit_preflight`, `run_atc` and 26 more |
|
|
348
352
|
| [Model Context Protocol TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk) | Model Context Protocol | MIT | dependency | all (core) |
|
|
349
353
|
| [mcp-sap-docs](https://github.com/marianfoo/mcp-sap-docs) | Marian Zeis (marianfoo) | Apache-2.0 | dependency | `abap_feature_matrix`, `docs_search`, `docs_fetch`, `clean_core_objects`, `clean_core_object`, `abap_lint` and 1 more |
|
|
350
354
|
| [abaplint](https://github.com/abaplint/abaplint) | Lars Hvam and contributors | MIT | dependency | `abap_lint` |
|
|
@@ -359,7 +363,7 @@ abap-adt-doZimple is built on other people's work, and says so: each tool lists
|
|
|
359
363
|
| [ABAP Remote FS (vscode_abap_remote_fs)](https://github.com/marcellourbani/vscode_abap_remote_fs) | Marcello Urbani | MIT | idea | `syntax_check` |
|
|
360
364
|
| [mcp-abap-adt](https://github.com/mario-andreschak/mcp-abap-adt) | mario-andreschak | MIT | idea | `search_objects`, `get_source`, `package_contents`, `ddic_type_info`, `transaction_info`, `table_contents` |
|
|
361
365
|
| [ARC-1](https://github.com/arc-mcp/arc-1) | arc-mcp (Marian Zeis and contributors) | MIT | idea | `transport_diff`, `atc_quickfix`, `gateway_errors` |
|
|
362
|
-
| [vibing-steampunk](https://github.com/oisee/vibing-steampunk) | oisee and contributors | MIT | idea | `co_change`, `api_release_state`, `jobs`, `application_log` |
|
|
366
|
+
| [vibing-steampunk](https://github.com/oisee/vibing-steampunk) | oisee and contributors | MIT | idea | `co_change`, `api_release_state`, `source_search`, `enhancements`, `jobs`, `application_log` |
|
|
363
367
|
| [ABAP Accelerator for Amazon Q Developer](https://github.com/aws-solutions-library-samples/guidance-for-deploying-sap-abap-accelerator-for-amazon-q-developer) | AWS Solutions Library Samples | MIT-0 | idea | all (core), `usage_stats` |
|
|
364
368
|
| [An O(ND) Difference Algorithm and Its Variations (1986)](https://doi.org/10.1007/BF01840446) | Eugene W. Myers | published algorithm | algorithm | `transport_diff`, `atc_quickfix` |
|
|
365
369
|
<!-- credits:end -->
|
package/dist/core/catalog.en.js
CHANGED
|
@@ -23,11 +23,14 @@ export const TOOLS_EN = {
|
|
|
23
23
|
run_atc: "**Run ATC.** Runs the ABAP Test Cockpit on an object or a transport and lists numbered findings (priority, line, check, message) with SAP's P1/P2/P3 totals.",
|
|
24
24
|
atc_quickfix: "**SAP-proposed fixes.** The fixes SAP offers (the same as Ctrl+1 in Eclipse) for an ATC finding or a line: create text symbol, extract constant, etc.",
|
|
25
25
|
api_release_state: "**Is this API released? What is its successor?** Release state of an SAP object (class, function module/BAPI, table, CDS…) by contract C0–C4 and its released successor, read from the system itself.",
|
|
26
|
+
sap_notes: "**SAP notes in the system (SNOTE).** Whether a note is downloaded in this system, its implementation and processing status, version, component and title, per note or filtered by status/component; no SAP portal or S-user needed.",
|
|
26
27
|
syntax_check: "**SAP syntax check.** SAP's real syntax check (not abaplint), also on code not saved yet.",
|
|
27
28
|
run_unit_tests: "**Run ABAP Unit.** Runs the ABAP Unit tests of a class or program (harmless and short only) and returns the result per method, with each failure in detail.",
|
|
28
29
|
search_objects: "**Search ABAP objects.** Searches repository objects by name (supports * wildcards).",
|
|
29
30
|
get_source: "**Read ABAP source.** Reads the source of any object: program, include, class (and its includes), interface, function module (without knowing its group), CDS, table/structure, etc.",
|
|
30
31
|
where_used: "**Where used.** Where-used list of an object: who references it, with package and owner.",
|
|
32
|
+
source_search: "**Search text in source code.** Searches a text or regex in the source of a package (with subpackages), a transport or a list of objects, and returns object, include, line and the matching line; reaches what where-used misses (dynamic calls, literals, Z code outside the reference index).",
|
|
33
|
+
enhancements: "**Enhancements and BAdIs.** Implementations of a BAdI (new and classic, active or not), source code enhancements implemented inside a program, class or function group (flagging those that overwrite standard code), or all enhancement implementations of a namespace by type.",
|
|
31
34
|
object_versions: "**Object versions.** Version history of an object (date, author, transport).",
|
|
32
35
|
package_contents: "**Package contents.** Objects of a development package grouped by type, with subpackages (TADIR/TDEVC, any release).",
|
|
33
36
|
ddic_type_info: "**Data element, domain or table type.** Definition of a DDIC type: data element (domain, type, length, texts), domain (type, length, fixed values, value table) or table type (line type, key).",
|
|
@@ -48,6 +51,7 @@ export const TOOLS_EN = {
|
|
|
48
51
|
abap_lint: "**abaplint on a snippet.** Runs abaplint locally (code never leaves the machine) on an ABAP snippet or source.",
|
|
49
52
|
docs_community_search: "**Search SAP Community.** Searches SAP Community (blogs and questions) by error message, class or concept.",
|
|
50
53
|
write_source: "**Save source to SAP.** Replaces the FULL source of an existing object (or class include) in the given transport, after a preview with syntax check and diff and a human confirmation.",
|
|
54
|
+
revert_source: "**Revert to an earlier version.** Writes back an earlier version of an object (the last active one, the one before it, or a numbered one from object_versions) through the same preview, fingerprint, lock and transport as write_source; never reverts on its own.",
|
|
51
55
|
activate: "**Activate object.** Activates an object and returns SAP's messages as they are (errors with line, warnings, objects left inactive).",
|
|
52
56
|
write_text_elements: "**Create or change text symbols.** Adds or changes text symbols (or selection texts) of a program/class/group, merging with the existing ones: nothing not mentioned is deleted.",
|
|
53
57
|
create_transport: "**Create transport request.** Creates a workbench request for an object's package BEFORE the first edit, so the change lands in the ticket's transport and not in a reused task.",
|
package/dist/core/catalog.js
CHANGED
|
@@ -40,6 +40,7 @@ export const GROUPS = [
|
|
|
40
40
|
{ name: "run_atc", credits: ADT },
|
|
41
41
|
{ name: "atc_quickfix", credits: ["abapAdtApi", "arc1", "myers"] },
|
|
42
42
|
{ name: "api_release_state", credits: ["abapAdtApi", "vsp"] },
|
|
43
|
+
{ name: "sap_notes", credits: ADT },
|
|
43
44
|
{ name: "syntax_check", credits: ["abapAdtApi", "abapFs"] },
|
|
44
45
|
{ name: "run_unit_tests", credits: ADT },
|
|
45
46
|
],
|
|
@@ -52,12 +53,14 @@ export const GROUPS = [
|
|
|
52
53
|
{ name: "search_objects", credits: ["abapAdtApi", "marioAdt"] },
|
|
53
54
|
{ name: "get_source", credits: ["abapAdtApi", "marioAdt"] },
|
|
54
55
|
{ name: "where_used", credits: ADT },
|
|
56
|
+
{ name: "source_search", credits: ["abapAdtApi", "vsp"] },
|
|
55
57
|
{ name: "object_versions", credits: ADT },
|
|
56
58
|
{ name: "package_contents", credits: ["abapAdtApi", "marioAdt"] },
|
|
57
59
|
{ name: "ddic_type_info", credits: ["abapAdtApi", "marioAdt"] },
|
|
58
60
|
{ name: "transaction_info", credits: ["abapAdtApi", "marioAdt"] },
|
|
59
61
|
{ name: "function_modules", credits: ADT },
|
|
60
62
|
{ name: "text_elements", credits: ADT },
|
|
63
|
+
{ name: "enhancements", credits: ["abapAdtApi", "vsp"] },
|
|
61
64
|
],
|
|
62
65
|
},
|
|
63
66
|
{
|
|
@@ -100,6 +103,7 @@ export const GROUPS = [
|
|
|
100
103
|
pitch: "Guardar cambios solo en desarrollo, en la orden correcta y con la sintaxis verificada antes.",
|
|
101
104
|
tools: [
|
|
102
105
|
{ name: "write_source", credits: ADT },
|
|
106
|
+
{ name: "revert_source", credits: ADT },
|
|
103
107
|
{ name: "activate", credits: ADT },
|
|
104
108
|
{ name: "write_text_elements", credits: ADT },
|
|
105
109
|
{ name: "create_transport", credits: ADT },
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Limita cuántas promesas corren a la vez (no saturar SAP). Conserva el orden de los resultados. */
|
|
2
|
+
export async function pool(items, n, fn) {
|
|
3
|
+
const out = new Array(items.length);
|
|
4
|
+
let next = 0;
|
|
5
|
+
await Promise.all(Array.from({ length: Math.min(n, items.length) }, async () => {
|
|
6
|
+
while (next < items.length) {
|
|
7
|
+
const i = next++;
|
|
8
|
+
out[i] = await fn(items[i]);
|
|
9
|
+
}
|
|
10
|
+
}));
|
|
11
|
+
return out;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=concurrency.js.map
|
package/dist/core/connection.js
CHANGED
|
@@ -8,6 +8,10 @@ import { normalizeValue, wrapSql } from "./sql.js";
|
|
|
8
8
|
const CACHE_DIR = join(homedir(), ".cache", "abap-adt-dozimple");
|
|
9
9
|
const DISCOVERY_TTL_MS = 7 * 24 * 3600 * 1000;
|
|
10
10
|
const REQUEST_TIMEOUT_MS = 120_000;
|
|
11
|
+
/** Circuit breaker: N fallos de red en la ventana abren el circuito durante CIRCUIT_OPEN_MS. */
|
|
12
|
+
const CIRCUIT_FAILURES = 3;
|
|
13
|
+
const CIRCUIT_WINDOW_MS = 60_000;
|
|
14
|
+
const CIRCUIT_OPEN_MS = 60_000;
|
|
11
15
|
/**
|
|
12
16
|
* TLS: con caFile se verifica contra ese certificado (lo correcto para un
|
|
13
17
|
* sistema con certificado propio); allowSelfSigned desactiva la verificación
|
|
@@ -30,9 +34,44 @@ export class SapConnection {
|
|
|
30
34
|
reader;
|
|
31
35
|
caps;
|
|
32
36
|
basisRelease;
|
|
37
|
+
networkFailures = [];
|
|
38
|
+
openUntil = 0;
|
|
33
39
|
constructor(system) {
|
|
34
40
|
this.system = system;
|
|
35
41
|
}
|
|
42
|
+
/** Olvida el cliente de lectura (sesión caducada): la siguiente llamada vuelve a entrar. */
|
|
43
|
+
resetReader() {
|
|
44
|
+
const old = this.reader;
|
|
45
|
+
this.reader = undefined;
|
|
46
|
+
old?.then((c) => c.logout()).catch(() => undefined);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Tres fallos de red en un minuto abren el circuito: durante un minuto ninguna tool intenta nada contra este
|
|
50
|
+
* sistema y responde al instante. Un sistema caído (VPN, host) dejaba de responder 120 s por cada llamada.
|
|
51
|
+
*/
|
|
52
|
+
noteNetworkFailure(now = Date.now()) {
|
|
53
|
+
this.networkFailures = this.networkFailures.filter((t) => now - t < CIRCUIT_WINDOW_MS);
|
|
54
|
+
this.networkFailures.push(now);
|
|
55
|
+
if (this.networkFailures.length >= CIRCUIT_FAILURES) {
|
|
56
|
+
this.openUntil = now + CIRCUIT_OPEN_MS;
|
|
57
|
+
this.networkFailures = [];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** Segundos que faltan para volver a intentar, o 0 si el circuito está cerrado. */
|
|
61
|
+
circuitOpenFor(now = Date.now()) {
|
|
62
|
+
return this.openUntil > now ? Math.ceil((this.openUntil - now) / 1000) : 0;
|
|
63
|
+
}
|
|
64
|
+
/** Cierra el circuito a mano (sap_systems con check): se vuelve a intentar ya. */
|
|
65
|
+
resetCircuit() {
|
|
66
|
+
this.openUntil = 0;
|
|
67
|
+
this.networkFailures = [];
|
|
68
|
+
}
|
|
69
|
+
assertCircuitClosed() {
|
|
70
|
+
const s = this.circuitOpenFor();
|
|
71
|
+
if (s) {
|
|
72
|
+
throw new ToolError("NETWORK", `${this.system.id} no respondió ${CIRCUIT_FAILURES} veces seguidas: no se vuelve a intentar durante ${s} s.`, "sap_systems(check=true) lo reintenta ahora mismo.");
|
|
73
|
+
}
|
|
74
|
+
}
|
|
36
75
|
async newClient() {
|
|
37
76
|
const s = this.system;
|
|
38
77
|
const password = await getPassword(s);
|
|
@@ -53,6 +92,7 @@ export class SapConnection {
|
|
|
53
92
|
}
|
|
54
93
|
/** Cliente de lectura, reutilizado entre llamadas. */
|
|
55
94
|
async adt() {
|
|
95
|
+
this.assertCircuitClosed();
|
|
56
96
|
if (!this.reader) {
|
|
57
97
|
this.reader = this.newClient().then((c) => this.login(c));
|
|
58
98
|
this.reader.catch(() => (this.reader = undefined));
|
|
@@ -61,6 +101,7 @@ export class SapConnection {
|
|
|
61
101
|
}
|
|
62
102
|
/** Ejecuta fn con una sesión stateful propia y la cierra siempre. */
|
|
63
103
|
async stateful(fn) {
|
|
104
|
+
this.assertCircuitClosed();
|
|
64
105
|
const c = await this.login(await this.newClient());
|
|
65
106
|
c.stateful = session_types.stateful;
|
|
66
107
|
try {
|
package/dist/core/errors.js
CHANGED
|
@@ -19,6 +19,9 @@ export function normalizeError(e, systemId) {
|
|
|
19
19
|
if (e instanceof ToolError)
|
|
20
20
|
return e;
|
|
21
21
|
const any = e;
|
|
22
|
+
// signal.throwIfAborted() entre pasos de una tool larga: cancelación del cliente, no fallo del servidor.
|
|
23
|
+
if (any?.name === "AbortError")
|
|
24
|
+
return new ToolError("CANCELLED", "Cancelado por el cliente antes de terminar.");
|
|
22
25
|
const code = errCode(any);
|
|
23
26
|
if (code && NETWORK_CODES.has(code)) {
|
|
24
27
|
return new ToolError("NETWORK", `No se llega a ${systemId ?? "SAP"} (${code}).`, "¿Está levantada la VPN de ese cliente?");
|
|
@@ -50,6 +53,16 @@ export function normalizeError(e, systemId) {
|
|
|
50
53
|
const message = sanitizeMessage(any?.message ? String(any.message) : String(e));
|
|
51
54
|
return new ToolError("INTERNAL", message || "Error sin mensaje");
|
|
52
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Sesión caducada a mitad de una lectura: token CSRF rechazado, o 401 en una sesión que ya había entrado. El registro
|
|
58
|
+
* la renueva y repite la lectura UNA vez; nunca una escritura (el bloqueo se perdió con la sesión).
|
|
59
|
+
*/
|
|
60
|
+
export function isSessionExpired(e) {
|
|
61
|
+
if (isCsrfError(e))
|
|
62
|
+
return true;
|
|
63
|
+
const st = e;
|
|
64
|
+
return (isHttpError(e) && st?.status === 401) || (isAdtError(e) && st?.err === 401);
|
|
65
|
+
}
|
|
53
66
|
export function renderError(te) {
|
|
54
67
|
const labels = {
|
|
55
68
|
NETWORK: "Sin conexión",
|
|
@@ -60,6 +73,7 @@ export function renderError(te) {
|
|
|
60
73
|
CAPABILITY: "No disponible en este sistema",
|
|
61
74
|
MODULE: "Módulo no habilitado",
|
|
62
75
|
INPUT: "Parámetros inválidos",
|
|
76
|
+
CANCELLED: "Cancelado",
|
|
63
77
|
INTERNAL: "Error interno",
|
|
64
78
|
};
|
|
65
79
|
return `${labels[te.kind]}: ${te.message}${te.hint ? `\n${te.hint}` : ""}\n` +
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { sqlLiteral } from "./objects.js";
|
|
2
|
+
/** Paquete y, si se pide, sus subpaquetes (hasta 5 niveles). `subs` son los hijos encontrados (directos si no es recursivo). */
|
|
3
|
+
export async function packageTree(sap, root, recursive) {
|
|
4
|
+
const packages = [root];
|
|
5
|
+
const subs = [];
|
|
6
|
+
let frontier = [root];
|
|
7
|
+
for (let level = 0; level < (recursive ? 5 : 1) && frontier.length; level++) {
|
|
8
|
+
const r = await sap.query(`SELECT devclass FROM tdevc WHERE parentcl IN ( ${frontier.map(sqlLiteral).join(", ")} )`, 2000);
|
|
9
|
+
frontier = r.values.map((v) => v.DEVCLASS);
|
|
10
|
+
subs.push(...frontier);
|
|
11
|
+
if (recursive)
|
|
12
|
+
packages.push(...frontier);
|
|
13
|
+
}
|
|
14
|
+
return { packages, subs };
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Objetos R3TR de esos paquetes (sin DEVC), ordenados por tipo y nombre. Sin los marcados como borrados
|
|
18
|
+
* (DELFLAG = 'X'): TADIR los conserva, pero ya no existen en el repositorio.
|
|
19
|
+
*/
|
|
20
|
+
export async function packageObjects(sap, packages, max, objectType) {
|
|
21
|
+
const typeFilter = objectType ? ` AND object = ${sqlLiteral(objectType.toUpperCase())}` : "";
|
|
22
|
+
const r = await sap.query(`SELECT devclass, pgmid, object, obj_name FROM tadir WHERE pgmid = 'R3TR' AND devclass IN ( ${packages.map(sqlLiteral).join(", ")} )` +
|
|
23
|
+
`${typeFilter} AND object <> 'DEVC' AND delflag = ' ' ORDER BY object, obj_name`, max);
|
|
24
|
+
return r.values;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=packages.js.map
|
package/dist/core/registry.js
CHANGED
|
@@ -3,11 +3,11 @@ import { join } from "node:path";
|
|
|
3
3
|
import { pathToFileURL } from "node:url";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { canWrite, resolveSystem } from "./config.js";
|
|
6
|
-
import { normalizeError, renderError, ToolError } from "./errors.js";
|
|
6
|
+
import { isSessionExpired, normalizeError, renderError, ToolError } from "./errors.js";
|
|
7
7
|
import { budget } from "./output.js";
|
|
8
8
|
import { appendAudit, auditArgs } from "./audit.js";
|
|
9
9
|
import { consumeTokenWithState, issueToken } from "./confirm.js";
|
|
10
|
-
import { renderNotes, withNotes } from "./notes.js";
|
|
10
|
+
import { addNote, renderNotes, withNotes } from "./notes.js";
|
|
11
11
|
import { assertAccess } from "./policy.js";
|
|
12
12
|
import { recordUsage } from "./telemetry.js";
|
|
13
13
|
/**
|
|
@@ -182,6 +182,15 @@ export async function invoke(def, rawArgs, env) {
|
|
|
182
182
|
throw new ToolError("INTERNAL", "No hay componentes auxiliares en este servidor.");
|
|
183
183
|
return env.sidecars.get(name);
|
|
184
184
|
},
|
|
185
|
+
signal: env.signal,
|
|
186
|
+
progress(message, current, total) {
|
|
187
|
+
try {
|
|
188
|
+
env.progress?.(message, current, total);
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
/* el progreso es cortesía: un fallo al notificar nunca rompe la tool */
|
|
192
|
+
}
|
|
193
|
+
},
|
|
185
194
|
};
|
|
186
195
|
const head = resolved ? `Sistema: ${resolved.system.id} (${resolved.source})\n${SAP_DATA_NOTE}\n\n` : "";
|
|
187
196
|
if (resolved && (def.access === "write" || def.access === "exec")) {
|
|
@@ -218,14 +227,23 @@ export async function invoke(def, rawArgs, env) {
|
|
|
218
227
|
throw new ToolError("INTERNAL", `No se ejecutó: no se pudo escribir el registro de auditoría (${e.message}).`);
|
|
219
228
|
}
|
|
220
229
|
}
|
|
221
|
-
const { result: res, notes } = await withNotes(() => def
|
|
230
|
+
const { result: res, notes } = await withNotes(() => runGuarded(def, args, ctx, sapConn, systemId));
|
|
222
231
|
const text = renderNotes(notes) + (typeof res === "string" ? res : res.text);
|
|
223
232
|
const isError = typeof res === "string" ? false : !!res.isError;
|
|
224
|
-
|
|
233
|
+
const structured = typeof res === "string" ? undefined : res.structured;
|
|
234
|
+
// Una tool con esquema de salida que responde bien sin datos estructurados es un bug: mejor verlo aquí que
|
|
235
|
+
// como error críptico del SDK en el cliente.
|
|
236
|
+
if (def.output && !isError && !structured)
|
|
237
|
+
throw new ToolError("INTERNAL", `${def.name} declara salida estructurada y no la devolvió.`);
|
|
238
|
+
outcome = { text: head + budget(text), isError, kind: isError ? "RESULT" : undefined, system: systemId, structured };
|
|
225
239
|
}
|
|
226
240
|
}
|
|
227
241
|
catch (e) {
|
|
228
242
|
let te = normalizeError(e, systemId);
|
|
243
|
+
// Solo los fallos de red REALES (de la librería) cuentan para el circuito: ni un timeout nuestro ni el propio
|
|
244
|
+
// aviso de circuito abierto.
|
|
245
|
+
if (te.kind === "NETWORK" && !(e instanceof ToolError) && sapConn)
|
|
246
|
+
sapConn.noteNetworkFailure();
|
|
229
247
|
// Un 404 de SAP (no un «objeto no existe» nuestro) sobre un endpoint que el
|
|
230
248
|
// discovery tampoco lista: ahora sí hay evidencia de que falta la función.
|
|
231
249
|
if (te.kind === "NOT_FOUND" && !(e instanceof ToolError) && missing.length && sapConn) {
|
|
@@ -251,6 +269,50 @@ export async function invoke(def, rawArgs, env) {
|
|
|
251
269
|
});
|
|
252
270
|
return outcome;
|
|
253
271
|
}
|
|
272
|
+
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
273
|
+
function withTimeout(p, ms, what, signal) {
|
|
274
|
+
let timer;
|
|
275
|
+
let onAbort;
|
|
276
|
+
const limit = new Promise((_, reject) => {
|
|
277
|
+
if (signal) {
|
|
278
|
+
onAbort = () => reject(new ToolError("CANCELLED", `${what}: cancelado por el cliente.`));
|
|
279
|
+
if (signal.aborted)
|
|
280
|
+
onAbort();
|
|
281
|
+
else
|
|
282
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
283
|
+
}
|
|
284
|
+
timer = setTimeout(() => reject(new ToolError("NETWORK", `${what}: tiempo agotado (${ms >= 1000 ? `${Math.round(ms / 1000)} s` : `${ms} ms`}).`, "Acota la petición (menos objetos, más filtro) o repite más tarde.")), ms);
|
|
285
|
+
});
|
|
286
|
+
return Promise.race([p, limit]).finally(() => {
|
|
287
|
+
clearTimeout(timer);
|
|
288
|
+
if (signal && onAbort)
|
|
289
|
+
signal.removeEventListener("abort", onAbort);
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Ejecuta la tool con su tiempo máximo y, si la sesión de lectura había caducado (CSRF o 401 tras haber entrado),
|
|
294
|
+
* la renueva y repite UNA vez. Nunca repite escrituras ni ejecuciones: el bloqueo se perdió con la sesión y un
|
|
295
|
+
* reintento podría escribir dos veces.
|
|
296
|
+
*/
|
|
297
|
+
async function runGuarded(def, args, ctx, sapConn, systemId) {
|
|
298
|
+
const ms = def.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
299
|
+
const attempt = () => {
|
|
300
|
+
if (ctx.signal?.aborted)
|
|
301
|
+
throw new ToolError("CANCELLED", `${def.name}: cancelado por el cliente.`);
|
|
302
|
+
return withTimeout(def.run(args, ctx), ms, def.name, ctx.signal);
|
|
303
|
+
};
|
|
304
|
+
try {
|
|
305
|
+
return await attempt();
|
|
306
|
+
}
|
|
307
|
+
catch (e) {
|
|
308
|
+
if (def.access === "read" && sapConn && isSessionExpired(e)) {
|
|
309
|
+
sapConn.resetReader();
|
|
310
|
+
addNote(`La sesión con ${systemId ?? "SAP"} había caducado: se renovó y la lectura se repitió.`);
|
|
311
|
+
return await attempt();
|
|
312
|
+
}
|
|
313
|
+
throw e;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
254
316
|
/** Elicitación de formulario, si el cliente la anuncia (ABAP_DZ_CONFIRM=token la desactiva). */
|
|
255
317
|
function elicitFor(server) {
|
|
256
318
|
const caps = server.server.getClientCapabilities()?.elicitation;
|
|
@@ -313,10 +375,21 @@ export function registerAll(server, defs, config, pool, sidecars) {
|
|
|
313
375
|
title: def.title,
|
|
314
376
|
description: describe(def, config),
|
|
315
377
|
inputSchema,
|
|
378
|
+
...(def.output ? { outputSchema: z.object(def.output) } : {}),
|
|
316
379
|
annotations: annotationsFor(def),
|
|
317
|
-
}, (async (args) => {
|
|
318
|
-
const
|
|
319
|
-
|
|
380
|
+
}, (async (args, extra) => {
|
|
381
|
+
const token = extra?._meta?.progressToken;
|
|
382
|
+
const progress = token !== undefined && extra?.sendNotification
|
|
383
|
+
? (message, current, total) => {
|
|
384
|
+
void extra.sendNotification({ method: "notifications/progress", params: { progressToken: token, progress: current ?? 0, total, message } }).catch(() => undefined);
|
|
385
|
+
}
|
|
386
|
+
: undefined;
|
|
387
|
+
const r = await invoke(def, args ?? {}, { config, pool, tools: defs, sidecars, elicit: elicitFor(server), signal: extra?.signal, progress });
|
|
388
|
+
return {
|
|
389
|
+
content: [{ type: "text", text: r.text }],
|
|
390
|
+
isError: r.isError,
|
|
391
|
+
...(r.structured ? { structuredContent: r.structured } : {}),
|
|
392
|
+
};
|
|
320
393
|
}));
|
|
321
394
|
published.push(def.name);
|
|
322
395
|
}
|