@arcadialdev/arcality 3.0.3 → 3.0.4
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/package.json +1 -1
- package/scripts/gen-and-run.mjs +24 -7
- package/src/KnowledgeService.ts +29 -29
- package/src/arcalityClient.mjs +31 -18
- package/src/configLoader.mjs +35 -35
- package/tests/_helpers/ArcalityReporter.js +15 -0
- package/tests/_helpers/agentic-runner.bundle.spec.js +660 -99
package/package.json
CHANGED
package/scripts/gen-and-run.mjs
CHANGED
|
@@ -609,6 +609,7 @@ async function waitForEnter(message = 'Presiona Enter para continuar...', option
|
|
|
609
609
|
if (process.stdin.isTTY && process.stdin.setRawMode) {
|
|
610
610
|
process.stdin.setRawMode(false);
|
|
611
611
|
}
|
|
612
|
+
process.stdin.resume();
|
|
612
613
|
|
|
613
614
|
const rl = createInterface({
|
|
614
615
|
input: process.stdin,
|
|
@@ -2158,6 +2159,8 @@ async function main() {
|
|
|
2158
2159
|
BASE_URL: projectConfig?.project?.baseUrl || dotEnv.BASE_URL || process.env.BASE_URL || fallbackBaseUrl || 'http://localhost:3000',
|
|
2159
2160
|
LOGIN_USER: projectConfig?.auth?.username || dotEnv.LOGIN_USER || process.env.LOGIN_USER,
|
|
2160
2161
|
LOGIN_PASSWORD: projectConfig?.auth?.password || dotEnv.LOGIN_PASSWORD || process.env.LOGIN_PASSWORD,
|
|
2162
|
+
ARCALITY_ASSETS_FILE: projectConfig?.assets?.manifestPath || dotEnv.ARCALITY_ASSETS_FILE || process.env.ARCALITY_ASSETS_FILE || path.join(ORIGINAL_CWD, '.arcality', 'assets.json'),
|
|
2163
|
+
ARCALITY_ASSETS_DIR: projectConfig?.assets?.directory || dotEnv.ARCALITY_ASSETS_DIR || process.env.ARCALITY_ASSETS_DIR || path.join(ORIGINAL_CWD, '.arcality', 'assets'),
|
|
2161
2164
|
DOMAIN_DIR: process.env.DOMAIN_DIR,
|
|
2162
2165
|
MISSIONS_DIR: process.env.MISSIONS_DIR,
|
|
2163
2166
|
CONTEXT_DIR: process.env.CONTEXT_DIR,
|
|
@@ -2245,7 +2248,18 @@ async function main() {
|
|
|
2245
2248
|
_origProcessExit(1);
|
|
2246
2249
|
}
|
|
2247
2250
|
|
|
2248
|
-
|
|
2251
|
+
let _postProcessSigintCount = 0;
|
|
2252
|
+
const postProcessSigint = () => {
|
|
2253
|
+
_postProcessSigintCount++;
|
|
2254
|
+
if (_postProcessSigintCount === 1) {
|
|
2255
|
+
console.log(chalk.yellow('\n⏳ Finalizando reporte de telemetría, por favor espera... (Ctrl+C de nuevo para salir ya)'));
|
|
2256
|
+
} else {
|
|
2257
|
+
console.log(chalk.red('\n🛑 Saliendo forzado por el usuario.'));
|
|
2258
|
+
process.removeAllListeners('SIGINT');
|
|
2259
|
+
process.removeAllListeners('SIGTERM');
|
|
2260
|
+
process.exit(0);
|
|
2261
|
+
}
|
|
2262
|
+
};
|
|
2249
2263
|
let missionResult = null;
|
|
2250
2264
|
let finalFailReason = null;
|
|
2251
2265
|
let finalUsageData = undefined;
|
|
@@ -2493,7 +2507,10 @@ async function main() {
|
|
|
2493
2507
|
// ── End Mission — SIEMPRE se llama, independientemente de si la evidencia subió ──
|
|
2494
2508
|
try {
|
|
2495
2509
|
const { endMission } = await import('../src/arcalityClient.mjs');
|
|
2496
|
-
await
|
|
2510
|
+
await Promise.race([
|
|
2511
|
+
endMission(mission.mission_id, missionResult || 'failed', finalUsageData, finalFailReason, finalReportUrl, finalFailReasoning),
|
|
2512
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), 8000))
|
|
2513
|
+
]);
|
|
2497
2514
|
if (process.argv.includes('--debug')) console.log(chalk.gray(`[DEBUG] endMission called → result: ${missionResult}, failReason: ${finalFailReason}`));
|
|
2498
2515
|
} catch (e) {
|
|
2499
2516
|
if (process.argv.includes('--debug')) console.log(chalk.yellow(`⚠️ Could not end mission in backend: ${e.message}`));
|
|
@@ -2519,11 +2536,11 @@ async function main() {
|
|
|
2519
2536
|
|
|
2520
2537
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
2521
2538
|
|
|
2522
|
-
// Ping project after report
|
|
2523
|
-
await
|
|
2524
|
-
process.env.ARCALITY_PROJECT_ID,
|
|
2525
|
-
|
|
2526
|
-
);
|
|
2539
|
+
// Ping project after report (with timeout so it cannot hang the exit path)
|
|
2540
|
+
await Promise.race([
|
|
2541
|
+
pingProject(process.env.ARCALITY_PROJECT_ID, process.env.ARCALITY_API_KEY),
|
|
2542
|
+
new Promise(resolve => setTimeout(resolve, 5000))
|
|
2543
|
+
]);
|
|
2527
2544
|
|
|
2528
2545
|
// Limpiar handlers del post-proceso ANTES de esperar Enter, ya que toda la telemetría y reportes ya finalizaron
|
|
2529
2546
|
process.removeListener('SIGINT', postProcessSigint);
|
package/src/KnowledgeService.ts
CHANGED
|
@@ -30,22 +30,22 @@ export interface PortalContextResponse {
|
|
|
30
30
|
fields: Array<{ field_identifier: string; field_type: string; is_required: boolean }>;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
export interface ProjectDto {
|
|
34
|
-
id: string;
|
|
35
|
-
name: string;
|
|
36
|
-
base_url?: string;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function isRealProjectId(projectId?: string | null): boolean {
|
|
40
|
-
const id = String(projectId || '').trim();
|
|
41
|
-
if (!id) return false;
|
|
42
|
-
if (id === 'undefined' || id === 'null') return false;
|
|
43
|
-
if (/^(local|mock)_/i.test(id)) return false;
|
|
44
|
-
if (/^0{8}-0{4}-0{4}-0{4}-0{12}$/i.test(id)) return false;
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export class KnowledgeService {
|
|
33
|
+
export interface ProjectDto {
|
|
34
|
+
id: string;
|
|
35
|
+
name: string;
|
|
36
|
+
base_url?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function isRealProjectId(projectId?: string | null): boolean {
|
|
40
|
+
const id = String(projectId || '').trim();
|
|
41
|
+
if (!id) return false;
|
|
42
|
+
if (id === 'undefined' || id === 'null') return false;
|
|
43
|
+
if (/^(local|mock)_/i.test(id)) return false;
|
|
44
|
+
if (/^0{8}-0{4}-0{4}-0{4}-0{12}$/i.test(id)) return false;
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export class KnowledgeService {
|
|
49
49
|
private static instance: KnowledgeService;
|
|
50
50
|
private apiBase: string;
|
|
51
51
|
private apiKey: string;
|
|
@@ -89,10 +89,10 @@ export class KnowledgeService {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
public getProjectId(): string | null {
|
|
92
|
-
if (!isRealProjectId(this.projectId)) {
|
|
93
|
-
this.projectId = process.env.ARCALITY_PROJECT_ID || null;
|
|
94
|
-
}
|
|
95
|
-
return isRealProjectId(this.projectId) ? this.projectId : null;
|
|
92
|
+
if (!isRealProjectId(this.projectId)) {
|
|
93
|
+
this.projectId = process.env.ARCALITY_PROJECT_ID || null;
|
|
94
|
+
}
|
|
95
|
+
return isRealProjectId(this.projectId) ? this.projectId : null;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
/**
|
|
@@ -107,7 +107,7 @@ export class KnowledgeService {
|
|
|
107
107
|
const data = await res.json();
|
|
108
108
|
return data.projects || [];
|
|
109
109
|
} catch (e: any) {
|
|
110
|
-
if (process.env.DEBUG) console.warn(chalk.yellow(`[KnowledgeService] Error al obtener proyectos: ${e.message}`));
|
|
110
|
+
if (process.env.DEBUG) console.warn(chalk.yellow(`[KnowledgeService] Error al obtener proyectos: ${e.message}`));
|
|
111
111
|
return [];
|
|
112
112
|
}
|
|
113
113
|
}
|
|
@@ -125,7 +125,7 @@ export class KnowledgeService {
|
|
|
125
125
|
if (!res.ok) return null;
|
|
126
126
|
return await res.json();
|
|
127
127
|
} catch (e: any) {
|
|
128
|
-
if (process.env.DEBUG) console.error(chalk.red(`[KnowledgeService] Falló creación de proyecto: ${e.message}`));
|
|
128
|
+
if (process.env.DEBUG) console.error(chalk.red(`[KnowledgeService] Falló creación de proyecto: ${e.message}`));
|
|
129
129
|
return null;
|
|
130
130
|
}
|
|
131
131
|
}
|
|
@@ -134,8 +134,8 @@ export class KnowledgeService {
|
|
|
134
134
|
* 2. Ingesta (Post-Percept)
|
|
135
135
|
*/
|
|
136
136
|
public async ingest(payload: PortalIngestRequest): Promise<void> {
|
|
137
|
-
if (!isRealProjectId(payload.project_id)) {
|
|
138
|
-
if (process.env.DEBUG) console.warn(chalk.yellow(`[Knowledge] Ingesta cancelada: project_id es inválido.`));
|
|
137
|
+
if (!isRealProjectId(payload.project_id)) {
|
|
138
|
+
if (process.env.DEBUG) console.warn(chalk.yellow(`[Knowledge] Ingesta cancelada: project_id es inválido.`));
|
|
139
139
|
return;
|
|
140
140
|
}
|
|
141
141
|
|
|
@@ -157,7 +157,7 @@ export class KnowledgeService {
|
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
} catch (e: any) {
|
|
160
|
-
if (process.env.DEBUG) console.warn(chalk.yellow(`[Knowledge] Error en ingesta: ${e.message}`));
|
|
160
|
+
if (process.env.DEBUG) console.warn(chalk.yellow(`[Knowledge] Error en ingesta: ${e.message}`));
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
163
|
|
|
@@ -179,7 +179,7 @@ export class KnowledgeService {
|
|
|
179
179
|
if (!res.ok) return null;
|
|
180
180
|
return await res.json();
|
|
181
181
|
} catch (e: any) {
|
|
182
|
-
if (process.env.DEBUG) console.warn(chalk.yellow(`[Knowledge] Error al obtener contexto: ${e.message}`));
|
|
182
|
+
if (process.env.DEBUG) console.warn(chalk.yellow(`[Knowledge] Error al obtener contexto: ${e.message}`));
|
|
183
183
|
return null;
|
|
184
184
|
}
|
|
185
185
|
}
|
|
@@ -207,7 +207,7 @@ export class KnowledgeService {
|
|
|
207
207
|
severity
|
|
208
208
|
})
|
|
209
209
|
});
|
|
210
|
-
if (process.env.DEBUG) console.log(chalk.green(`[Knowledge] Nueva regla registrada: ${title}`));
|
|
210
|
+
if (process.env.DEBUG) console.log(chalk.green(`[Knowledge] Nueva regla registrada: ${title}`));
|
|
211
211
|
} catch (e) { }
|
|
212
212
|
}
|
|
213
213
|
|
|
@@ -233,7 +233,7 @@ export class KnowledgeService {
|
|
|
233
233
|
source: "agent_auto"
|
|
234
234
|
})
|
|
235
235
|
});
|
|
236
|
-
if (process.env.DEBUG) console.log(chalk.green(`[Knowledge] Nuevo campo catalogado: ${identifier}`));
|
|
236
|
+
if (process.env.DEBUG) console.log(chalk.green(`[Knowledge] Nuevo campo catalogado: ${identifier}`));
|
|
237
237
|
} catch (e) { }
|
|
238
238
|
}
|
|
239
239
|
|
|
@@ -259,7 +259,7 @@ export class KnowledgeService {
|
|
|
259
259
|
source: "agent_auto"
|
|
260
260
|
})
|
|
261
261
|
});
|
|
262
|
-
if (process.env.DEBUG) console.log(chalk.green(`[Knowledge] Nuevo conocimiento guardado: ${title}`));
|
|
262
|
+
if (process.env.DEBUG) console.log(chalk.green(`[Knowledge] Nuevo conocimiento guardado: ${title}`));
|
|
263
263
|
} catch (e) { }
|
|
264
264
|
}
|
|
265
265
|
|
package/src/arcalityClient.mjs
CHANGED
|
@@ -306,10 +306,16 @@ function simpleHash(str) {
|
|
|
306
306
|
hash = ((hash << 5) - hash) + char;
|
|
307
307
|
hash |= 0;
|
|
308
308
|
}
|
|
309
|
-
return 'ph_' + Math.abs(hash).toString(36);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
|
|
309
|
+
return 'ph_' + Math.abs(hash).toString(36);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function normalizeBugClassification(value) {
|
|
313
|
+
const text = String(value || '').trim();
|
|
314
|
+
const allowed = ['[Backend Bug]', '[Frontend Bug]', '[UI Regression]', '[Flaky/Stale Selector]'];
|
|
315
|
+
return allowed.find(category => text.includes(category)) || '';
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
313
319
|
* Crea una Task en Azure DevOps cuando se detecta un bug en el portal.
|
|
314
320
|
* Si la organización no tiene la integración configurada (HTTP 400) se registra
|
|
315
321
|
* sólo en modo DEBUG y NO interrumpe el flujo del test runner.
|
|
@@ -318,7 +324,7 @@ function simpleHash(str) {
|
|
|
318
324
|
* @param {string} description - Descripción detallada del bug detectado
|
|
319
325
|
* @returns {Promise<{success: boolean, taskUrl?: string, error?: string}>}
|
|
320
326
|
*/
|
|
321
|
-
export async function createAdoTask(title, description) {
|
|
327
|
+
export async function createAdoTask(title, description, metadata = {}) {
|
|
322
328
|
const apiBase = getEffectiveApiBase();
|
|
323
329
|
if (!apiBase) {
|
|
324
330
|
if (process.env.DEBUG) console.log(`[ADO] No hay ARCALITY_API_URL configurado. Omitiendo creación de Task en Azure DevOps.`);
|
|
@@ -337,19 +343,26 @@ export async function createAdoTask(title, description) {
|
|
|
337
343
|
return { success: false, error: 'no_api_key' };
|
|
338
344
|
}
|
|
339
345
|
|
|
340
|
-
try {
|
|
341
|
-
const controller = new AbortController();
|
|
342
|
-
const timeout = setTimeout(() => controller.abort(), 15000);
|
|
343
|
-
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
346
|
+
try {
|
|
347
|
+
const controller = new AbortController();
|
|
348
|
+
const timeout = setTimeout(() => controller.abort(), 15000);
|
|
349
|
+
const bugClassification = normalizeBugClassification(metadata?.classification);
|
|
350
|
+
const taskTitle = bugClassification && !String(title).includes(bugClassification)
|
|
351
|
+
? `${bugClassification} ${title}`
|
|
352
|
+
: title;
|
|
353
|
+
const taskDescription = bugClassification && !String(description).includes('Clasificacion')
|
|
354
|
+
? `**Clasificacion:** ${bugClassification}\n\n${description}`
|
|
355
|
+
: description;
|
|
356
|
+
|
|
357
|
+
const res = await fetch(`${apiBase}/api/v1/integrations/azuredevops/task`, {
|
|
358
|
+
method: 'POST',
|
|
359
|
+
headers: {
|
|
360
|
+
'Content-Type': 'application/json',
|
|
361
|
+
'x-api-key': key
|
|
362
|
+
},
|
|
363
|
+
body: JSON.stringify({ project_id: projectId, title: taskTitle, description: taskDescription }),
|
|
364
|
+
signal: controller.signal
|
|
365
|
+
});
|
|
353
366
|
clearTimeout(timeout);
|
|
354
367
|
|
|
355
368
|
if (res.status === 400) {
|
package/src/configLoader.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// src/configLoader.mjs
|
|
2
2
|
// Configuration loader — supports arcality.config (primary) and .env / global config (fallback)
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import os from 'node:os';
|
|
6
|
-
import dotenv from 'dotenv';
|
|
7
|
-
import { isRealProjectId } from './configManager.mjs';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import dotenv from 'dotenv';
|
|
7
|
+
import { isRealProjectId } from './configManager.mjs';
|
|
8
8
|
|
|
9
9
|
dotenv.config();
|
|
10
10
|
|
|
@@ -59,22 +59,22 @@ export function loadConfig() {
|
|
|
59
59
|
ARCALITY_PROJECT_ID: localConfig?.projectId || process.env.ARCALITY_PROJECT_ID,
|
|
60
60
|
};
|
|
61
61
|
|
|
62
|
-
if (!config.ARCALITY_API_KEY) {
|
|
63
|
-
throw new Error('ARCALITY_API_KEY is not defined. Run `arcality init` or set it in your .env file.');
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (!isRealProjectId(config.ARCALITY_PROJECT_ID)) {
|
|
67
|
-
throw new Error('ARCALITY_PROJECT_ID is not connected to a real Arcality project. Run `arcality init`.');
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return config;
|
|
71
|
-
}
|
|
62
|
+
if (!config.ARCALITY_API_KEY) {
|
|
63
|
+
throw new Error('ARCALITY_API_KEY is not defined. Run `arcality init` or set it in your .env file.');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!isRealProjectId(config.ARCALITY_PROJECT_ID)) {
|
|
67
|
+
throw new Error('ARCALITY_PROJECT_ID is not connected to a real Arcality project. Run `arcality init`.');
|
|
68
|
+
}
|
|
72
69
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
70
|
+
return config;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function loadProjectId() {
|
|
74
|
+
const localConfig = loadLocalArcalityConfig();
|
|
75
|
+
const projectId = localConfig?.projectId || process.env.ARCALITY_PROJECT_ID;
|
|
76
|
+
return isRealProjectId(projectId) ? projectId : null;
|
|
77
|
+
}
|
|
78
78
|
|
|
79
79
|
|
|
80
80
|
/**
|
|
@@ -133,25 +133,25 @@ export function getApiKey() {
|
|
|
133
133
|
return { key: null, source: 'none' };
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
/**
|
|
137
|
-
* Loads supported Arcality runtime values into process.env.
|
|
138
|
-
*/
|
|
139
|
-
export function setupProcessEnv() {
|
|
140
|
-
const config = loadGlobalConfig();
|
|
141
|
-
if (!config) return;
|
|
142
|
-
|
|
143
|
-
// Load Arcality API Key
|
|
144
|
-
getApiKey();
|
|
145
|
-
|
|
146
|
-
// Also inject arcality.config values into process.env for backward compat
|
|
147
|
-
const localConfig = loadLocalArcalityConfig();
|
|
136
|
+
/**
|
|
137
|
+
* Loads supported Arcality runtime values into process.env.
|
|
138
|
+
*/
|
|
139
|
+
export function setupProcessEnv() {
|
|
140
|
+
const config = loadGlobalConfig();
|
|
141
|
+
if (!config) return;
|
|
142
|
+
|
|
143
|
+
// Load Arcality API Key
|
|
144
|
+
getApiKey();
|
|
145
|
+
|
|
146
|
+
// Also inject arcality.config values into process.env for backward compat
|
|
147
|
+
const localConfig = loadLocalArcalityConfig();
|
|
148
148
|
if (localConfig) {
|
|
149
149
|
if (localConfig.apiKey && !process.env.ARCALITY_API_KEY) {
|
|
150
150
|
process.env.ARCALITY_API_KEY = localConfig.apiKey;
|
|
151
151
|
}
|
|
152
|
-
if (isRealProjectId(localConfig.projectId) && !process.env.ARCALITY_PROJECT_ID) {
|
|
153
|
-
process.env.ARCALITY_PROJECT_ID = localConfig.projectId;
|
|
154
|
-
}
|
|
152
|
+
if (isRealProjectId(localConfig.projectId) && !process.env.ARCALITY_PROJECT_ID) {
|
|
153
|
+
process.env.ARCALITY_PROJECT_ID = localConfig.projectId;
|
|
154
|
+
}
|
|
155
155
|
if (localConfig.project?.baseUrl && !process.env.BASE_URL) {
|
|
156
156
|
process.env.BASE_URL = localConfig.project.baseUrl;
|
|
157
157
|
}
|
|
@@ -747,6 +747,11 @@ class ArcalityReporter {
|
|
|
747
747
|
background: var(--brand-soft);
|
|
748
748
|
border-color: rgba(181,108,255,0.26);
|
|
749
749
|
}
|
|
750
|
+
.tag-bug {
|
|
751
|
+
color: #b7e4ff;
|
|
752
|
+
background: rgba(80,170,255,0.12);
|
|
753
|
+
border-color: rgba(80,170,255,0.3);
|
|
754
|
+
}
|
|
750
755
|
.tag-retry {
|
|
751
756
|
color: #ffe2a6;
|
|
752
757
|
background: var(--warning-soft);
|
|
@@ -941,6 +946,8 @@ class ArcalityReporter {
|
|
|
941
946
|
const isOpen = this.results.length === 1 || tone === 'failed' || i === 0;
|
|
942
947
|
const successSummary = r.attachments.find(a => a.name === 'success_summary');
|
|
943
948
|
const successText = this.getAttachmentText(successSummary);
|
|
949
|
+
const bugClassificationAtt = r.attachments.find(a => a.name === 'bug_classification');
|
|
950
|
+
const bugClassification = (this.getAttachmentText(bugClassificationAtt) || '').trim();
|
|
944
951
|
const evidenceHtml = this.renderVisualEvidence(r.attachments);
|
|
945
952
|
const contextHtml = this.renderTextAttachmentSection(r.attachments, 'qa_context_summary', 'Contexto QA aplicado');
|
|
946
953
|
const securityHtml = this._renderSecuritySection(r.attachments, i);
|
|
@@ -954,6 +961,7 @@ class ArcalityReporter {
|
|
|
954
961
|
<div class="test-meta">
|
|
955
962
|
<span class="tag tag-status-${tone}">${this.getStatusLabel(r.result.status)}</span>
|
|
956
963
|
<span class="tag tag-project">${this.escapeHtml(projectName)}</span>
|
|
964
|
+
${bugClassification ? `<span class="tag tag-bug">${this.escapeHtml(bugClassification)}</span>` : ''}
|
|
957
965
|
${r.result.retry > 0 ? `<span class="tag tag-retry">Retry #${r.result.retry}</span>` : ''}
|
|
958
966
|
<span class="tag">${this.formatDuration(r.result.duration)}</span>
|
|
959
967
|
</div>
|
|
@@ -972,6 +980,13 @@ class ArcalityReporter {
|
|
|
972
980
|
</div>
|
|
973
981
|
` : ''}
|
|
974
982
|
|
|
983
|
+
${r.result.error && bugClassification ? `
|
|
984
|
+
<div class="outcome-block error">
|
|
985
|
+
<span class="outcome-label">Clasificacion AI</span>
|
|
986
|
+
<pre class="error-block">${this.escapeHtml(bugClassification)}</pre>
|
|
987
|
+
</div>
|
|
988
|
+
` : ''}
|
|
989
|
+
|
|
975
990
|
${r.result.error ? `
|
|
976
991
|
<div class="outcome-block error">
|
|
977
992
|
<span class="outcome-label">Diagnostico del bloqueo</span>
|