UNPKG

@debugg-ai/debugg-ai-mcp

Version:

Zero-Config, Fully AI-Managed End-to-End Testing for all code gen platforms.

134 lines (133 loc) 6.23 kB
/** * search_executions handler (bead 49b) * * Absorbs list_executions + get_execution. * * Modes: * uuid: {uuid} → {filter:{uuid}, pageInfo:{totalCount:1,...}, executions:[fullDetail]} * fullDetail includes nodeExecutions, state, errorInfo. * filter: {status?, projectUuid?, page?, pageSize?} → {filter, pageInfo, executions:[summary]} */ import { Logger } from '../utils/logger.js'; import { handleExternalServiceError } from '../utils/errors.js'; import { DebuggAIServerClient } from '../services/index.js'; import { config } from '../config/index.js'; import { toPaginationParams } from '../utils/pagination.js'; import { fetchImageAsBase64, imageContentBlock, resourceLinkBlock, artifactResourceLinks } from '../utils/imageUtils.js'; const logger = new Logger({ module: 'searchExecutionsHandler' }); function notFound(uuid) { return { content: [{ type: 'text', text: JSON.stringify({ error: 'NotFound', message: `Execution ${uuid} not found.`, uuid }, null, 2), }], isError: true, }; } export async function searchExecutionsHandler(input, _context) { const start = Date.now(); logger.toolStart('search_executions', input); try { const client = new DebuggAIServerClient(config.api.key); await client.init(); if (input.uuid) { try { const execution = await client.workflows.getExecution(input.uuid); const payload = { filter: { uuid: input.uuid }, pageInfo: { page: 1, pageSize: 1, totalCount: 1, totalPages: 1, hasMore: false }, executions: [execution], }; logger.toolComplete('search_executions', Date.now() - start); const content = [ { type: 'text', text: JSON.stringify(payload, null, 2) }, ]; const SCREENSHOT_URL_KEYS = ['finalScreenshot', 'screenshot', 'screenshotUrl', 'screenshotUri']; const GIF_KEYS = ['runGif', 'gifUrl', 'gif', 'videoUrl', 'recordingUrl']; const nodes = execution.nodeExecutions ?? []; const subworkflowNode = nodes.find((n) => n.nodeType === 'subworkflow.run'); let screenshotEmbedded = false; let screenshotUrl = null; let gifUrl = null; const screenshotB64 = subworkflowNode?.outputData?.screenshotB64; if (typeof screenshotB64 === 'string' && screenshotB64) { content.push(imageContentBlock(screenshotB64, 'image/png')); screenshotEmbedded = true; } for (const node of nodes) { const data = node.outputData ?? {}; if (!screenshotEmbedded && !screenshotUrl) { for (const key of SCREENSHOT_URL_KEYS) { if (typeof data[key] === 'string' && data[key]) { screenshotUrl = data[key]; break; } } } if (!gifUrl) { for (const key of GIF_KEYS) { if (typeof data[key] === 'string' && data[key]) { gifUrl = data[key]; break; } } } if ((screenshotEmbedded || screenshotUrl) && gifUrl) break; } if (!screenshotEmbedded && screenshotUrl) { const img = await fetchImageAsBase64(screenshotUrl).catch(() => null); if (img) content.push(imageContentBlock(img.data, img.mimeType)); } // Artifact links (bead 8qndk): run recording (legacy GIF field) + the // browserSession presigned URLs (HAR / console log / recording). Linked, // not base64-inlined. Screenshot stays inline above for vision. const artifactLinks = [ ...(gifUrl ? [resourceLinkBlock(gifUrl, `run-recording-${input.uuid}.gif`, { mimeType: 'image/gif', title: 'Run recording', description: 'Animated recording of the execution (presigned URL — open or fetch on demand).', })] : []), ...artifactResourceLinks(execution.browserSession), ]; const seenArtifactUris = new Set(); for (const link of artifactLinks) { if (link.uri && !seenArtifactUris.has(link.uri)) { seenArtifactUris.add(link.uri); content.push(link); } } return { content }; } catch (err) { if (err?.statusCode === 404 || err?.response?.status === 404) return notFound(input.uuid); throw err; } } const pagination = toPaginationParams({ page: input.page, pageSize: input.pageSize }); const { pageInfo, executions } = await client.workflows.listExecutions({ status: input.status, projectId: input.projectUuid, page: pagination.page, pageSize: pagination.pageSize, }); const payload = { filter: { status: input.status ?? null, projectUuid: input.projectUuid ?? null, }, pageInfo, executions, }; logger.toolComplete('search_executions', Date.now() - start); return { content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }] }; } catch (error) { logger.toolError('search_executions', error, Date.now() - start); throw handleExternalServiceError(error, 'DebuggAI', 'search_executions'); } }