@alvarobr/n8n-nodes-glpi-connect 1.1.0 → 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/README.md CHANGED
@@ -38,6 +38,8 @@ Creado por **Álvaro Blanca Rodríguez**.
38
38
  ### Otros
39
39
  - **Usuario → Identificar por correo**: usuario de GLPI asociado a un correo.
40
40
  - **Base de conocimiento → Buscar preguntas frecuentes**: artículos FAQ públicos.
41
+ - **Base de conocimiento → Índice de preguntas frecuentes**: títulos e ID de todas las FAQ públicas. Páselo al prompt del agente para que sepa qué artículos existen y no busque a ciegas.
42
+ - **Base de conocimiento → Leer artículo**: contenido completo de una FAQ pública por su ID.
41
43
  - **Petición API → Petición personalizada**: cualquier ruta de `apirest.php` con la sesión ya gestionada. No comprueba permisos por usuario, así que **no debe conectarse a un agente de IA**.
42
44
 
43
45
  ## Seguridad
@@ -112,7 +112,11 @@ const properties = [
112
112
  type: 'options',
113
113
  noDataExpression: true,
114
114
  displayOptions: show(['conocimiento']),
115
- options: [{ name: 'Buscar preguntas frecuentes', value: 'buscar', action: 'Buscar en la base de conocimiento' }],
115
+ options: [
116
+ { name: 'Buscar preguntas frecuentes', value: 'buscar', action: 'Buscar en la base de conocimiento' },
117
+ { name: 'Índice de preguntas frecuentes', value: 'indice', action: 'Listar los títulos de la base de conocimiento', description: 'Títulos e ID de las preguntas frecuentes públicas, para que la IA sepa qué hay antes de consultar' },
118
+ { name: 'Leer artículo', value: 'leer', action: 'Leer un artículo de la base de conocimiento', description: 'Contenido completo de una pregunta frecuente por su ID' },
119
+ ],
116
120
  default: 'buscar',
117
121
  },
118
122
  {
@@ -350,7 +354,15 @@ const properties = [
350
354
  required: true,
351
355
  default: '',
352
356
  placeholder: 'vpn',
353
- displayOptions: show(['conocimiento']),
357
+ displayOptions: show(['conocimiento'], ['buscar']),
358
+ },
359
+ {
360
+ displayName: 'ID del artículo',
361
+ name: 'articuloId',
362
+ type: 'number',
363
+ required: true,
364
+ default: 0,
365
+ displayOptions: show(['conocimiento'], ['leer']),
354
366
  },
355
367
  // --- Petición API ---------------------------------------------------
356
368
  {
@@ -462,6 +474,10 @@ async function runOperation(ctx, client, users, i) {
462
474
  const resource = ctx.getNodeParameter('resource', i);
463
475
  const operation = ctx.getNodeParameter('operation', i);
464
476
  if (resource === 'conocimiento') {
477
+ if (operation === 'indice')
478
+ return ops.faqIndex(client);
479
+ if (operation === 'leer')
480
+ return ops.readFaq(client, Number(ctx.getNodeParameter('articuloId', i)));
465
481
  return ops.searchFaq(client, ctx.getNodeParameter('busqueda', i));
466
482
  }
467
483
  if (resource === 'api') {
@@ -21,6 +21,8 @@ exports.linkAsset = linkAsset;
21
21
  exports.createTicket = createTicket;
22
22
  exports.myAssets = myAssets;
23
23
  exports.computerSheet = computerSheet;
24
+ exports.faqIndex = faqIndex;
25
+ exports.readFaq = readFaq;
24
26
  exports.searchFaq = searchFaq;
25
27
  exports.apiRequest = apiRequest;
26
28
  const GlpiClient_1 = require("./GlpiClient");
@@ -707,6 +709,37 @@ async function computerSheet(client, user, computerId, search) {
707
709
  // ----------------------------------------------------------------------
708
710
  // Base de conocimiento
709
711
  // ----------------------------------------------------------------------
712
+ const FAQ_INDEX_LIMIT = 200;
713
+ /** Solo las preguntas frecuentes se consideran públicas para el usuario. */
714
+ function isPublicFaq(article) {
715
+ return !!article && typeof article === 'object' && (article.is_faq === undefined || Number(article.is_faq) === 1);
716
+ }
717
+ /** Títulos de todas las preguntas frecuentes, para que la IA sepa qué existe antes de consultar. */
718
+ async function faqIndex(client) {
719
+ const response = await client.raw('GET', 'KnowbaseItem', { qs: { range: `0-${FAQ_INDEX_LIMIT - 1}`, sort: 'name', order: 'ASC' } });
720
+ if (response.statusCode >= 300 || !Array.isArray(response.body)) {
721
+ return { ok: false, error: 'No se ha podido consultar la base de conocimiento.' };
722
+ }
723
+ const articles = response.body.filter(isPublicFaq).map((a) => ({ id: Number(a.id), titulo: (0, utils_1.plainText)(a.name, 200) }));
724
+ return (0, utils_1.compact)({
725
+ ok: true,
726
+ total: articles.length,
727
+ articulos: articles,
728
+ aviso: response.body.length >= FAQ_INDEX_LIMIT ? `Solo se muestran los ${FAQ_INDEX_LIMIT} primeros artículos.` : undefined,
729
+ });
730
+ }
731
+ /** Contenido completo de una pregunta frecuente. */
732
+ async function readFaq(client, articleId) {
733
+ const id = Number(articleId);
734
+ if (!Number.isSafeInteger(id) || id <= 0)
735
+ return { ok: false, error: 'Falta el ID del artículo. Use el índice de la base de conocimiento.' };
736
+ const response = await client.raw('GET', `KnowbaseItem/${id}`);
737
+ if (response.statusCode >= 300 || !response.body || Array.isArray(response.body) || !isPublicFaq(response.body)) {
738
+ return { ok: false, error: `No existe una pregunta frecuente pública con el ID ${id}.` };
739
+ }
740
+ const a = response.body;
741
+ return { ok: true, articulo: { id, titulo: (0, utils_1.plainText)(a.name, 200), contenido: (0, utils_1.plainText)(a.answer, 8000), enlace: `${client.front}/helpdesk.faq.php?id=${id}` } };
742
+ }
710
743
  async function searchFaq(client, text) {
711
744
  const term = String(text ?? '').trim();
712
745
  if (term.length < 2)
@@ -720,10 +753,9 @@ async function searchFaq(client, text) {
720
753
  if (r.statusCode >= 300 || !Array.isArray(r.body))
721
754
  continue;
722
755
  for (const a of r.body) {
723
- if (!a || typeof a !== 'object' || seen.has(a.id))
756
+ if (!a || seen.has(a.id))
724
757
  continue;
725
- // Solo preguntas frecuentes (públicas)
726
- if (a.is_faq !== undefined && Number(a.is_faq) !== 1)
758
+ if (!isPublicFaq(a))
727
759
  continue;
728
760
  seen.add(a.id);
729
761
  articles.push({ id: a.id, titulo: (0, utils_1.plainText)(a.name, 200), contenido: (0, utils_1.plainText)(a.answer, 1500), enlace: `${client.front}/helpdesk.faq.php?id=${a.id}` });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alvarobr/n8n-nodes-glpi-connect",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Nodo de n8n para GLPI: autoservicio IT seguro para flujos y agentes de IA. Tickets, inventario, software y base de conocimiento con sesión automática, permisos por usuario y confirmación de cambios.",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",