@arcadialdev/arcality 2.6.7 → 3.0.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/bin/arcality.mjs +11 -0
- package/package.json +1 -1
- package/playwright.config.js +24 -5
- package/scripts/gen-and-run.mjs +122 -100
- package/src/arcalityClient.mjs +128 -1
- package/src/configLoader.mjs +6 -2
- package/src/configManager.mjs +1 -1
- package/tests/_helpers/ArcalityReporter.js +234 -251
- package/tests/_helpers/agentic-runner.bundle.spec.js +563 -109
package/src/arcalityClient.mjs
CHANGED
|
@@ -93,6 +93,16 @@ function getEffectiveApiKey() {
|
|
|
93
93
|
return key;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Gets the effective Project ID.
|
|
98
|
+
* Priority: arcality.config > ARCALITY_PROJECT_ID env var
|
|
99
|
+
*/
|
|
100
|
+
function loadProjectId() {
|
|
101
|
+
const localConfig = loadArcalityConfig();
|
|
102
|
+
if (localConfig?.projectId) return localConfig.projectId;
|
|
103
|
+
return process.env.ARCALITY_PROJECT_ID || null;
|
|
104
|
+
}
|
|
105
|
+
|
|
96
106
|
// ═══════════════════════════════════════════════════════
|
|
97
107
|
// PUBLIC API
|
|
98
108
|
// ═══════════════════════════════════════════════════════
|
|
@@ -244,6 +254,54 @@ export async function startMission(prompt, targetUrl) {
|
|
|
244
254
|
};
|
|
245
255
|
}
|
|
246
256
|
|
|
257
|
+
/**
|
|
258
|
+
* Fetches missions from the backend API.
|
|
259
|
+
* @param {string} projectId
|
|
260
|
+
* @param {string} tags - Comma separated tags (optional)
|
|
261
|
+
* @returns {Promise<Object>} Contains success flag and missions array
|
|
262
|
+
*/
|
|
263
|
+
export async function fetchMissions(projectId, tags) {
|
|
264
|
+
const key = getEffectiveApiKey();
|
|
265
|
+
if (!key) return { success: false, error: 'no_api_key', missions: [] };
|
|
266
|
+
|
|
267
|
+
const apiBase = getEffectiveApiBase();
|
|
268
|
+
if (!apiBase) {
|
|
269
|
+
// MOCK MODE: Return empty array for now since there's no backend
|
|
270
|
+
return { success: true, mode: 'mock', missions: [] };
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
try {
|
|
274
|
+
const url = new URL(`${apiBase}/api/v1/projects/${projectId}/missions`);
|
|
275
|
+
if (tags) {
|
|
276
|
+
url.searchParams.append('tags', tags);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const controller = new AbortController();
|
|
280
|
+
const timeout = setTimeout(() => controller.abort(), 10000);
|
|
281
|
+
const res = await fetch(url.toString(), {
|
|
282
|
+
method: 'GET',
|
|
283
|
+
headers: {
|
|
284
|
+
'x-api-key': key
|
|
285
|
+
},
|
|
286
|
+
signal: controller.signal
|
|
287
|
+
});
|
|
288
|
+
clearTimeout(timeout);
|
|
289
|
+
|
|
290
|
+
if (!res.ok) {
|
|
291
|
+
const errText = await res.text();
|
|
292
|
+
console.warn(`[DEBUG] fetchMissions failed: ${res.status} - ${errText}`);
|
|
293
|
+
return { success: false, error: 'server_error', missions: [] };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const data = await res.json();
|
|
297
|
+
return { success: true, missions: data.missions || [] };
|
|
298
|
+
} catch (e) {
|
|
299
|
+
const reason = e?.name === 'AbortError' ? 'timeout (10s)' : (e?.message || 'unknown');
|
|
300
|
+
console.warn(`⚠️ fetchMissions failed (${apiBase}): ${reason}.`);
|
|
301
|
+
return { success: false, error: reason, missions: [] };
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
247
305
|
/**
|
|
248
306
|
* Marks a mission as completed.
|
|
249
307
|
* @param {string} missionId
|
|
@@ -281,6 +339,75 @@ function simpleHash(str) {
|
|
|
281
339
|
return 'ph_' + Math.abs(hash).toString(36);
|
|
282
340
|
}
|
|
283
341
|
|
|
342
|
+
/**
|
|
343
|
+
* Crea una Task en Azure DevOps cuando se detecta un bug en el portal.
|
|
344
|
+
* Si la organización no tiene la integración configurada (HTTP 400) se registra
|
|
345
|
+
* sólo en modo DEBUG y NO interrumpe el flujo del test runner.
|
|
346
|
+
*
|
|
347
|
+
* @param {string} title - Título de la tarea (generalmente la descripción corta del bug)
|
|
348
|
+
* @param {string} description - Descripción detallada del bug detectado
|
|
349
|
+
* @returns {Promise<{success: boolean, taskUrl?: string, error?: string}>}
|
|
350
|
+
*/
|
|
351
|
+
export async function createAdoTask(title, description) {
|
|
352
|
+
const apiBase = getEffectiveApiBase();
|
|
353
|
+
if (!apiBase) {
|
|
354
|
+
if (process.env.DEBUG) console.log(`[ADO] No hay ARCALITY_API_URL configurado. Omitiendo creación de Task en Azure DevOps.`);
|
|
355
|
+
return { success: false, error: 'no_api_url' };
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const projectId = loadProjectId();
|
|
359
|
+
if (!projectId) {
|
|
360
|
+
if (process.env.DEBUG) console.log(`[ADO] No hay Project ID configurado. Omitiendo creación de Task en Azure DevOps.`);
|
|
361
|
+
return { success: false, error: 'no_project_id' };
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const key = getEffectiveApiKey();
|
|
365
|
+
if (!key) {
|
|
366
|
+
if (process.env.DEBUG) console.log(`[ADO] No hay API key configurada. Omitiendo creación de Task en Azure DevOps.`);
|
|
367
|
+
return { success: false, error: 'no_api_key' };
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
try {
|
|
371
|
+
const controller = new AbortController();
|
|
372
|
+
const timeout = setTimeout(() => controller.abort(), 15000);
|
|
373
|
+
|
|
374
|
+
const res = await fetch(`${apiBase}/api/v1/integrations/azuredevops/task`, {
|
|
375
|
+
method: 'POST',
|
|
376
|
+
headers: {
|
|
377
|
+
'Content-Type': 'application/json',
|
|
378
|
+
'x-api-key': key
|
|
379
|
+
},
|
|
380
|
+
body: JSON.stringify({ project_id: projectId, title, description }),
|
|
381
|
+
signal: controller.signal
|
|
382
|
+
});
|
|
383
|
+
clearTimeout(timeout);
|
|
384
|
+
|
|
385
|
+
if (res.status === 400) {
|
|
386
|
+
const data = await res.json().catch(() => ({}));
|
|
387
|
+
// La organización no tiene la integración de ADO configurada — no es un error crítico
|
|
388
|
+
if (data?.error === 'create_task_failed') {
|
|
389
|
+
if (process.env.DEBUG) console.log(`[ADO] Integración no configurada para esta organización: ${data.message}`);
|
|
390
|
+
} else {
|
|
391
|
+
console.warn(`⚠️ [ADO] Error al crear Task en Azure DevOps: ${data?.message || 'Bad Request'}`);
|
|
392
|
+
}
|
|
393
|
+
return { success: false, error: data?.message || 'create_task_failed' };
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (!res.ok) {
|
|
397
|
+
console.warn(`⚠️ [ADO] Error HTTP ${res.status} al crear Task en Azure DevOps.`);
|
|
398
|
+
return { success: false, error: `http_${res.status}` };
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const data = await res.json();
|
|
402
|
+
console.log(`✅ [ADO] Task creada en Azure DevOps: ${data.task_url || '(sin URL)'}`);
|
|
403
|
+
return { success: true, taskUrl: data.task_url };
|
|
404
|
+
} catch (e) {
|
|
405
|
+
const reason = e?.name === 'AbortError' ? 'timeout (15s)' : (e?.message || 'unknown');
|
|
406
|
+
console.warn(`⚠️ [ADO] No se pudo crear la Task en Azure DevOps: ${reason}`);
|
|
407
|
+
return { success: false, error: reason };
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
284
411
|
/**
|
|
285
412
|
* Obtiene el Token SAS y el path base para subir evidencias.
|
|
286
413
|
* @param {string} missionId
|
|
@@ -309,7 +436,7 @@ export async function getEvidenceSasToken(missionId, projectId) {
|
|
|
309
436
|
console.log(`[DEBUG] SAS Token API Failed: ${res.status} - ${txt}`);
|
|
310
437
|
return null;
|
|
311
438
|
}
|
|
312
|
-
|
|
439
|
+
|
|
313
440
|
const data = await res.json();
|
|
314
441
|
return data;
|
|
315
442
|
} catch (e) {
|
package/src/configLoader.mjs
CHANGED
|
@@ -18,8 +18,7 @@ export const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
|
18
18
|
* @returns {string}
|
|
19
19
|
*/
|
|
20
20
|
export function getApiUrl() {
|
|
21
|
-
|
|
22
|
-
return 'https://arcalityqadev.arcadial.lat';
|
|
21
|
+
return 'https://arcalityqadev.arcadial.lat';
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
/**
|
|
@@ -66,6 +65,11 @@ export function loadConfig() {
|
|
|
66
65
|
return config;
|
|
67
66
|
}
|
|
68
67
|
|
|
68
|
+
export function loadProjectId() {
|
|
69
|
+
const localConfig = loadLocalArcalityConfig();
|
|
70
|
+
return localConfig?.projectId || process.env.ARCALITY_PROJECT_ID;
|
|
71
|
+
}
|
|
72
|
+
|
|
69
73
|
|
|
70
74
|
/**
|
|
71
75
|
* Reads the user's global config (~/.arcality/config.json)
|
package/src/configManager.mjs
CHANGED
|
@@ -127,7 +127,7 @@ export function injectConfigToEnv(config) {
|
|
|
127
127
|
|
|
128
128
|
if (config.apiKey) process.env.ARCALITY_API_KEY = config.apiKey;
|
|
129
129
|
if (config.projectId) process.env.ARCALITY_PROJECT_ID = config.projectId;
|
|
130
|
-
if (config.project?.baseUrl) {
|
|
130
|
+
if (config.project?.baseUrl && !process.env.BASE_URL) {
|
|
131
131
|
process.env.BASE_URL = config.project.baseUrl;
|
|
132
132
|
}
|
|
133
133
|
if (config.auth?.username) process.env.LOGIN_USER = config.auth.username;
|