@testrelic/maestro-analytics 1.0.1 → 1.1.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/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cloud-config.ts","../src/config.ts","../src/parsers/junit-parser.ts","../src/parsers/command-parser.ts","../src/parsers/flow-parser.ts","../src/parsers/log-parser.ts","../src/parsers/ai-report-parser.ts","../src/artifact-collector.ts","../src/timeline-builder.ts","../src/summary-builder.ts","../src/html-css.ts","../src/html-logo.ts","../src/html-js-render.ts","../src/html-js-drawer.ts","../src/html-js-steps.ts","../src/html-js-assertions.ts","../src/html-js-ai-defects.ts","../src/html-js-interactions.ts","../src/html-template.ts","../src/html-report.ts","../src/console-summary.ts","../src/browser-open.ts","../src/maestro-runner.ts","../src/cloud-auth.ts","../src/git-metadata.ts","../src/cloud-queue.ts","../src/cloud-client.ts","../src/cloud-upload.ts","../src/cloud-artifact-upload.ts","../src/ci-detector.ts","../src/cloud-reporter.ts","../src/report-orchestrator.ts","../src/merge.ts"],"names":["CONFIG_DIR","CONFIG_FILENAME","LEGACY_CONFIG_FILENAME","MAX_WALK_UP_LEVELS","DANGEROUS_KEYS","DEFAULT_CLOUD_CONFIG","DURATION_UNITS","discoverConfigFile","startDir","currentDir","resolve","i","candidate","join","existsSync","statSync","legacyCandidate","parentDir","dirname","parseConfigFile","filePath","content","readFileSync","parsed","isSafeObject","obj","key","val","resolveEnvVar","value","bracketMatch","dollarMatch","resolveEnvVars","result","parseDuration","match","amount","unit","multiplier","mergeCloudConfig","fileConfig","reporterOptions","target","cloud","repoSection","queue","ms","resolved","envApiKey","envEndpoint","isValidEndpointUrl","envUpload","envTimeout","config","isValidCloudConfig","hasPrototypePollution","isValidMaestroConfig","input","resolveConfig","options","createError","ErrorCode","outputPath","resolveCloudFromMerge","configPath","resolvedFile","merged","parser","XMLParser","toArray","parseProperties","raw","p","parseTestCase","name","classname","time","id","rawStatus","failure","error","skipped","status","parseTestSuite","device","tests","failures","errors","testCases","parseJUnitXml","xmlContent","suites","container","totalTests","totalFailures","totalErrors","totalSkipped","totalTime","suite","parseJUnitFile","INTERACTION_COMMANDS","ASSERTION_COMMANDS","NAVIGATION_COMMANDS","DEVICE_COMMANDS","MEDIA_COMMANDS","SCRIPT_COMMANDS","FLOW_CONTROL_COMMANDS","AI_COMMANDS","categorizeCommand","command","MAESTRO_COMMAND_NAME_MAP","normalizeMaestroCommandName","withoutSuffix","extractMaestroSelector","params","sel","cond","inner","parseRawCommand","index","baseTimestamp","selector","commandObj","rawKey","meta","mapStatus","duration","timestamp","lower","parseCommandsJson","jsonContent","base","entry","commands","parseCommandsFile","discoverCommandFiles","artifactsDir","readdirSync","f","basename","extractSubflowRefs","body","refs","item","flow","extractHookRefs","hooks","items","parseFlowYaml","parts","headerContent","bodyContent","header","parseDocument","bodyCommands","appId","tags","t","env","k","v","properties","onFlowStart","onFlowComplete","subflowRefs","parseFlowFile","discoverFlowFiles","dir","results","walk","entries","fullPath","ext","extname","LOG_LINE_REGEX","TIMESTAMP_ONLY_REGEX","parseLevel","upper","parseLogContent","lines","today","line","trimmed","last","parseLogFile","detectPlatformFromLogs","msg","discoverLogFiles","DEFECT_SECTION_REGEX","SEVERITY_CRITICAL_REGEX","SEVERITY_WARNING_REGEX","UI_DEFECT_REGEX","SPELLING_REGEX","I18N_REGEX","LAYOUT_REGEX","A11Y_REGEX","classifyDefectType","text","classifySeverity","stripHtmlTags","html","extractDefectsFromHtml","defects","plainText","listItemRegex","itemHtml","itemText","paragraphRegex","pText","sentences","s","sentence","parseAiReportHtml","htmlContent","parseAiReportFile","discoverAiReports","IMAGE_EXTENSIONS","VIDEO_EXTENSIONS","walkDir","recurse","current","collectArtifacts","testOutputDir","debugOutputDir","screenshotPaths","videoPaths","logPaths","commandJsonPaths","aiReportPaths","junitReportPath","allFiles","seen","getArtifactStats","artifacts","mapCommandCategory","category","commandToAction","cmd","buildTestResult","actions","a","b","buildTimelineEntry","testResult","buildTimeline","flows","EMPTY_STATUS_RANGE","buildSummary","timeline","total","passed","failed","flaky","timedout","totalAssertions","passedAssertions","failedAssertions","totalNavigations","totalActionSteps","actionCategoryCounts","uniqueUrls","test","action","cat","CSS","LOGO_SVG_RAW","LOGO_SVG","JS_RENDER","JS_DRAWER","JS_STEPS","JS_ASSERTIONS","JS_AI_DEFECTS","JS_INTERACTIONS","JS","renderHtmlDocument","reportJson","safeJson","generateHtmlReport","report","aiDefects","htmlPath","htmlDir","mkdirSync","writeFileSync","pad","padding","printConsoleSummary","summary","htmlReportPath","quiet","top","bottom","blank","output","catParts","catMap","catStr","critical","d","warning","info","defectParts","openInBrowser","platform","exec","generateTempDir","randomBytes","tmpdir","buildMaestroArgs","tempDir","args","junitPath","runMaestro","resolvePromise","stdoutChunks","stderrChunks","proc","spawn","chunk","code","err","LOCALHOST_HOSTS","enforceHttps","endpoint","url","HEALTH_CHECK_TIMEOUT_MS","healthCheck","controller","timer","sleep","parseCloudError","response","exchangeToken","apiKey","timeout","retryAfter","waitMs","retryResponse","isAuthError","refreshAccessToken","currentRefreshToken","resolveRepo","accessToken","gitId","displayName","branch","requestBody","responseBody","GIT_TIMEOUT_MS","execGit","cwd","execSync","collectGitMetadata","commitSha","commitMessage","commitAuthor","rawRemoteUrl","getRemoteUrl","remoteUrl","normalizeGitRemoteUrl","originUrl","remotes","firstRemote","normalized","deriveRepoDisplayName","normalizedUrl","segments","readPackageJsonName","dirPath","pkg","deriveNonGitProjectId","pkgName","QUEUE_ENTRY_VERSION","FLUSH_BATCH_SIZE","writeToQueue","queueDirectory","runId","type","reason","targetEndpoint","method","payload","headers","filename","tmpPath","renameSync","flushQueue","files","batches","batch","file","isValidQueueEntry","unlinkSync","queueEntry","cleanupExpiredQueue","maxAge","now","queuedAt","queuedTime","TOKEN_REFRESH_BUFFER_MS","PROJECT_CACHE_TTL_MS","HEALTH_CHECK_INTERVAL_MS","FLUSH_TIMEOUT_MS","DASHBOARD_SETTINGS_URL","CloudClient","cloudConfig","tokenResult","expiresAt","refreshResult","statusCode","effectiveGitId","cached","cachePath","cache","currentKeyHash","repoId","cacheDir","createHash","queueDir","GZIP_THRESHOLD_BYTES","RETRY_DELAYS_MS","MAX_RETRIES","buildUploadPayload","repoGitId","git","ci","environment","retryWithBackoff","init","onTokenRefresh","attempt","newToken","refreshedInit","prepareBody","json","compressed","gzipSync","uploadBatchRun","stripNulls","initRealtimeRun","finalizeRun","cloudRunId","UPLOAD_CONCURRENCY","CONTENT_TYPE_MAP","activeUploads","pendingUploads","acquireSlot","releaseSlot","getContentType","getFileSize","requestUploadUrl","request","sizeBytes","contentType","putFileToPresignedUrl","presignedUrl","nodeStream","createReadStream","webStream","Readable","confirmUpload","artifactId","uploadArtifact","maxSizeMb","urlResult","confirmed","uploadArtifacts","requests","promises","req","detectGitHubActions","detectGitLabCI","detectJenkins","detectCircleCI","detectBitbucketPipelines","runUrl","detectors","detectCI","envVars","detect","flattenTimelineForCloud","flat","timelineEntry","getEffectiveGitId","cloudClient","finalizeAndUpload","testRunId","completedAt","totalDuration","videoArtifacts","perTestVideoCount","hasArtifacts","token","path","perTestPaths","va","resultEntries","uploaded","r","videoCount","perFlowVideoCount","ssCount","perFlowNote","label","batchGit","batchCi","batchGitId","flatTimeline","flatReport","queueGit","queueCi","queueGitId","queuePayload","matchVideoToFlow","flowFile","flowRecordingPath","flowBase","exact","forward","reverse","videoBase","recBase","byRec","byRecPartial","matchScreenshotsToFlow","matched","nameBase","extractRecordingPath","buildFlowRecordingMap","commandSteps","map","recPath","platformToOs","flowToTestResult","orchestrateReport","startedAt","randomUUID","logFiles","logFile","logEntries","detected","flowMetadataMap","flowFiles","commandFiles","cmdFile","allAiDefects","aiReportFiles","aiFile","aiReport","flowRecordingMap","flowResults","junit","allCommands","assertionCommands","c","nonAssertionCommands","testCase","durationMs","flowStartedAt","flowCompletedAt","recordingPath","videoPathsToUpload","testId","singleVideo","matchedPaths","unmatchedVideos","testsForUpload","isTimelineEntry","mergeReports","reportPaths","allTimeline","timelineEntries","earliestStart","latestEnd","reportPath","startMs","endMs","mergeReportsFromDirectory"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACHZ,CAAA,CAAA,IAAMC,EAAAA,CAAe,CAAA;AAAA;AAAA;AAAA;AAAA,8BAAA,CAAA,CAMfC,EAAAA,CAAW,CAAA;AAAA;AAAA;AAAA;ACNjB,8BAAA,CAAA,CAAA,IAAMC,EAAAA,CAAY;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACAlB,CAAA,CAAA,IAAMC,EAAAA,CAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;ACAlB,CAAA,CAAA,IAAMC,EAAAA,CAAW;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;ACDjB,CAAA,CAAA,IAAMC,EAAAA,CAAgB;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACCtB,CAAA,CAAA,IAAMC,EAAAA,CAAgB;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAtB,CAAA,CAAA,IAAMC,EAAAA,CAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;ACa/B,CAAA,CAAA,IAAMC,EAAAA,CAAK;AAAA;AAAA;AAAA;AAAA;AAAA;;AAA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","file":"index.js","sourcesContent":["/**\n * Cloud configuration discovery, parsing, and merging.\n *\n * Discovers `.testrelic/testrelic-config.json` config files, resolves environment variable\n * references, and merges configuration from multiple sources.\n *\n * Priority order (highest wins):\n * 1. Environment variables (TESTRELIC_API_KEY, TESTRELIC_CLOUD_ENDPOINT, etc.)\n * 2. CLI options\n * 3. .testrelic/testrelic-config.json config file\n * 4. Built-in defaults\n */\n\nimport { readFileSync, existsSync, statSync } from 'node:fs';\nimport { join, dirname, resolve } from 'node:path';\nimport type { CloudConfig, UploadStrategy, CloudReporterOptions } from '@testrelic/core';\nimport { isValidCloudConfig, isValidEndpointUrl } from '@testrelic/core';\n\nconst CONFIG_DIR = '.testrelic';\nconst CONFIG_FILENAME = 'testrelic-config.json';\nconst LEGACY_CONFIG_FILENAME = '.testrelic';\nconst MAX_WALK_UP_LEVELS = 5;\n\nconst DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n\nconst DEFAULT_CLOUD_CONFIG: CloudConfig = Object.freeze({\n apiKey: null,\n endpoint: 'https://platform.testrelic.ai/api/v1',\n uploadStrategy: 'batch',\n timeout: 30000,\n projectName: null,\n queueMaxAge: 604800000,\n queueDirectory: `${CONFIG_DIR}/queue`,\n uploadArtifacts: true,\n artifactMaxSizeMb: 50,\n});\n\nconst DURATION_UNITS: Record<string, number> = {\n s: 1000,\n m: 60 * 1000,\n h: 60 * 60 * 1000,\n d: 24 * 60 * 60 * 1000,\n};\n\nexport function discoverConfigFile(startDir: string): string | null {\n let currentDir = resolve(startDir);\n for (let i = 0; i <= MAX_WALK_UP_LEVELS; i++) {\n const candidate = join(currentDir, CONFIG_DIR, CONFIG_FILENAME);\n if (existsSync(candidate)) {\n try {\n if (statSync(candidate).isFile()) return candidate;\n } catch {\n // ignore\n }\n }\n\n const legacyCandidate = join(currentDir, LEGACY_CONFIG_FILENAME);\n if (existsSync(legacyCandidate)) {\n try {\n if (statSync(legacyCandidate).isFile()) {\n process.stderr.write(\n '[testrelic] Deprecation: config file \".testrelic\" has moved to \".testrelic/testrelic-config.json\". ' +\n 'Please migrate your config file to the new location.\\n',\n );\n return legacyCandidate;\n }\n } catch {\n // ignore\n }\n }\n\n const parentDir = dirname(currentDir);\n if (parentDir === currentDir) break;\n currentDir = parentDir;\n }\n return null;\n}\n\nexport function parseConfigFile(filePath: string): Record<string, unknown> | null {\n try {\n const content = readFileSync(filePath, 'utf-8');\n const parsed: unknown = JSON.parse(content);\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return null;\n }\n if (!isSafeObject(parsed as Record<string, unknown>)) {\n return null;\n }\n return parsed as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\nfunction isSafeObject(obj: Record<string, unknown>): boolean {\n for (const key of Object.keys(obj)) {\n if (DANGEROUS_KEYS.has(key)) return false;\n const val = obj[key];\n if (typeof val === 'object' && val !== null && !Array.isArray(val)) {\n if (!isSafeObject(val as Record<string, unknown>)) return false;\n }\n }\n return true;\n}\n\nexport function resolveEnvVar(value: string): string | null {\n const bracketMatch = /^\\$\\{([A-Za-z_][A-Za-z0-9_]*)\\}$/.exec(value);\n if (bracketMatch) {\n return process.env[bracketMatch[1]] ?? null;\n }\n const dollarMatch = /^\\$([A-Za-z_][A-Za-z0-9_]*)$/.exec(value);\n if (dollarMatch) {\n return process.env[dollarMatch[1]] ?? null;\n }\n return value;\n}\n\nexport function resolveEnvVars(obj: Record<string, unknown>): Record<string, unknown> {\n const result = Object.create(null) as Record<string, unknown>;\n for (const key of Object.keys(obj)) {\n const val = obj[key];\n if (typeof val === 'string' && val.startsWith('$')) {\n result[key] = resolveEnvVar(val);\n } else if (typeof val === 'object' && val !== null && !Array.isArray(val)) {\n result[key] = resolveEnvVars(val as Record<string, unknown>);\n } else {\n result[key] = val;\n }\n }\n return result;\n}\n\nexport function parseDuration(value: string): number | null {\n const match = /^(\\d+)\\s*([smhd])$/.exec(value.trim());\n if (!match) return null;\n const amount = parseInt(match[1], 10);\n const unit = match[2];\n const multiplier = DURATION_UNITS[unit];\n if (!multiplier || amount <= 0) return null;\n return amount * multiplier;\n}\n\nexport function mergeCloudConfig(\n fileConfig: Record<string, unknown> | null,\n reporterOptions: CloudReporterOptions | undefined,\n): CloudConfig {\n const target = Object.create(null) as Record<string, unknown>;\n\n Object.assign(target, DEFAULT_CLOUD_CONFIG);\n\n if (fileConfig) {\n const cloud = fileConfig.cloud as Record<string, unknown> | undefined;\n if (cloud && typeof cloud === 'object') {\n if (typeof cloud.endpoint === 'string') target.endpoint = cloud.endpoint;\n if (typeof cloud.upload === 'string') target.uploadStrategy = cloud.upload;\n if (typeof cloud.timeout === 'number') target.timeout = cloud.timeout;\n if (typeof cloud.apiKey === 'string' && cloud.apiKey.length > 0) {\n target.apiKey = cloud.apiKey;\n }\n }\n const repoSection = (fileConfig['testrelic-repo'] ?? fileConfig.project) as Record<string, unknown> | undefined;\n if (repoSection && typeof repoSection === 'object') {\n if (typeof repoSection.name === 'string') target.projectName = repoSection.name;\n }\n const queue = fileConfig.queue as Record<string, unknown> | undefined;\n if (queue && typeof queue === 'object') {\n if (typeof queue.maxAge === 'string') {\n const ms = parseDuration(queue.maxAge as string);\n if (ms !== null) target.queueMaxAge = ms;\n }\n if (typeof queue.directory === 'string') target.queueDirectory = queue.directory;\n }\n }\n\n if (reporterOptions) {\n if (typeof reporterOptions.apiKey === 'string' && reporterOptions.apiKey.length > 0) {\n const resolved = reporterOptions.apiKey.startsWith('$')\n ? resolveEnvVar(reporterOptions.apiKey)\n : reporterOptions.apiKey;\n if (resolved) target.apiKey = resolved;\n }\n if (typeof reporterOptions.endpoint === 'string') {\n const resolved = reporterOptions.endpoint.startsWith('$')\n ? resolveEnvVar(reporterOptions.endpoint)\n : reporterOptions.endpoint;\n if (resolved) target.endpoint = resolved;\n }\n if (typeof reporterOptions.upload === 'string') target.uploadStrategy = reporterOptions.upload;\n if (typeof reporterOptions.timeout === 'number') target.timeout = reporterOptions.timeout;\n if (typeof reporterOptions.projectName === 'string') target.projectName = reporterOptions.projectName;\n if (typeof reporterOptions.queueMaxAge === 'string') {\n const ms = parseDuration(reporterOptions.queueMaxAge);\n if (ms !== null) target.queueMaxAge = ms;\n }\n if (typeof reporterOptions.queueDirectory === 'string') target.queueDirectory = reporterOptions.queueDirectory;\n if (typeof reporterOptions.uploadArtifacts === 'boolean') target.uploadArtifacts = reporterOptions.uploadArtifacts;\n if (typeof reporterOptions.artifactMaxSizeMb === 'number') target.artifactMaxSizeMb = reporterOptions.artifactMaxSizeMb;\n }\n\n const envApiKey = process.env.TESTRELIC_API_KEY;\n if (envApiKey && envApiKey.length > 0) {\n target.apiKey = envApiKey;\n }\n\n const envEndpoint = process.env.TESTRELIC_CLOUD_ENDPOINT;\n if (envEndpoint && isValidEndpointUrl(envEndpoint)) {\n target.endpoint = envEndpoint;\n }\n\n const envUpload = process.env.TESTRELIC_UPLOAD_STRATEGY;\n if (envUpload && ['batch', 'realtime', 'both'].includes(envUpload)) {\n target.uploadStrategy = envUpload as UploadStrategy;\n }\n\n const envTimeout = process.env.TESTRELIC_CLOUD_TIMEOUT;\n if (envTimeout) {\n const parsed = parseInt(envTimeout, 10);\n if (!isNaN(parsed) && parsed >= 1000 && parsed <= 120000) {\n target.timeout = parsed;\n }\n }\n\n const config = Object.freeze(target) as unknown as CloudConfig;\n\n if (!isValidCloudConfig(config)) {\n return DEFAULT_CLOUD_CONFIG;\n }\n\n return config;\n}\n\nexport { DEFAULT_CLOUD_CONFIG };\n","/**\n * Config resolution with prototype pollution prevention.\n * Merges user options into defaults and freezes the result.\n */\n\nimport type { CloudConfig, CloudReporterOptions } from '@testrelic/core';\nimport { createError, ErrorCode } from '@testrelic/core';\nimport type { MaestroReporterConfig, ResolvedMaestroConfig } from './types.js';\nimport { discoverConfigFile, parseConfigFile, resolveEnvVars, mergeCloudConfig } from './cloud-config.js';\n\nconst DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n\nfunction hasPrototypePollution(obj: unknown): boolean {\n if (typeof obj !== 'object' || obj === null) return false;\n for (const key of Object.keys(obj)) {\n if (DANGEROUS_KEYS.has(key)) return true;\n }\n return false;\n}\n\nfunction isValidMaestroConfig(input: unknown): input is MaestroReporterConfig {\n if (typeof input !== 'object' || input === null) return false;\n if (hasPrototypePollution(input)) return false;\n\n const obj = input as Record<string, unknown>;\n if (obj.outputPath !== undefined && typeof obj.outputPath !== 'string') return false;\n if (obj.htmlReportPath !== undefined && typeof obj.htmlReportPath !== 'string') return false;\n if (obj.openReport !== undefined && typeof obj.openReport !== 'boolean') return false;\n if (obj.includeScreenshots !== undefined && typeof obj.includeScreenshots !== 'boolean') return false;\n if (obj.includeVideo !== undefined && typeof obj.includeVideo !== 'boolean') return false;\n if (obj.includeAiAnalysis !== undefined && typeof obj.includeAiAnalysis !== 'boolean') return false;\n if (obj.includeLogs !== undefined && typeof obj.includeLogs !== 'boolean') return false;\n if (obj.includeFlowMetadata !== undefined && typeof obj.includeFlowMetadata !== 'boolean') return false;\n if (obj.quiet !== undefined && typeof obj.quiet !== 'boolean') return false;\n\n if (obj.metadata !== undefined && obj.metadata !== null) {\n if (typeof obj.metadata !== 'object') return false;\n if (hasPrototypePollution(obj.metadata)) return false;\n }\n\n return true;\n}\n\nexport function resolveConfig(options?: Partial<MaestroReporterConfig>): ResolvedMaestroConfig {\n if (options !== undefined && !isValidMaestroConfig(options)) {\n throw createError(ErrorCode.CONFIG_INVALID, 'Invalid Maestro reporter configuration');\n }\n\n const target = Object.create(null) as Record<string, unknown>;\n const outputPath = options?.outputPath ?? './test-results/testrelic-maestro.json';\n\n target.outputPath = outputPath;\n target.htmlReportPath = options?.htmlReportPath ?? outputPath.replace(/\\.json$/, '.html');\n target.openReport = options?.openReport ?? true;\n target.includeScreenshots = options?.includeScreenshots ?? true;\n target.includeVideo = options?.includeVideo ?? true;\n target.includeAiAnalysis = options?.includeAiAnalysis ?? true;\n target.includeLogs = options?.includeLogs ?? true;\n target.includeFlowMetadata = options?.includeFlowMetadata ?? true;\n target.flowsDir = options?.flowsDir ?? null;\n target.testRunId = options?.testRunId ?? null;\n target.metadata = options?.metadata ?? null;\n target.quiet = options?.quiet ?? false;\n target.reportMode = options?.reportMode ?? 'batch';\n\n target.cloud = resolveCloudFromMerge(options?.cloud ?? null);\n\n return Object.freeze(target) as unknown as ResolvedMaestroConfig;\n}\n\nfunction resolveCloudFromMerge(\n reporterOptions: CloudReporterOptions | null | undefined,\n): CloudConfig | null {\n const configPath = discoverConfigFile(process.cwd());\n const fileConfig = configPath ? parseConfigFile(configPath) : null;\n const resolvedFile = fileConfig ? resolveEnvVars(fileConfig) : null;\n const merged = mergeCloudConfig(\n resolvedFile as Record<string, unknown> | null,\n reporterOptions ?? undefined,\n );\n return merged.apiKey ? merged : null;\n}\n","/**\n * JUnit XML parser for Maestro test reports.\n *\n * Parses the JUnit XML generated by `maestro test --format junit`\n * into structured JUnitReport objects.\n */\n\nimport { XMLParser } from 'fast-xml-parser';\nimport { readFileSync } from 'node:fs';\nimport type { JUnitReport, JUnitTestSuite, JUnitTestCase, JUnitProperty } from '../types.js';\n\nconst parser = new XMLParser({\n ignoreAttributes: false,\n attributeNamePrefix: '@_',\n allowBooleanAttributes: true,\n parseAttributeValue: true,\n trimValues: true,\n});\n\nfunction toArray<T>(value: T | T[] | undefined | null): T[] {\n if (value === undefined || value === null) return [];\n return Array.isArray(value) ? value : [value];\n}\n\nfunction parseProperties(raw: unknown): JUnitProperty[] {\n if (!raw || typeof raw !== 'object') return [];\n const container = raw as Record<string, unknown>;\n const props = toArray(container.property as Record<string, string> | Record<string, string>[]);\n return props.map((p) => ({\n name: String(p['@_name'] ?? ''),\n value: String(p['@_value'] ?? ''),\n }));\n}\n\nfunction parseTestCase(raw: Record<string, unknown>): JUnitTestCase {\n const name = String(raw['@_name'] ?? 'unnamed');\n const classname = String(raw['@_classname'] ?? name);\n const time = Number(raw['@_time'] ?? 0);\n const id = raw['@_id'] != null ? String(raw['@_id']) : null;\n const rawStatus = String(raw['@_status'] ?? '').toUpperCase();\n\n const failure = raw.failure as Record<string, unknown> | undefined;\n const error = raw.error as Record<string, unknown> | undefined;\n const skipped = raw.skipped !== undefined;\n\n let status: JUnitTestCase['status'] = 'SUCCESS';\n if (failure) status = 'FAILURE';\n else if (error) status = 'ERROR';\n else if (skipped) status = 'SKIPPED';\n else if (rawStatus === 'FAILURE' || rawStatus === 'FAILED') status = 'FAILURE';\n else if (rawStatus === 'ERROR') status = 'ERROR';\n else if (rawStatus === 'SKIPPED') status = 'SKIPPED';\n\n return {\n id,\n name,\n classname,\n time,\n status,\n failureMessage: failure ? String(failure['@_message'] ?? failure['#text'] ?? '') : null,\n failureType: failure ? String(failure['@_type'] ?? '') : null,\n errorMessage: error ? String(error['@_message'] ?? error['#text'] ?? '') : null,\n errorType: error ? String(error['@_type'] ?? '') : null,\n properties: parseProperties(raw.properties),\n };\n}\n\nfunction parseTestSuite(raw: Record<string, unknown>): JUnitTestSuite {\n const name = String(raw['@_name'] ?? 'Test Suite');\n const device = raw['@_device'] != null ? String(raw['@_device']) : null;\n const tests = Number(raw['@_tests'] ?? 0);\n const failures = Number(raw['@_failures'] ?? 0);\n const errors = Number(raw['@_errors'] ?? 0);\n const skipped = Number(raw['@_skipped'] ?? 0);\n const time = Number(raw['@_time'] ?? 0);\n\n const rawCases = toArray(raw.testcase as Record<string, unknown> | Record<string, unknown>[]);\n const testCases = rawCases.map(parseTestCase);\n\n return {\n name,\n device,\n tests,\n failures,\n errors,\n skipped,\n time,\n testCases,\n properties: parseProperties(raw.properties),\n };\n}\n\nexport function parseJUnitXml(xmlContent: string): JUnitReport {\n const parsed = parser.parse(xmlContent) as Record<string, unknown>;\n\n let suites: JUnitTestSuite[] = [];\n\n if (parsed.testsuites) {\n const container = parsed.testsuites as Record<string, unknown>;\n const rawSuites = toArray(container.testsuite as Record<string, unknown> | Record<string, unknown>[]);\n suites = rawSuites.map(parseTestSuite);\n } else if (parsed.testsuite) {\n const rawSuites = toArray(parsed.testsuite as Record<string, unknown> | Record<string, unknown>[]);\n suites = rawSuites.map(parseTestSuite);\n }\n\n let totalTests = 0;\n let totalFailures = 0;\n let totalErrors = 0;\n let totalSkipped = 0;\n let totalTime = 0;\n\n for (const suite of suites) {\n totalTests += suite.tests;\n totalFailures += suite.failures;\n totalErrors += suite.errors;\n totalSkipped += suite.skipped;\n totalTime += suite.time;\n }\n\n return { testSuites: suites, totalTests, totalFailures, totalErrors, totalSkipped, totalTime };\n}\n\nexport function parseJUnitFile(filePath: string): JUnitReport {\n const content = readFileSync(filePath, 'utf-8');\n return parseJUnitXml(content);\n}\n","/**\n * Parser for Maestro commands-*.json files.\n *\n * These files are generated in the --test-output-dir and contain\n * per-command execution data for each flow.\n */\n\nimport { readFileSync, readdirSync, existsSync } from 'node:fs';\nimport { join, basename } from 'node:path';\nimport type { MaestroCommandStep, MaestroCommandCategory } from '../types.js';\n\nconst INTERACTION_COMMANDS = new Set([\n 'tapOn', 'doubleTapOn', 'longPressOn', 'inputText', 'eraseText',\n 'pasteText', 'swipe', 'scroll', 'scrollUntilVisible', 'hideKeyboard',\n 'pressKey', 'setClipboard', 'copyTextFrom',\n]);\n\nconst ASSERTION_COMMANDS = new Set([\n 'assertVisible', 'assertNotVisible', 'assertTrue', 'assertScreenshot',\n 'assertNoDefectsWithAI', 'assertWithAI',\n // Maestro internal command name used for assertVisible/assertNotVisible\n 'assertCondition',\n]);\n\nconst NAVIGATION_COMMANDS = new Set([\n 'launchApp', 'killApp', 'stopApp', 'clearState', 'openLink', 'back',\n 'clearKeychain',\n]);\n\nconst DEVICE_COMMANDS = new Set([\n 'setAirplaneMode', 'toggleAirplaneMode', 'setLocation', 'setOrientation',\n 'setPermissions', 'addMedia', 'travel',\n]);\n\nconst MEDIA_COMMANDS = new Set([\n 'takeScreenshot', 'startRecording', 'stopRecording',\n]);\n\nconst SCRIPT_COMMANDS = new Set([\n 'runScript', 'evalScript',\n]);\n\nconst FLOW_CONTROL_COMMANDS = new Set([\n 'runFlow', 'repeat', 'retry', 'waitForAnimationToEnd', 'extendedWaitUntil',\n]);\n\nconst AI_COMMANDS = new Set([\n 'assertWithAI', 'assertNoDefectsWithAI', 'extractTextWithAI',\n]);\n\nexport function categorizeCommand(command: string): MaestroCommandCategory {\n if (AI_COMMANDS.has(command)) return 'ai';\n if (ASSERTION_COMMANDS.has(command)) return 'assertion';\n if (INTERACTION_COMMANDS.has(command)) return 'interaction';\n if (NAVIGATION_COMMANDS.has(command)) return 'navigation';\n if (DEVICE_COMMANDS.has(command)) return 'device';\n if (MEDIA_COMMANDS.has(command)) return 'media';\n if (SCRIPT_COMMANDS.has(command)) return 'script';\n if (FLOW_CONTROL_COMMANDS.has(command)) return 'flow_control';\n return 'other';\n}\n\n/**\n * Maestro 2.x format: each entry has a nested \"command\" object whose single\n * key is the command type (e.g. \"launchAppCommand\") and a \"metadata\" object\n * with status, timestamp (Unix ms), and duration.\n */\ninterface MaestroCommandMetadata {\n status?: string;\n timestamp?: number;\n duration?: number;\n sequenceNumber?: number;\n}\n\ninterface RawCommandEntry {\n /** Either a plain string (legacy) or a nested {commandType: params} object (Maestro 2.x) */\n command?: string | Record<string, unknown>;\n commandName?: string;\n name?: string;\n status?: string;\n duration?: number;\n durationMs?: number;\n timestamp?: string;\n time?: string;\n error?: string;\n errorMessage?: string;\n selector?: string;\n metadata?: MaestroCommandMetadata;\n [key: string]: unknown;\n}\n\n/**\n * Maestro internal command key names → user-facing Maestro command names.\n * Only entries that differ after stripping the \"Command\" suffix are listed.\n */\nconst MAESTRO_COMMAND_NAME_MAP: Record<string, string> = {\n tapOnElement: 'tapOn',\n assertCondition: 'assertVisible',\n backPress: 'back',\n applyConfiguration: 'applyConfiguration',\n defineVariables: 'defineVariables',\n};\n\n/**\n * Normalise the raw key from Maestro's commands JSON.\n * e.g. \"launchAppCommand\" → \"launchApp\", \"tapOnElement\" → \"tapOn\"\n */\nfunction normalizeMaestroCommandName(key: string): string {\n const withoutSuffix = key.replace(/Command$/, '');\n return MAESTRO_COMMAND_NAME_MAP[withoutSuffix] ?? withoutSuffix;\n}\n\n/**\n * Extract a human-readable selector/target string from the nested Maestro\n * command params object (used to enrich the step title in the timeline).\n */\nfunction extractMaestroSelector(params: Record<string, unknown>): string | undefined {\n // tapOn: { selector: { textRegex, id, text, ... } }\n if (params.selector && typeof params.selector === 'object') {\n const sel = params.selector as Record<string, unknown>;\n if (typeof sel.textRegex === 'string') return sel.textRegex;\n if (typeof sel.text === 'string') return sel.text;\n if (typeof sel.id === 'string') return `#${sel.id}`;\n }\n // assertCondition: { condition: { visible: { textRegex, text, ... } } }\n if (params.condition && typeof params.condition === 'object') {\n const cond = params.condition as Record<string, unknown>;\n const inner = (cond.visible ?? cond.notVisible) as Record<string, unknown> | undefined;\n if (inner) {\n if (typeof inner.textRegex === 'string') return inner.textRegex;\n if (typeof inner.text === 'string') return inner.text;\n }\n }\n // screenshot/recording: { path: \"...\" }\n if (typeof params.path === 'string') return params.path;\n return undefined;\n}\n\nfunction parseRawCommand(raw: RawCommandEntry, index: number, baseTimestamp: string): MaestroCommandStep {\n let command: string;\n let selector: string | undefined;\n\n if (typeof raw.command === 'object' && raw.command !== null && !Array.isArray(raw.command)) {\n // Maestro 2.x nested format: { \"launchAppCommand\": { ...params } }\n const commandObj = raw.command as Record<string, unknown>;\n const rawKey = Object.keys(commandObj)[0] ?? `step-${index}`;\n command = normalizeMaestroCommandName(rawKey);\n const params = (commandObj[rawKey] as Record<string, unknown>) ?? {};\n selector = extractMaestroSelector(params);\n } else {\n command = (raw.command as string | undefined) ?? raw.commandName ?? raw.name ?? `step-${index}`;\n selector = raw.selector ?? undefined;\n }\n\n // Prefer Maestro's metadata block for status/duration/timestamp\n const meta = raw.metadata ?? {};\n const status = mapStatus((meta.status as string | undefined) ?? raw.status);\n const duration = (meta.duration as number | undefined) ?? raw.duration ?? raw.durationMs ?? 0;\n\n // Maestro stores timestamps as Unix-millisecond integers\n let timestamp: string;\n if (typeof meta.timestamp === 'number') {\n timestamp = new Date(meta.timestamp).toISOString();\n } else {\n timestamp = raw.timestamp ?? raw.time ?? baseTimestamp;\n }\n\n const error = raw.error ?? raw.errorMessage ?? undefined;\n\n return {\n command,\n category: categorizeCommand(command),\n status,\n duration,\n timestamp,\n ...(selector ? { selector } : {}),\n ...(error ? { error } : {}),\n };\n}\n\nfunction mapStatus(status?: string): MaestroCommandStep['status'] {\n if (!status) return 'completed';\n const lower = status.toLowerCase();\n if (lower === 'failed' || lower === 'error') return 'failed';\n if (lower === 'skipped') return 'skipped';\n return 'completed';\n}\n\nexport function parseCommandsJson(jsonContent: string, baseTimestamp?: string): MaestroCommandStep[] {\n const base = baseTimestamp ?? new Date().toISOString();\n try {\n const parsed: unknown = JSON.parse(jsonContent);\n if (Array.isArray(parsed)) {\n return (parsed as RawCommandEntry[]).map((entry, i) => parseRawCommand(entry, i, base));\n }\n if (typeof parsed === 'object' && parsed !== null) {\n const obj = parsed as Record<string, unknown>;\n const commands = obj.commands ?? obj.steps ?? obj.data;\n if (Array.isArray(commands)) {\n return (commands as RawCommandEntry[]).map((entry, i) => parseRawCommand(entry, i, base));\n }\n }\n return [];\n } catch {\n return [];\n }\n}\n\nexport function parseCommandsFile(filePath: string, baseTimestamp?: string): MaestroCommandStep[] {\n try {\n const content = readFileSync(filePath, 'utf-8');\n return parseCommandsJson(content, baseTimestamp);\n } catch {\n return [];\n }\n}\n\nexport function discoverCommandFiles(artifactsDir: string): string[] {\n if (!existsSync(artifactsDir)) return [];\n try {\n return readdirSync(artifactsDir, { recursive: true })\n .map(String)\n .filter((f) => basename(f).startsWith('commands') && f.endsWith('.json'))\n .map((f) => join(artifactsDir, f));\n } catch {\n return [];\n }\n}\n","/**\n * Flow YAML header parser.\n *\n * Extracts metadata from Maestro Flow YAML files:\n * appId, name, tags, properties, env, hooks, and subflow references.\n */\n\nimport { readFileSync, readdirSync, existsSync } from 'node:fs';\nimport { join, extname } from 'node:path';\nimport { parseDocument } from 'yaml';\nimport type { MaestroFlowMetadata } from '../types.js';\n\nfunction extractSubflowRefs(body: unknown[]): string[] {\n const refs: string[] = [];\n for (const item of body) {\n if (typeof item === 'object' && item !== null) {\n const entry = item as Record<string, unknown>;\n if (typeof entry.runFlow === 'string') {\n refs.push(entry.runFlow);\n } else if (typeof entry.runFlow === 'object' && entry.runFlow !== null) {\n const flow = entry.runFlow as Record<string, unknown>;\n if (typeof flow.file === 'string') refs.push(flow.file);\n }\n }\n }\n return refs;\n}\n\nfunction extractHookRefs(hooks: unknown): string[] {\n if (!hooks) return [];\n const refs: string[] = [];\n const items = Array.isArray(hooks) ? hooks : [hooks];\n for (const item of items) {\n if (typeof item === 'string') {\n refs.push(item);\n } else if (typeof item === 'object' && item !== null) {\n const entry = item as Record<string, unknown>;\n if (typeof entry.runFlow === 'string') refs.push(entry.runFlow);\n else if (typeof entry.runFlow === 'object' && entry.runFlow !== null) {\n const flow = entry.runFlow as Record<string, unknown>;\n if (typeof flow.file === 'string') refs.push(flow.file);\n }\n if (typeof entry.runScript === 'string') refs.push(entry.runScript);\n }\n }\n return refs;\n}\n\nexport function parseFlowYaml(content: string, filePath: string): MaestroFlowMetadata {\n const parts = content.split(/^---\\s*$/m, 2);\n const headerContent = parts[0] ?? '';\n const bodyContent = parts[1] ?? '';\n\n let header: Record<string, unknown> = {};\n try {\n const doc = parseDocument(headerContent);\n const parsed = doc.toJS();\n if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {\n header = parsed as Record<string, unknown>;\n }\n } catch {\n // Malformed YAML header — return empty metadata\n }\n\n let bodyCommands: unknown[] = [];\n if (bodyContent.trim()) {\n try {\n const bodyDoc = parseDocument(bodyContent);\n const parsed = bodyDoc.toJS();\n if (Array.isArray(parsed)) bodyCommands = parsed;\n } catch {\n // Malformed body — ignore\n }\n }\n\n const appId = typeof header.appId === 'string' ? header.appId : null;\n const name = typeof header.name === 'string' ? header.name : null;\n\n const tags: string[] = [];\n if (Array.isArray(header.tags)) {\n for (const t of header.tags) {\n if (typeof t === 'string') tags.push(t);\n }\n }\n\n const env: Record<string, string> = {};\n if (typeof header.env === 'object' && header.env !== null && !Array.isArray(header.env)) {\n for (const [k, v] of Object.entries(header.env as Record<string, unknown>)) {\n env[k] = String(v);\n }\n }\n\n const properties: Record<string, string> = {};\n if (typeof header.properties === 'object' && header.properties !== null && !Array.isArray(header.properties)) {\n for (const [k, v] of Object.entries(header.properties as Record<string, unknown>)) {\n properties[k] = String(v);\n }\n }\n\n const onFlowStart = extractHookRefs(header.onFlowStart);\n const onFlowComplete = extractHookRefs(header.onFlowComplete);\n const subflowRefs = extractSubflowRefs(bodyCommands);\n\n return {\n appId,\n name,\n tags,\n env,\n properties,\n onFlowStart,\n onFlowComplete,\n subflowRefs,\n filePath,\n };\n}\n\nexport function parseFlowFile(filePath: string): MaestroFlowMetadata {\n const content = readFileSync(filePath, 'utf-8');\n return parseFlowYaml(content, filePath);\n}\n\nexport function discoverFlowFiles(dir: string): string[] {\n if (!existsSync(dir)) return [];\n const results: string[] = [];\n\n function walk(currentDir: string): void {\n try {\n const entries = readdirSync(currentDir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = join(currentDir, entry.name);\n if (entry.isDirectory()) {\n walk(fullPath);\n } else if (entry.isFile()) {\n const ext = extname(entry.name).toLowerCase();\n if (ext === '.yaml' || ext === '.yml') {\n results.push(fullPath);\n }\n }\n }\n } catch {\n // Skip unreadable directories\n }\n }\n\n walk(dir);\n return results;\n}\n","/**\n * Parser for maestro.log files.\n *\n * Extracts structured log entries with timestamps, levels, and messages.\n * Also detects device/platform metadata from log content.\n */\n\nimport { readFileSync, existsSync } from 'node:fs';\nimport { join, basename } from 'node:path';\nimport { readdirSync } from 'node:fs';\nimport type { MaestroLogEntry, MaestroPlatform } from '../types.js';\n\nconst LOG_LINE_REGEX = /^(\\d{4}-\\d{2}-\\d{2}[T ]\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:?\\d{2})?)\\s+(\\w+)\\s+(.+)$/;\nconst TIMESTAMP_ONLY_REGEX = /^\\[?(\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?)\\]?\\s+(\\w+)\\s+(.+)$/;\n\nfunction parseLevel(raw: string): MaestroLogEntry['level'] {\n const upper = raw.toUpperCase();\n if (upper === 'DEBUG' || upper === 'TRACE' || upper === 'VERBOSE') return 'DEBUG';\n if (upper === 'INFO') return 'INFO';\n if (upper === 'WARN' || upper === 'WARNING') return 'WARN';\n if (upper === 'ERROR' || upper === 'FATAL' || upper === 'SEVERE') return 'ERROR';\n return 'INFO';\n}\n\nexport function parseLogContent(content: string): MaestroLogEntry[] {\n const entries: MaestroLogEntry[] = [];\n const lines = content.split('\\n');\n const today = new Date().toISOString().split('T')[0];\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n\n let match = LOG_LINE_REGEX.exec(trimmed);\n if (match) {\n entries.push({\n timestamp: match[1],\n level: parseLevel(match[2]),\n message: match[3],\n source: null,\n });\n continue;\n }\n\n match = TIMESTAMP_ONLY_REGEX.exec(trimmed);\n if (match) {\n entries.push({\n timestamp: `${today}T${match[1]}`,\n level: parseLevel(match[2]),\n message: match[3],\n source: null,\n });\n continue;\n }\n\n if (entries.length > 0) {\n const last = entries[entries.length - 1];\n entries[entries.length - 1] = {\n ...last,\n message: last.message + '\\n' + trimmed,\n };\n }\n }\n\n return entries;\n}\n\nexport function parseLogFile(filePath: string): MaestroLogEntry[] {\n try {\n const content = readFileSync(filePath, 'utf-8');\n return parseLogContent(content);\n } catch {\n return [];\n }\n}\n\nexport function detectPlatformFromLogs(entries: MaestroLogEntry[]): MaestroPlatform {\n for (const entry of entries) {\n const msg = entry.message.toLowerCase();\n if (msg.includes('android') || msg.includes('adb') || msg.includes('uiautomator') || msg.includes('emulator')) {\n return 'android';\n }\n if (msg.includes('ios') || msg.includes('xcuitest') || msg.includes('simulator') || msg.includes('xctest')) {\n return 'ios';\n }\n if (msg.includes('web driver') || msg.includes('chrome') || msg.includes('chromium')) {\n return 'web';\n }\n }\n return 'unknown';\n}\n\nexport function discoverLogFiles(dir: string): string[] {\n if (!existsSync(dir)) return [];\n try {\n return readdirSync(dir, { recursive: true })\n .map(String)\n .filter((f) => basename(f) === 'maestro.log' || f.endsWith('.log'))\n .map((f) => join(dir, f));\n } catch {\n return [];\n }\n}\n","/**\n * Parser for Maestro AI analysis reports.\n *\n * Maestro's --analyze flag generates HTML insight reports identifying\n * UI defects, spelling errors, and i18n issues. This parser extracts\n * structured defect data from those reports.\n */\n\nimport { readFileSync, existsSync, readdirSync } from 'node:fs';\nimport { join, basename, extname } from 'node:path';\nimport type { AIAnalysisReport, AIDefect, AIDefectSeverity } from '../types.js';\n\nconst DEFECT_SECTION_REGEX = /(?:defect|issue|bug|error|warning|problem)/i;\nconst SEVERITY_CRITICAL_REGEX = /(?:critical|severe|major|blocker)/i;\nconst SEVERITY_WARNING_REGEX = /(?:warning|moderate|minor)/i;\n\nconst UI_DEFECT_REGEX = /(?:cut[\\s-]?off|overlap|truncat|misalign|overflow|clip|obscur|hidden\\s+text|broken\\s+layout)/i;\nconst SPELLING_REGEX = /(?:spelling|typo|misspell|grammar)/i;\nconst I18N_REGEX = /(?:i18n|internationali[sz]ation|locali[sz]ation|untranslated|missing\\s+translation)/i;\nconst LAYOUT_REGEX = /(?:layout|spacing|padding|margin|alignment|centering|position)/i;\nconst A11Y_REGEX = /(?:accessib|a11y|contrast|screen\\s*reader|alt\\s*text|aria)/i;\n\nfunction classifyDefectType(text: string): AIDefect['type'] {\n if (SPELLING_REGEX.test(text)) return 'spelling';\n if (I18N_REGEX.test(text)) return 'i18n';\n if (A11Y_REGEX.test(text)) return 'accessibility';\n if (LAYOUT_REGEX.test(text)) return 'layout';\n return 'ui';\n}\n\nfunction classifySeverity(text: string): AIDefectSeverity {\n if (SEVERITY_CRITICAL_REGEX.test(text)) return 'critical';\n if (SEVERITY_WARNING_REGEX.test(text)) return 'warning';\n return 'info';\n}\n\nfunction stripHtmlTags(html: string): string {\n return html.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim();\n}\n\nfunction extractDefectsFromHtml(html: string): AIDefect[] {\n const defects: AIDefect[] = [];\n const plainText = stripHtmlTags(html);\n\n const listItemRegex = /<li[^>]*>([\\s\\S]*?)<\\/li>/gi;\n let match;\n\n while ((match = listItemRegex.exec(html)) !== null) {\n const itemHtml = match[1];\n const itemText = stripHtmlTags(itemHtml);\n if (!itemText || itemText.length < 5) continue;\n\n if (DEFECT_SECTION_REGEX.test(itemText) || UI_DEFECT_REGEX.test(itemText) ||\n SPELLING_REGEX.test(itemText) || I18N_REGEX.test(itemText)) {\n defects.push({\n type: classifyDefectType(itemText),\n severity: classifySeverity(itemText),\n description: itemText,\n location: null,\n screenshot: null,\n });\n }\n }\n\n if (defects.length === 0) {\n const paragraphRegex = /<p[^>]*>([\\s\\S]*?)<\\/p>/gi;\n while ((match = paragraphRegex.exec(html)) !== null) {\n const pText = stripHtmlTags(match[1]);\n if (!pText || pText.length < 10) continue;\n\n if (UI_DEFECT_REGEX.test(pText) || SPELLING_REGEX.test(pText) ||\n I18N_REGEX.test(pText) || LAYOUT_REGEX.test(pText) || A11Y_REGEX.test(pText)) {\n defects.push({\n type: classifyDefectType(pText),\n severity: classifySeverity(pText),\n description: pText,\n location: null,\n screenshot: null,\n });\n }\n }\n }\n\n if (defects.length === 0 && plainText.length > 20) {\n const sentences = plainText.split(/[.!?]+/).filter((s) => s.trim().length > 10);\n for (const sentence of sentences) {\n const trimmed = sentence.trim();\n if (UI_DEFECT_REGEX.test(trimmed) || SPELLING_REGEX.test(trimmed) ||\n I18N_REGEX.test(trimmed) || LAYOUT_REGEX.test(trimmed) || A11Y_REGEX.test(trimmed)) {\n defects.push({\n type: classifyDefectType(trimmed),\n severity: classifySeverity(trimmed),\n description: trimmed,\n location: null,\n screenshot: null,\n });\n }\n }\n }\n\n return defects;\n}\n\nexport function parseAiReportHtml(htmlContent: string): AIAnalysisReport {\n const defects = extractDefectsFromHtml(htmlContent);\n return {\n defects,\n totalDefects: defects.length,\n hasIssues: defects.length > 0,\n rawHtml: htmlContent,\n };\n}\n\nexport function parseAiReportFile(filePath: string): AIAnalysisReport {\n try {\n const content = readFileSync(filePath, 'utf-8');\n return parseAiReportHtml(content);\n } catch {\n return { defects: [], totalDefects: 0, hasIssues: false, rawHtml: null };\n }\n}\n\nexport function discoverAiReports(dir: string): string[] {\n if (!existsSync(dir)) return [];\n try {\n return readdirSync(dir, { recursive: true })\n .map(String)\n .filter((f) => {\n const name = basename(f).toLowerCase();\n const ext = extname(f).toLowerCase();\n return ext === '.html' && (name.includes('insight') || name.includes('ai') || name.includes('analysis'));\n })\n .map((f) => join(dir, f));\n } catch {\n return [];\n }\n}\n","/**\n * Artifact collector for Maestro test output directories.\n *\n * Scans --test-output-dir and --debug-output for screenshots, video,\n * logs, command JSONs, and AI reports.\n */\n\nimport { existsSync, readdirSync, statSync } from 'node:fs';\nimport { join, basename, extname } from 'node:path';\nimport type { CollectedArtifacts } from './types.js';\n\nconst IMAGE_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg', '.webp']);\nconst VIDEO_EXTENSIONS = new Set(['.mp4', '.webm', '.mov']);\n\nfunction walkDir(dir: string): string[] {\n const results: string[] = [];\n if (!existsSync(dir)) return results;\n\n function recurse(current: string): void {\n try {\n const entries = readdirSync(current, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = join(current, entry.name);\n if (entry.isDirectory()) {\n recurse(fullPath);\n } else if (entry.isFile()) {\n results.push(fullPath);\n }\n }\n } catch {\n // Skip unreadable directories\n }\n }\n\n recurse(dir);\n return results;\n}\n\nexport function collectArtifacts(\n testOutputDir?: string,\n debugOutputDir?: string,\n): CollectedArtifacts {\n const screenshotPaths: string[] = [];\n const videoPaths: string[] = [];\n const logPaths: string[] = [];\n const commandJsonPaths: string[] = [];\n const aiReportPaths: string[] = [];\n let junitReportPath: string | null = null;\n\n const allFiles: string[] = [];\n if (testOutputDir) allFiles.push(...walkDir(testOutputDir));\n if (debugOutputDir) allFiles.push(...walkDir(debugOutputDir));\n\n const seen = new Set<string>();\n\n for (const filePath of allFiles) {\n if (seen.has(filePath)) continue;\n seen.add(filePath);\n\n const name = basename(filePath).toLowerCase();\n const ext = extname(filePath).toLowerCase();\n\n if (IMAGE_EXTENSIONS.has(ext)) {\n screenshotPaths.push(filePath);\n continue;\n }\n\n if (VIDEO_EXTENSIONS.has(ext)) {\n videoPaths.push(filePath);\n continue;\n }\n\n if (name === 'maestro.log' || (ext === '.log' && name.includes('maestro'))) {\n logPaths.push(filePath);\n continue;\n }\n\n if (name.startsWith('commands') && ext === '.json') {\n commandJsonPaths.push(filePath);\n continue;\n }\n\n if (ext === '.html' && (name.includes('insight') || name.includes('ai') || name.includes('analysis'))) {\n aiReportPaths.push(filePath);\n continue;\n }\n\n if (ext === '.xml' && (name.includes('report') || name.includes('junit'))) {\n if (!junitReportPath) junitReportPath = filePath;\n continue;\n }\n }\n\n return {\n screenshotPaths,\n videoPaths,\n logPaths,\n commandJsonPaths,\n aiReportPaths,\n junitReportPath,\n };\n}\n\nexport function getArtifactStats(artifacts: CollectedArtifacts): Record<string, number> {\n return {\n screenshots: artifacts.screenshotPaths.length,\n videos: artifacts.videoPaths.length,\n logs: artifacts.logPaths.length,\n commandFiles: artifacts.commandJsonPaths.length,\n aiReports: artifacts.aiReportPaths.length,\n junitReport: artifacts.junitReportPath ? 1 : 0,\n };\n}\n","/**\n * Builds TimelineEntry arrays from parsed Maestro flow results.\n * Maps Maestro data to the @testrelic/core TimelineEntry shape.\n */\n\nimport type {\n TimelineEntry,\n TestResult,\n TestStatus,\n TestArtifacts,\n ActionStep,\n ActionCategory,\n} from '@testrelic/core';\nimport type { MaestroFlowResult, MaestroCommandStep } from './types.js';\n\nfunction mapCommandCategory(category: MaestroCommandStep['category']): ActionCategory {\n switch (category) {\n case 'interaction': return 'ui_action';\n case 'assertion': return 'assertion';\n case 'ai': return 'assertion';\n case 'navigation': return 'custom_step';\n case 'device': return 'custom_step';\n case 'media': return 'custom_step';\n case 'script': return 'custom_step';\n case 'flow_control': return 'custom_step';\n case 'recording': return 'custom_step';\n default: return 'custom_step';\n }\n}\n\nfunction commandToAction(cmd: MaestroCommandStep): ActionStep {\n return {\n title: cmd.selector ? `${cmd.command} → ${cmd.selector}` : cmd.command,\n category: mapCommandCategory(cmd.category),\n status: cmd.status === 'failed' ? 'failed' : 'passed',\n duration: cmd.duration,\n timestamp: cmd.timestamp,\n videoOffset: null,\n error: cmd.error ?? null,\n children: [],\n };\n}\n\nfunction buildTestResult(flow: MaestroFlowResult): TestResult {\n const actions: ActionStep[] = [\n ...flow.commands.map(commandToAction),\n ...flow.assertions.map(commandToAction),\n ];\n\n actions.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());\n\n const artifacts: TestArtifacts | null =\n flow.screenshotPaths.length > 0 || flow.videoPath\n ? {\n screenshot: flow.screenshotPaths[0] ?? undefined,\n video: flow.videoPath ?? undefined,\n }\n : null;\n\n const tags = [...flow.tags];\n if (flow.platform !== 'unknown' && !tags.includes(flow.platform)) {\n tags.push(flow.platform);\n }\n\n return {\n title: flow.flowName,\n status: flow.status,\n duration: flow.duration,\n startedAt: flow.startedAt,\n completedAt: flow.completedAt,\n retryCount: 0,\n tags,\n failure: flow.failureMessage\n ? { message: flow.failureMessage, line: null, code: null, stack: null }\n : null,\n testId: `${flow.flowFile}::${flow.flowName}`,\n filePath: flow.flowFile,\n suiteName: flow.appId ?? 'maestro-suite',\n testType: 'mobile',\n isFlaky: false,\n retryStatus: null,\n expectedStatus: 'passed',\n actualStatus: flow.status,\n artifacts,\n networkRequests: null,\n apiCalls: null,\n apiAssertions: null,\n actions: actions.length > 0 ? actions : null,\n consoleLogs: null,\n };\n}\n\nexport function buildTimelineEntry(flow: MaestroFlowResult): TimelineEntry {\n const testResult = buildTestResult(flow);\n\n return {\n url: flow.appId ?? 'maestro-flow',\n navigationType: 'page_load',\n visitedAt: flow.startedAt,\n duration: flow.duration,\n specFile: flow.flowFile,\n domContentLoadedAt: null,\n networkIdleAt: null,\n networkStats: null,\n tests: [testResult],\n };\n}\n\nexport function buildTimeline(flows: MaestroFlowResult[]): TimelineEntry[] {\n return flows.map(buildTimelineEntry);\n}\n","/**\n * Computes Summary stats from TimelineEntry arrays.\n */\n\nimport type { Summary, TimelineEntry } from '@testrelic/core';\n\ntype ApiCallsByStatusRange = Record<'2xx' | '3xx' | '4xx' | '5xx' | 'error', number>;\n\nconst EMPTY_STATUS_RANGE: ApiCallsByStatusRange = {\n '2xx': 0, '3xx': 0, '4xx': 0, '5xx': 0, error: 0,\n};\n\nexport function buildSummary(timeline: readonly TimelineEntry[]): Summary {\n let total = 0;\n let passed = 0;\n let failed = 0;\n let flaky = 0;\n let skipped = 0;\n let timedout = 0;\n let totalAssertions = 0;\n let passedAssertions = 0;\n let failedAssertions = 0;\n const totalNavigations = timeline.length;\n let totalActionSteps = 0;\n const actionCategoryCounts: Record<string, number> = {};\n const uniqueUrls = new Set<string>();\n\n for (const entry of timeline) {\n uniqueUrls.add(entry.url);\n\n for (const test of entry.tests) {\n total++;\n switch (test.status) {\n case 'passed': passed++; break;\n case 'failed': failed++; break;\n case 'flaky': flaky++; break;\n case 'skipped': skipped++; break;\n case 'timedout': timedout++; break;\n }\n\n if (test.actions) {\n for (const action of test.actions) {\n totalActionSteps++;\n if (action.category === 'assertion') {\n totalAssertions++;\n if (action.status === 'passed') passedAssertions++;\n else failedAssertions++;\n } else {\n const cat = action.category;\n actionCategoryCounts[cat] = (actionCategoryCounts[cat] ?? 0) + 1;\n }\n }\n }\n }\n }\n\n return {\n total,\n passed,\n failed,\n flaky,\n skipped,\n timedout,\n totalApiCalls: 0,\n uniqueApiUrls: 0,\n apiCallsByMethod: {},\n apiCallsByStatusRange: EMPTY_STATUS_RANGE,\n apiResponseTime: null,\n totalAssertions,\n passedAssertions,\n failedAssertions,\n totalNavigations,\n uniqueNavigationUrls: uniqueUrls.size,\n totalTimelineSteps: timeline.length,\n totalActionSteps,\n actionStepsByCategory: actionCategoryCounts,\n };\n}\n","/**\n * Client-side CSS for TestRelic Maestro Report.\n *\n * Design tokens copied verbatim from Appium html-css.ts.\n * Maestro-specific extensions (waterfall, progress rings, AI cards,\n * fix playbooks, duration heatmap) are additive — never overriding.\n */\nexport const CSS = `\n/* ── Theme Variables (verbatim from Appium) ── */\n:root,[data-theme=\"dark\"]{\n --bg:#0f1117;--bg-1:#161b22;--bg-2:#1c2128;--bg-3:#21262d;--bg-code:#13111c;\n --fg:#e6edf3;--fg-1:#8b949e;--fg-2:#484f58;--fg-code:#d4d4d4;--fg-err:#f8d7da;\n --bd:rgba(255,255,255,0.06);--bd-s:rgba(255,255,255,0.04);--bd-m:rgba(255,255,255,0.08);--bd-l:rgba(255,255,255,0.1);--bd-xs:rgba(255,255,255,0.03);\n --hvr:rgba(255,255,255,0.025);--hvr-s:rgba(255,255,255,0.02);\n --overlay-bg:rgba(0,0,0,.55);--shadow-c:rgba(0,0,0,.35);\n --lb-bg:rgba(0,0,0,.92);--lb-shadow:rgba(0,0,0,.6);--lb-btn:rgba(255,255,255,.1);--lb-btn-h:rgba(255,255,255,.2);\n --scroll-thumb:#21262d;\n}\n[data-theme=\"light\"]{\n --bg:#ffffff;--bg-1:#f6f8fa;--bg-2:#eaeef2;--bg-3:#d0d7de;--bg-code:#f6f8fa;\n --fg:#1f2328;--fg-1:#656d76;--fg-2:#8b949e;--fg-code:#24292e;--fg-err:#82071e;\n --bd:rgba(0,0,0,0.08);--bd-s:rgba(0,0,0,0.04);--bd-m:rgba(0,0,0,0.12);--bd-l:rgba(0,0,0,0.15);--bd-xs:rgba(0,0,0,0.03);\n --hvr:rgba(0,0,0,0.04);--hvr-s:rgba(0,0,0,0.03);\n --overlay-bg:rgba(0,0,0,.3);--shadow-c:rgba(0,0,0,.1);\n --lb-bg:rgba(0,0,0,.85);--lb-shadow:rgba(0,0,0,.3);--lb-btn:rgba(0,0,0,.12);--lb-btn-h:rgba(0,0,0,.2);\n --scroll-thumb:#d0d7de;\n}\n*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}\nbody{font-family:Inter,system-ui,-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;background:var(--bg);color:var(--fg);line-height:1.5;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}\n\n/* ── Layout ── */\n.wrap{max-width:960px;margin:0 auto;padding:32px 24px 64px}\n\n/* ── Topbar (same as Appium) ── */\n.top-bar{display:flex;align-items:center;gap:14px;margin-bottom:20px;flex-wrap:wrap}\n.top-bar-actions{display:flex;align-items:center;gap:8px;flex-shrink:0;margin-left:auto}\n.brand{display:flex;align-items:center;gap:10px}\n.brand svg{flex-shrink:0}\n.brand-text{display:flex;flex-direction:column}\n.brand-title{font-size:17px;font-weight:700;color:var(--fg);line-height:1.2}\n.brand-sub{font-size:10px;font-weight:600;color:#03b79c;letter-spacing:.06em;text-transform:uppercase;margin-top:1px}\n.run-meta{display:flex;flex-wrap:wrap;gap:4px 14px;font-size:11px;color:var(--fg-1);flex:1;min-width:0}\n.run-meta-item{display:flex;align-items:center;gap:4px}\n.run-meta-label{font-weight:600}\n.run-id-tag{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:10px;background:var(--bg-2);padding:1px 6px;border-radius:4px;color:var(--fg-1)}\n.ci-tag{display:inline-flex;align-items:center;gap:4px;background:rgba(59,130,246,0.12);color:#60a5fa;padding:1px 6px;border-radius:4px;font-size:10px;font-weight:600}\n.platform-tag{display:inline-flex;align-items:center;gap:4px;background:rgba(3,183,156,0.12);color:#2dd4a8;padding:1px 6px;border-radius:4px;font-size:10px;font-weight:600}\n\n/* ── Theme Toggle (same as Appium) ── */\n.theme-toggle{display:inline-flex;border:1px solid var(--bd-m);border-radius:8px;overflow:hidden;background:var(--bg);flex-shrink:0}\n.theme-btn{padding:6px 10px;border:none;background:transparent;color:var(--fg-2);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .15s;font-family:inherit}\n.theme-btn:not(:last-child){border-right:1px solid var(--bd-m)}\n.theme-btn.active{background:var(--bg-3);color:var(--fg)}\n.theme-btn:hover:not(.active){color:var(--fg-1);background:var(--hvr)}\n.theme-btn svg{width:14px;height:14px}\n\n/* ── CTA Button (same as Appium) ── */\n.cta-btn{display:inline-flex;align-items:center;gap:5px;font-size:11px;font-weight:500;color:#e6edf3;background:linear-gradient(135deg,rgba(3,183,156,0.15),rgba(14,165,233,0.15));border:1px solid rgba(3,183,156,0.3);padding:6px 14px;border-radius:8px;text-decoration:none;white-space:nowrap;flex-shrink:0;transition:all .2s;letter-spacing:.01em}\n.cta-btn:hover{background:linear-gradient(135deg,rgba(3,183,156,0.25),rgba(14,165,233,0.25));border-color:rgba(3,183,156,0.5);box-shadow:0 0 16px rgba(3,183,156,0.15);color:#fff}\n.cta-btn strong{font-weight:700;color:#2dd4a8}\n.cta-btn:hover strong{color:#5eead4}\n.cta-icon{width:13px;height:13px;color:#2dd4a8;flex-shrink:0}\n.cta-arrow{width:11px;height:11px;color:#2dd4a8;flex-shrink:0;transition:transform .2s}\n.cta-btn:hover .cta-arrow{transform:translateX(2px)}\n[data-theme=\"light\"] .cta-btn{color:#1f2328;background:linear-gradient(135deg,rgba(3,183,156,0.08),rgba(14,165,233,0.08));border-color:rgba(3,183,156,0.25)}\n[data-theme=\"light\"] .cta-btn:hover{background:linear-gradient(135deg,rgba(3,183,156,0.15),rgba(14,165,233,0.15));border-color:rgba(3,183,156,0.4);color:#0f172a}\n[data-theme=\"light\"] .cta-btn strong{color:#059669}\n[data-theme=\"light\"] .cta-icon,[data-theme=\"light\"] .cta-arrow{color:#059669}\n\n/* ── Hero Strip (Maestro: progress rings + summary chips) ── */\n.hero-strip{border:1px solid var(--bd);border-radius:10px;background:var(--bg-1);padding:20px;margin-bottom:20px}\n.hero-top{display:flex;align-items:center;gap:24px;margin-bottom:16px;flex-wrap:wrap}\n.hero-rings{display:flex;gap:20px;flex-shrink:0}\n.ring-wrap{text-align:center}\n.ring-wrap svg{display:block;margin:0 auto 4px}\n.ring-label{font-size:9px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--fg-2)}\n.hero-chips{display:flex;gap:8px;flex:1;flex-wrap:wrap}\n.summary-chip{flex:1;text-align:center;padding:12px 4px;border-radius:8px;border:1px solid var(--bd-s);min-width:70px;cursor:pointer;transition:border-color .15s}\n.summary-chip:hover{border-color:var(--bd-m)}\n.summary-chip.active{border-color:#03b79c}\n.summary-chip .s-count{font-size:22px;font-weight:800;line-height:1}\n.summary-chip .s-label{font-size:9px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;margin-top:4px;color:var(--fg-2)}\n.s-total{background:rgba(99,102,241,0.06)}.s-total .s-count{color:#818cf8}\n.s-passed{background:rgba(34,197,94,0.06)}.s-passed .s-count{color:#22c55e}\n.s-failed{background:rgba(239,68,68,0.06)}.s-failed .s-count{color:#ef4444}\n.s-flaky{background:rgba(245,158,11,0.06)}.s-flaky .s-count{color:#f59e0b}\n.s-skipped{background:rgba(107,114,128,0.06)}.s-skipped .s-count{color:#6b7280}\n.s-ai{background:rgba(168,85,247,0.06)}.s-ai .s-count{color:#a855f7}\n.hero-footer{display:flex;gap:14px;font-size:11px;color:var(--fg-1);flex-wrap:wrap;padding-top:12px;border-top:1px solid var(--bd-s)}\n\n/* ── Execution Waterfall ── */\n.waterfall-card{border:1px solid var(--bd);border-radius:10px;background:var(--bg-1);padding:16px;margin-bottom:20px}\n.waterfall-title{font-size:11px;font-weight:600;color:var(--fg-2);text-transform:uppercase;letter-spacing:.06em;margin-bottom:10px}\n.waterfall-row{display:flex;align-items:center;gap:8px;padding:3px 0;font-size:11px;cursor:pointer;transition:background .1s;border-radius:4px;padding:3px 6px;margin:0 -6px}\n.waterfall-row:hover{background:var(--hvr)}\n.waterfall-name{width:140px;flex-shrink:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--fg-1);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:10px}\n.waterfall-track{flex:1;height:14px;background:var(--bg-2);border-radius:3px;overflow:hidden;position:relative}\n.waterfall-bar{height:100%;border-radius:3px;min-width:2px;transition:width .3s}\n.wb-passed{background:rgba(34,197,94,0.35)}\n.wb-failed{background:rgba(239,68,68,0.35)}\n.wb-skipped{background:rgba(107,114,128,0.2)}\n.waterfall-dur{width:56px;flex-shrink:0;text-align:right;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:10px;color:var(--fg-2)}\n\n/* ── Filter Bar (same pattern as Appium: search + status chips + filter icon) ── */\n.filter-bar{display:flex;align-items:center;gap:8px;margin-bottom:24px;flex-wrap:wrap}\n.filter-chips{display:flex;gap:5px;flex-wrap:wrap}\n.filter-chip{font-size:12px;font-weight:500;padding:5px 14px;border-radius:9999px;border:1px solid var(--bd-m);background:transparent;color:var(--fg-1);cursor:pointer;transition:all .15s;font-family:inherit;white-space:nowrap}\n.filter-chip:hover{border-color:#03b79c;color:var(--fg)}\n.filter-chip.active{background:rgba(3,183,156,0.12);border-color:#03b79c;color:#2dd4a8}\n.filter-chip--dimmed{opacity:.45}\n.chip-count{font-weight:700;margin-left:3px}\n.filter-clear{font-size:11px;font-weight:600;color:#03b79c;background:none;border:none;cursor:pointer;padding:4px 8px;border-radius:6px;font-family:inherit;transition:background .15s}\n.filter-clear:hover{background:rgba(3,183,156,0.1)}\n.filter-indicator{font-size:11px;color:var(--fg-2)}\n.search-box{position:relative;width:260px;flex-shrink:1;min-width:160px;margin-left:0}\n.search-input{width:100%;padding:6px 10px 6px 32px;border:1px solid var(--bd-m);border-radius:8px;background:var(--bg-1);color:var(--fg);font-size:12px;font-family:inherit;outline:none;transition:border-color .15s}\n.search-input::placeholder{color:var(--fg-2)}\n.search-input:focus{border-color:#03b79c}\n.search-icon{position:absolute;left:10px;top:50%;transform:translateY(-50%);color:var(--fg-2);pointer-events:none}\n\n/* ── Filter Icon Button (opens tag drawer) ── */\n.filter-icon-btn{position:relative;display:flex;align-items:center;justify-content:center;width:34px;height:34px;border-radius:8px;border:1px solid var(--bd-m);background:transparent;color:var(--fg-2);cursor:pointer;transition:all .15s;flex-shrink:0;margin-left:auto}\n.filter-icon-btn:hover{background:var(--hvr);color:var(--fg);border-color:var(--bd-l)}\n.filter-icon-badge{position:absolute;top:5px;right:5px;width:7px;height:7px;border-radius:50%;background:#03b79c}\n\n/* ── Filter Drawer (right panel for tags + file filters) ── */\n.filter-drawer-backdrop{position:fixed;inset:0;background:var(--overlay-bg);z-index:100;opacity:0;pointer-events:none;transition:opacity .25s}\n.filter-drawer-backdrop.open{opacity:1;pointer-events:auto}\n.filter-drawer{position:fixed;top:0;right:0;bottom:0;width:320px;max-width:90vw;z-index:110;background:var(--bg-1);border-left:1px solid var(--bd);transform:translateX(100%);transition:transform .3s cubic-bezier(.4,0,.2,1);display:flex;flex-direction:column;overflow:hidden;box-shadow:-4px 0 24px var(--shadow-c)}\n.filter-drawer.open{transform:translateX(0)}\n.filter-drawer-bar{display:flex;align-items:center;justify-content:space-between;padding:14px 20px;border-bottom:1px solid var(--bd);background:linear-gradient(180deg,rgba(3,183,156,0.04) 0%,transparent 100%);flex-shrink:0}\n.filter-drawer-title{font-size:12px;font-weight:600;color:var(--fg-1);text-transform:uppercase;letter-spacing:.06em}\n.filter-drawer-close{width:32px;height:32px;border-radius:8px;border:1px solid var(--bd-m);background:transparent;color:var(--fg-1);font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .15s,color .15s}\n.filter-drawer-close:hover{background:var(--bd);color:var(--fg)}\n.filter-drawer-body{flex:1;overflow-y:auto;padding:20px;scrollbar-width:thin;scrollbar-color:var(--scroll-thumb) transparent}\n.filter-drawer-body::-webkit-scrollbar{width:5px}\n.filter-drawer-body::-webkit-scrollbar-thumb{background:var(--bg-3);border-radius:3px}\n.filter-drawer-section{margin-bottom:20px}\n.filter-drawer-section-label{display:block;font-size:10px;font-weight:600;color:var(--fg-2);text-transform:uppercase;letter-spacing:.04em;margin-bottom:8px}\n.filter-drawer-actions{display:flex;align-items:center;gap:12px;padding-top:16px;border-top:1px solid var(--bd)}\n\n/* ── Flow List (same pattern as Appium file-group/test-row) ── */\n.file-group{margin-bottom:16px}\n.file-group-header{font-size:11px;font-weight:600;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;color:var(--fg-2);padding:0 4px 6px}\n.file-group-card{border:1px solid var(--bd);border-radius:10px;overflow:hidden;background:var(--bg-1)}\n.test-row{display:flex;align-items:center;gap:12px;padding:12px 18px;cursor:pointer;transition:background .1s;border-bottom:1px solid var(--bd-s)}\n.test-row:last-child{border-bottom:none}\n.test-row:hover{background:var(--hvr)}\n.test-row.active{background:rgba(3,183,156,0.07)}\n.status-indicator{width:10px;height:10px;border-radius:50%;flex-shrink:0}\n.si-passed{background:#22c55e}.si-failed{background:#ef4444}.si-flaky{background:#f59e0b}.si-skipped{background:#6b7280}.si-timedout{background:#f97316}\n.test-row-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}\n.test-row-title{font-size:14px;font-weight:500;color:var(--fg);line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.test-row-badges{display:flex;gap:5px;align-items:center;flex-wrap:wrap}\n.trb{font-size:10px;font-weight:600;padding:1px 7px;border-radius:4px}\n.trb-app{background:rgba(59,130,246,0.12);color:#60a5fa}\n.trb-tag{background:rgba(139,92,246,0.12);color:#a78bfa}\n.trb-platform{background:rgba(3,183,156,0.12);color:#2dd4a8}\n.test-row-dur{font-size:13px;font-weight:700;color:var(--fg-1);flex-shrink:0;white-space:nowrap}\n.test-row-arrow{color:var(--fg-2);flex-shrink:0;transition:color .15s}\n.test-row:hover .test-row-arrow{color:var(--fg-1)}\n.test-row-error{font-size:11px;color:#ef4444;margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.no-tests{text-align:center;padding:48px 20px;color:var(--fg-2);font-size:14px}\n\n/* ── Drawer (same as Appium) ── */\n.drawer-backdrop{position:fixed;inset:0;background:var(--overlay-bg);z-index:100;opacity:0;pointer-events:none;transition:opacity .25s}\n.drawer-backdrop.open{opacity:1;pointer-events:auto}\n.drawer{position:fixed;top:0;right:0;bottom:0;width:50vw;max-width:50vw;z-index:110;background:var(--bg-1);border-left:1px solid var(--bd);transform:translateX(100%);transition:transform .3s cubic-bezier(.4,0,.2,1);display:flex;flex-direction:column;overflow:hidden;box-shadow:-8px 0 40px var(--shadow-c)}\n.drawer.open{transform:translateX(0)}\n.drawer-bar{display:flex;align-items:center;justify-content:space-between;padding:14px 20px;border-bottom:1px solid var(--bd);background:linear-gradient(180deg,rgba(3,183,156,0.04) 0%,transparent 100%);flex-shrink:0}\n.drawer-bar-title{font-size:12px;font-weight:600;color:var(--fg-1);text-transform:uppercase;letter-spacing:.06em}\n.drawer-close{width:32px;height:32px;border-radius:8px;border:1px solid var(--bd-m);background:transparent;color:var(--fg-1);font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .15s,color .15s}\n.drawer-close:hover{background:var(--bd);color:var(--fg)}\n.drawer-body{flex:1;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--scroll-thumb) transparent}\n.drawer-body::-webkit-scrollbar{width:5px}\n.drawer-body::-webkit-scrollbar-track{background:transparent}\n.drawer-body::-webkit-scrollbar-thumb{background:var(--bg-3);border-radius:3px}\n\n/* ── Drawer Tabs (same seg-ctrl as Appium) ── */\n.seg-ctrl{display:flex;gap:2px;padding:8px 12px 0;border-bottom:1px solid var(--bd);background:var(--bg-1);flex-wrap:wrap}\n.seg-btn{display:flex;align-items:center;gap:5px;font-size:11px;padding:5px 12px;border-radius:6px 6px 0 0;border:none;background:none;color:var(--fg-2);cursor:pointer;font-weight:500;transition:all .15s;border-bottom:2px solid transparent;margin-bottom:-1px;font-family:inherit}\n.seg-btn:hover{color:var(--fg-1);background:var(--hvr)}\n.seg-btn.active{color:#03b79c;border-bottom-color:#03b79c;font-weight:600;background:var(--bg)}\n.seg-pane{display:none;padding:20px 24px}\n.seg-pane.active{display:block;animation:trFadeIn .15s ease-out}\n@keyframes trFadeIn{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}\n\n/* ── Overview Tab ── */\n.detail-header{margin-bottom:20px;padding-bottom:16px;border-bottom:1px solid var(--bd)}\n.detail-top{display:flex;align-items:flex-start;gap:12px;margin-bottom:10px}\n.detail-status-badge{font-size:11px;font-weight:700;padding:4px 14px;border-radius:9999px;text-transform:uppercase;letter-spacing:.04em;flex-shrink:0;margin-top:2px}\n.dbg-passed{background:rgba(34,197,94,0.1);color:#22c55e;border:1px solid rgba(34,197,94,0.2)}\n.dbg-failed{background:rgba(239,68,68,0.1);color:#ef4444;border:1px solid rgba(239,68,68,0.2)}\n.dbg-skipped{background:rgba(107,114,128,0.1);color:#6b7280;border:1px solid rgba(107,114,128,0.2)}\n.dbg-flaky{background:rgba(245,158,11,0.1);color:#f59e0b;border:1px solid rgba(245,158,11,0.2)}\n.dbg-timedout{background:rgba(249,115,22,0.1);color:#f97316;border:1px solid rgba(249,115,22,0.2)}\n.detail-title-section{flex:1;min-width:0}\n.detail-title{font-size:16px;font-weight:700;color:var(--fg);line-height:1.35;margin-bottom:6px}\n.detail-meta{display:flex;flex-wrap:wrap;gap:5px;align-items:center}\n.dm-badge{font-size:10px;font-weight:600;padding:2px 8px;border-radius:4px}\n.dm-app{background:rgba(59,130,246,0.12);color:#60a5fa}\n.dm-tag{background:rgba(139,92,246,0.12);color:#a78bfa}\n.dm-platform{background:rgba(3,183,156,0.12);color:#2dd4a8}\n.detail-duration{font-size:24px;font-weight:800;color:var(--fg);flex-shrink:0;white-space:nowrap}\n.detail-props{display:grid;grid-template-columns:auto 1fr;gap:4px 16px;font-size:12px;margin-top:12px}\n.detail-props-k{color:var(--fg-2);font-weight:600;text-transform:uppercase;letter-spacing:.04em;font-size:10px}\n.detail-props-v{color:var(--fg);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n\n/* ── Failure Panel (same as Appium) ── */\n.failure-panel{margin-bottom:20px;border:1px solid rgba(239,68,68,0.2);border-radius:10px;overflow:hidden;background:var(--bg-2)}\n.failure-panel-header{display:flex;align-items:center;gap:8px;padding:10px 14px;background:rgba(239,68,68,0.08);font-size:12px;font-weight:600;color:#ef4444}\n.failure-message{padding:12px 14px;background:var(--bg-code);color:var(--fg-err);font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;white-space:pre-wrap;word-break:break-word;line-height:1.6;position:relative}\n.copy-btn{position:absolute;top:8px;right:8px;font-size:10px;padding:3px 10px;border-radius:4px;border:1px solid var(--bd-m);background:var(--bg-3);color:var(--fg-1);cursor:pointer;font-family:inherit;transition:all .15s}\n.copy-btn:hover{background:var(--bg-2);color:var(--fg)}\n\n/* ── Fix Hint (Maestro-specific, teal accent) ── */\n.fix-hint{margin-bottom:20px;border:1px solid rgba(3,183,156,0.2);border-radius:10px;overflow:hidden;background:var(--bg-2)}\n.fix-hint-header{display:flex;align-items:center;gap:8px;padding:10px 14px;background:rgba(3,183,156,0.06);font-size:12px;font-weight:600;color:#2dd4a8}\n.fix-hint-body{padding:12px 14px;font-size:12px;color:var(--fg-1);line-height:1.7}\n.fix-hint-body ul{margin:4px 0 0 16px;list-style:disc}\n.fix-hint-body li{margin:2px 0}\n.fix-hint-body code{font-size:11px;padding:1px 5px;background:var(--bg-code);border-radius:3px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;color:var(--fg-code)}\n.fix-hint-section{margin-top:10px;padding-top:8px;border-top:1px solid var(--bd-s)}\n.fix-hint-section-title{font-size:10px;font-weight:600;color:var(--fg-2);text-transform:uppercase;letter-spacing:.04em;margin-bottom:4px}\n.fix-code{margin:6px 0;padding:8px 12px;background:var(--bg-code);border:1px solid var(--bd);border-radius:6px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;color:var(--fg-code);white-space:pre-wrap;line-height:1.5}\n\n/* ── Subflow Graph ── */\n.subflow-tree{margin-top:16px;padding:12px;background:var(--bg-2);border:1px solid var(--bd-s);border-radius:8px;font-size:11px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;color:var(--fg-1)}\n.subflow-tree .sf-node{padding:2px 0}\n.subflow-tree .sf-child{padding-left:20px;border-left:1px solid var(--bd-m)}\n\n/* ── Steps Tab ── */\n.steps-header{font-size:11px;font-weight:700;color:var(--fg-1);text-transform:uppercase;letter-spacing:.06em;margin-bottom:12px;display:flex;align-items:center;gap:8px}\n.steps-stat{font-weight:400;color:var(--fg-2);font-size:10px}\n.steps-waterfall{margin-bottom:16px;border:1px solid var(--bd);border-radius:8px;background:var(--bg-2);padding:8px 12px;overflow-x:auto}\n.sw-row{display:flex;align-items:center;gap:6px;padding:2px 0;font-size:10px}\n.sw-name{width:120px;flex-shrink:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--fg-2);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}\n.sw-track{flex:1;height:10px;background:var(--bg-3);border-radius:2px;overflow:hidden}\n.sw-bar{height:100%;border-radius:2px;min-width:1px}\n.sw-bar-green{background:#22c55e}.sw-bar-amber{background:#f59e0b}.sw-bar-red{background:#ef4444}.sw-bar-grey{background:#6b7280}\n.sw-dur{width:52px;flex-shrink:0;text-align:right;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;color:var(--fg-2)}\n.cmd-step{display:flex;align-items:center;gap:8px;padding:6px 12px;border-radius:6px;font-size:12px;transition:background .1s;border-left:3px solid transparent}\n.cmd-step:hover{background:var(--hvr)}\n.cmd-step-failed{background:rgba(239,68,68,0.04);border-left-color:#ef4444}\n.cmd-step-failed:hover{background:rgba(239,68,68,0.08)}\n.cmd-step-slow{border-left-color:#f59e0b;background:rgba(245,158,11,0.03)}\n.cmd-step-slow:hover{background:rgba(245,158,11,0.07)}\n.cmd-step-fast{border-left-color:#22c55e}\n.cmd-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}\n.cmd-dot-pass{background:#22c55e}.cmd-dot-fail{background:#ef4444}\n.cmd-badge{font-size:9px;font-weight:600;padding:2px 7px;border-radius:4px;text-transform:uppercase;letter-spacing:.03em;flex-shrink:0;white-space:nowrap}\n.cmd-badge-ui{background:rgba(59,130,246,0.12);color:#60a5fa}\n.cmd-badge-assert{background:rgba(34,197,94,0.12);color:#22c55e}\n.cmd-badge-nav{background:rgba(168,85,247,0.12);color:#a78bfa}\n.cmd-badge-device{background:rgba(249,115,22,0.12);color:#fb923c}\n.cmd-badge-ai{background:rgba(236,72,153,0.12);color:#f472b6}\n.cmd-badge-cmd{background:var(--bg-3);color:var(--fg-2)}\n.cmd-badge-slow{background:rgba(245,158,11,0.15);color:#f59e0b;margin-left:auto}\n.cmd-title{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--fg);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px}\n.cmd-dur{font-size:10px;color:var(--fg-2);flex-shrink:0;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}\n.cmd-error{font-size:11px;color:#ef4444;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;white-space:pre-wrap;word-break:break-word;line-height:1.5;padding:4px 12px 8px 31px}\n.cmd-connector{width:2px;height:4px;background:var(--bd-m);margin-left:15px}\n.steps-legend{display:flex;flex-wrap:wrap;gap:10px;margin-top:16px;padding-top:12px;border-top:1px solid var(--bd);font-size:10px;color:var(--fg-2)}\n.steps-legend span{display:inline-flex;align-items:center;gap:4px}\n\n/* ── Assertions Tab ── */\n.assert-header{display:flex;align-items:center;gap:16px;margin-bottom:16px;flex-wrap:wrap}\n.assert-ring{flex-shrink:0}\n.assert-stats{font-size:12px;color:var(--fg-1);line-height:1.6}\n.assert-stats strong{font-weight:700}\n.assert-bar-wrap{flex:1;min-width:100px}\n.assert-bar{height:6px;background:var(--bg-3);border-radius:3px;overflow:hidden}\n.assert-bar-fill{height:100%;background:#22c55e;border-radius:3px;transition:width .3s}\n.assert-pct{font-size:11px;font-weight:700;color:var(--fg-1);margin-top:2px;text-align:right}\n.assert-table{width:100%;border-collapse:collapse;font-size:12px}\n.assert-table th{text-align:left;padding:6px 10px;font-size:10px;font-weight:600;color:var(--fg-2);text-transform:uppercase;letter-spacing:.04em;border-bottom:1px solid var(--bd)}\n.assert-table td{padding:6px 10px;border-bottom:1px solid var(--bd-s);vertical-align:top}\n.assert-table tr:hover{background:var(--hvr)}\n.assert-result{font-weight:700;font-size:13px}\n.ar-pass{color:#22c55e}.ar-fail{color:#ef4444}\n.assert-error-row td{padding:2px 10px 8px 40px;border-bottom:1px solid var(--bd-s);color:#ef4444;font-size:11px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}\n\n/* ── Screenshots Tab ── */\n.screenshot-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:12px}\n.screenshot-card{border:1px solid var(--bd);border-radius:8px;overflow:hidden;background:var(--bg-2);cursor:pointer;transition:border-color .15s}\n.screenshot-card:hover{border-color:#03b79c}\n.screenshot-card img{width:100%;display:block;object-fit:contain}\n.screenshot-card-label{padding:6px 10px;font-size:10px;font-weight:600;color:var(--fg-2);border-top:1px solid var(--bd-s)}\n.screenshot-card.failure-shot{border-color:rgba(239,68,68,0.3)}\n.screenshot-card.failure-shot .screenshot-card-label{background:rgba(239,68,68,0.06);color:#ef4444}\n.screenshot-empty{padding:24px 14px;text-align:center;font-size:12px;color:var(--fg-2);font-style:italic;border:1px dashed var(--bd-m);border-radius:10px}\n\n/* ── AI Defects Tab ── */\n.ai-card{border:1px solid var(--bd);border-radius:10px;overflow:hidden;background:var(--bg-2);margin-bottom:12px;border-left:3px solid transparent}\n.ai-card-critical{border-left-color:rgba(239,68,68,0.5)}\n.ai-card-warning{border-left-color:rgba(245,158,11,0.5)}\n.ai-card-info{border-left-color:rgba(59,130,246,0.5)}\n.ai-card-header{display:flex;align-items:center;gap:8px;padding:10px 14px;border-bottom:1px solid var(--bd-s)}\n.ai-severity{font-size:10px;font-weight:700;padding:2px 10px;border-radius:9999px;text-transform:uppercase;letter-spacing:.04em}\n.ai-sev-critical{background:rgba(239,68,68,0.12);color:#ef4444;border:1px solid rgba(239,68,68,0.2)}\n.ai-sev-warning{background:rgba(245,158,11,0.12);color:#f59e0b;border:1px solid rgba(245,158,11,0.2)}\n.ai-sev-info{background:rgba(59,130,246,0.12);color:#60a5fa;border:1px solid rgba(59,130,246,0.2)}\n.ai-type{font-size:11px;font-weight:600;color:var(--fg-1);text-transform:capitalize}\n.ai-desc{padding:10px 14px;font-size:12px;color:var(--fg);line-height:1.6}\n.ai-fix{padding:10px 14px;border-top:1px solid var(--bd-s);background:rgba(3,183,156,0.03)}\n.ai-fix-label{font-size:10px;font-weight:700;color:#2dd4a8;text-transform:uppercase;letter-spacing:.06em;margin-bottom:6px}\n.ai-fix-body{font-size:12px;color:var(--fg-1);line-height:1.7}\n.ai-fix-body ul{margin:0 0 0 16px;list-style:disc}\n.ai-fix-body li{margin:2px 0}\n.ai-fix-code{margin:6px 0;padding:8px 12px;background:var(--bg-code);border:1px solid var(--bd);border-radius:6px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;color:var(--fg-code);white-space:pre-wrap;line-height:1.5}\n.ai-fix-platform{font-size:10px;font-weight:600;color:var(--fg-2);margin:8px 0 2px;text-transform:uppercase;letter-spacing:.04em}\n.ai-empty{text-align:center;padding:32px;color:#22c55e;font-weight:600;font-size:14px}\n\n/* ── Lightbox (same as Appium) ── */\n.lightbox-overlay{position:fixed;inset:0;background:var(--lb-bg);z-index:1000;display:flex;align-items:center;justify-content:center;cursor:zoom-out;animation:trFadeIn .15s}\n.lightbox-overlay img{max-width:92vw;max-height:92vh;border-radius:8px;box-shadow:0 8px 40px var(--lb-shadow)}\n.lightbox-close{position:fixed;top:16px;right:16px;z-index:1001;width:40px;height:40px;border-radius:50%;border:none;background:var(--lb-btn);color:#fff;font-size:22px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .15s;backdrop-filter:blur(8px)}\n.lightbox-close:hover{background:var(--lb-btn-h)}\n.lightbox-nav{position:fixed;top:50%;z-index:1001;width:40px;height:40px;border-radius:50%;border:none;background:var(--lb-btn);color:#fff;font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .15s;transform:translateY(-50%);backdrop-filter:blur(8px)}\n.lightbox-nav:hover{background:var(--lb-btn-h)}\n.lightbox-prev{left:16px}\n.lightbox-next{right:16px}\n.lightbox-caption{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);z-index:1001;font-size:12px;color:#fff;background:rgba(0,0,0,.6);padding:4px 16px;border-radius:8px;backdrop-filter:blur(8px)}\n\n/* ── Footer ── */\n.report-footer{text-align:center;padding:24px;font-size:12px;color:var(--fg-2)}\n.report-footer a{color:#03b79c;text-decoration:none}\n.report-footer a:hover{text-decoration:underline}\n\n/* ── Loading (same as Appium) ── */\n.loading-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:var(--bg);z-index:9999;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px}\n.loading-spinner{width:36px;height:36px;border:3px solid var(--bd-l);border-top-color:var(--fg-1);border-radius:50%;animation:spin .8s linear infinite}\n.loading-text{font:13px/1 inherit;color:var(--fg-1)}\n@keyframes spin{to{transform:rotate(360deg)}}\n\n/* ── Scrollbar (same as Appium) ── */\n*{scrollbar-width:thin;scrollbar-color:var(--scroll-thumb) transparent}\n::-webkit-scrollbar{width:6px;height:6px}\n::-webkit-scrollbar-track{background:transparent}\n::-webkit-scrollbar-thumb{background:var(--scroll-thumb);border-radius:3px}\n::-webkit-scrollbar-thumb:hover{background:var(--fg-2)}\n::-webkit-scrollbar-corner{background:transparent}\n\n/* ── Responsive (same breakpoints as Appium) ── */\n@media(max-width:1100px){.drawer{width:60vw;max-width:60vw}}\n@media(max-width:800px){.drawer{width:85vw;max-width:85vw}}\n@media(max-width:640px){\n .wrap{padding:16px 12px 48px}\n .top-bar{flex-direction:column;align-items:flex-start;gap:8px}\n .top-bar-actions{margin-left:0}\n .hero-top{flex-direction:column;align-items:stretch}\n .hero-rings{justify-content:center}\n .hero-chips{flex-wrap:wrap}\n .summary-chip{min-width:60px}\n .filter-bar{flex-direction:column;align-items:stretch}\n .search-box{width:100%;margin-left:0}\n .drawer{width:100%;max-width:100%}\n .filter-drawer{width:100%;max-width:100%}\n .cta-btn{font-size:10px;padding:5px 10px;order:10}\n .detail-title{font-size:14px}\n .detail-duration{font-size:20px}\n .waterfall-name{width:80px}\n}\n\n/* ── Print (same as Appium) ── */\n@media print{\n body{background:#fff;color:#000}\n .drawer-backdrop,.drawer,.lightbox-overlay,.lightbox-close,.lightbox-nav,.lightbox-caption{display:none}\n .test-row-arrow{display:none}\n .summary-chip,.filter-chip,.status-indicator,.detail-status-badge,.dm-badge,.trb,.cmd-badge,.ai-severity{print-color-adjust:exact;-webkit-print-color-adjust:exact}\n}\n`;\n","/**\n * TestRelic Logo SVG — shared brand asset.\n */\n\nexport const LOGO_SVG_RAW = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 196 247\" fill=\"none\">\n<path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M4.75 1.08009C2.034 2.66209 -0.130999 7.63009 0.610001 10.5821C0.969001 12.0121 3.021 15.2131 5.17 17.6971C14.375 28.3321 18 39.7791 18 58.2101V71.0001H12.434C1.16501 71.0001 0 73.4641 0 97.3051C0 106.858 0.298003 128.922 0.662003 146.337L1.323 178H33.606C65.786 178 65.897 178.007 68.544 180.284C72.228 183.453 72.244 189.21 68.579 192.877L65.957 195.5L33.479 195.801L1 196.103V204.551V213H24.577H48.154L51.077 215.923C55.007 219.853 55.007 224.147 51.077 228.077L48.154 231H26.469H4.783L5.41901 233.534C5.76901 234.927 7.143 238.527 8.472 241.534L10.89 247H40.945H71L71.006 241.75C71.017 230.748 76.027 221.606 84.697 216.767C97.854 209.424 114.086 213.895 121.323 226.857C123.659 231.041 124.418 233.833 124.789 239.607L125.263 247H155.187H185.11L187.528 241.534C188.857 238.527 190.231 234.927 190.581 233.534L191.217 231H169.531H147.846L144.923 228.077C142.928 226.082 142 224.152 142 222C142 219.848 142.928 217.918 144.923 215.923L147.846 213H171.423H195V204.551V196.103L162.521 195.801L130.043 195.5L127.421 192.877C123.991 189.445 123.835 183.869 127.074 180.421L129.349 178H162.013H194.677L195.338 146.337C195.702 128.922 196 106.858 196 97.3051C196 73.4641 194.835 71.0001 183.566 71.0001H178V58.2101C178 39.6501 181.397 28.7731 190.538 18.0651C195.631 12.0971 196.572 9.00809 194.511 5.02109C192.672 1.46509 190.197 9.12233e-05 186.028 9.12233e-05C179.761 9.12233e-05 168.713 14.8831 163.388 30.5001C160.975 37.5771 160.608 40.3751 160.213 54.7501L159.765 71.0001H150.732H141.7L142.286 53.2501C142.904 34.5511 144.727 24.3761 148.938 16.1211C151.823 10.4671 151.628 5.90109 148.364 2.63609C145 -0.726907 140.105 -0.887909 136.596 2.25009C133.481 5.03609 128.686 17.0811 126.507 27.5921C125.569 32.1191 124.617 43.0901 124.28 53.2501L123.69 71.0001H115.345H107V38.4231V5.84609L104.077 2.92309C102.082 0.928088 100.152 9.12233e-05 98 9.12233e-05C95.848 9.12233e-05 93.918 0.928088 91.923 2.92309L89 5.84609V38.4231V71.0001H80.655H72.31L71.72 53.2501C71.383 43.0901 70.431 32.1191 69.493 27.5921C67.314 17.0811 62.519 5.03609 59.404 2.25009C55.998 -0.795909 51.059 -0.710905 47.646 2.45209C44.221 5.62609 44.191 9.92109 47.539 17.4911C51.71 26.9241 53.007 34.4791 53.676 53.2501L54.31 71.0001H45.272H36.235L35.787 54.7501C35.392 40.3751 35.025 37.5771 32.612 30.5001C27.194 14.6091 16.228 -0.02891 9.787 0.03009C7.979 0.04709 5.712 0.519093 4.75 1.08009ZM42.687 108.974C33.431 112.591 20.036 125.024 18.408 131.512C17.476 135.223 20.677 140.453 28.253 147.599C37.495 156.319 44.191 159.471 53.5 159.485C59.317 159.494 61.645 158.953 67.274 156.289C77.634 151.385 88.987 139.161 88.996 132.9C89.004 127.304 76.787 114.707 66.745 109.956C59.16 106.368 50.285 106.006 42.687 108.974ZM129.255 109.904C119.151 114.768 106.996 127.33 107.004 132.9C107.013 139.108 118.562 151.475 128.939 156.389C134.338 158.945 136.744 159.496 142.521 159.498C152.526 159.501 160.369 155.502 169.771 145.605C180.444 134.368 180.278 130.975 168.486 119.388C160.043 111.094 152.727 107.595 143 107.201C136.364 106.933 134.78 107.244 129.255 109.904ZM48.5 125.922C46.3 126.969 43.152 128.945 41.505 130.314L38.511 132.802L40.504 135.005C41.601 136.216 44.434 138.342 46.8 139.728C52.577 143.114 57.36 142.466 64.009 137.395L68.978 133.606L66.756 131.24C60.944 125.054 54.357 123.135 48.5 125.922ZM138.386 125.063C136.674 125.571 133.375 127.677 131.057 129.743L126.841 133.5L131.901 137.343C138.65 142.468 143.407 143.124 149.2 139.728C151.566 138.342 154.351 136.269 155.389 135.122C157.684 132.587 156.742 131.097 150.58 127.511C145.438 124.519 142.329 123.895 138.386 125.063ZM91.923 162.923C89.043 165.804 89 166.008 89 176.973C89 184.805 89.435 188.941 90.47 190.941C92.356 194.589 96.918 196.273 101.03 194.84C105.82 193.17 107 189.638 107 176.973C107 166.008 106.957 165.804 104.077 162.923C102.082 160.928 100.152 160 98 160C95.848 160 93.918 160.928 91.923 162.923ZM94.5 232.155C91.026 234.055 90 236.229 90 241.691V247H98H106V242.082C106 235.732 105.37 234.242 101.928 232.463C98.591 230.737 97.197 230.679 94.5 232.155Z\" fill=\"url(#paint0_linear_55_11)\"/>\n<defs><linearGradient id=\"paint0_linear_55_11\" x1=\"98\" y1=\"0.000244141\" x2=\"98\" y2=\"247\" gradientUnits=\"userSpaceOnUse\">\n<stop stop-color=\"#03B79C\"/><stop offset=\"0.504808\" stop-color=\"#4EDAA4\"/><stop offset=\"0.865285\" stop-color=\"#84F3AA\"/>\n</linearGradient></defs></svg>`;\n\nexport const LOGO_SVG = `<svg width=\"32\" height=\"40\" viewBox=\"0 0 196 247\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M4.75 1.08009C2.034 2.66209 -0.130999 7.63009 0.610001 10.5821C0.969001 12.0121 3.021 15.2131 5.17 17.6971C14.375 28.3321 18 39.7791 18 58.2101V71.0001H12.434C1.16501 71.0001 0 73.4641 0 97.3051C0 106.858 0.298003 128.922 0.662003 146.337L1.323 178H33.606C65.786 178 65.897 178.007 68.544 180.284C72.228 183.453 72.244 189.21 68.579 192.877L65.957 195.5L33.479 195.801L1 196.103V204.551V213H24.577H48.154L51.077 215.923C55.007 219.853 55.007 224.147 51.077 228.077L48.154 231H26.469H4.783L5.41901 233.534C5.76901 234.927 7.143 238.527 8.472 241.534L10.89 247H40.945H71L71.006 241.75C71.017 230.748 76.027 221.606 84.697 216.767C97.854 209.424 114.086 213.895 121.323 226.857C123.659 231.041 124.418 233.833 124.789 239.607L125.263 247H155.187H185.11L187.528 241.534C188.857 238.527 190.231 234.927 190.581 233.534L191.217 231H169.531H147.846L144.923 228.077C142.928 226.082 142 224.152 142 222C142 219.848 142.928 217.918 144.923 215.923L147.846 213H171.423H195V204.551V196.103L162.521 195.801L130.043 195.5L127.421 192.877C123.991 189.445 123.835 183.869 127.074 180.421L129.349 178H162.013H194.677L195.338 146.337C195.702 128.922 196 106.858 196 97.3051C196 73.4641 194.835 71.0001 183.566 71.0001H178V58.2101C178 39.6501 181.397 28.7731 190.538 18.0651C195.631 12.0971 196.572 9.00809 194.511 5.02109C192.672 1.46509 190.197 9.12233e-05 186.028 9.12233e-05C179.761 9.12233e-05 168.713 14.8831 163.388 30.5001C160.975 37.5771 160.608 40.3751 160.213 54.7501L159.765 71.0001H150.732H141.7L142.286 53.2501C142.904 34.5511 144.727 24.3761 148.938 16.1211C151.823 10.4671 151.628 5.90109 148.364 2.63609C145 -0.726907 140.105 -0.887909 136.596 2.25009C133.481 5.03609 128.686 17.0811 126.507 27.5921C125.569 32.1191 124.617 43.0901 124.28 53.2501L123.69 71.0001H115.345H107V38.4231V5.84609L104.077 2.92309C102.082 0.928088 100.152 9.12233e-05 98 9.12233e-05C95.848 9.12233e-05 93.918 0.928088 91.923 2.92309L89 5.84609V38.4231V71.0001H80.655H72.31L71.72 53.2501C71.383 43.0901 70.431 32.1191 69.493 27.5921C67.314 17.0811 62.519 5.03609 59.404 2.25009C55.998 -0.795909 51.059 -0.710905 47.646 2.45209C44.221 5.62609 44.191 9.92109 47.539 17.4911C51.71 26.9241 53.007 34.4791 53.676 53.2501L54.31 71.0001H45.272H36.235L35.787 54.7501C35.392 40.3751 35.025 37.5771 32.612 30.5001C27.194 14.6091 16.228 -0.02891 9.787 0.03009C7.979 0.04709 5.712 0.519093 4.75 1.08009ZM42.687 108.974C33.431 112.591 20.036 125.024 18.408 131.512C17.476 135.223 20.677 140.453 28.253 147.599C37.495 156.319 44.191 159.471 53.5 159.485C59.317 159.494 61.645 158.953 67.274 156.289C77.634 151.385 88.987 139.161 88.996 132.9C89.004 127.304 76.787 114.707 66.745 109.956C59.16 106.368 50.285 106.006 42.687 108.974ZM129.255 109.904C119.151 114.768 106.996 127.33 107.004 132.9C107.013 139.108 118.562 151.475 128.939 156.389C134.338 158.945 136.744 159.496 142.521 159.498C152.526 159.501 160.369 155.502 169.771 145.605C180.444 134.368 180.278 130.975 168.486 119.388C160.043 111.094 152.727 107.595 143 107.201C136.364 106.933 134.78 107.244 129.255 109.904ZM48.5 125.922C46.3 126.969 43.152 128.945 41.505 130.314L38.511 132.802L40.504 135.005C41.601 136.216 44.434 138.342 46.8 139.728C52.577 143.114 57.36 142.466 64.009 137.395L68.978 133.606L66.756 131.24C60.944 125.054 54.357 123.135 48.5 125.922ZM138.386 125.063C136.674 125.571 133.375 127.677 131.057 129.743L126.841 133.5L131.901 137.343C138.65 142.468 143.407 143.124 149.2 139.728C151.566 138.342 154.351 136.269 155.389 135.122C157.684 132.587 156.742 131.097 150.58 127.511C145.438 124.519 142.329 123.895 138.386 125.063ZM91.923 162.923C89.043 165.804 89 166.008 89 176.973C89 184.805 89.435 188.941 90.47 190.941C92.356 194.589 96.918 196.273 101.03 194.84C105.82 193.17 107 189.638 107 176.973C107 166.008 106.957 165.804 104.077 162.923C102.082 160.928 100.152 160 98 160C95.848 160 93.918 160.928 91.923 162.923ZM94.5 232.155C91.026 234.055 90 236.229 90 241.691V247H98H106V242.082C106 235.732 105.37 234.242 101.928 232.463C98.591 230.737 97.197 230.679 94.5 232.155Z\" fill=\"url(#paint0_linear_55_11)\"/>\n<defs><linearGradient id=\"paint0_linear_55_11\" x1=\"98\" y1=\"0.000244141\" x2=\"98\" y2=\"247\" gradientUnits=\"userSpaceOnUse\">\n<stop stop-color=\"#03B79C\"/><stop offset=\"0.504808\" stop-color=\"#4EDAA4\"/><stop offset=\"0.865285\" stop-color=\"#84F3AA\"/>\n</linearGradient></defs></svg>`;\n","/**\n * Client-side JS: Data transform, SVG progress rings, summary strip,\n * execution waterfall, and flow list rendering.\n */\nexport const JS_RENDER = `\n/* ── Utilities ── */\nfunction esc(s){if(!s)return '';return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\"/g,'&quot;').replace(/'/g,'&#39;')}\nfunction fmtDur(ms){if(!ms||ms<=0)return '\\\\u2014';if(ms<1000)return Math.round(ms)+'ms';if(ms<60000)return (ms/1000).toFixed(1)+'s';var m=Math.floor(ms/60000),s=Math.round((ms%60000)/1000);return s>0?m+'m '+s+'s':m+'m'}\nfunction fmtDate(iso){try{return new Date(iso).toLocaleString()}catch(e){return iso}}\nfunction $(id){return document.getElementById(id)}\n\n/* ── Data Transform ── */\nvar flows=[];\n(report.timeline||[]).forEach(function(entry,ei){\n if(!entry.tests)return;\n entry.tests.forEach(function(t,ti){\n flows.push({entry:entry,test:t,idx:ei*1000+ti});\n });\n});\nflows.sort(function(a,b){\n if(a.test.filePath!==b.test.filePath)return a.test.filePath<b.test.filePath?-1:1;\n return (a.test.startedAt||'')<(b.test.startedAt||'')?-1:1;\n});\n\nvar summary=report.summary||{};\nvar currentFilter='all';\nvar searchQuery='';\nvar openFlowIdx=-1;\n\nfunction detectPlatform(){\n for(var i=0;i<flows.length;i++){\n var tags=flows[i].test.tags||[];\n for(var j=0;j<tags.length;j++){\n var t=tags[j].toLowerCase();\n if(t==='android'||t==='ios'||t==='web')return t;\n }\n }\n return '';\n}\n\n/* ── SVG Progress Ring ── */\nfunction svgRing(pct,size,stroke,color){\n var r=(size-stroke)/2;\n var c=2*Math.PI*r;\n var offset=c*(1-pct/100);\n return '<svg width=\"'+size+'\" height=\"'+size+'\" viewBox=\"0 0 '+size+' '+size+'\">'\n +'<circle cx=\"'+(size/2)+'\" cy=\"'+(size/2)+'\" r=\"'+r+'\" fill=\"none\" stroke=\"var(--bd-l)\" stroke-width=\"'+stroke+'\"/>'\n +'<circle cx=\"'+(size/2)+'\" cy=\"'+(size/2)+'\" r=\"'+r+'\" fill=\"none\" stroke=\"'+color+'\" stroke-width=\"'+stroke+'\"'\n +' stroke-dasharray=\"'+c+'\" stroke-dashoffset=\"'+offset+'\" stroke-linecap=\"round\" transform=\"rotate(-90 '+(size/2)+' '+(size/2)+')\"/>'\n +'<text x=\"'+(size/2)+'\" y=\"'+(size/2+5)+'\" text-anchor=\"middle\" fill=\"var(--fg)\" font-size=\"'+(size>60?14:12)+'\" font-weight=\"700\">'+Math.round(pct)+'%</text>'\n +'</svg>';\n}\n\n/* ── Render: Run Meta ── */\nfunction renderRunMeta(){\n var el=$('run-meta');if(!el)return;\n var h='';\n h+='<span class=\"run-meta-item\"><span class=\"run-meta-label\">Run</span><span class=\"run-id-tag\">'+esc((report.testRunId||'').substring(0,12))+'</span></span>';\n h+='<span class=\"run-meta-item\"><span class=\"run-meta-label\">Duration</span>'+fmtDur(report.totalDuration)+'</span>';\n h+='<span class=\"run-meta-item\">'+fmtDate(report.startedAt)+'</span>';\n if(report.ci){\n h+='<span class=\"run-meta-item\"><span class=\"ci-tag\">'+esc(report.ci.provider)+'</span></span>';\n if(report.ci.branch)h+='<span class=\"run-meta-item\"><span class=\"run-meta-label\">Branch</span>'+esc(report.ci.branch)+'</span>';\n if(report.ci.commitSha)h+='<span class=\"run-meta-item\"><span class=\"run-meta-label\">Commit</span><span class=\"run-id-tag\">'+esc(report.ci.commitSha.substring(0,8))+'</span></span>';\n }\n var plat=detectPlatform();\n if(plat)h+='<span class=\"platform-tag\">'+esc(plat)+'</span>';\n el.innerHTML=h;\n}\n\n/* ── Render: Hero Strip (rings + chips) ── */\nfunction renderHeroStrip(){\n var el=$('hero-strip');if(!el)return;\n var passRate=summary.total>0?Math.round((summary.passed/summary.total)*100):0;\n var assertRate=summary.totalAssertions>0?Math.round((summary.passedAssertions/summary.totalAssertions)*100):0;\n\n var h='<div class=\"hero-top\">';\n h+='<div class=\"hero-rings\">';\n h+='<div class=\"ring-wrap\">'+svgRing(passRate,64,5,'#22c55e')+'<div class=\"ring-label\">Pass Rate</div></div>';\n h+='<div class=\"ring-wrap\">'+svgRing(assertRate,64,5,'#a78bfa')+'<div class=\"ring-label\">Assert Rate</div></div>';\n h+='</div>';\n\n h+='<div class=\"hero-chips\">';\n var chips=[\n {cls:'s-total',n:summary.total||0,l:'Total',f:'all'},\n {cls:'s-failed',n:summary.failed||0,l:'Failed',f:'failed'},\n {cls:'s-passed',n:summary.passed||0,l:'Passed',f:'passed'},\n {cls:'s-skipped',n:summary.skipped||0,l:'Skipped',f:'skipped'},\n {cls:'s-ai',n:aiDefects.length,l:'AI Defects',f:null}\n ];\n chips.forEach(function(c){\n h+='<div class=\"summary-chip '+c.cls+'\" data-filter=\"'+(c.f||'')+'\">'\n +'<div class=\"s-count\">'+c.n+'</div>'\n +'<div class=\"s-label\">'+c.l+'</div></div>';\n });\n h+='</div></div>';\n\n h+='<div class=\"hero-footer\">';\n h+='<span>Total Duration: <strong>'+fmtDur(report.totalDuration)+'</strong></span>';\n var plat=detectPlatform();\n if(plat)h+='<span>Platform: <strong>'+esc(plat)+'</strong></span>';\n if(summary.totalActionSteps)h+='<span>Steps: <strong>'+summary.totalActionSteps+'</strong></span>';\n if(summary.totalAssertions)h+='<span>Assertions: <strong>'+summary.totalAssertions+'</strong></span>';\n h+='</div>';\n\n el.innerHTML=h;\n\n el.querySelectorAll('.summary-chip[data-filter]').forEach(function(chip){\n chip.addEventListener('click',function(){\n var f=chip.dataset.filter;\n if(f){currentFilter=f;renderFilterBar();renderFlowList()}\n });\n });\n}\n\n/* ── Render: Execution Waterfall ── */\nfunction renderWaterfall(){\n var el=$('waterfall');if(!el)return;\n if(flows.length===0){el.style.display='none';return}\n var maxDur=0;\n flows.forEach(function(f){if(f.test.duration>maxDur)maxDur=f.test.duration});\n if(maxDur===0)maxDur=1;\n\n var h='<div class=\"waterfall-title\">Execution Timeline</div>';\n flows.forEach(function(f){\n var t=f.test;\n var pct=Math.max(1,Math.round((t.duration/maxDur)*100));\n var cls=t.status==='passed'?'wb-passed':t.status==='failed'?'wb-failed':'wb-skipped';\n h+='<div class=\"waterfall-row\" data-wf-idx=\"'+f.idx+'\">'\n +'<div class=\"waterfall-name\" title=\"'+esc(t.title)+'\">'+esc(t.title)+'</div>'\n +'<div class=\"waterfall-track\"><div class=\"waterfall-bar '+cls+'\" style=\"width:'+pct+'%\"></div></div>'\n +'<div class=\"waterfall-dur\">'+fmtDur(t.duration)+'</div>'\n +'</div>';\n });\n el.innerHTML=h;\n}\n\n/* ── Tag filter state ── */\nvar activeTagFilter='';\n\n/* ── Compute tag counts ── */\nfunction getTagCounts(){\n var tags={};\n flows.forEach(function(f){(f.test.tags||[]).forEach(function(t){\n var low=t.toLowerCase();if(low!=='android'&&low!=='ios'&&low!=='web')tags[t]=(tags[t]||0)+1;\n })});\n return tags;\n}\n\n/* ── Render: Filter Bar (clean: search + status pills + filter icon) ── */\nfunction renderFilterBar(){\n var el=$('filter-bar');if(!el)return;\n var h='';\n\n h+='<div class=\"search-box\"><svg class=\"search-icon\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"M21 21l-4.35-4.35\"/></svg>'\n +'<input class=\"search-input\" id=\"search-input\" type=\"text\" placeholder=\"Search flows...\" value=\"'+esc(searchQuery)+'\"></div>';\n\n h+='<div class=\"filter-chips\">';\n var statuses=['passed','failed','skipped'];\n var statusLabels={passed:'Passed',failed:'Failed',skipped:'Skipped'};\n statuses.forEach(function(s){\n var cnt=flows.filter(function(f){return f.test.status===s}).length;\n var cls='filter-chip'+(currentFilter===s?' active':'')+(cnt===0?' filter-chip--dimmed':'');\n h+='<button class=\"'+cls+'\" data-status=\"'+s+'\">'+statusLabels[s]+' <span class=\"chip-count\">'+cnt+'</span></button>';\n });\n h+='</div>';\n\n if(currentFilter!=='all'||activeTagFilter){\n var parts=[];\n if(currentFilter!=='all')parts.push(currentFilter);\n if(activeTagFilter)parts.push('#'+activeTagFilter);\n h+='<span class=\"filter-indicator\">Filtering: '+esc(parts.join(' + '))+'</span>';\n h+='<button class=\"filter-clear\">Clear all</button>';\n }\n\n var tagCounts=getTagCounts();\n var hasTagFilter=!!activeTagFilter;\n h+='<button class=\"filter-icon-btn\" title=\"Tags &amp; filters\">'\n +'<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M22 3H2l8 9.46V19l4 2v-8.54L22 3z\"/></svg>'\n +(hasTagFilter?'<span class=\"filter-icon-badge\"></span>':'')\n +'</button>';\n\n el.innerHTML=h;\n\n renderFilterDrawerBody(tagCounts);\n}\n\n/* ── Render: Filter Drawer Body ── */\nfunction renderFilterDrawerBody(tagCounts){\n var el=$('filter-drawer-body');if(!el)return;\n var tags=Object.keys(tagCounts).sort();\n if(tags.length===0){el.innerHTML='<div style=\"font-size:12px;color:var(--fg-2)\">No tags found in flow metadata.</div>';return}\n\n var h='<div class=\"filter-drawer-section\">';\n h+='<span class=\"filter-drawer-section-label\">Tags</span>';\n h+='<div class=\"filter-chips\">';\n tags.forEach(function(t){\n var cnt=tagCounts[t];\n var cls='filter-chip'+(activeTagFilter===t?' active':'');\n h+='<button class=\"'+cls+'\" data-tag=\"'+esc(t)+'\">'+esc(t)+' <span class=\"chip-count\">'+cnt+'</span></button>';\n });\n h+='</div></div>';\n\n var specCounts={};\n flows.forEach(function(f){\n var key=f.test.filePath||f.entry.specFile||'';\n if(key)specCounts[key]=(specCounts[key]||0)+1;\n });\n var specs=Object.keys(specCounts).sort();\n if(specs.length>1){\n h+='<div class=\"filter-drawer-section\">';\n h+='<span class=\"filter-drawer-section-label\">Flow Files</span>';\n h+='<div class=\"filter-chips\">';\n specs.forEach(function(sp){\n var short=sp.split(/[\\\\/\\\\\\\\]/).pop()||sp;\n h+='<button class=\"filter-chip\" data-tag=\"'+esc(short)+'\" title=\"'+esc(sp)+'\">'+esc(short)+' <span class=\"chip-count\">'+specCounts[sp]+'</span></button>';\n });\n h+='</div></div>';\n }\n\n if(activeTagFilter){\n h+='<div class=\"filter-drawer-actions\"><button class=\"filter-clear\">Clear all filters</button></div>';\n }\n\n el.innerHTML=h;\n}\n\n/* ── Render: Flow List ── */\nfunction getFilteredFlows(){\n return flows.filter(function(f){\n if(currentFilter!=='all'&&f.test.status!==currentFilter)return false;\n if(activeTagFilter){\n var tags=(f.test.tags||[]).map(function(t){return t.toLowerCase()});\n if(tags.indexOf(activeTagFilter.toLowerCase())<0)return false;\n }\n if(searchQuery){\n var q=searchQuery.toLowerCase();\n var title=(f.test.title||'').toLowerCase();\n var tagStr=(f.test.tags||[]).join(' ').toLowerCase();\n var file=(f.test.filePath||'').toLowerCase();\n if(title.indexOf(q)<0&&tagStr.indexOf(q)<0&&file.indexOf(q)<0)return false;\n }\n return true;\n });\n}\n\nfunction renderFlowList(){\n var el=$('flow-list');if(!el)return;\n var filtered=getFilteredFlows();\n if(filtered.length===0){el.innerHTML='<div class=\"no-tests\">No flows match the current filter.</div>';return}\n\n var groups={};\n filtered.forEach(function(f){\n var key=f.test.filePath||f.entry.specFile||'flows';\n if(!groups[key])groups[key]=[];\n groups[key].push(f);\n });\n\n var h='';\n for(var key in groups){\n if(!groups.hasOwnProperty(key))continue;\n h+='<div class=\"file-group\">';\n h+='<div class=\"file-group-header\">'+esc(key)+'</div>';\n h+='<div class=\"file-group-card\">';\n groups[key].forEach(function(f){\n var t=f.test;\n var badges='';\n if(f.entry.url&&f.entry.url!=='maestro-flow')badges+='<span class=\"trb trb-app\">'+esc(f.entry.url)+'</span>';\n (t.tags||[]).forEach(function(tag){\n var low=tag.toLowerCase();\n if(low==='android'||low==='ios'||low==='web')badges+='<span class=\"trb trb-platform\">'+esc(tag)+'</span>';\n else badges+='<span class=\"trb trb-tag\">'+esc(tag)+'</span>';\n });\n var activeClass=f.idx===openFlowIdx?' active':'';\n h+='<div class=\"test-row'+activeClass+'\" data-flow-idx=\"'+f.idx+'\">'\n +'<div class=\"status-indicator si-'+esc(t.status)+'\"></div>'\n +'<div class=\"test-row-info\">'\n +'<div class=\"test-row-title\">'+esc(t.title)+'</div>'\n +'<div class=\"test-row-badges\">'+badges+'</div>'\n +(t.failure?'<div class=\"test-row-error\">'+esc(t.failure.message||'')+'</div>':'')\n +'</div>'\n +'<div class=\"test-row-dur\">'+fmtDur(t.duration)+'</div>'\n +'<svg class=\"test-row-arrow\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"m9 18 6-6-6-6\"/></svg>'\n +'</div>';\n });\n h+='</div></div>';\n }\n el.innerHTML=h;\n}\n`;\n","/**\n * Client-side JS: Drawer lifecycle, tab switching, and Overview tab rendering\n * with failure diagnostic panel, root cause analysis, and fix code snippets.\n */\nexport const JS_DRAWER = `\n/* ── Drawer Open/Close ── */\nfunction openDrawer(flowIdx){\n openFlowIdx=flowIdx;\n renderFlowList();\n var flow=flows.find(function(f){return f.idx===flowIdx});\n if(!flow)return;\n $('drawer-backdrop').classList.add('open');\n $('drawer').classList.add('open');\n renderDrawerContent(flow);\n}\nfunction closeDrawer(){\n openFlowIdx=-1;\n renderFlowList();\n $('drawer-backdrop').classList.remove('open');\n $('drawer').classList.remove('open');\n}\n\nfunction renderDrawerContent(flow){\n var tabs=['Overview','Steps','Assertions','Screenshots','AI Defects'];\n var h='<div class=\"seg-ctrl\">';\n tabs.forEach(function(name,i){\n h+='<button class=\"seg-btn'+(i===0?' active':'')+'\" data-seg=\"tab'+i+'\">'+name+'</button>';\n });\n h+='</div>';\n\n h+='<div class=\"seg-pane active\" data-seg-pane=\"tab0\">'+renderOverviewTab(flow)+'</div>';\n h+='<div class=\"seg-pane\" data-seg-pane=\"tab1\">'+renderStepsTab(flow)+'</div>';\n h+='<div class=\"seg-pane\" data-seg-pane=\"tab2\">'+renderAssertionsTab(flow)+'</div>';\n h+='<div class=\"seg-pane\" data-seg-pane=\"tab3\">'+renderScreenshotsTab()+'</div>';\n h+='<div class=\"seg-pane\" data-seg-pane=\"tab4\">'+renderAiDefectsTab()+'</div>';\n\n $('drawer-body').innerHTML=h;\n}\n\n/* ── Overview Tab ── */\nfunction renderOverviewTab(flow){\n var t=flow.test;\n var h='<div class=\"detail-header\"><div class=\"detail-top\">';\n h+='<span class=\"detail-status-badge dbg-'+esc(t.status)+'\">'+esc(t.status)+'</span>';\n h+='<div class=\"detail-title-section\"><div class=\"detail-title\">'+esc(t.title)+'</div>';\n h+='<div class=\"detail-meta\">';\n if(flow.entry.url&&flow.entry.url!=='maestro-flow')h+='<span class=\"dm-badge dm-app\">'+esc(flow.entry.url)+'</span>';\n (t.tags||[]).forEach(function(tag){\n var low=tag.toLowerCase();\n if(low==='android'||low==='ios'||low==='web')h+='<span class=\"dm-badge dm-platform\">'+esc(tag)+'</span>';\n else h+='<span class=\"dm-badge dm-tag\">'+esc(tag)+'</span>';\n });\n h+='</div></div>';\n h+='<div class=\"detail-duration\">'+fmtDur(t.duration)+'</div>';\n h+='</div>';\n\n h+='<div class=\"detail-props\">';\n h+='<span class=\"detail-props-k\">Flow File</span><span class=\"detail-props-v\">'+esc(t.filePath||flow.entry.specFile||'\\\\u2014')+'</span>';\n h+='<span class=\"detail-props-k\">App ID</span><span class=\"detail-props-v\">'+esc(flow.entry.url||'\\\\u2014')+'</span>';\n h+='<span class=\"detail-props-k\">Suite</span><span class=\"detail-props-v\">'+esc(t.suiteName||'\\\\u2014')+'</span>';\n h+='<span class=\"detail-props-k\">Started</span><span class=\"detail-props-v\">'+esc(t.startedAt?fmtDate(t.startedAt):'\\\\u2014')+'</span>';\n h+='<span class=\"detail-props-k\">Test ID</span><span class=\"detail-props-v\">'+esc(t.testId||'\\\\u2014')+'</span>';\n h+='</div></div>';\n\n if(t.failure){\n h+='<div class=\"failure-panel\">';\n h+='<div class=\"failure-panel-header\"><svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"m15 9-6 6M9 9l6 6\"/></svg> Failure Diagnostic</div>';\n h+='<div class=\"failure-message\">'+esc(t.failure.message||'Unknown error')+'<button class=\"copy-btn\" data-copy=\"'+esc(t.failure.message||'')+'\">Copy</button></div>';\n h+='</div>';\n h+=renderRootCauseAnalysis(t,flow);\n }\n\n if(t.tags&&t.tags.length>0){\n var subflows=[];\n (t.actions||[]).forEach(function(a){\n if(a.title&&a.title.indexOf('runFlow')>=0)subflows.push(a.title.replace('runFlow \\\\u2192 ',''));\n });\n if(subflows.length>0){\n h+='<div class=\"subflow-tree\"><div class=\"sf-node\">'+esc(t.filePath||t.title)+'</div>';\n subflows.forEach(function(sf){h+='<div class=\"sf-child\"><div class=\"sf-node\">\\\\u2514\\\\u2500 '+esc(sf)+' (subflow)</div></div>'});\n h+='</div>';\n }\n }\n\n return h;\n}\n\n/* ── Root Cause Analysis ── */\nfunction renderRootCauseAnalysis(t,flow){\n var msg=(t.failure&&t.failure.message)||'';\n var lower=msg.toLowerCase();\n var actions=t.actions||[];\n var failedSteps=actions.filter(function(a){return a.status==='failed'});\n\n var analysis='';\n if(lower.indexOf('not found')>=0||lower.indexOf('not visible')>=0||lower.indexOf('could not find')>=0){\n var prevFailed=failedSteps.length>1;\n analysis+='<p>The test attempted to interact with an element that was not present in the view hierarchy.';\n if(prevFailed)analysis+=' The preceding step also failed, suggesting the target screen did not fully load.';\n analysis+='</p>';\n }else if(lower.indexOf('timeout')>=0||lower.indexOf('timed out')>=0){\n analysis+='<p>The operation exceeded its timeout threshold. This typically indicates a slow network response, a heavy UI render, or the target element appearing after the timeout window.</p>';\n }else if(lower.indexOf('assert')>=0){\n analysis+='<p>An assertion did not match the expected state. This could indicate a UI regression, dynamic content change, or a race condition between the app and the test.</p>';\n }else{\n analysis+='<p>Review the step timeline to identify the exact failing command and its preceding context.</p>';\n }\n\n var h='<div class=\"fix-hint\">';\n h+='<div class=\"fix-hint-header\"><svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 16v-4M12 8h.01\"/></svg> Root Cause Analysis</div>';\n h+='<div class=\"fix-hint-body\">';\n h+='<div class=\"fix-hint-section\"><div class=\"fix-hint-section-title\">What Went Wrong</div>'+analysis+'</div>';\n\n h+='<div class=\"fix-hint-section\"><div class=\"fix-hint-section-title\">Recommended Actions</div><ul>';\n if(lower.indexOf('not found')>=0||lower.indexOf('not visible')>=0){\n h+='<li>Add <code>extendedWaitUntil</code> before the interaction:</li>';\n h+='</ul><div class=\"fix-code\">- extendedWaitUntil:\\\\n visible: \"Pay Now\"\\\\n timeout: 10000</div><ul>';\n h+='<li>Verify the selector with <strong>Maestro Studio</strong> on the target device</li>';\n h+='<li>Add an <code>assertVisible</code> guard before <code>tapOn</code></li>';\n h+='<li>Check if the element requires scrolling into view first</li>';\n h+='<li>Inspect network requests \\\\u2014 the backend API may be slow</li>';\n }else if(lower.indexOf('timeout')>=0||lower.indexOf('timed out')>=0){\n h+='<li>Increase timeout with <code>extendedWaitUntil</code> (default 5s may be too short)</li>';\n h+='<li>Check for loading spinners or network requests blocking the UI</li>';\n h+='<li>Run on a faster emulator or physical device to rule out performance</li>';\n h+='<li>Add <code>waitForAnimationToEnd</code> before the assertion</li>';\n }else if(lower.indexOf('assert')>=0){\n h+='<li>Verify the expected text/element exists on the current screen</li>';\n h+='<li>Check for dynamic content that changes between runs</li>';\n h+='<li>Use <code>assertWithAI</code> for fuzzy visual assertions</li>';\n h+='<li>Add <code>waitForAnimationToEnd</code> before <code>assertVisible</code></li>';\n }else{\n h+='<li>Review the step timeline for the exact failing command</li>';\n h+='<li>Run the flow in <strong>Maestro Studio</strong> to debug interactively</li>';\n h+='<li>Check device logs for crash traces or ANR</li>';\n h+='<li>Try <code>clearState</code> before <code>launchApp</code></li>';\n }\n h+='</ul></div>';\n\n h+='</div></div>';\n return h;\n}\n`;\n","/**\n * Client-side JS: Step waterfall mini-chart and step timeline\n * with duration heatmap colouring and SLOW badges.\n */\nexport const JS_STEPS = `\nfunction renderStepsTab(flow){\n var actions=flow.test.actions||[];\n if(actions.length===0)return '<div class=\"no-tests\">No step data available for this flow.</div>';\n\n var failCount=actions.filter(function(a){return a.status==='failed'}).length;\n var slowCount=actions.filter(function(a){return a.duration>2000}).length;\n var maxDur=0;\n actions.forEach(function(a){if(a.duration>maxDur)maxDur=a.duration});\n\n var h='<div class=\"steps-header\">'+actions.length+' steps'\n +'<span class=\"steps-stat\">'+failCount+' failed</span>'\n +(slowCount?'<span class=\"steps-stat\">'+slowCount+' slow (&gt;2s)</span>':'')\n +'<span class=\"steps-stat\">slowest: '+fmtDur(maxDur)+'</span></div>';\n\n /* Mini waterfall */\n h+='<div class=\"steps-waterfall\">';\n var wfMax=maxDur||1;\n actions.forEach(function(a){\n var pct=Math.max(1,Math.round((a.duration/wfMax)*100));\n var barCls=a.status==='failed'?'sw-bar-red':a.duration>2000?'sw-bar-amber':a.duration>500?'sw-bar-amber':'sw-bar-green';\n var nameStr=a.title||'step';\n if(nameStr.length>18)nameStr=nameStr.substring(0,18)+'\\\\u2026';\n h+='<div class=\"sw-row\">'\n +'<div class=\"sw-name\" title=\"'+esc(a.title)+'\">'+esc(nameStr)+'</div>'\n +'<div class=\"sw-track\"><div class=\"sw-bar '+barCls+'\" style=\"width:'+pct+'%\"></div></div>'\n +'<div class=\"sw-dur\">'+fmtDur(a.duration)+'</div></div>';\n });\n h+='</div>';\n\n /* Step list */\n actions.forEach(function(a,i){\n var isFailed=a.status==='failed';\n var isSlow=a.duration>2000&&!isFailed;\n var isFast=a.duration<=500&&!isFailed;\n var rowCls='cmd-step';\n if(isFailed)rowCls+=' cmd-step-failed';\n else if(isSlow)rowCls+=' cmd-step-slow';\n else if(isFast)rowCls+=' cmd-step-fast';\n var dotCls='cmd-dot '+(isFailed?'cmd-dot-fail':'cmd-dot-pass');\n h+='<div class=\"'+rowCls+'\">';\n h+='<div class=\"'+dotCls+'\"></div>';\n h+=getCmdBadge(a.category);\n h+='<div class=\"cmd-title\" title=\"'+esc(a.title)+'\">'+esc(a.title)+'</div>';\n if(isSlow||isFailed&&a.duration>2000)h+='<span class=\"cmd-badge cmd-badge-slow\">SLOW</span>';\n h+='<div class=\"cmd-dur\">'+fmtDur(a.duration)+'</div>';\n h+='</div>';\n if(a.error)h+='<div class=\"cmd-error\">'+esc(a.error)+'</div>';\n if(i<actions.length-1)h+='<div class=\"cmd-connector\"></div>';\n });\n\n h+='<div class=\"steps-legend\">';\n h+='<span><span class=\"cmd-badge cmd-badge-ui\">UI</span> ui_action</span>';\n h+='<span><span class=\"cmd-badge cmd-badge-assert\">ASSERT</span> assertion</span>';\n h+='<span><span class=\"cmd-badge cmd-badge-ai\">AI</span> ai</span>';\n h+='<span><span class=\"cmd-badge cmd-badge-cmd\">CMD</span> custom_step</span>';\n h+='<span><span class=\"cmd-badge cmd-badge-slow\">SLOW</span> &gt; 2s</span>';\n h+='</div>';\n h+='<div class=\"steps-legend\" style=\"margin-top:4px\">';\n h+='<span>Duration: <span style=\"color:#22c55e;font-weight:700\">&lt;500ms</span></span>';\n h+='<span style=\"color:#f59e0b;font-weight:700\">500ms\\\\u20132s</span>';\n h+='<span style=\"color:#ef4444;font-weight:700\">&gt;2s</span>';\n h+='</div>';\n return h;\n}\n\nfunction getCmdBadge(cat){\n var m={ui_action:['UI','cmd-badge-ui'],assertion:['ASSERT','cmd-badge-assert'],navigation:['NAV','cmd-badge-nav'],custom_step:['CMD','cmd-badge-cmd'],ai:['AI','cmd-badge-ai'],device:['DEV','cmd-badge-device']};\n var pair=m[cat]||['CMD','cmd-badge-cmd'];\n return '<span class=\"cmd-badge '+pair[1]+'\">'+pair[0]+'</span>';\n}\n`;\n","/**\n * Client-side JS: Assertions tab with SVG progress ring and detail table.\n */\nexport const JS_ASSERTIONS = `\nfunction renderAssertionsTab(flow){\n var actions=flow.test.actions||[];\n var asserts=actions.filter(function(a){return a.category==='assertion'});\n if(asserts.length===0)return '<div class=\"no-tests\">No assertions recorded for this flow.</div>';\n\n var passCount=asserts.filter(function(a){return a.status==='passed'}).length;\n var failCount=asserts.length-passCount;\n var pct=Math.round((passCount/asserts.length)*100);\n\n var h='<div class=\"assert-header\">';\n h+='<div class=\"assert-ring\">'+svgRing(pct,56,4,'#22c55e')+'</div>';\n h+='<div class=\"assert-stats\"><strong>'+asserts.length+'</strong> assertions<br>';\n h+='<span style=\"color:#22c55e;font-weight:700\">'+passCount+' passed</span> &middot; ';\n h+='<span style=\"color:#ef4444;font-weight:700\">'+failCount+' failed</span></div>';\n h+='<div class=\"assert-bar-wrap\"><div class=\"assert-bar\"><div class=\"assert-bar-fill\" style=\"width:'+pct+'%\"></div></div>';\n h+='<div class=\"assert-pct\">'+pct+'% pass rate</div></div>';\n h+='</div>';\n\n h+='<table class=\"assert-table\"><thead><tr><th>Result</th><th>Command</th><th>Duration</th></tr></thead><tbody>';\n asserts.forEach(function(a){\n var icon=a.status==='passed'?'\\\\u2713':'\\\\u2717';\n var cls=a.status==='passed'?'ar-pass':'ar-fail';\n h+='<tr><td class=\"assert-result '+cls+'\">'+icon+'</td>';\n h+='<td style=\"font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px\">'+esc(a.title)+'</td>';\n h+='<td class=\"cmd-dur\">'+fmtDur(a.duration)+'</td></tr>';\n if(a.error)h+='<tr class=\"assert-error-row\"><td></td><td colspan=\"2\">'+esc(a.error)+'</td></tr>';\n });\n h+='</tbody></table>';\n return h;\n}\n`;\n","/**\n * Client-side JS: AI defect cards with severity accent stripes\n * and platform-specific fix playbooks.\n */\nexport const JS_AI_DEFECTS = `\nfunction renderAiDefectsTab(){\n if(aiDefects.length===0)return '<div class=\"ai-empty\">\\\\u2714 No AI defects detected.</div>';\n var h='<div style=\"font-size:12px;color:var(--fg-1);margin-bottom:16px\">'+aiDefects.length+' defect'+(aiDefects.length>1?'s':'')+' found by Maestro AI analysis</div>';\n\n aiDefects.forEach(function(d){\n var sev=d.severity||'info';\n var type=(d.type||'ui').toLowerCase();\n h+='<div class=\"ai-card ai-card-'+sev+'\">';\n h+='<div class=\"ai-card-header\"><span class=\"ai-severity ai-sev-'+sev+'\">'+esc(sev)+'</span><span class=\"ai-type\">'+esc(d.type||'UI')+'</span></div>';\n h+='<div class=\"ai-desc\">'+esc(d.description||'')+'</div>';\n h+='<div class=\"ai-fix\"><div class=\"ai-fix-label\">Fix Playbook</div><div class=\"ai-fix-body\">'+getFixPlaybook(type,d)+'</div></div>';\n h+='</div>';\n });\n return h;\n}\n\nfunction getFixPlaybook(type,d){\n if(type==='ui')return uiFixPlaybook(d);\n if(type==='spelling')return spellingFixPlaybook(d);\n if(type==='layout')return layoutFixPlaybook(d);\n if(type==='i18n')return i18nFixPlaybook(d);\n if(type==='accessibility')return a11yFixPlaybook(d);\n return genericFixPlaybook(d);\n}\n\nfunction uiFixPlaybook(d){\n return '<div class=\"ai-fix-platform\">Android (XML)</div>'\n +'<div class=\"ai-fix-code\">android:layout_width=\"wrap_content\"\\\\nandroid:minWidth=\"120dp\"\\\\nandroid:paddingHorizontal=\"16dp\"</div>'\n +'<div class=\"ai-fix-platform\">Jetpack Compose</div>'\n +'<div class=\"ai-fix-code\">Modifier\\\\n .widthIn(min = 120.dp)\\\\n .padding(horizontal = 16.dp)</div>'\n +'<div class=\"ai-fix-platform\">Verify</div>'\n +'<div class=\"ai-fix-code\">maestro test --device Pixel_4a ./flows</div>'\n +'<ul><li>Test on smallest supported screen (360dp width)</li>'\n +'<li>Run <code>assertNoDefectsWithAI</code> to catch regressions</li></ul>';\n}\n\nfunction spellingFixPlaybook(d){\n var desc=d.description||'';\n var match=desc.match(/\"([^\"]+)\"/);\n var typo=match?match[1]:'typo';\n return '<div class=\"ai-fix-platform\">Find &amp; Replace</div>'\n +'<div class=\"ai-fix-code\">grep -r \"'+esc(typo)+'\" --include=\"*.xml\" --include=\"*.strings\"\\\\ngrep -r \"'+esc(typo)+'\" --include=\"*.kt\" --include=\"*.swift\"</div>'\n +'<ul><li>Android: <code>res/values/strings.xml</code></li>'\n +'<li>iOS: <code>Localizable.strings</code></li>'\n +'<li>Add a CI linting rule for common typos</li></ul>';\n}\n\nfunction layoutFixPlaybook(d){\n return '<div class=\"ai-fix-platform\">Android (XML)</div>'\n +'<div class=\"ai-fix-code\">android:maxLines=\"1\"\\\\nandroid:ellipsize=\"end\"</div>'\n +'<div class=\"ai-fix-platform\">iOS (Swift)</div>'\n +'<div class=\"ai-fix-code\">label.lineBreakMode = .byTruncatingTail\\\\nlabel.numberOfLines = 1</div>'\n +'<ul><li>Use ConstraintLayout / Auto Layout to prevent overlap</li>'\n +'<li>Test with long strings (20+ characters)</li></ul>';\n}\n\nfunction i18nFixPlaybook(d){\n return '<ul><li>Audit translation files for missing keys</li>'\n +'<li>Run extraction tool to find untranslated strings</li>'\n +'<li>Verify all locales in CI with locale-specific flows</li></ul>'\n +'<div class=\"ai-fix-platform\">Example Flow</div>'\n +'<div class=\"ai-fix-code\">env:\\\\n LANG: fr_FR\\\\n---\\\\n- launchApp\\\\n- assertVisible: \"Bienvenue\"</div>';\n}\n\nfunction a11yFixPlaybook(d){\n return '<div class=\"ai-fix-platform\">Android</div>'\n +'<div class=\"ai-fix-code\">android:contentDescription=\"1 item in cart\"</div>'\n +'<div class=\"ai-fix-platform\">iOS (Swift)</div>'\n +'<div class=\"ai-fix-code\">cartBadge.accessibilityLabel = \"1 item in cart\"</div>'\n +'<ul><li>WCAG 2.1 SC 1.1.1: Non-text Content</li>'\n +'<li>Ensure 4.5:1 contrast ratio (SC 1.4.3)</li>'\n +'<li>Test with TalkBack (Android) / VoiceOver (iOS)</li></ul>';\n}\n\nfunction genericFixPlaybook(d){\n return '<ul><li>Review the defect description and identify the affected screen</li>'\n +'<li>Reproduce on a physical device</li>'\n +'<li>Add <code>assertNoDefectsWithAI</code> for regression detection</li></ul>';\n}\n`;\n","/**\n * Client-side JS: Event delegation, theme toggle, search,\n * filter chips, lightbox with prev/next/keyboard, and screenshots.\n */\nexport const JS_INTERACTIONS = `\n/* ── Theme ── */\nfunction applyTheme(){\n var pref=localStorage.getItem('tr-theme')||'system';\n var resolved=pref==='system'?(window.matchMedia('(prefers-color-scheme:light)').matches?'light':'dark'):pref;\n document.documentElement.setAttribute('data-theme',resolved);\n document.querySelectorAll('.theme-btn').forEach(function(b){\n b.classList.toggle('active',b.dataset.themeVal===pref);\n });\n}\n\n/* ── Screenshots Tab ── */\nfunction renderScreenshotsTab(){\n if(screenshotPaths.length===0)return '<div class=\"screenshot-empty\">No screenshots captured for this run.</div>';\n var h='<div class=\"screenshot-grid\">';\n screenshotPaths.forEach(function(p,i){\n var name=p.split(/[\\\\/\\\\\\\\]/).pop()||'screenshot';\n var isFail=name.toLowerCase().indexOf('fail')>=0||name.toLowerCase().indexOf('error')>=0;\n h+='<div class=\"screenshot-card'+(isFail?' failure-shot':'')+'\" data-lb-idx=\"'+i+'\">'\n +'<img src=\"'+esc(p)+'\" alt=\"'+esc(name)+'\" loading=\"lazy\" onerror=\"this.parentElement.style.display=\\\\'none\\\\'\"/>'\n +'<div class=\"screenshot-card-label\">'+(isFail?'Failure Screenshot':esc(name))+'</div></div>';\n });\n h+='</div>';\n return h;\n}\n\n/* ── Lightbox ── */\nfunction openLightbox(idx){\n if(idx<0||idx>=screenshotPaths.length)return;\n var existing=document.querySelector('.lightbox-overlay');\n if(existing)existing.remove();\n\n var name=screenshotPaths[idx].split(/[\\\\/\\\\\\\\]/).pop()||'screenshot';\n var div=document.createElement('div');\n div.className='lightbox-overlay';\n div.innerHTML='<img src=\"'+esc(screenshotPaths[idx])+'\"/>'\n +'<button class=\"lightbox-close\">&times;</button>'\n +(idx>0?'<button class=\"lightbox-nav lightbox-prev\">&lsaquo;</button>':'')\n +(idx<screenshotPaths.length-1?'<button class=\"lightbox-nav lightbox-next\">&rsaquo;</button>':'')\n +'<div class=\"lightbox-caption\">'+esc(name)+' ('+(idx+1)+' / '+screenshotPaths.length+')</div>';\n document.body.appendChild(div);\n\n function cleanup(){div.remove();document.removeEventListener('keydown',keyHandler)}\n function keyHandler(e){\n if(e.key==='Escape'){cleanup()}\n if(e.key==='ArrowLeft'&&idx>0){cleanup();openLightbox(idx-1)}\n if(e.key==='ArrowRight'&&idx<screenshotPaths.length-1){cleanup();openLightbox(idx+1)}\n }\n div.querySelector('.lightbox-close').addEventListener('click',cleanup);\n div.addEventListener('click',function(e){if(e.target===div)cleanup()});\n var prev=div.querySelector('.lightbox-prev');\n var next=div.querySelector('.lightbox-next');\n if(prev)prev.addEventListener('click',function(e){e.stopPropagation();cleanup();openLightbox(idx-1)});\n if(next)next.addEventListener('click',function(e){e.stopPropagation();cleanup();openLightbox(idx+1)});\n document.addEventListener('keydown',keyHandler);\n}\n\n/* ── Event Delegation ── */\ndocument.addEventListener('click',function(e){\n var target=e.target;\n\n /* Test row -> open drawer */\n var row=target.closest('.test-row');\n if(row){openDrawer(parseInt(row.dataset.flowIdx));return}\n\n /* Waterfall row -> open drawer */\n var wfRow=target.closest('.waterfall-row');\n if(wfRow){openDrawer(parseInt(wfRow.dataset.wfIdx));return}\n\n /* Drawer close */\n if(target.closest('#drawer-close')){closeDrawer();return}\n if(target.closest('#drawer-backdrop')){closeDrawer();return}\n\n /* Filter chip (status) */\n var statusChip=target.closest('.filter-chip[data-status]');\n if(statusChip){\n var s=statusChip.dataset.status;\n currentFilter=currentFilter===s?'all':s;\n renderFilterBar();renderFlowList();\n return;\n }\n\n /* Filter chip (tag — in filter drawer) */\n var tagChip=target.closest('.filter-chip[data-tag]');\n if(tagChip){\n var tag=tagChip.dataset.tag;\n activeTagFilter=activeTagFilter===tag?'':tag;\n renderFilterBar();renderFlowList();\n return;\n }\n\n /* Filter icon -> open filter drawer */\n if(target.closest('.filter-icon-btn')){\n $('filter-drawer-backdrop').classList.add('open');\n $('filter-drawer').classList.add('open');\n return;\n }\n\n /* Filter drawer close */\n if(target.closest('#filter-drawer-close')||target.closest('#filter-drawer-backdrop')){\n $('filter-drawer-backdrop').classList.remove('open');\n $('filter-drawer').classList.remove('open');\n return;\n }\n\n /* Clear all filters */\n if(target.closest('.filter-clear')){\n currentFilter='all';searchQuery='';activeTagFilter='';\n $('filter-drawer-backdrop').classList.remove('open');\n $('filter-drawer').classList.remove('open');\n renderFilterBar();renderFlowList();\n return;\n }\n\n /* Seg-ctrl tab switching */\n var segBtn=target.closest('.seg-btn');\n if(segBtn){\n var parent=segBtn.parentElement;\n if(parent&&parent.classList.contains('seg-ctrl')){\n var container=parent.parentElement;\n parent.querySelectorAll('.seg-btn').forEach(function(b){b.classList.remove('active')});\n segBtn.classList.add('active');\n container.querySelectorAll('.seg-pane').forEach(function(p){p.classList.remove('active')});\n var pane=container.querySelector('[data-seg-pane=\"'+segBtn.dataset.seg+'\"]');\n if(pane)pane.classList.add('active');\n }\n return;\n }\n\n /* Copy button */\n var copyBtn=target.closest('.copy-btn');\n if(copyBtn){\n e.stopPropagation();\n var text=copyBtn.dataset.copy||'';\n navigator.clipboard.writeText(text).then(function(){copyBtn.textContent='Copied!'});\n setTimeout(function(){copyBtn.textContent='Copy'},1500);\n return;\n }\n\n /* Screenshot lightbox */\n var card=target.closest('.screenshot-card');\n if(card&&card.dataset.lbIdx!==undefined){openLightbox(parseInt(card.dataset.lbIdx));return}\n});\n\n/* Theme buttons */\ndocument.querySelectorAll('.theme-btn').forEach(function(b){\n b.addEventListener('click',function(){\n localStorage.setItem('tr-theme',b.dataset.themeVal);\n applyTheme();\n });\n});\n\n/* Search input */\ndocument.addEventListener('input',function(e){\n if(e.target&&e.target.id==='search-input'){\n searchQuery=e.target.value.toLowerCase();\n renderFlowList();\n }\n});\n\n/* Escape to close drawer */\ndocument.addEventListener('keydown',function(e){\n if(e.key==='Escape'&&$('drawer').classList.contains('open'))closeDrawer();\n});\n`;\n","/**\n * HTML Template for TestRelic Maestro Report.\n *\n * Orchestrator: imports CSS, logo, and JS modules; assembles them\n * into a single self-contained HTML document. Same architecture as\n * the Appium html-template.ts.\n */\n\nimport { CSS } from './html-css.js';\nimport { LOGO_SVG, LOGO_SVG_RAW } from './html-logo.js';\nimport { JS_RENDER } from './html-js-render.js';\nimport { JS_DRAWER } from './html-js-drawer.js';\nimport { JS_STEPS } from './html-js-steps.js';\nimport { JS_ASSERTIONS } from './html-js-assertions.js';\nimport { JS_AI_DEFECTS } from './html-js-ai-defects.js';\nimport { JS_INTERACTIONS } from './html-js-interactions.js';\n\nconst JS = `\n(function(){\n var payload=JSON.parse(document.getElementById('report-data').textContent);\n var report=payload.report;\n var aiDefects=payload.aiDefects||[];\n var screenshotPaths=payload.screenshotPaths||[];\n\n ${JS_RENDER}\n ${JS_DRAWER}\n ${JS_STEPS}\n ${JS_ASSERTIONS}\n ${JS_AI_DEFECTS}\n ${JS_INTERACTIONS}\n\n applyTheme();\n renderRunMeta();\n renderHeroStrip();\n renderWaterfall();\n renderFilterBar();\n renderFlowList();\n\n var _lo=document.getElementById('loading-overlay');if(_lo)_lo.remove();\n})();`;\n\nexport function renderHtmlDocument(reportJson: string): string {\n const safeJson = reportJson.replace(/<\\//g, '<\\\\/');\n return `<!-- TestRelic Maestro Report — self-contained, no external dependencies -->\n<!DOCTYPE html>\n<html lang=\"en\" data-theme=\"\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'none'; style-src 'unsafe-inline'; script-src 'unsafe-inline'; img-src data: blob: file: 'self'; media-src blob: 'self'; connect-src 'self';\">\n<title>TestRelic Maestro Test Report</title>\n<link rel=\"icon\" type=\"image/svg+xml\" href=\"data:image/svg+xml;base64,${Buffer.from(LOGO_SVG_RAW).toString('base64')}\">\n<style>${CSS}</style>\n<script>(function(){var p=localStorage.getItem('tr-theme')||'system';var t=p==='system'?(window.matchMedia('(prefers-color-scheme:light)').matches?'light':'dark'):p;document.documentElement.setAttribute('data-theme',t);})()</script>\n</head>\n<body>\n<div class=\"wrap\">\n <div class=\"top-bar\">\n <div class=\"brand\">\n ${LOGO_SVG}\n <div class=\"brand-text\">\n <span class=\"brand-title\">TestRelic AI</span>\n <span class=\"brand-sub\">Maestro Test Report</span>\n </div>\n </div>\n <div class=\"run-meta\" id=\"run-meta\"></div>\n <div class=\"top-bar-actions\">\n <a class=\"cta-btn\" href=\"https://testrelic.ai\" target=\"_blank\" rel=\"noopener noreferrer\"><svg class=\"cta-icon\" viewBox=\"0 0 16 16\" fill=\"none\"><path d=\"M8 1l1.545 4.955L14.5 7.5l-4.955 1.545L8 14l-1.545-4.955L1.5 7.5l4.955-1.545L8 1z\" fill=\"currentColor\"/></svg>Advanced Insights with AI<span style=\"color:var(--fg-2);margin:0 2px\">&ndash;</span><strong>Get Started</strong><svg class=\"cta-arrow\" viewBox=\"0 0 12 12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"><path d=\"M2.5 6h7M6.5 3l3 3-3 3\"/></svg></a>\n <div class=\"theme-toggle\">\n <button class=\"theme-btn\" data-theme-val=\"system\" title=\"System\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><rect x=\"2\" y=\"3\" width=\"20\" height=\"14\" rx=\"2\"/><path d=\"M8 21h8M12 17v4\"/></svg></button>\n <button class=\"theme-btn\" data-theme-val=\"light\" title=\"Light\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"5\"/><path d=\"M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42\"/></svg></button>\n <button class=\"theme-btn\" data-theme-val=\"dark\" title=\"Dark\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z\"/></svg></button>\n </div>\n </div>\n </div>\n\n <div id=\"hero-strip\" class=\"hero-strip\"></div>\n <div id=\"waterfall\" class=\"waterfall-card\"></div>\n <div id=\"filter-bar\" class=\"filter-bar\"></div>\n <div id=\"flow-list\"></div>\n\n <div class=\"report-footer\">\n Generated by <a href=\"https://testrelic.ai\">TestRelic AI</a> &middot; <code>@testrelic/maestro-analytics</code> &middot; <a href=\"https://docs.testrelic.ai\">Documentation</a>\n </div>\n</div>\n\n<div class=\"loading-overlay\" id=\"loading-overlay\"><div class=\"loading-spinner\"></div><div class=\"loading-text\">Loading report...</div></div>\n<div class=\"filter-drawer-backdrop\" id=\"filter-drawer-backdrop\"></div>\n<aside class=\"filter-drawer\" id=\"filter-drawer\">\n <div class=\"filter-drawer-bar\">\n <span class=\"filter-drawer-title\">Filters</span>\n <button class=\"filter-drawer-close\" id=\"filter-drawer-close\">&times;</button>\n </div>\n <div class=\"filter-drawer-body\" id=\"filter-drawer-body\"></div>\n</aside>\n<div class=\"drawer-backdrop\" id=\"drawer-backdrop\"></div>\n<aside class=\"drawer\" id=\"drawer\">\n <div class=\"drawer-bar\">\n <span class=\"drawer-bar-title\">Flow Details</span>\n <button class=\"drawer-close\" id=\"drawer-close\">&times;</button>\n </div>\n <div class=\"drawer-body\" id=\"drawer-body\"></div>\n</aside>\n<script id=\"report-data\" type=\"application/json\">${safeJson}</script>\n<script>${JS}</script>\n</body>\n</html>`;\n}\n","/**\n * Generate and write the HTML report file.\n *\n * Serialises report data + AI defects + screenshot paths to JSON\n * and delegates to renderHtmlDocument() from html-template.ts.\n */\n\nimport { existsSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport type { TestRunReport } from '@testrelic/core';\nimport type { AIDefect } from './types.js';\nimport { renderHtmlDocument } from './html-template.js';\n\nexport function generateHtmlReport(\n report: TestRunReport,\n aiDefects: AIDefect[],\n screenshotPaths: string[],\n outputPath: string,\n): void {\n const htmlPath = resolve(outputPath);\n const htmlDir = dirname(htmlPath);\n if (!existsSync(htmlDir)) mkdirSync(htmlDir, { recursive: true });\n\n const payload = { report, aiDefects, screenshotPaths };\n const reportJson = JSON.stringify(payload);\n const html = renderHtmlDocument(reportJson);\n writeFileSync(htmlPath, html, 'utf-8');\n}\n","/**\n * Formatted console summary output for Maestro test runs.\n */\n\nimport type { Summary } from '@testrelic/core';\nimport type { AIDefect } from './types.js';\n\nconst BOX_WIDTH = 62;\n\nfunction pad(text: string): string {\n const padding = BOX_WIDTH - 2 - text.length;\n return padding > 0 ? text + ' '.repeat(padding) : text;\n}\n\nfunction line(text: string): string {\n return `\\u2502 ${pad(text)} \\u2502\\n`;\n}\n\nexport function printConsoleSummary(\n summary: Summary,\n outputPath: string,\n htmlReportPath: string,\n quiet: boolean,\n aiDefects?: AIDefect[],\n): void {\n if (quiet) return;\n if (summary.total === 0) return;\n\n const top = `\\u250C${'─'.repeat(BOX_WIDTH)}\\u2510\\n`;\n const bottom = `\\u2514${'─'.repeat(BOX_WIDTH)}\\u2518\\n`;\n const blank = line('');\n\n let output = top;\n output += line('TestRelic AI - Maestro Test Report');\n output += blank;\n\n const parts: string[] = [];\n if (summary.passed > 0) parts.push(`${summary.passed} \\u2713`);\n if (summary.failed > 0) parts.push(`${summary.failed} \\u2717`);\n if (summary.flaky > 0) parts.push(`${summary.flaky} \\u26A0`);\n if (summary.skipped > 0) parts.push(`${summary.skipped} skipped`);\n if (summary.timedout > 0) parts.push(`${summary.timedout} timedout`);\n output += line(`Flows: ${summary.total} total (${parts.join(' ')})`);\n\n if (summary.totalAssertions > 0) {\n output += line(\n `Assertions: ${summary.totalAssertions} total (${summary.passedAssertions} \\u2713 ${summary.failedAssertions} \\u2717)`,\n );\n }\n\n if (summary.totalActionSteps > 0) {\n const catParts: string[] = [];\n const catMap = summary.actionStepsByCategory;\n if (catMap['ui_action']) catParts.push(`${catMap['ui_action']} UI`);\n if (catMap['navigation']) catParts.push(`${catMap['navigation']} nav`);\n if (catMap['custom_step']) catParts.push(`${catMap['custom_step']} custom`);\n const catStr = catParts.length > 0 ? ` (${catParts.join(' ')})` : '';\n output += line(`Steps: ${summary.totalActionSteps} total${catStr}`);\n }\n\n if (aiDefects && aiDefects.length > 0) {\n const critical = aiDefects.filter((d) => d.severity === 'critical').length;\n const warning = aiDefects.filter((d) => d.severity === 'warning').length;\n const info = aiDefects.filter((d) => d.severity === 'info').length;\n const defectParts: string[] = [];\n if (critical > 0) defectParts.push(`${critical} critical`);\n if (warning > 0) defectParts.push(`${warning} warning`);\n if (info > 0) defectParts.push(`${info} info`);\n output += line(`AI Defects: ${aiDefects.length} found (${defectParts.join(' ')})`);\n }\n\n output += line(`Report: ${htmlReportPath}`);\n output += line(`Data: ${outputPath}`);\n\n output += bottom;\n output += 'For more information visit us at https://docs.testrelic.ai\\n';\n\n process.stderr.write(output);\n}\n","/**\n * Open a file path in the user's default browser.\n */\n\nimport { exec } from 'node:child_process';\n\nexport function openInBrowser(filePath: string): void {\n const platform = process.platform;\n let command: string;\n\n if (platform === 'darwin') {\n command = `open \"${filePath}\"`;\n } else if (platform === 'win32') {\n command = `start \"\" \"${filePath}\"`;\n } else {\n command = `xdg-open \"${filePath}\"`;\n }\n\n exec(command, () => {\n // Silently ignore errors\n });\n}\n","/**\n * Maestro CLI runner — spawns `maestro test` as a child process\n * with injected flags for report generation and artifact capture.\n */\n\nimport { spawn } from 'node:child_process';\nimport { mkdirSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport { tmpdir } from 'node:os';\nimport { randomBytes } from 'node:crypto';\nimport type { MaestroTestOptions } from './types.js';\n\nexport interface MaestroRunResult {\n readonly exitCode: number;\n readonly junitPath: string;\n readonly testOutputDir: string;\n readonly debugOutputDir: string;\n readonly stdout: string;\n readonly stderr: string;\n}\n\nfunction generateTempDir(): string {\n const id = randomBytes(8).toString('hex');\n return join(tmpdir(), `testrelic-maestro-${id}`);\n}\n\nexport function buildMaestroArgs(options: MaestroTestOptions, tempDir: string): string[] {\n const args: string[] = [];\n\n if (options.platform) {\n args.push('--platform', options.platform);\n }\n if (options.device) {\n args.push('--device', options.device);\n }\n\n args.push('test');\n\n const junitPath = join(tempDir, 'report.xml');\n const testOutputDir = join(tempDir, 'artifacts');\n const debugOutputDir = join(tempDir, 'debug');\n\n args.push('--format', 'junit');\n args.push('--output', junitPath);\n args.push('--test-output-dir', testOutputDir);\n args.push('--debug-output', debugOutputDir);\n\n if (options.analyze) {\n args.push('--analyze');\n }\n if (options.includeTags) {\n args.push('--include-tags', options.includeTags);\n }\n if (options.excludeTags) {\n args.push('--exclude-tags', options.excludeTags);\n }\n if (options.shards && options.shards > 1) {\n args.push('--shards', String(options.shards));\n }\n if (options.config) {\n args.push('--config', options.config);\n }\n if (options.env) {\n for (const [key, value] of Object.entries(options.env)) {\n args.push('-e', `${key}=${value}`);\n }\n }\n\n if (options.maestroArgs) {\n args.push(...options.maestroArgs);\n }\n\n args.push(...options.flowPaths);\n\n return args;\n}\n\nexport async function runMaestro(options: MaestroTestOptions): Promise<MaestroRunResult> {\n const tempDir = options.outputDir ? resolve(options.outputDir) : generateTempDir();\n\n mkdirSync(join(tempDir, 'artifacts'), { recursive: true });\n mkdirSync(join(tempDir, 'debug'), { recursive: true });\n\n const args = buildMaestroArgs(options, tempDir);\n const junitPath = join(tempDir, 'report.xml');\n const testOutputDir = join(tempDir, 'artifacts');\n const debugOutputDir = join(tempDir, 'debug');\n\n return new Promise<MaestroRunResult>((resolvePromise) => {\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n\n const proc = spawn('maestro', args, {\n stdio: ['inherit', 'pipe', 'pipe'],\n shell: true,\n });\n\n proc.stdout.on('data', (chunk: Buffer) => {\n stdoutChunks.push(chunk);\n if (!options.quiet) process.stdout.write(chunk);\n });\n\n proc.stderr.on('data', (chunk: Buffer) => {\n stderrChunks.push(chunk);\n if (!options.quiet) process.stderr.write(chunk);\n });\n\n proc.on('close', (code) => {\n resolvePromise({\n exitCode: code ?? 1,\n junitPath,\n testOutputDir,\n debugOutputDir,\n stdout: Buffer.concat(stdoutChunks).toString('utf-8'),\n stderr: Buffer.concat(stderrChunks).toString('utf-8'),\n });\n });\n\n proc.on('error', (err) => {\n resolvePromise({\n exitCode: 127,\n junitPath,\n testOutputDir,\n debugOutputDir,\n stdout: '',\n stderr: `Failed to start maestro CLI: ${err.message}`,\n });\n });\n });\n}\n","/**\n * Cloud authentication — token exchange and refresh.\n * Ported from @testrelic/appium-analytics for Maestro integration.\n */\n\nimport { ErrorCode, createError } from '@testrelic/core';\n\nexport interface TokenExchangeResult {\n readonly accessToken: string;\n readonly refreshToken: string;\n readonly expiresIn: number;\n readonly orgId: string;\n readonly orgName: string;\n readonly userId: string;\n readonly userName: string;\n}\n\nexport interface TokenRefreshResult {\n readonly accessToken: string;\n readonly refreshToken: string;\n readonly expiresIn: number;\n}\n\nexport interface AuthError {\n readonly code: 'invalid_key' | 'expired_key' | 'validation_error' | 'rate_limited' | 'server_error' | 'network_error' | 'timeout';\n readonly message: string;\n readonly statusCode: number | null;\n}\n\nconst LOCALHOST_HOSTS = new Set(['localhost', '127.0.0.1', '0.0.0.0']);\n\nexport function enforceHttps(endpoint: string): void {\n const url = new URL(endpoint);\n if (url.protocol === 'https:') return;\n if (url.protocol === 'http:' && LOCALHOST_HOSTS.has(url.hostname)) return;\n throw createError(\n ErrorCode.CLOUD_CONFIG_INVALID,\n `HTTPS is required for cloud communication. Got: ${url.protocol}//${url.hostname}`,\n );\n}\n\nconst HEALTH_CHECK_TIMEOUT_MS = 3000;\n\nexport async function healthCheck(endpoint: string): Promise<boolean> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), HEALTH_CHECK_TIMEOUT_MS);\n try {\n const response = await fetch(`${endpoint}/health`, {\n method: 'GET',\n signal: controller.signal,\n });\n return response.ok;\n } catch {\n return false;\n } finally {\n clearTimeout(timer);\n }\n}\n\nasync function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function parseCloudError(response: Response): Promise<string> {\n try {\n const body = await response.json() as Record<string, unknown>;\n const error = body.error as Record<string, unknown> | undefined;\n if (error && typeof error.message === 'string') {\n return error.message;\n }\n } catch {\n // not JSON\n }\n return `HTTP ${response.status}`;\n}\n\nexport async function exchangeToken(\n endpoint: string,\n apiKey: string,\n timeout: number,\n): Promise<TokenExchangeResult | AuthError> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeout);\n try {\n const response = await fetch(`${endpoint}/sdk/auth/token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ apiKey }),\n signal: controller.signal,\n });\n\n if (response.ok) {\n const body = await response.json() as Record<string, unknown>;\n return {\n accessToken: body.accessToken as string,\n refreshToken: body.refreshToken as string,\n expiresIn: body.expiresIn as number,\n orgId: body.orgId as string,\n orgName: body.orgName as string,\n userId: body.userId as string,\n userName: body.userName as string,\n };\n }\n\n if (response.status === 429) {\n const retryAfter = response.headers.get('Retry-After');\n const waitMs = retryAfter ? parseInt(retryAfter, 10) * 1000 : 5000;\n if (!isNaN(waitMs) && waitMs > 0) {\n await sleep(waitMs);\n const retryResponse = await fetch(`${endpoint}/sdk/auth/token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ apiKey }),\n signal: controller.signal,\n });\n if (retryResponse.ok) {\n const body = await retryResponse.json() as Record<string, unknown>;\n return {\n accessToken: body.accessToken as string,\n refreshToken: body.refreshToken as string,\n expiresIn: body.expiresIn as number,\n orgId: body.orgId as string,\n orgName: body.orgName as string,\n userId: body.userId as string,\n userName: body.userName as string,\n };\n }\n }\n return { code: 'rate_limited', message: 'Rate limited during token exchange.', statusCode: 429 };\n }\n\n if (response.status === 400) {\n const errorMessage = await parseCloudError(response);\n return { code: 'validation_error', message: errorMessage, statusCode: 400 };\n }\n if (response.status === 401) {\n return { code: 'invalid_key', message: 'API key is invalid or has been revoked.', statusCode: 401 };\n }\n if (response.status === 403) {\n return { code: 'expired_key', message: 'API key has expired.', statusCode: 403 };\n }\n\n const errorMessage = await parseCloudError(response);\n return { code: 'server_error', message: errorMessage, statusCode: response.status };\n } catch (err) {\n if (err instanceof DOMException && err.name === 'AbortError') {\n return { code: 'timeout', message: 'Token exchange timed out.', statusCode: null };\n }\n return { code: 'network_error', message: 'Failed to reach cloud for token exchange.', statusCode: null };\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport function isAuthError(result: TokenExchangeResult | AuthError): result is AuthError {\n return 'code' in result && typeof (result as AuthError).code === 'string'\n && ['invalid_key', 'expired_key', 'validation_error', 'rate_limited', 'server_error', 'network_error', 'timeout'].includes((result as AuthError).code);\n}\n\nexport async function refreshAccessToken(\n endpoint: string,\n currentRefreshToken: string,\n timeout: number,\n): Promise<TokenRefreshResult | null> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeout);\n try {\n let response = await fetch(`${endpoint}/sdk/auth/refresh`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ refreshToken: currentRefreshToken }),\n signal: controller.signal,\n });\n\n if (response.status === 429) {\n const retryAfter = response.headers.get('Retry-After');\n const waitMs = retryAfter ? parseInt(retryAfter, 10) * 1000 : 5000;\n if (!isNaN(waitMs) && waitMs > 0) {\n await sleep(waitMs);\n response = await fetch(`${endpoint}/sdk/auth/refresh`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ refreshToken: currentRefreshToken }),\n signal: controller.signal,\n });\n }\n }\n\n if (!response.ok) return null;\n\n const body = await response.json() as Record<string, unknown>;\n return {\n accessToken: body.accessToken as string,\n refreshToken: body.refreshToken as string,\n expiresIn: body.expiresIn as number,\n };\n } catch {\n return null;\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport interface RepoResolveResult {\n readonly repoId: string;\n readonly displayName: string;\n}\n\nexport async function resolveRepo(\n endpoint: string,\n accessToken: string,\n gitId: string,\n displayName: string,\n timeout: number,\n branch?: string | null,\n): Promise<RepoResolveResult | null> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeout);\n try {\n const requestBody: Record<string, string> = { gitId, displayName };\n if (branch) requestBody.branch = branch;\n const response = await fetch(`${endpoint}/repos/resolve`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${accessToken}`,\n },\n body: JSON.stringify(requestBody),\n signal: controller.signal,\n });\n\n if (!response.ok) return null;\n\n const responseBody = await response.json() as Record<string, unknown>;\n return {\n repoId: responseBody.repoId as string,\n displayName: responseBody.displayName as string,\n };\n } catch {\n return null;\n } finally {\n clearTimeout(timer);\n }\n}\n","/**\n * Git metadata collection and project ID normalization.\n *\n * Collects branch, commit, message, and remote URL using git CLI.\n * Each field fails independently.\n */\n\nimport { execSync } from 'node:child_process';\nimport { readFileSync } from 'node:fs';\nimport { basename, join } from 'node:path';\nimport type { GitMetadata } from '@testrelic/core';\n\nconst GIT_TIMEOUT_MS = 5000;\n\nfunction execGit(command: string, cwd?: string): string | null {\n try {\n const result = execSync(command, {\n cwd,\n timeout: GIT_TIMEOUT_MS,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n return result.trim() || null;\n } catch {\n return null;\n }\n}\n\nexport function collectGitMetadata(cwd?: string): GitMetadata {\n const branch = execGit('git rev-parse --abbrev-ref HEAD', cwd);\n const commitSha = execGit('git rev-parse --short HEAD', cwd);\n const commitMessage = execGit('git log -1 --pretty=%s', cwd);\n const commitAuthor =\n execGit('git log -1 --format=%an', cwd) ??\n execGit('git config user.name', cwd);\n const rawRemoteUrl = getRemoteUrl(cwd);\n const remoteUrl = rawRemoteUrl ? normalizeGitRemoteUrl(rawRemoteUrl) : null;\n\n return { branch, commitSha, commitMessage, commitAuthor, remoteUrl };\n}\n\nfunction getRemoteUrl(cwd?: string): string | null {\n const originUrl = execGit('git remote get-url origin', cwd);\n if (originUrl) return originUrl;\n\n const remotes = execGit('git remote', cwd);\n if (!remotes) return null;\n\n const firstRemote = remotes.split('\\n')[0]?.trim();\n if (!firstRemote) return null;\n\n return execGit(`git remote get-url ${firstRemote}`, cwd);\n}\n\nexport function normalizeGitRemoteUrl(url: string): string {\n let normalized = url.trim();\n normalized = normalized.replace(/^[a-z+]+:\\/\\//, '');\n normalized = normalized.replace(/^[^@]+@/, '');\n normalized = normalized.replace(/:(?!\\d)/, '/');\n normalized = normalized.replace(/\\.git$/, '');\n normalized = normalized.replace(/\\/+$/, '');\n normalized = normalized.replace(/^[^@/]+@/, '');\n return normalized.toLowerCase();\n}\n\nexport function deriveRepoDisplayName(normalizedUrl: string): string {\n const segments = normalizedUrl.split('/').filter(Boolean);\n return segments[segments.length - 1] ?? normalizedUrl;\n}\n\nexport function readPackageJsonName(dirPath: string): string | null {\n try {\n const raw = readFileSync(join(dirPath, 'package.json'), 'utf-8');\n const pkg = JSON.parse(raw) as Record<string, unknown>;\n return typeof pkg.name === 'string' && pkg.name.length > 0 ? pkg.name : null;\n } catch {\n return null;\n }\n}\n\nexport function deriveNonGitProjectId(dirPath: string): string {\n const pkgName = readPackageJsonName(dirPath);\n if (pkgName) return pkgName;\n return `local/${basename(dirPath)}`;\n}\n","/**\n * Cloud queue — offline storage and retry for failed uploads.\n */\n\nimport {\n mkdirSync, writeFileSync, readFileSync, readdirSync,\n unlinkSync, renameSync, statSync,\n} from 'node:fs';\nimport { join } from 'node:path';\nimport type { QueueEntry } from '@testrelic/core';\nimport { isValidQueueEntry } from '@testrelic/core';\n\nconst QUEUE_ENTRY_VERSION = '1.0.0';\nconst FLUSH_BATCH_SIZE = 10;\n\nexport function writeToQueue(\n queueDirectory: string,\n runId: string,\n type: string,\n reason: string,\n targetEndpoint: string,\n method: string,\n payload: Record<string, unknown>,\n headers: Record<string, string>,\n): void {\n try {\n mkdirSync(queueDirectory, { recursive: true });\n const timestamp = Date.now();\n const filename = `${timestamp}-${runId}-${type}.json`;\n const filePath = join(queueDirectory, filename);\n const entry: QueueEntry = { version: QUEUE_ENTRY_VERSION, queuedAt: new Date(timestamp).toISOString(), reason, retryCount: 0, targetEndpoint, method, payload, headers };\n const tmpPath = filePath + '.tmp';\n writeFileSync(tmpPath, JSON.stringify(entry, null, 2), 'utf-8');\n renameSync(tmpPath, filePath);\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOSPC') {\n process.stderr.write('\\u26A0 TestRelic: Disk full \\u2014 unable to queue upload data.\\n');\n } else {\n process.stderr.write('\\u26A0 TestRelic: Unable to queue upload data.\\n');\n }\n }\n}\n\nexport async function flushQueue(queueDirectory: string, endpoint: string, accessToken: string): Promise<void> {\n let files: string[];\n try {\n files = readdirSync(queueDirectory).filter((f) => f.endsWith('.json') && !f.endsWith('.tmp')).sort();\n } catch { return; }\n\n if (files.length === 0) return;\n\n const batches: string[][] = [];\n for (let i = 0; i < files.length; i += FLUSH_BATCH_SIZE) {\n batches.push(files.slice(i, i + FLUSH_BATCH_SIZE));\n }\n\n for (const batch of batches) {\n for (const file of batch) {\n const filePath = join(queueDirectory, file);\n try {\n const stat = statSync(filePath);\n if (!stat.isFile()) continue;\n const raw = readFileSync(filePath, 'utf-8');\n const entry = JSON.parse(raw) as unknown;\n if (!isValidQueueEntry(entry)) {\n unlinkSync(filePath);\n continue;\n }\n const queueEntry = entry as QueueEntry;\n const response = await fetch(queueEntry.targetEndpoint, {\n method: queueEntry.method,\n headers: { ...queueEntry.headers, 'Authorization': `Bearer ${accessToken}` },\n body: JSON.stringify(queueEntry.payload),\n });\n if (response.ok) { unlinkSync(filePath); } else { return; }\n } catch { return; }\n }\n }\n}\n\nexport function cleanupExpiredQueue(queueDirectory: string, maxAge: number): void {\n try {\n const files = readdirSync(queueDirectory).filter((f) => f.endsWith('.json') && !f.endsWith('.tmp'));\n const now = Date.now();\n for (const file of files) {\n const filePath = join(queueDirectory, file);\n try {\n const raw = readFileSync(filePath, 'utf-8');\n const entry = JSON.parse(raw) as Record<string, unknown>;\n const queuedAt = entry.queuedAt;\n if (typeof queuedAt === 'string') {\n const queuedTime = new Date(queuedAt).getTime();\n if (now - queuedTime > maxAge) unlinkSync(filePath);\n }\n } catch {\n try { unlinkSync(filePath); } catch { /* ignore */ }\n }\n }\n } catch { /* queue dir doesn't exist */ }\n}\n","/**\n * CloudClient — manages cloud connectivity lifecycle for Maestro analytics.\n * Handles auth, repo resolution, token refresh, and queue flush.\n */\n\nimport { readFileSync, writeFileSync, mkdirSync, renameSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { createHash } from 'node:crypto';\nimport type { CloudConfig, AuthState, AuthMode, GitMetadata } from '@testrelic/core';\nimport {\n enforceHttps,\n healthCheck,\n exchangeToken,\n refreshAccessToken,\n isAuthError,\n resolveRepo,\n} from './cloud-auth.js';\nimport type { TokenExchangeResult } from './cloud-auth.js';\nimport { collectGitMetadata, normalizeGitRemoteUrl, deriveRepoDisplayName, deriveNonGitProjectId, readPackageJsonName } from './git-metadata.js';\nimport { cleanupExpiredQueue, flushQueue } from './cloud-queue.js';\n\nconst TOKEN_REFRESH_BUFFER_MS = 300_000;\nconst PROJECT_CACHE_TTL_MS = 86_400_000;\nconst HEALTH_CHECK_INTERVAL_MS = 60_000;\nconst FLUSH_TIMEOUT_MS = 30_000;\nconst DASHBOARD_SETTINGS_URL = 'https://platform.testrelic.ai/settings/api-keys';\n\ninterface RepoCache {\n readonly repoId: string;\n readonly gitId: string;\n readonly displayName: string;\n readonly resolvedAt: number;\n readonly apiKeyHash: string;\n}\n\nexport class CloudClient {\n private config: CloudConfig | null;\n private authState: AuthState;\n private gitMetadata: GitMetadata | null = null;\n private repoId: string | null = null;\n private failureReason: string | null = null;\n private flushPromise: Promise<void> | null = null;\n private healthCheckTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(cloudConfig: CloudConfig | null) {\n this.config = cloudConfig;\n this.authState = {\n mode: 'pending',\n accessToken: null,\n refreshToken: null,\n expiresAt: null,\n orgId: null,\n orgName: null,\n userId: null,\n userName: null,\n };\n }\n\n async initialize(): Promise<void> {\n if (!this.config || !this.config.apiKey) {\n this.setLocalMode('no_api_key');\n process.stderr.write('\\u2139 TestRelic: No API key configured. Running in local mode.\\n');\n return;\n }\n\n try {\n enforceHttps(this.config.endpoint);\n this.gitMetadata = collectGitMetadata();\n\n const healthy = await healthCheck(this.config.endpoint);\n if (!healthy) {\n this.setLocalMode('cloud_unreachable');\n process.stderr.write('\\u26A0 TestRelic: Cloud unreachable. Switching to local mode.\\n');\n return;\n }\n\n const result = await exchangeToken(this.config.endpoint, this.config.apiKey, this.config.timeout);\n if (isAuthError(result)) {\n this.handleAuthError(result.code, result.statusCode);\n return;\n }\n\n const tokenResult = result as TokenExchangeResult;\n this.authState = {\n mode: 'cloud',\n accessToken: tokenResult.accessToken,\n refreshToken: tokenResult.refreshToken,\n expiresAt: Date.now() + tokenResult.expiresIn * 1000,\n orgId: tokenResult.orgId,\n orgName: tokenResult.orgName,\n userId: tokenResult.userId,\n userName: tokenResult.userName,\n };\n\n process.stderr.write(`\\u2713 TestRelic: Connected to cloud (${tokenResult.orgName} / ${tokenResult.userName})\\n`);\n\n await this.resolveRepoId();\n\n if (this.config.queueDirectory) {\n cleanupExpiredQueue(this.config.queueDirectory, this.config.queueMaxAge);\n this.startBackgroundFlush();\n }\n\n this.startHealthCheck();\n } catch (err) {\n this.setLocalMode('unexpected_error');\n process.stderr.write(`\\u26A0 TestRelic: Unexpected error during cloud initialization. ${err instanceof Error ? err.message : String(err)}\\n`);\n }\n }\n\n getMode(): AuthMode { return this.authState.mode; }\n isCloudMode(): boolean { return this.authState.mode === 'cloud'; }\n isLocalMode(): boolean { return this.authState.mode === 'local'; }\n getAccessToken(): string | null { return this.authState.accessToken; }\n getRepoId(): string | null { return this.repoId; }\n getGitMetadata(): GitMetadata | null { return this.gitMetadata; }\n getConfig(): CloudConfig | null { return this.config; }\n getFailureReason(): string | null { return this.failureReason; }\n getEndpoint(): string { return this.config?.endpoint ?? 'https://platform.testrelic.ai/api/v1'; }\n\n async ensureValidToken(): Promise<boolean> {\n if (!this.isCloudMode() || !this.authState.accessToken) return false;\n const expiresAt = this.authState.expiresAt ?? 0;\n if (Date.now() + TOKEN_REFRESH_BUFFER_MS < expiresAt) return true;\n if (!this.config || !this.authState.refreshToken) {\n this.switchToLocalMode('token_expired_no_refresh');\n return false;\n }\n try {\n const refreshResult = await refreshAccessToken(this.config.endpoint, this.authState.refreshToken, this.config.timeout);\n if (!refreshResult) {\n this.switchToLocalMode('token_refresh_failed');\n return false;\n }\n this.authState.accessToken = refreshResult.accessToken;\n this.authState.refreshToken = refreshResult.refreshToken;\n this.authState.expiresAt = Date.now() + refreshResult.expiresIn * 1000;\n return true;\n } catch {\n this.switchToLocalMode('token_refresh_error');\n return false;\n }\n }\n\n switchToLocalMode(reason: string): void {\n if (this.authState.mode === 'local') return;\n this.authState.mode = 'local';\n this.failureReason = reason;\n process.stderr.write(`\\u26A0 TestRelic: Switched to local mode (${reason}).\\n`);\n }\n\n async dispose(): Promise<void> {\n if (this.healthCheckTimer) {\n clearInterval(this.healthCheckTimer);\n this.healthCheckTimer = null;\n }\n if (this.flushPromise) {\n try {\n await Promise.race([this.flushPromise, new Promise<void>((resolve) => setTimeout(resolve, FLUSH_TIMEOUT_MS))]);\n } catch { /* ignore */ }\n this.flushPromise = null;\n }\n this.authState = { mode: 'local', accessToken: null, refreshToken: null, expiresAt: null, orgId: null, orgName: null, userId: null, userName: null };\n this.repoId = null;\n this.gitMetadata = null;\n }\n\n private setLocalMode(reason: string): void {\n this.authState.mode = 'local';\n this.failureReason = reason;\n }\n\n private handleAuthError(code: string, statusCode: number | null): void {\n switch (code) {\n case 'invalid_key':\n this.setLocalMode('invalid_api_key');\n process.stderr.write(`\\u26A0 TestRelic: API key is invalid or revoked. Running in local mode.\\n \\u2192 Manage keys: ${DASHBOARD_SETTINGS_URL}\\n`);\n break;\n case 'expired_key':\n this.setLocalMode('expired_api_key');\n process.stderr.write(`\\u26A0 TestRelic: API key has expired. Running in local mode.\\n \\u2192 Generate a new key: ${DASHBOARD_SETTINGS_URL}\\n`);\n break;\n case 'rate_limited':\n this.setLocalMode('rate_limited');\n process.stderr.write('\\u26A0 TestRelic: Rate limited during authentication. Running in local mode.\\n');\n break;\n default:\n this.setLocalMode(`auth_error_${statusCode ?? 'unknown'}`);\n process.stderr.write('\\u26A0 TestRelic: Cloud authentication failed. Running in local mode.\\n');\n break;\n }\n }\n\n private async resolveRepoId(): Promise<void> {\n if (!this.config || !this.authState.accessToken) return;\n const gitId = this.gitMetadata?.remoteUrl ? normalizeGitRemoteUrl(this.gitMetadata.remoteUrl) : null;\n const displayName = gitId ? deriveRepoDisplayName(gitId) : this.config.projectName ?? deriveNonGitProjectId(process.cwd());\n const effectiveGitId = gitId ?? (this.config.projectName ?? deriveNonGitProjectId(process.cwd()));\n\n const cached = this.readRepoCache(effectiveGitId);\n if (cached) { this.repoId = cached.repoId; return; }\n\n try {\n const resolved = await resolveRepo(this.config.endpoint, this.authState.accessToken, effectiveGitId, displayName, this.config.timeout, this.gitMetadata?.branch);\n if (resolved) {\n this.repoId = resolved.repoId;\n this.writeRepoCache(effectiveGitId, resolved.repoId, resolved.displayName);\n }\n } catch { /* non-fatal */ }\n }\n\n private readRepoCache(gitId: string): RepoCache | null {\n if (!this.config) return null;\n const cachePath = join(this.config.queueDirectory, '..', 'cache', 'repo.json');\n try {\n if (!existsSync(cachePath)) return null;\n const raw = readFileSync(cachePath, 'utf-8');\n const cache = JSON.parse(raw) as RepoCache;\n if (cache.gitId !== gitId) return null;\n if (Date.now() - cache.resolvedAt > PROJECT_CACHE_TTL_MS) return null;\n const currentKeyHash = this.hashApiKey();\n if (currentKeyHash && cache.apiKeyHash !== currentKeyHash) return null;\n return cache;\n } catch { return null; }\n }\n\n private writeRepoCache(gitId: string, repoId: string, displayName: string): void {\n if (!this.config) return;\n const cacheDir = join(this.config.queueDirectory, '..', 'cache');\n const cachePath = join(cacheDir, 'repo.json');\n try {\n mkdirSync(cacheDir, { recursive: true });\n const cache: RepoCache = { repoId, gitId, displayName, resolvedAt: Date.now(), apiKeyHash: this.hashApiKey() ?? '' };\n const tmpPath = cachePath + '.tmp';\n writeFileSync(tmpPath, JSON.stringify(cache, null, 2), 'utf-8');\n renameSync(tmpPath, cachePath);\n } catch { /* non-fatal */ }\n }\n\n private hashApiKey(): string | null {\n if (!this.config?.apiKey) return null;\n return createHash('sha256').update(this.config.apiKey).digest('hex').substring(0, 16);\n }\n\n private startBackgroundFlush(): void {\n if (!this.config || !this.authState.accessToken) return;\n const accessToken = this.authState.accessToken;\n const queueDir = this.config.queueDirectory;\n const endpoint = this.config.endpoint;\n this.flushPromise = Promise.race([\n flushQueue(queueDir, endpoint, accessToken),\n new Promise<void>((resolve) => setTimeout(resolve, FLUSH_TIMEOUT_MS)),\n ]).catch(() => { /* non-fatal */ });\n }\n\n private startHealthCheck(): void {\n if (!this.config) return;\n const endpoint = this.config.endpoint;\n this.healthCheckTimer = setInterval(async () => {\n if (this.isCloudMode()) return;\n if (!this.failureReason || !['cloud_unreachable', 'auth_timeout', 'network_error'].includes(this.failureReason)) return;\n try {\n const healthy = await healthCheck(endpoint);\n if (!healthy) return;\n if (this.config?.apiKey) {\n const result = await exchangeToken(this.config.endpoint, this.config.apiKey, this.config.timeout);\n if (!isAuthError(result)) {\n const tokenResult = result as TokenExchangeResult;\n this.authState = { mode: 'cloud', accessToken: tokenResult.accessToken, refreshToken: tokenResult.refreshToken, expiresAt: Date.now() + tokenResult.expiresIn * 1000, orgId: tokenResult.orgId, orgName: tokenResult.orgName, userId: tokenResult.userId, userName: tokenResult.userName };\n this.failureReason = null;\n process.stderr.write('\\u2713 TestRelic: Cloud connectivity restored.\\n');\n this.startBackgroundFlush();\n }\n }\n } catch { /* ignore */ }\n }, HEALTH_CHECK_INTERVAL_MS);\n }\n}\n","/**\n * Cloud upload — batch and realtime upload functions.\n * Adapted from appium-analytics with testFramework: 'maestro'.\n */\n\nimport { gzipSync } from 'node:zlib';\nimport type {\n TestRunReport,\n GitMetadata,\n CIMetadata,\n Summary,\n TimelineEntry,\n TimelineStep,\n} from '@testrelic/core';\n\nexport interface TestResultForUpload {\n readonly testId: string;\n readonly title: string;\n readonly status: string;\n readonly duration: number;\n readonly suiteName: string;\n readonly filePath: string;\n readonly testType: string;\n readonly isFlaky: boolean;\n readonly startedAt: string;\n readonly completedAt: string;\n readonly tags: readonly string[];\n readonly failure: unknown | null;\n readonly retry: number;\n readonly retryCount: number;\n readonly retryStatus: string | null;\n readonly source?: string;\n readonly platform?: string;\n /** Human-readable OS name, e.g. \"Android\", \"iOS\" */\n readonly os?: string;\n /** Device name / serial, e.g. \"emulator-5554\", \"iPhone 15 Pro\" */\n readonly deviceName?: string;\n}\n\nexport interface RunUploadPayload {\n readonly runId: string;\n readonly repoGitId: string;\n readonly startedAt: string;\n readonly summary: Summary;\n readonly timeline: readonly (TimelineEntry | TimelineStep | Record<string, unknown>)[];\n readonly tests?: readonly TestResultForUpload[];\n readonly branch?: string;\n readonly commit?: string;\n readonly commitMessage?: string;\n readonly commitAuthor?: string;\n readonly finishedAt?: string;\n readonly duration?: number;\n readonly ciProvider?: string;\n readonly ciRunUrl?: string;\n readonly environment?: string;\n readonly testFramework?: string;\n}\n\nexport interface UploadResult {\n readonly success: boolean;\n readonly statusCode: number | null;\n readonly error: string | null;\n}\n\nexport interface UploadFailure {\n readonly success: false;\n readonly reason: string;\n readonly statusCode: number | null;\n readonly payload: Record<string, unknown>;\n readonly targetEndpoint: string;\n readonly method: string;\n}\n\nconst GZIP_THRESHOLD_BYTES = 1_048_576;\nconst RETRY_DELAYS_MS = [1000, 3000, 9000];\nconst MAX_RETRIES = 3;\n\nexport function buildUploadPayload(\n report: TestRunReport,\n repoGitId: string,\n git: GitMetadata | null,\n ci: CIMetadata | null,\n tests?: readonly TestResultForUpload[],\n): RunUploadPayload {\n const environment = ci?.provider && ci.provider !== 'unknown' ? ci.provider : 'local';\n const payload: RunUploadPayload = {\n runId: report.testRunId,\n repoGitId,\n startedAt: report.startedAt,\n summary: report.summary,\n timeline: report.timeline,\n environment,\n testFramework: 'maestro',\n ...(tests && tests.length > 0 ? { tests } : {}),\n ...(git?.branch ? { branch: git.branch } : {}),\n ...(git?.commitSha ? { commit: git.commitSha } : {}),\n ...(git?.commitMessage ? { commitMessage: git.commitMessage } : {}),\n ...(git?.commitAuthor ? { commitAuthor: git.commitAuthor } : {}),\n ...(report.completedAt ? { finishedAt: report.completedAt } : {}),\n ...(report.totalDuration ? { duration: report.totalDuration } : {}),\n ...(ci?.provider ? { ciProvider: ci.provider } : {}),\n ...(ci?.runUrl ? { ciRunUrl: ci.runUrl } : {}),\n };\n return payload;\n}\n\nasync function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function retryWithBackoff(\n url: string,\n init: RequestInit,\n onTokenRefresh?: () => Promise<string | null>,\n): Promise<Response | null> {\n for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {\n try {\n const response = await fetch(url, init);\n\n if (response.ok) return response;\n\n if (response.status === 401 && onTokenRefresh && attempt === 0) {\n const newToken = await onTokenRefresh();\n if (newToken) {\n const refreshedInit = {\n ...init,\n headers: { ...init.headers as Record<string, string>, Authorization: `Bearer ${newToken}` },\n };\n const retryResponse = await fetch(url, refreshedInit);\n if (retryResponse.ok) return retryResponse;\n }\n return null;\n }\n\n if (response.status === 429 && attempt < MAX_RETRIES - 1) {\n const retryAfter = response.headers.get('Retry-After');\n const waitMs = retryAfter ? parseInt(retryAfter, 10) * 1000 : RETRY_DELAYS_MS[attempt];\n if (!isNaN(waitMs) && waitMs > 0) {\n await sleep(waitMs);\n } else {\n await sleep(RETRY_DELAYS_MS[attempt]);\n }\n continue;\n }\n\n if (response.status >= 500 && attempt < MAX_RETRIES - 1) {\n await sleep(RETRY_DELAYS_MS[attempt]);\n continue;\n }\n\n return response;\n } catch {\n if (attempt < MAX_RETRIES - 1) {\n await sleep(RETRY_DELAYS_MS[attempt]);\n continue;\n }\n return null;\n }\n }\n return null;\n}\n\nfunction prepareBody(payload: unknown): { body: Buffer | string; headers: Record<string, string> } {\n const json = JSON.stringify(payload);\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n\n if (Buffer.byteLength(json, 'utf-8') > GZIP_THRESHOLD_BYTES) {\n const compressed = gzipSync(Buffer.from(json, 'utf-8'));\n headers['Content-Encoding'] = 'gzip';\n return { body: compressed, headers };\n }\n\n return { body: json, headers };\n}\n\nexport async function uploadBatchRun(\n endpoint: string,\n accessToken: string,\n payload: RunUploadPayload,\n onTokenRefresh?: () => Promise<string | null>,\n): Promise<UploadResult | UploadFailure> {\n const url = `${endpoint}/runs`;\n const { body, headers } = prepareBody(payload);\n headers['Authorization'] = `Bearer ${accessToken}`;\n\n const response = await retryWithBackoff(url, { method: 'POST', headers, body }, onTokenRefresh);\n\n if (response?.ok) {\n return { success: true, statusCode: response.status, error: null };\n }\n\n if (response?.status === 409) {\n return { success: true, statusCode: 409, error: null };\n }\n\n return {\n success: false,\n reason: response ? `Upload failed with status ${response.status}` : 'Upload failed after retries',\n statusCode: response?.status ?? null,\n payload: payload as unknown as Record<string, unknown>,\n targetEndpoint: url,\n method: 'POST',\n };\n}\n\nexport interface RealtimeInitResult {\n readonly runId: string;\n}\n\nfunction stripNulls(obj: Record<string, unknown>): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(obj)) {\n if (val !== null && val !== undefined) result[key] = val;\n }\n return result;\n}\n\nexport async function initRealtimeRun(\n endpoint: string,\n accessToken: string,\n payload: {\n runId: string;\n repoGitId: string;\n branch: string | null;\n commit: string | null;\n commitMessage?: string | null;\n commitAuthor?: string | null;\n startedAt: string;\n totalTests: number | null;\n ciProvider: string | null;\n ciRunUrl: string | null;\n environment?: string | null;\n testFramework?: string;\n },\n): Promise<RealtimeInitResult | null> {\n const url = `${endpoint}/runs/init`;\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${accessToken}`,\n },\n body: JSON.stringify(stripNulls(payload as unknown as Record<string, unknown>)),\n });\n if (!response.ok) return null;\n const responseBody = await response.json() as Record<string, unknown>;\n return { runId: responseBody.runId as string };\n } catch {\n return null;\n }\n}\n\nexport async function finalizeRun(\n endpoint: string,\n accessToken: string,\n cloudRunId: string,\n payload: {\n finishedAt: string;\n duration: number;\n summary: Summary;\n commitMessage?: string | null;\n },\n): Promise<boolean> {\n const url = `${endpoint}/runs/${cloudRunId}/finalize`;\n const response = await retryWithBackoff(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${accessToken}`,\n },\n body: JSON.stringify(payload),\n });\n return response?.ok ?? false;\n}\n","/**\n * Cloud artifact upload — binary file upload via presigned S3 URLs.\n */\n\nimport { createReadStream, statSync } from 'node:fs';\nimport { basename } from 'node:path';\nimport { Readable } from 'node:stream';\nimport type { ReadableStream as WebReadableStream } from 'node:stream/web';\n\nexport interface ArtifactUploadRequest {\n readonly filePath: string;\n readonly runId: string;\n readonly testId: string;\n readonly type: 'screenshot' | 'video' | 'trace' | 'other';\n}\n\nexport interface ArtifactUploadResult {\n readonly success: boolean;\n readonly storageKey: string | null;\n readonly artifactId: string | null;\n readonly error: string | null;\n}\n\ninterface UploadUrlResponse {\n readonly artifactId: string;\n readonly uploadUrl: string;\n readonly storageKey: string;\n}\n\nconst UPLOAD_CONCURRENCY = 5;\nconst RETRY_DELAYS_MS = [1000, 3000, 9000];\nconst MAX_RETRIES = 3;\n\nconst CONTENT_TYPE_MAP: Record<string, string> = {\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.webp': 'image/webp',\n '.webm': 'video/webm',\n '.mp4': 'video/mp4',\n '.mov': 'video/quicktime',\n '.zip': 'application/zip',\n};\n\nlet activeUploads = 0;\nconst pendingUploads: Array<() => void> = [];\n\nasync function acquireSlot(): Promise<void> {\n if (activeUploads >= UPLOAD_CONCURRENCY) {\n await new Promise<void>((resolve) => pendingUploads.push(resolve));\n }\n activeUploads++;\n}\n\nfunction releaseSlot(): void {\n activeUploads--;\n if (pendingUploads.length > 0) pendingUploads.shift()!();\n}\n\nasync function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction getContentType(filePath: string): string {\n const ext = filePath.substring(filePath.lastIndexOf('.')).toLowerCase();\n return CONTENT_TYPE_MAP[ext] ?? 'application/octet-stream';\n}\n\nfunction getFileSize(filePath: string): number {\n try { return statSync(filePath).size; } catch { return 0; }\n}\n\nasync function requestUploadUrl(\n endpoint: string, accessToken: string, request: ArtifactUploadRequest, sizeBytes: number, contentType: string,\n): Promise<UploadUrlResponse | null> {\n const url = `${endpoint}/artifacts/upload-url`;\n const body = JSON.stringify({ runId: request.runId, testId: request.testId, fileName: basename(request.filePath), contentType, type: request.type, sizeBytes });\n\n for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {\n try {\n const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${accessToken}` }, body });\n if (response.ok) return (await response.json()) as UploadUrlResponse;\n if (response.status >= 500 && attempt < MAX_RETRIES - 1) { await sleep(RETRY_DELAYS_MS[attempt]); continue; }\n return null;\n } catch {\n if (attempt < MAX_RETRIES - 1) { await sleep(RETRY_DELAYS_MS[attempt]); continue; }\n return null;\n }\n }\n return null;\n}\n\nasync function putFileToPresignedUrl(presignedUrl: string, filePath: string, contentType: string, sizeBytes: number): Promise<boolean> {\n for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {\n try {\n const nodeStream = createReadStream(filePath);\n const webStream = Readable.toWeb(nodeStream) as WebReadableStream<Uint8Array>;\n const response = await fetch(presignedUrl, {\n method: 'PUT',\n headers: { 'Content-Type': contentType, 'Content-Length': String(sizeBytes) },\n body: webStream,\n duplex: 'half',\n });\n if (response.ok) return true;\n if (response.status >= 500 && attempt < MAX_RETRIES - 1) { await sleep(RETRY_DELAYS_MS[attempt]); continue; }\n return false;\n } catch {\n if (attempt < MAX_RETRIES - 1) { await sleep(RETRY_DELAYS_MS[attempt]); continue; }\n return false;\n }\n }\n return false;\n}\n\nasync function confirmUpload(endpoint: string, accessToken: string, artifactId: string): Promise<boolean> {\n try {\n const response = await fetch(`${endpoint}/artifacts/confirm`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${accessToken}` },\n body: JSON.stringify({ artifactId }),\n });\n return response.ok;\n } catch { return false; }\n}\n\nexport async function uploadArtifact(endpoint: string, accessToken: string, request: ArtifactUploadRequest, maxSizeMb: number): Promise<ArtifactUploadResult> {\n const sizeBytes = getFileSize(request.filePath);\n if (sizeBytes === 0) return { success: false, storageKey: null, artifactId: null, error: 'file_not_found_or_empty' };\n if (sizeBytes > maxSizeMb * 1024 * 1024) return { success: false, storageKey: null, artifactId: null, error: 'file_too_large' };\n const contentType = getContentType(request.filePath);\n\n await acquireSlot();\n try {\n const urlResult = await requestUploadUrl(endpoint, accessToken, request, sizeBytes, contentType);\n if (!urlResult) return { success: false, storageKey: null, artifactId: null, error: 'upload_url_request_failed' };\n const uploaded = await putFileToPresignedUrl(urlResult.uploadUrl, request.filePath, contentType, sizeBytes);\n if (!uploaded) return { success: false, storageKey: urlResult.storageKey, artifactId: urlResult.artifactId, error: 'presigned_put_failed' };\n const confirmed = await confirmUpload(endpoint, accessToken, urlResult.artifactId);\n return { success: confirmed, storageKey: urlResult.storageKey, artifactId: urlResult.artifactId, error: confirmed ? null : 'confirm_failed' };\n } finally { releaseSlot(); }\n}\n\nexport async function uploadArtifacts(endpoint: string, accessToken: string, requests: readonly ArtifactUploadRequest[], maxSizeMb: number): Promise<Map<string, ArtifactUploadResult>> {\n const results = new Map<string, ArtifactUploadResult>();\n const promises = requests.map(async (req) => {\n const result = await uploadArtifact(endpoint, accessToken, req, maxSizeMb);\n results.set(req.filePath, result);\n });\n await Promise.allSettled(promises);\n return results;\n}\n","/**\n * CI provider auto-detection via environment variables.\n * Strategy pattern: check providers in priority order, first match wins.\n */\n\nimport type { CIMetadata } from '@testrelic/core';\n\ntype DetectFn = (env: Record<string, string | undefined>) => CIMetadata | null;\n\nfunction detectGitHubActions(env: Record<string, string | undefined>): CIMetadata | null {\n if (env.GITHUB_ACTIONS !== 'true') return null;\n return {\n provider: 'github-actions',\n buildId: env.GITHUB_RUN_ID ?? null,\n commitSha: env.GITHUB_SHA ?? null,\n branch: env.GITHUB_REF_NAME ?? null,\n runUrl: env.GITHUB_SERVER_URL && env.GITHUB_REPOSITORY && env.GITHUB_RUN_ID\n ? `${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}/actions/runs/${env.GITHUB_RUN_ID}`\n : null,\n };\n}\n\nfunction detectGitLabCI(env: Record<string, string | undefined>): CIMetadata | null {\n if (env.GITLAB_CI !== 'true') return null;\n return {\n provider: 'gitlab-ci',\n buildId: env.CI_PIPELINE_ID ?? null,\n commitSha: env.CI_COMMIT_SHA ?? null,\n branch: env.CI_COMMIT_BRANCH ?? env.CI_COMMIT_REF_NAME ?? null,\n runUrl: env.CI_PIPELINE_URL ?? null,\n };\n}\n\nfunction detectJenkins(env: Record<string, string | undefined>): CIMetadata | null {\n if (!env.JENKINS_URL) return null;\n let branch = env.GIT_BRANCH ?? null;\n if (branch?.startsWith('origin/')) {\n branch = branch.slice('origin/'.length);\n }\n return {\n provider: 'jenkins',\n buildId: env.BUILD_ID ?? null,\n commitSha: env.GIT_COMMIT ?? null,\n branch,\n runUrl: env.BUILD_URL ?? null,\n };\n}\n\nfunction detectCircleCI(env: Record<string, string | undefined>): CIMetadata | null {\n if (env.CIRCLECI !== 'true') return null;\n return {\n provider: 'circleci',\n buildId: env.CIRCLE_BUILD_NUM ?? null,\n commitSha: env.CIRCLE_SHA1 ?? null,\n branch: env.CIRCLE_BRANCH ?? null,\n runUrl: env.CIRCLE_BUILD_URL ?? null,\n };\n}\n\nfunction detectBitbucketPipelines(env: Record<string, string | undefined>): CIMetadata | null {\n if (!env.BITBUCKET_PIPELINE_UUID) return null;\n const runUrl = env.BITBUCKET_WORKSPACE && env.BITBUCKET_REPO_SLUG && env.BITBUCKET_PIPELINE_UUID\n ? `https://bitbucket.org/${env.BITBUCKET_WORKSPACE}/${env.BITBUCKET_REPO_SLUG}/pipelines/results/${env.BITBUCKET_PIPELINE_UUID}`\n : null;\n return {\n provider: 'bitbucket-pipelines',\n buildId: env.BITBUCKET_BUILD_NUMBER ?? null,\n commitSha: env.BITBUCKET_COMMIT ?? null,\n branch: env.BITBUCKET_BRANCH ?? null,\n runUrl,\n };\n}\n\nconst detectors: DetectFn[] = [\n detectGitHubActions,\n detectGitLabCI,\n detectJenkins,\n detectCircleCI,\n detectBitbucketPipelines,\n];\n\nexport function detectCI(env?: Record<string, string | undefined>): CIMetadata | null {\n const envVars = env ?? process.env;\n for (const detect of detectors) {\n const result = detect(envVars);\n if (result) return result;\n }\n return null;\n}\n","/**\n * Cloud reporter orchestration for Maestro analytics.\n * Handles batch upload and artifact upload to the TestRelic platform.\n */\n\nimport type { CloudConfig, TestRunReport, Summary, TimelineEntry, ActionStep } from '@testrelic/core';\nimport { CloudClient } from './cloud-client.js';\nimport { buildUploadPayload, uploadBatchRun, finalizeRun } from './cloud-upload.js';\nimport type { UploadFailure, TestResultForUpload } from './cloud-upload.js';\nimport { uploadArtifacts } from './cloud-artifact-upload.js';\nimport type { ArtifactUploadRequest } from './cloud-artifact-upload.js';\nimport { writeToQueue } from './cloud-queue.js';\nimport { detectCI } from './ci-detector.js';\nimport { normalizeGitRemoteUrl, deriveNonGitProjectId } from './git-metadata.js';\n\nfunction flattenTimelineForCloud(timeline: readonly (TimelineEntry | Record<string, unknown>)[]): Record<string, unknown>[] {\n const flat: Record<string, unknown>[] = [];\n\n for (const entry of timeline) {\n const timelineEntry = entry as TimelineEntry;\n const tests = timelineEntry.tests;\n if (!Array.isArray(tests) || tests.length === 0) {\n flat.push(entry as Record<string, unknown>);\n continue;\n }\n\n for (const test of tests) {\n flat.push({\n timestamp: test.startedAt,\n title: test.title,\n category: 'test',\n navigationType: timelineEntry.navigationType,\n url: timelineEntry.url,\n visitedAt: timelineEntry.visitedAt,\n duration: test.duration,\n status: test.status,\n test_status: test.status,\n test_id: test.testId,\n test_title: test.title,\n specfile: test.filePath,\n suiteName: test.suiteName,\n type: 'test',\n ...(test.failure ? { error: test.failure.message, errorMessage: test.failure.message } : {}),\n });\n\n for (const action of (test.actions ?? []) as ActionStep[]) {\n flat.push({\n timestamp: action.timestamp,\n title: action.title,\n category: action.category,\n status: action.status,\n test_status: test.status,\n duration: action.duration,\n error: action.error,\n test_id: test.testId,\n test_title: test.title,\n specfile: test.filePath,\n url: timelineEntry.url,\n type: action.category === 'assertion' ? 'assert' : 'action',\n });\n }\n }\n }\n\n return flat;\n}\n\nfunction getEffectiveGitId(cloudClient: CloudClient): string {\n const gitMeta = cloudClient.getGitMetadata();\n const remoteUrl = gitMeta?.remoteUrl;\n if (remoteUrl) return normalizeGitRemoteUrl(remoteUrl);\n const projectName = cloudClient.getConfig()?.projectName;\n return projectName ?? deriveNonGitProjectId(process.cwd());\n}\n\n/** A video (or other artifact) explicitly associated with a specific test/flow. */\nexport interface PerTestArtifact {\n readonly path: string;\n readonly testId: string;\n}\n\nexport async function finalizeAndUpload(\n cloudClient: CloudClient,\n cloudConfig: CloudConfig | undefined | null,\n testRunId: string,\n report: TestRunReport,\n completedAt: string,\n totalDuration: number,\n summary: Summary,\n screenshotPaths?: readonly string[],\n videoPaths?: readonly string[],\n tests?: readonly TestResultForUpload[],\n /** Per-flow video artifacts — each is uploaded with its exact flow testId. */\n videoArtifacts?: readonly PerTestArtifact[],\n): Promise<void> {\n try {\n const perTestVideoCount = videoArtifacts?.length ?? 0;\n const hasArtifacts =\n (screenshotPaths?.length ?? 0) +\n (videoPaths?.length ?? 0) +\n perTestVideoCount > 0;\n\n if (cloudClient.isCloudMode() && hasArtifacts) {\n const tokenValid = await cloudClient.ensureValidToken();\n if (tokenValid) {\n const endpoint = cloudClient.getEndpoint();\n const token = cloudClient.getAccessToken()!;\n const maxSizeMb = cloudConfig?.artifactMaxSizeMb ?? 50;\n\n const requests: ArtifactUploadRequest[] = [];\n for (const path of (screenshotPaths ?? [])) {\n requests.push({ filePath: path, runId: testRunId, testId: 'maestro-suite', type: 'screenshot' });\n }\n // Per-flow videos — uploaded with the flow's own testId so the frontend\n // can resolve the correct recording per test case.\n const perTestPaths = new Set<string>();\n for (const va of (videoArtifacts ?? [])) {\n requests.push({ filePath: va.path, runId: testRunId, testId: va.testId, type: 'video' });\n perTestPaths.add(va.path);\n }\n // Any remaining videos (not matched to a specific flow) fall back to the\n // suite-level bucket so they are still visible in the run overview.\n for (const path of (videoPaths ?? [])) {\n if (!perTestPaths.has(path)) {\n requests.push({ filePath: path, runId: testRunId, testId: 'maestro-suite', type: 'video' });\n }\n }\n\n if (requests.length > 0) {\n const results = await uploadArtifacts(endpoint, token, requests, maxSizeMb);\n const resultEntries = Array.from(results.entries());\n const uploaded = resultEntries.filter(([, r]) => r.success).length;\n const failed = resultEntries.filter(([, r]) => !r.success);\n\n if (uploaded > 0) {\n const videoCount = requests.filter(r => r.type === 'video').length;\n const perFlowVideoCount = (videoArtifacts?.length ?? 0);\n const ssCount = requests.filter(r => r.type === 'screenshot').length;\n const parts: string[] = [];\n if (ssCount > 0) parts.push(`${ssCount} screenshot(s)`);\n if (videoCount > 0) {\n const perFlowNote = perFlowVideoCount > 0 ? ` (${perFlowVideoCount} per-flow)` : '';\n parts.push(`${videoCount} video(s)${perFlowNote}`);\n }\n process.stderr.write(`\\u2713 TestRelic: Uploaded ${uploaded} artifact(s) [${parts.join(', ')}] to cloud storage.\\n`);\n }\n\n if (failed.length > 0) {\n process.stderr.write(`\\u26A0 TestRelic: ${failed.length} artifact(s) failed to upload:\\n`);\n for (const [filePath, result] of failed) {\n const req = requests.find((r) => r.filePath === filePath);\n const label = req ? `[${req.type}] ${filePath}` : filePath;\n process.stderr.write(` \\u2717 ${label} \\u2014 reason: ${result.error ?? 'unknown'}\\n`);\n }\n }\n }\n }\n }\n\n if (cloudClient.isCloudMode()) {\n const tokenValid = await cloudClient.ensureValidToken();\n if (tokenValid) {\n const batchGit = cloudClient.getGitMetadata();\n const batchCi = detectCI();\n const batchGitId = getEffectiveGitId(cloudClient);\n\n const flatTimeline = flattenTimelineForCloud(report.timeline as (TimelineEntry | Record<string, unknown>)[]);\n const flatReport = { ...report, timeline: flatTimeline };\n const payload = buildUploadPayload(flatReport as unknown as TestRunReport, batchGitId, batchGit, batchCi, tests);\n\n const result = await uploadBatchRun(\n cloudClient.getEndpoint(),\n cloudClient.getAccessToken()!,\n payload,\n async () => {\n const refreshed = await cloudClient.ensureValidToken();\n return refreshed ? cloudClient.getAccessToken() : null;\n },\n );\n\n if (result.success) {\n process.stderr.write(`\\u2713 TestRelic: Cloud upload succeeded \\u2192 ${cloudClient.getEndpoint()}/runs/${testRunId}\\n`);\n } else {\n const failure = result as UploadFailure;\n const queueDir = cloudConfig?.queueDirectory ?? '.testrelic/queue';\n writeToQueue(queueDir, testRunId, 'batch', failure.reason, failure.targetEndpoint, failure.method, failure.payload, { 'Content-Type': 'application/json' });\n process.stderr.write('\\u26A0 TestRelic: Cloud upload failed, queued for retry.\\n');\n }\n } else {\n const queueDir = cloudConfig?.queueDirectory ?? '.testrelic/queue';\n const queueGit = cloudClient.getGitMetadata();\n const queueCi = detectCI();\n const queueGitId = getEffectiveGitId(cloudClient);\n const queuePayload = buildUploadPayload(report, queueGitId, queueGit, queueCi);\n writeToQueue(queueDir, testRunId, 'batch', cloudClient.getFailureReason() ?? 'token_invalid', `${cloudClient.getEndpoint()}/runs`, 'POST', queuePayload as unknown as Record<string, unknown>, { 'Content-Type': 'application/json' });\n }\n }\n\n await cloudClient.dispose();\n } catch {\n try { await cloudClient.dispose(); } catch { /* ignore */ }\n }\n}\n","/**\n * Report orchestrator — ties all parsers together to produce\n * a TestRunReport from Maestro's output artifacts.\n */\n\nimport { existsSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { dirname, resolve, basename, extname } from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport type { TestRunReport, TimelineEntry } from '@testrelic/core';\nimport type {\n MaestroFlowResult,\n AIDefect,\n ResolvedMaestroConfig,\n CollectedArtifacts,\n MaestroPlatform,\n} from './types.js';\nimport { parseJUnitFile } from './parsers/junit-parser.js';\nimport { parseCommandsFile, discoverCommandFiles } from './parsers/command-parser.js';\nimport { parseFlowFile, discoverFlowFiles } from './parsers/flow-parser.js';\nimport { parseLogFile, detectPlatformFromLogs, discoverLogFiles } from './parsers/log-parser.js';\nimport { parseAiReportFile, discoverAiReports } from './parsers/ai-report-parser.js';\nimport { collectArtifacts } from './artifact-collector.js';\nimport { buildTimeline } from './timeline-builder.js';\nimport { buildSummary } from './summary-builder.js';\nimport { generateHtmlReport } from './html-report.js';\nimport { printConsoleSummary } from './console-summary.js';\nimport { openInBrowser } from './browser-open.js';\nimport { CloudClient } from './cloud-client.js';\nimport { finalizeAndUpload } from './cloud-reporter.js';\nimport type { PerTestArtifact } from './cloud-reporter.js';\nimport type { TestResultForUpload } from './cloud-upload.js';\n\n/**\n * Try to find the recording file that belongs to a specific Maestro flow.\n *\n * Matching strategy (tried in order):\n * 1. Exact: video base name == flow base name (e.g. \"login\" == \"login\")\n * 2. Forward: video filename contains flow base (e.g. \"login-recording.mp4\")\n * 3. Reverse: flow name contains video base (e.g. \"Login Flow\" → \"login\")\n * 4. Commands: the flow's commands JSON has a startRecording entry whose path\n * parameter matches a video filename.\n *\n * Returns `null` when no candidate is found so the caller can fall back.\n */\nfunction matchVideoToFlow(\n flowFile: string,\n videoPaths: readonly string[],\n flowRecordingPath?: string | null,\n): string | null {\n if (videoPaths.length === 0) return null;\n const flowBase = basename(flowFile, extname(flowFile)).toLowerCase();\n\n // Exact: video base name == flow base name\n const exact = videoPaths.find(\n (v) => basename(v, extname(v)).toLowerCase() === flowBase,\n );\n if (exact) return exact;\n\n // Forward: video filename contains flow base\n const forward = videoPaths.find(\n (v) => basename(v).toLowerCase().includes(flowBase),\n );\n if (forward) return forward;\n\n // Reverse: flow name contains or starts with video base name\n const reverse = videoPaths.find((v) => {\n const videoBase = basename(v, extname(v)).toLowerCase();\n return videoBase.length >= 3 && (flowBase.startsWith(videoBase) || flowBase.includes(videoBase));\n });\n if (reverse) return reverse;\n\n // Commands-JSON: match by the startRecording path extracted from the\n // flow's commands file (e.g. startRecording: \"recording\" → \"recording.mp4\")\n if (flowRecordingPath) {\n const recBase = basename(flowRecordingPath, extname(flowRecordingPath)).toLowerCase();\n const byRec = videoPaths.find((v) => basename(v, extname(v)).toLowerCase() === recBase);\n if (byRec) return byRec;\n const byRecPartial = videoPaths.find((v) => basename(v).toLowerCase().includes(recBase));\n if (byRecPartial) return byRecPartial;\n }\n\n return null;\n}\n\n/**\n * Filter screenshots that belong to a specific Maestro flow.\n */\nfunction matchScreenshotsToFlow(flowFile: string, screenshotPaths: readonly string[]): string[] {\n if (screenshotPaths.length === 0) return [];\n const flowBase = basename(flowFile, extname(flowFile)).toLowerCase();\n const matched = screenshotPaths.filter((s) => {\n const name = basename(s).toLowerCase();\n const nameBase = basename(s, extname(s)).toLowerCase();\n if (name.startsWith(flowBase) || name.includes(flowBase)) return true;\n return nameBase.length >= 3 && (flowBase.startsWith(nameBase) || flowBase.includes(nameBase));\n });\n return matched.length > 0 ? matched : screenshotPaths.slice();\n}\n\n/**\n * Extract the `startRecording` path from a list of parsed command steps.\n * Maestro's commands-*.json contains entries like:\n * { command: \"startRecording\", metadata: { path: \"recording\" } }\n * Returns the path string or null if no recording was started.\n */\nfunction extractRecordingPath(\n commands: ReturnType<typeof parseCommandsFile>,\n): string | null {\n for (const cmd of commands) {\n if (cmd.command === 'startRecording') {\n const meta = cmd.metadata as Record<string, unknown> | undefined;\n const path = meta?.path ?? meta?.recording ?? meta?.fileName;\n if (typeof path === 'string') return path;\n // When startRecording uses shorthand (\"startRecording: name\"), the\n // value ends up in the selector or as the command argument itself.\n if (typeof cmd.selector === 'string') return cmd.selector;\n }\n }\n return null;\n}\n\n/**\n * Build a mapping from flow-name → recording path by inspecting the\n * commands JSON files. Commands files are named `commands-{flowName}.json`.\n */\nfunction buildFlowRecordingMap(\n commandSteps: Map<string, ReturnType<typeof parseCommandsFile>>,\n): Map<string, string> {\n const map = new Map<string, string>();\n for (const [filePath, commands] of commandSteps) {\n const name = basename(filePath)\n .replace(/^commands[-_]?/i, '')\n .replace(/\\.json$/i, '')\n .toLowerCase();\n const recPath = extractRecordingPath(commands);\n if (recPath && name) map.set(name, recPath);\n }\n return map;\n}\n\n/**\n * Map a Maestro platform identifier to a human-readable OS string suitable\n * for display in the dashboard (e.g. 'android' → 'Android').\n */\nfunction platformToOs(platform: MaestroPlatform): string | undefined {\n switch (platform) {\n case 'android': return 'Android';\n case 'ios': return 'iOS';\n case 'web': return 'Web';\n default: return undefined;\n }\n}\n\n/**\n * Convert a MaestroFlowResult into the TestResultForUpload shape expected by\n * the server's batch-upload endpoint. This is what makes individual flows\n * visible as test cases in the TestRelic dashboard.\n */\nfunction flowToTestResult(flow: MaestroFlowResult): TestResultForUpload {\n return {\n testId: `${flow.flowFile}::${flow.flowName}`,\n title: flow.flowName,\n status: flow.status,\n duration: flow.duration,\n suiteName: flow.appId ?? 'maestro-suite',\n filePath: flow.flowFile,\n testType: 'mobile',\n isFlaky: false,\n startedAt: flow.startedAt,\n completedAt: flow.completedAt,\n tags: flow.tags,\n failure: flow.failureMessage\n ? { message: flow.failureMessage }\n : null,\n retry: 0,\n retryCount: 0,\n retryStatus: null,\n source: 'maestro',\n platform: flow.platform !== 'unknown' ? flow.platform : undefined,\n os: platformToOs(flow.platform),\n deviceName: flow.deviceId,\n };\n}\n\nexport interface OrchestratorInput {\n readonly junitPath?: string;\n readonly testOutputDir?: string;\n readonly debugOutputDir?: string;\n readonly flowsDir?: string;\n readonly config: ResolvedMaestroConfig;\n /** Device serial / name used during this run, e.g. \"emulator-5554\". Passed through to each flow result and uploaded as deviceName. */\n readonly device?: string;\n /**\n * Explicit mobile platform override ('android' | 'ios').\n * When set, this takes precedence over log-based detection so callers that\n * already know the target platform (e.g. the E2E script using adb) do not\n * rely on heuristic detection that may return 'unknown'.\n */\n readonly platform?: MaestroPlatform;\n}\n\nexport interface OrchestratorResult {\n readonly report: TestRunReport;\n readonly flowResults: MaestroFlowResult[];\n readonly aiDefects: AIDefect[];\n readonly artifacts: CollectedArtifacts;\n}\n\nexport async function orchestrateReport(input: OrchestratorInput): Promise<OrchestratorResult> {\n const { config } = input;\n const startedAt = new Date().toISOString();\n const testRunId = config.testRunId ?? randomUUID();\n\n const artifacts = collectArtifacts(input.testOutputDir, input.debugOutputDir);\n const junitPath = input.junitPath ?? artifacts.junitReportPath;\n\n // Caller-supplied platform takes priority; log detection is a fallback.\n let platform: MaestroPlatform = input.platform ?? 'unknown';\n if (platform === 'unknown') {\n const logFiles = input.debugOutputDir ? discoverLogFiles(input.debugOutputDir) : artifacts.logPaths;\n for (const logFile of logFiles) {\n const logEntries = parseLogFile(logFile);\n const detected = detectPlatformFromLogs(logEntries);\n if (detected !== 'unknown') { platform = detected; break; }\n }\n }\n\n const flowMetadataMap = new Map<string, ReturnType<typeof parseFlowFile>>();\n if (input.flowsDir && existsSync(input.flowsDir)) {\n const flowFiles = discoverFlowFiles(input.flowsDir);\n for (const flowFile of flowFiles) {\n const meta = parseFlowFile(flowFile);\n const name = meta.name ?? flowFile;\n flowMetadataMap.set(name, meta);\n }\n }\n\n const commandSteps = new Map<string, ReturnType<typeof parseCommandsFile>>();\n const commandFiles = input.testOutputDir ? discoverCommandFiles(input.testOutputDir) : artifacts.commandJsonPaths;\n for (const cmdFile of commandFiles) {\n commandSteps.set(cmdFile, parseCommandsFile(cmdFile));\n }\n\n const allAiDefects: AIDefect[] = [];\n const aiReportFiles = input.testOutputDir ? discoverAiReports(input.testOutputDir) : artifacts.aiReportPaths;\n for (const aiFile of aiReportFiles) {\n const aiReport = parseAiReportFile(aiFile);\n allAiDefects.push(...aiReport.defects);\n }\n\n // Build a flow-name → recording-path map from the commands JSONs so we can\n // match videos even when filenames don't correlate with flow filenames\n // (e.g. startRecording: \"recording\" in login_flow.yaml → recording.mp4).\n const flowRecordingMap = buildFlowRecordingMap(commandSteps);\n\n const flowResults: MaestroFlowResult[] = [];\n\n if (junitPath && existsSync(junitPath)) {\n const junit = parseJUnitFile(junitPath);\n\n const allCommands = Array.from(commandSteps.values()).flat();\n const assertionCommands = allCommands.filter((c) => c.category === 'assertion');\n const nonAssertionCommands = allCommands.filter((c) => c.category !== 'assertion');\n\n for (const suite of junit.testSuites) {\n for (const testCase of suite.testCases) {\n const name = testCase.name;\n const meta = flowMetadataMap.get(name);\n\n const status = testCase.status === 'SUCCESS' ? 'passed'\n : testCase.status === 'SKIPPED' ? 'skipped'\n : 'failed';\n\n const durationMs = testCase.time * 1000;\n const flowStartedAt = startedAt;\n const flowCompletedAt = new Date(new Date(flowStartedAt).getTime() + durationMs).toISOString();\n\n const flowFile = testCase.classname || name;\n const flowBase = basename(flowFile, extname(flowFile)).toLowerCase();\n const recordingPath = flowRecordingMap.get(flowBase) ?? null;\n\n flowResults.push({\n flowName: name,\n flowFile,\n appId: meta?.appId ?? null,\n platform,\n deviceId: input.device,\n status,\n duration: durationMs,\n startedAt: flowStartedAt,\n completedAt: flowCompletedAt,\n tags: meta?.tags ?? [],\n properties: meta?.properties ?? {},\n commands: nonAssertionCommands,\n assertions: assertionCommands,\n screenshotPaths: matchScreenshotsToFlow(flowFile, artifacts.screenshotPaths),\n videoPath: matchVideoToFlow(flowFile, artifacts.videoPaths, recordingPath),\n aiDefects: allAiDefects,\n logEntries: [],\n failureMessage: testCase.failureMessage ?? testCase.errorMessage ?? null,\n failureType: testCase.failureType ?? testCase.errorType ?? null,\n subflowRefs: meta?.subflowRefs ?? [],\n metadata: meta ?? null,\n });\n }\n }\n }\n\n if (flowResults.length === 0 && flowMetadataMap.size > 0) {\n for (const [, meta] of flowMetadataMap) {\n flowResults.push({\n flowName: meta.name ?? meta.filePath,\n flowFile: meta.filePath,\n appId: meta.appId,\n platform,\n deviceId: input.device,\n status: 'passed',\n duration: 0,\n startedAt,\n completedAt: startedAt,\n tags: meta.tags,\n properties: meta.properties,\n commands: [],\n assertions: [],\n screenshotPaths: [],\n videoPath: null,\n aiDefects: [],\n logEntries: [],\n failureMessage: null,\n failureType: null,\n subflowRefs: meta.subflowRefs,\n metadata: meta,\n });\n }\n }\n\n const timeline: TimelineEntry[] = buildTimeline(flowResults);\n const summary = buildSummary(timeline);\n const completedAt = new Date().toISOString();\n const totalDuration = Date.now() - new Date(startedAt).getTime();\n\n const report: TestRunReport = {\n schemaVersion: '1.0.0',\n testRunId,\n startedAt,\n completedAt,\n totalDuration,\n summary,\n ci: null,\n metadata: config.metadata ?? null,\n timeline,\n shardRunIds: null,\n };\n\n mkdirSync(dirname(resolve(config.outputPath)), { recursive: true });\n writeFileSync(resolve(config.outputPath), JSON.stringify(report, null, 2), 'utf-8');\n\n generateHtmlReport(report, allAiDefects, artifacts.screenshotPaths, resolve(config.htmlReportPath));\n\n printConsoleSummary(summary, config.outputPath, config.htmlReportPath, config.quiet, allAiDefects);\n\n if (config.openReport) {\n openInBrowser(resolve(config.htmlReportPath));\n }\n\n if (config.cloud) {\n const cloudClient = new CloudClient(config.cloud);\n await cloudClient.initialize();\n\n const videoPathsToUpload = config.includeVideo ? artifacts.videoPaths : [];\n\n // Build per-flow video associations so each flow shows its own recording\n // in the cloud platform instead of a shared suite-level clip.\n const videoArtifacts: PerTestArtifact[] = [];\n if (videoPathsToUpload.length > 0) {\n // First pass: try to match videos to flows using all heuristics.\n for (const flow of flowResults) {\n const flowBase = basename(flow.flowFile, extname(flow.flowFile)).toLowerCase();\n const recordingPath = flowRecordingMap.get(flowBase) ?? null;\n const matched = matchVideoToFlow(flow.flowFile, videoPathsToUpload, recordingPath);\n if (matched) {\n const testId = `${flow.flowFile}::${flow.flowName}`;\n if (!videoArtifacts.some((va) => va.path === matched && va.testId === testId)) {\n videoArtifacts.push({ path: matched, testId });\n }\n }\n }\n\n // When only one video exists for the entire suite and some flows were\n // not matched, assign that single video to ALL unmatched flows. This\n // covers the common case of a single startRecording in the first flow\n // that captures the whole suite session.\n if (videoPathsToUpload.length === 1) {\n const singleVideo = videoPathsToUpload[0];\n for (const flow of flowResults) {\n const testId = `${flow.flowFile}::${flow.flowName}`;\n if (!videoArtifacts.some((va) => va.testId === testId)) {\n videoArtifacts.push({ path: singleVideo, testId });\n }\n }\n }\n }\n\n // Videos not matched to any flow are passed via the legacy `videoPaths`\n // param so they still appear under the suite-level umbrella.\n const matchedPaths = new Set(videoArtifacts.map((va) => va.path));\n const unmatchedVideos = videoPathsToUpload.filter((v) => !matchedPaths.has(v));\n\n // Convert each flow result to the TestResultForUpload shape so the server\n // stores them as individual test cases (visible in the dashboard test list).\n const testsForUpload: TestResultForUpload[] = flowResults.map(flowToTestResult);\n\n await finalizeAndUpload(\n cloudClient,\n config.cloud,\n testRunId,\n report,\n completedAt,\n totalDuration,\n summary,\n config.includeScreenshots ? artifacts.screenshotPaths : [],\n unmatchedVideos,\n testsForUpload,\n videoArtifacts,\n );\n }\n\n return { report, flowResults, aiDefects: allAiDefects, artifacts };\n}\n","/**\n * Merge multiple Maestro report JSON files into a single report.\n */\n\nimport { readFileSync, readdirSync, writeFileSync, existsSync } from 'node:fs';\nimport { join, extname } from 'node:path';\nimport type { TestRunReport, TimelineEntry, TimelineStep } from '@testrelic/core';\nimport { buildSummary } from './summary-builder.js';\n\nfunction isTimelineEntry(item: TimelineEntry | TimelineStep): item is TimelineEntry {\n return 'tests' in item && 'visitedAt' in item;\n}\n\nexport function mergeReports(reportPaths: string[]): TestRunReport {\n const allTimeline: (TimelineEntry | TimelineStep)[] = [];\n const timelineEntries: TimelineEntry[] = [];\n let earliestStart = '';\n let latestEnd = '';\n let runId = '';\n\n for (const reportPath of reportPaths) {\n try {\n const content = readFileSync(reportPath, 'utf-8');\n const report = JSON.parse(content) as TestRunReport;\n\n if (!runId) runId = report.testRunId;\n if (!earliestStart || report.startedAt < earliestStart) earliestStart = report.startedAt;\n if (!latestEnd || (report.completedAt && report.completedAt > latestEnd)) latestEnd = report.completedAt;\n\n for (const item of report.timeline) {\n allTimeline.push(item);\n if (isTimelineEntry(item)) timelineEntries.push(item);\n }\n } catch {\n process.stderr.write(`\\u26A0 TestRelic: Unable to read report file: ${reportPath}\\n`);\n }\n }\n\n const summary = buildSummary(timelineEntries);\n const startMs = earliestStart ? new Date(earliestStart).getTime() : Date.now();\n const endMs = latestEnd ? new Date(latestEnd).getTime() : Date.now();\n\n return {\n schemaVersion: '1.0.0',\n testRunId: runId || `maestro-merged-${Date.now()}`,\n startedAt: earliestStart || new Date().toISOString(),\n completedAt: latestEnd || new Date().toISOString(),\n totalDuration: endMs - startMs,\n summary,\n ci: null,\n metadata: null,\n timeline: allTimeline,\n shardRunIds: null,\n };\n}\n\nexport function mergeReportsFromDirectory(dirPath: string, outputPath: string): TestRunReport {\n if (!existsSync(dirPath)) {\n throw new Error(`Directory does not exist: ${dirPath}`);\n }\n\n const reportPaths = readdirSync(dirPath)\n .filter((f) => extname(f) === '.json' && f.includes('testrelic'))\n .map((f) => join(dirPath, f));\n\n const merged = mergeReports(reportPaths);\n writeFileSync(outputPath, JSON.stringify(merged, null, 2), 'utf-8');\n return merged;\n}\n"]}
1
+ {"version":3,"sources":["../src/cloud-config.ts","../src/config.ts","../src/parsers/junit-parser.ts","../src/parsers/command-parser.ts","../src/parsers/flow-parser.ts","../src/parsers/log-parser.ts","../src/parsers/ai-report-parser.ts","../src/artifact-collector.ts","../src/timeline-builder.ts","../src/summary-builder.ts","../src/html-css.ts","../src/html-logo.ts","../src/html-js-render.ts","../src/html-js-drawer.ts","../src/html-js-steps.ts","../src/html-js-assertions.ts","../src/html-js-ai-defects.ts","../src/html-js-interactions.ts","../src/html-template.ts","../src/html-report.ts","../src/console-summary.ts","../src/browser-open.ts","../src/maestro-runner.ts","../src/cloud-auth.ts","../src/git-metadata.ts","../src/cloud-queue.ts","../src/cloud-client.ts","../src/cloud-upload.ts","../src/cloud-artifact-upload.ts","../src/ci-detector.ts","../src/cloud-reporter.ts","../src/report-orchestrator.ts","../src/merge.ts"],"names":["CONFIG_DIR","CONFIG_FILENAME","LEGACY_CONFIG_FILENAME","MAX_WALK_UP_LEVELS","DANGEROUS_KEYS","DEFAULT_CLOUD_CONFIG","DURATION_UNITS","discoverConfigFile","startDir","currentDir","resolve","i","candidate","join","existsSync","statSync","legacyCandidate","parentDir","dirname","parseConfigFile","filePath","content","readFileSync","parsed","isSafeObject","obj","key","val","resolveEnvVar","value","bracketMatch","dollarMatch","resolveEnvVars","result","parseDuration","match","amount","unit","multiplier","mergeCloudConfig","fileConfig","reporterOptions","target","cloud","repoSection","queue","ms","resolved","envApiKey","envEndpoint","isValidEndpointUrl","envUpload","envTimeout","config","isValidCloudConfig","hasPrototypePollution","isValidMaestroConfig","input","resolveConfig","options","createError","ErrorCode","outputPath","resolveCloudFromMerge","configPath","resolvedFile","merged","parser","XMLParser","toArray","parseProperties","raw","p","parseTestCase","name","classname","time","id","rawStatus","failure","error","skipped","status","parseTestSuite","device","tests","failures","errors","testCases","parseJUnitXml","xmlContent","suites","container","totalTests","totalFailures","totalErrors","totalSkipped","totalTime","suite","parseJUnitFile","INTERACTION_COMMANDS","ASSERTION_COMMANDS","NAVIGATION_COMMANDS","DEVICE_COMMANDS","MEDIA_COMMANDS","SCRIPT_COMMANDS","FLOW_CONTROL_COMMANDS","AI_COMMANDS","categorizeCommand","command","MAESTRO_COMMAND_NAME_MAP","normalizeMaestroCommandName","withoutSuffix","extractMaestroSelector","params","sel","cond","inner","parseRawCommand","index","baseTimestamp","selector","commandObj","rawKey","meta","mapStatus","duration","timestamp","lower","parseCommandsJson","jsonContent","base","entry","commands","parseCommandsFile","discoverCommandFiles","artifactsDir","readdirSync","f","basename","extractSubflowRefs","body","refs","item","flow","extractHookRefs","hooks","items","parseFlowYaml","parts","headerContent","bodyContent","header","parseDocument","bodyCommands","appId","tags","t","env","k","v","properties","onFlowStart","onFlowComplete","subflowRefs","parseFlowFile","discoverFlowFiles","dir","results","walk","entries","fullPath","ext","extname","LOG_LINE_REGEX","TIMESTAMP_ONLY_REGEX","parseLevel","upper","parseLogContent","lines","today","line","trimmed","last","parseLogFile","detectPlatformFromLogs","msg","discoverLogFiles","DEFECT_SECTION_REGEX","SEVERITY_CRITICAL_REGEX","SEVERITY_WARNING_REGEX","UI_DEFECT_REGEX","SPELLING_REGEX","I18N_REGEX","LAYOUT_REGEX","A11Y_REGEX","classifyDefectType","text","classifySeverity","stripHtmlTags","html","extractDefectsFromHtml","defects","plainText","listItemRegex","itemHtml","itemText","paragraphRegex","pText","sentences","s","sentence","parseAiReportHtml","htmlContent","parseAiReportFile","discoverAiReports","IMAGE_EXTENSIONS","VIDEO_EXTENSIONS","walkDir","recurse","current","collectArtifacts","testOutputDir","debugOutputDir","screenshotPaths","videoPaths","logPaths","commandJsonPaths","aiReportPaths","junitReportPath","allFiles","seen","getArtifactStats","artifacts","mapCommandCategory","category","commandToAction","cmd","buildTestResult","actions","a","b","buildTimelineEntry","testResult","buildTimeline","flows","EMPTY_STATUS_RANGE","buildSummary","timeline","total","passed","failed","flaky","timedout","totalAssertions","passedAssertions","failedAssertions","totalNavigations","totalActionSteps","actionCategoryCounts","uniqueUrls","test","action","cat","CSS","LOGO_SVG_RAW","LOGO_SVG","JS_RENDER","JS_DRAWER","JS_STEPS","JS_ASSERTIONS","JS_AI_DEFECTS","JS_INTERACTIONS","JS","renderHtmlDocument","reportJson","safeJson","generateHtmlReport","report","aiDefects","htmlPath","htmlDir","mkdirSync","writeFileSync","pad","padding","printConsoleSummary","summary","htmlReportPath","quiet","top","bottom","blank","output","catParts","catMap","catStr","critical","d","warning","info","defectParts","openInBrowser","platform","exec","generateTempDir","randomBytes","tmpdir","buildMaestroArgs","tempDir","args","junitPath","runMaestro","resolvePromise","stdoutChunks","stderrChunks","proc","spawn","chunk","code","err","LOCALHOST_HOSTS","enforceHttps","endpoint","url","HEALTH_CHECK_TIMEOUT_MS","healthCheck","controller","timer","sleep","parseCloudError","response","exchangeToken","apiKey","timeout","retryAfter","waitMs","retryResponse","isAuthError","refreshAccessToken","currentRefreshToken","resolveRepo","accessToken","gitId","displayName","branch","requestBody","responseBody","GIT_TIMEOUT_MS","execGit","cwd","execSync","collectGitMetadata","commitSha","commitMessage","commitAuthor","rawRemoteUrl","getRemoteUrl","remoteUrl","normalizeGitRemoteUrl","originUrl","remotes","firstRemote","normalized","deriveRepoDisplayName","normalizedUrl","segments","readPackageJsonName","dirPath","pkg","deriveNonGitProjectId","pkgName","QUEUE_ENTRY_VERSION","FLUSH_BATCH_SIZE","writeToQueue","queueDirectory","runId","type","reason","targetEndpoint","method","payload","headers","filename","tmpPath","renameSync","flushQueue","files","batches","batch","file","isValidQueueEntry","unlinkSync","queueEntry","cleanupExpiredQueue","maxAge","now","queuedAt","queuedTime","TOKEN_REFRESH_BUFFER_MS","PROJECT_CACHE_TTL_MS","HEALTH_CHECK_INTERVAL_MS","FLUSH_TIMEOUT_MS","DASHBOARD_SETTINGS_URL","CloudClient","cloudConfig","tokenResult","expiresAt","refreshResult","statusCode","effectiveGitId","cached","cachePath","cache","currentKeyHash","repoId","cacheDir","createHash","queueDir","GZIP_THRESHOLD_BYTES","RETRY_DELAYS_MS","MAX_RETRIES","buildUploadPayload","repoGitId","git","ci","environment","retryWithBackoff","init","onTokenRefresh","attempt","newToken","refreshedInit","prepareBody","json","compressed","gzipSync","uploadBatchRun","stripNulls","initRealtimeRun","finalizeRun","cloudRunId","UPLOAD_CONCURRENCY","CONTENT_TYPE_MAP","activeUploads","pendingUploads","acquireSlot","releaseSlot","getContentType","getFileSize","requestUploadUrl","request","sizeBytes","contentType","putFileToPresignedUrl","presignedUrl","nodeStream","createReadStream","webStream","Readable","confirmUpload","artifactId","uploadArtifact","maxSizeMb","urlResult","confirmed","uploadArtifacts","requests","promises","req","detectGitHubActions","detectGitLabCI","detectJenkins","detectCircleCI","detectBitbucketPipelines","runUrl","detectors","detectCI","envVars","detect","flattenTimelineForCloud","flat","timelineEntry","getEffectiveGitId","cloudClient","finalizeAndUpload","testRunId","completedAt","totalDuration","videoArtifacts","perTestVideoCount","hasArtifacts","token","path","perTestPaths","va","resultEntries","uploaded","r","videoCount","perFlowVideoCount","ssCount","perFlowNote","label","batchGit","batchCi","batchGitId","flatTimeline","flatReport","queueGit","queueCi","queueGitId","queuePayload","matchVideoToFlow","flowFile","flowRecordingPath","flowBase","exact","forward","reverse","videoBase","recBase","byRec","byRecPartial","matchScreenshotsToFlow","matched","nameBase","extractRecordingPath","buildFlowRecordingMap","commandSteps","map","recPath","commandsForFlow","stripCmdPrefix","cmdFile","cmds","stripped","platformToOs","flowToTestResult","consoleLogs","logEntriesForFlow","allEntries","flowStartedAt","flowCompletedAt","startMs","endMs","orchestrateReport","startedAt","randomUUID","logFiles","allLogEntries","logFile","detected","flowMetadataMap","flowFiles","commandFiles","allAiDefects","aiReportFiles","aiFile","aiReport","flowRecordingMap","flowResults","junit","allCommands","allAssertions","c","allNonAssertions","testCase","durationMs","recordingPath","flowCmds","matchedAnyFile","perFlowCommands","perFlowAssertions","videoPathsToUpload","testId","singleVideo","matchedPaths","unmatchedVideos","testsForUpload","isTimelineEntry","mergeReports","reportPaths","allTimeline","timelineEntries","earliestStart","latestEnd","reportPath","mergeReportsFromDirectory"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACHZ,CAAA,CAAA,IAAMC,EAAAA,CAAe,CAAA;AAAA;AAAA;AAAA;AAAA,8BAAA,CAAA,CAMfC,EAAAA,CAAW,CAAA;AAAA;AAAA;AAAA;ACNjB,8BAAA,CAAA,CAAA,IAAMC,EAAAA,CAAY;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACAlB,CAAA,CAAA,IAAMC,EAAAA,CAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;ACAlB,CAAA,CAAA,IAAMC,EAAAA,CAAW;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;ACDjB,CAAA,CAAA,IAAMC,EAAAA,CAAgB;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACCtB,CAAA,CAAA,IAAMC,EAAAA,CAAgB;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAtB,CAAA,CAAA,IAAMC,EAAAA,CAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;ACa/B,CAAA,CAAA,IAAMC,EAAAA,CAAK;AAAA;AAAA;AAAA;AAAA;AAAA;;AAA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","file":"index.js","sourcesContent":["/**\n * Cloud configuration discovery, parsing, and merging.\n *\n * Discovers `.testrelic/testrelic-config.json` config files, resolves environment variable\n * references, and merges configuration from multiple sources.\n *\n * Priority order (highest wins):\n * 1. Environment variables (TESTRELIC_API_KEY, TESTRELIC_CLOUD_ENDPOINT, etc.)\n * 2. CLI options\n * 3. .testrelic/testrelic-config.json config file\n * 4. Built-in defaults\n */\n\nimport { readFileSync, existsSync, statSync } from 'node:fs';\nimport { join, dirname, resolve } from 'node:path';\nimport type { CloudConfig, UploadStrategy, CloudReporterOptions } from '@testrelic/core';\nimport { isValidCloudConfig, isValidEndpointUrl } from '@testrelic/core';\n\nconst CONFIG_DIR = '.testrelic';\nconst CONFIG_FILENAME = 'testrelic-config.json';\nconst LEGACY_CONFIG_FILENAME = '.testrelic';\nconst MAX_WALK_UP_LEVELS = 5;\n\nconst DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n\nconst DEFAULT_CLOUD_CONFIG: CloudConfig = Object.freeze({\n apiKey: null,\n endpoint: 'https://platform.testrelic.ai/api/v1',\n uploadStrategy: 'batch',\n timeout: 30000,\n projectName: null,\n queueMaxAge: 604800000,\n queueDirectory: `${CONFIG_DIR}/queue`,\n uploadArtifacts: true,\n artifactMaxSizeMb: 50,\n});\n\nconst DURATION_UNITS: Record<string, number> = {\n s: 1000,\n m: 60 * 1000,\n h: 60 * 60 * 1000,\n d: 24 * 60 * 60 * 1000,\n};\n\nexport function discoverConfigFile(startDir: string): string | null {\n let currentDir = resolve(startDir);\n for (let i = 0; i <= MAX_WALK_UP_LEVELS; i++) {\n const candidate = join(currentDir, CONFIG_DIR, CONFIG_FILENAME);\n if (existsSync(candidate)) {\n try {\n if (statSync(candidate).isFile()) return candidate;\n } catch {\n // ignore\n }\n }\n\n const legacyCandidate = join(currentDir, LEGACY_CONFIG_FILENAME);\n if (existsSync(legacyCandidate)) {\n try {\n if (statSync(legacyCandidate).isFile()) {\n process.stderr.write(\n '[testrelic] Deprecation: config file \".testrelic\" has moved to \".testrelic/testrelic-config.json\". ' +\n 'Please migrate your config file to the new location.\\n',\n );\n return legacyCandidate;\n }\n } catch {\n // ignore\n }\n }\n\n const parentDir = dirname(currentDir);\n if (parentDir === currentDir) break;\n currentDir = parentDir;\n }\n return null;\n}\n\nexport function parseConfigFile(filePath: string): Record<string, unknown> | null {\n try {\n const content = readFileSync(filePath, 'utf-8');\n const parsed: unknown = JSON.parse(content);\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return null;\n }\n if (!isSafeObject(parsed as Record<string, unknown>)) {\n return null;\n }\n return parsed as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\nfunction isSafeObject(obj: Record<string, unknown>): boolean {\n for (const key of Object.keys(obj)) {\n if (DANGEROUS_KEYS.has(key)) return false;\n const val = obj[key];\n if (typeof val === 'object' && val !== null && !Array.isArray(val)) {\n if (!isSafeObject(val as Record<string, unknown>)) return false;\n }\n }\n return true;\n}\n\nexport function resolveEnvVar(value: string): string | null {\n const bracketMatch = /^\\$\\{([A-Za-z_][A-Za-z0-9_]*)\\}$/.exec(value);\n if (bracketMatch) {\n return process.env[bracketMatch[1]] ?? null;\n }\n const dollarMatch = /^\\$([A-Za-z_][A-Za-z0-9_]*)$/.exec(value);\n if (dollarMatch) {\n return process.env[dollarMatch[1]] ?? null;\n }\n return value;\n}\n\nexport function resolveEnvVars(obj: Record<string, unknown>): Record<string, unknown> {\n const result = Object.create(null) as Record<string, unknown>;\n for (const key of Object.keys(obj)) {\n const val = obj[key];\n if (typeof val === 'string' && val.startsWith('$')) {\n result[key] = resolveEnvVar(val);\n } else if (typeof val === 'object' && val !== null && !Array.isArray(val)) {\n result[key] = resolveEnvVars(val as Record<string, unknown>);\n } else {\n result[key] = val;\n }\n }\n return result;\n}\n\nexport function parseDuration(value: string): number | null {\n const match = /^(\\d+)\\s*([smhd])$/.exec(value.trim());\n if (!match) return null;\n const amount = parseInt(match[1], 10);\n const unit = match[2];\n const multiplier = DURATION_UNITS[unit];\n if (!multiplier || amount <= 0) return null;\n return amount * multiplier;\n}\n\nexport function mergeCloudConfig(\n fileConfig: Record<string, unknown> | null,\n reporterOptions: CloudReporterOptions | undefined,\n): CloudConfig {\n const target = Object.create(null) as Record<string, unknown>;\n\n Object.assign(target, DEFAULT_CLOUD_CONFIG);\n\n if (fileConfig) {\n const cloud = fileConfig.cloud as Record<string, unknown> | undefined;\n if (cloud && typeof cloud === 'object') {\n if (typeof cloud.endpoint === 'string') target.endpoint = cloud.endpoint;\n if (typeof cloud.upload === 'string') target.uploadStrategy = cloud.upload;\n if (typeof cloud.timeout === 'number') target.timeout = cloud.timeout;\n if (typeof cloud.apiKey === 'string' && cloud.apiKey.length > 0) {\n target.apiKey = cloud.apiKey;\n }\n }\n const repoSection = (fileConfig['testrelic-repo'] ?? fileConfig.project) as Record<string, unknown> | undefined;\n if (repoSection && typeof repoSection === 'object') {\n if (typeof repoSection.name === 'string') target.projectName = repoSection.name;\n }\n const queue = fileConfig.queue as Record<string, unknown> | undefined;\n if (queue && typeof queue === 'object') {\n if (typeof queue.maxAge === 'string') {\n const ms = parseDuration(queue.maxAge as string);\n if (ms !== null) target.queueMaxAge = ms;\n }\n if (typeof queue.directory === 'string') target.queueDirectory = queue.directory;\n }\n }\n\n if (reporterOptions) {\n if (typeof reporterOptions.apiKey === 'string' && reporterOptions.apiKey.length > 0) {\n const resolved = reporterOptions.apiKey.startsWith('$')\n ? resolveEnvVar(reporterOptions.apiKey)\n : reporterOptions.apiKey;\n if (resolved) target.apiKey = resolved;\n }\n if (typeof reporterOptions.endpoint === 'string') {\n const resolved = reporterOptions.endpoint.startsWith('$')\n ? resolveEnvVar(reporterOptions.endpoint)\n : reporterOptions.endpoint;\n if (resolved) target.endpoint = resolved;\n }\n if (typeof reporterOptions.upload === 'string') target.uploadStrategy = reporterOptions.upload;\n if (typeof reporterOptions.timeout === 'number') target.timeout = reporterOptions.timeout;\n if (typeof reporterOptions.projectName === 'string') target.projectName = reporterOptions.projectName;\n if (typeof reporterOptions.queueMaxAge === 'string') {\n const ms = parseDuration(reporterOptions.queueMaxAge);\n if (ms !== null) target.queueMaxAge = ms;\n }\n if (typeof reporterOptions.queueDirectory === 'string') target.queueDirectory = reporterOptions.queueDirectory;\n if (typeof reporterOptions.uploadArtifacts === 'boolean') target.uploadArtifacts = reporterOptions.uploadArtifacts;\n if (typeof reporterOptions.artifactMaxSizeMb === 'number') target.artifactMaxSizeMb = reporterOptions.artifactMaxSizeMb;\n }\n\n const envApiKey = process.env.TESTRELIC_API_KEY;\n if (envApiKey && envApiKey.length > 0) {\n target.apiKey = envApiKey;\n }\n\n const envEndpoint = process.env.TESTRELIC_CLOUD_ENDPOINT;\n if (envEndpoint && isValidEndpointUrl(envEndpoint)) {\n target.endpoint = envEndpoint;\n }\n\n const envUpload = process.env.TESTRELIC_UPLOAD_STRATEGY;\n if (envUpload && ['batch', 'realtime', 'both'].includes(envUpload)) {\n target.uploadStrategy = envUpload as UploadStrategy;\n }\n\n const envTimeout = process.env.TESTRELIC_CLOUD_TIMEOUT;\n if (envTimeout) {\n const parsed = parseInt(envTimeout, 10);\n if (!isNaN(parsed) && parsed >= 1000 && parsed <= 120000) {\n target.timeout = parsed;\n }\n }\n\n const config = Object.freeze(target) as unknown as CloudConfig;\n\n if (!isValidCloudConfig(config)) {\n return DEFAULT_CLOUD_CONFIG;\n }\n\n return config;\n}\n\nexport { DEFAULT_CLOUD_CONFIG };\n","/**\n * Config resolution with prototype pollution prevention.\n * Merges user options into defaults and freezes the result.\n */\n\nimport type { CloudConfig, CloudReporterOptions } from '@testrelic/core';\nimport { createError, ErrorCode } from '@testrelic/core';\nimport type { MaestroReporterConfig, ResolvedMaestroConfig } from './types.js';\nimport { discoverConfigFile, parseConfigFile, resolveEnvVars, mergeCloudConfig } from './cloud-config.js';\n\nconst DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);\n\nfunction hasPrototypePollution(obj: unknown): boolean {\n if (typeof obj !== 'object' || obj === null) return false;\n for (const key of Object.keys(obj)) {\n if (DANGEROUS_KEYS.has(key)) return true;\n }\n return false;\n}\n\nfunction isValidMaestroConfig(input: unknown): input is MaestroReporterConfig {\n if (typeof input !== 'object' || input === null) return false;\n if (hasPrototypePollution(input)) return false;\n\n const obj = input as Record<string, unknown>;\n if (obj.outputPath !== undefined && typeof obj.outputPath !== 'string') return false;\n if (obj.htmlReportPath !== undefined && typeof obj.htmlReportPath !== 'string') return false;\n if (obj.openReport !== undefined && typeof obj.openReport !== 'boolean') return false;\n if (obj.includeScreenshots !== undefined && typeof obj.includeScreenshots !== 'boolean') return false;\n if (obj.includeVideo !== undefined && typeof obj.includeVideo !== 'boolean') return false;\n if (obj.includeAiAnalysis !== undefined && typeof obj.includeAiAnalysis !== 'boolean') return false;\n if (obj.includeLogs !== undefined && typeof obj.includeLogs !== 'boolean') return false;\n if (obj.includeFlowMetadata !== undefined && typeof obj.includeFlowMetadata !== 'boolean') return false;\n if (obj.quiet !== undefined && typeof obj.quiet !== 'boolean') return false;\n\n if (obj.metadata !== undefined && obj.metadata !== null) {\n if (typeof obj.metadata !== 'object') return false;\n if (hasPrototypePollution(obj.metadata)) return false;\n }\n\n return true;\n}\n\nexport function resolveConfig(options?: Partial<MaestroReporterConfig>): ResolvedMaestroConfig {\n if (options !== undefined && !isValidMaestroConfig(options)) {\n throw createError(ErrorCode.CONFIG_INVALID, 'Invalid Maestro reporter configuration');\n }\n\n const target = Object.create(null) as Record<string, unknown>;\n const outputPath = options?.outputPath ?? './test-results/testrelic-maestro.json';\n\n target.outputPath = outputPath;\n target.htmlReportPath = options?.htmlReportPath ?? outputPath.replace(/\\.json$/, '.html');\n target.openReport = options?.openReport ?? true;\n target.includeScreenshots = options?.includeScreenshots ?? true;\n target.includeVideo = options?.includeVideo ?? true;\n target.includeAiAnalysis = options?.includeAiAnalysis ?? true;\n target.includeLogs = options?.includeLogs ?? true;\n target.includeFlowMetadata = options?.includeFlowMetadata ?? true;\n target.flowsDir = options?.flowsDir ?? null;\n target.testRunId = options?.testRunId ?? null;\n target.metadata = options?.metadata ?? null;\n target.quiet = options?.quiet ?? false;\n target.reportMode = options?.reportMode ?? 'batch';\n\n target.cloud = resolveCloudFromMerge(options?.cloud ?? null);\n\n return Object.freeze(target) as unknown as ResolvedMaestroConfig;\n}\n\nfunction resolveCloudFromMerge(\n reporterOptions: CloudReporterOptions | null | undefined,\n): CloudConfig | null {\n const configPath = discoverConfigFile(process.cwd());\n const fileConfig = configPath ? parseConfigFile(configPath) : null;\n const resolvedFile = fileConfig ? resolveEnvVars(fileConfig) : null;\n const merged = mergeCloudConfig(\n resolvedFile as Record<string, unknown> | null,\n reporterOptions ?? undefined,\n );\n return merged.apiKey ? merged : null;\n}\n","/**\n * JUnit XML parser for Maestro test reports.\n *\n * Parses the JUnit XML generated by `maestro test --format junit`\n * into structured JUnitReport objects.\n */\n\nimport { XMLParser } from 'fast-xml-parser';\nimport { readFileSync } from 'node:fs';\nimport type { JUnitReport, JUnitTestSuite, JUnitTestCase, JUnitProperty } from '../types.js';\n\nconst parser = new XMLParser({\n ignoreAttributes: false,\n attributeNamePrefix: '@_',\n allowBooleanAttributes: true,\n parseAttributeValue: true,\n trimValues: true,\n});\n\nfunction toArray<T>(value: T | T[] | undefined | null): T[] {\n if (value === undefined || value === null) return [];\n return Array.isArray(value) ? value : [value];\n}\n\nfunction parseProperties(raw: unknown): JUnitProperty[] {\n if (!raw || typeof raw !== 'object') return [];\n const container = raw as Record<string, unknown>;\n const props = toArray(container.property as Record<string, string> | Record<string, string>[]);\n return props.map((p) => ({\n name: String(p['@_name'] ?? ''),\n value: String(p['@_value'] ?? ''),\n }));\n}\n\nfunction parseTestCase(raw: Record<string, unknown>): JUnitTestCase {\n const name = String(raw['@_name'] ?? 'unnamed');\n const classname = String(raw['@_classname'] ?? name);\n const time = Number(raw['@_time'] ?? 0);\n const id = raw['@_id'] != null ? String(raw['@_id']) : null;\n const rawStatus = String(raw['@_status'] ?? '').toUpperCase();\n\n const failure = raw.failure as Record<string, unknown> | undefined;\n const error = raw.error as Record<string, unknown> | undefined;\n const skipped = raw.skipped !== undefined;\n\n let status: JUnitTestCase['status'] = 'SUCCESS';\n if (failure) status = 'FAILURE';\n else if (error) status = 'ERROR';\n else if (skipped) status = 'SKIPPED';\n else if (rawStatus === 'FAILURE' || rawStatus === 'FAILED') status = 'FAILURE';\n else if (rawStatus === 'ERROR') status = 'ERROR';\n else if (rawStatus === 'SKIPPED') status = 'SKIPPED';\n\n return {\n id,\n name,\n classname,\n time,\n status,\n failureMessage: failure ? String(failure['@_message'] ?? failure['#text'] ?? '') : null,\n failureType: failure ? String(failure['@_type'] ?? '') : null,\n errorMessage: error ? String(error['@_message'] ?? error['#text'] ?? '') : null,\n errorType: error ? String(error['@_type'] ?? '') : null,\n properties: parseProperties(raw.properties),\n };\n}\n\nfunction parseTestSuite(raw: Record<string, unknown>): JUnitTestSuite {\n const name = String(raw['@_name'] ?? 'Test Suite');\n const device = raw['@_device'] != null ? String(raw['@_device']) : null;\n const tests = Number(raw['@_tests'] ?? 0);\n const failures = Number(raw['@_failures'] ?? 0);\n const errors = Number(raw['@_errors'] ?? 0);\n const skipped = Number(raw['@_skipped'] ?? 0);\n const time = Number(raw['@_time'] ?? 0);\n\n const rawCases = toArray(raw.testcase as Record<string, unknown> | Record<string, unknown>[]);\n const testCases = rawCases.map(parseTestCase);\n\n return {\n name,\n device,\n tests,\n failures,\n errors,\n skipped,\n time,\n testCases,\n properties: parseProperties(raw.properties),\n };\n}\n\nexport function parseJUnitXml(xmlContent: string): JUnitReport {\n const parsed = parser.parse(xmlContent) as Record<string, unknown>;\n\n let suites: JUnitTestSuite[] = [];\n\n if (parsed.testsuites) {\n const container = parsed.testsuites as Record<string, unknown>;\n const rawSuites = toArray(container.testsuite as Record<string, unknown> | Record<string, unknown>[]);\n suites = rawSuites.map(parseTestSuite);\n } else if (parsed.testsuite) {\n const rawSuites = toArray(parsed.testsuite as Record<string, unknown> | Record<string, unknown>[]);\n suites = rawSuites.map(parseTestSuite);\n }\n\n let totalTests = 0;\n let totalFailures = 0;\n let totalErrors = 0;\n let totalSkipped = 0;\n let totalTime = 0;\n\n for (const suite of suites) {\n totalTests += suite.tests;\n totalFailures += suite.failures;\n totalErrors += suite.errors;\n totalSkipped += suite.skipped;\n totalTime += suite.time;\n }\n\n return { testSuites: suites, totalTests, totalFailures, totalErrors, totalSkipped, totalTime };\n}\n\nexport function parseJUnitFile(filePath: string): JUnitReport {\n const content = readFileSync(filePath, 'utf-8');\n return parseJUnitXml(content);\n}\n","/**\n * Parser for Maestro commands-*.json files.\n *\n * These files are generated in the --test-output-dir and contain\n * per-command execution data for each flow.\n */\n\nimport { readFileSync, readdirSync, existsSync } from 'node:fs';\nimport { join, basename } from 'node:path';\nimport type { MaestroCommandStep, MaestroCommandCategory } from '../types.js';\n\nconst INTERACTION_COMMANDS = new Set([\n 'tapOn', 'doubleTapOn', 'longPressOn', 'inputText', 'eraseText',\n 'pasteText', 'swipe', 'scroll', 'scrollUntilVisible', 'hideKeyboard',\n 'pressKey', 'setClipboard', 'copyTextFrom',\n]);\n\nconst ASSERTION_COMMANDS = new Set([\n 'assertVisible', 'assertNotVisible', 'assertTrue', 'assertScreenshot',\n 'assertNoDefectsWithAI', 'assertWithAI',\n // Maestro internal command name used for assertVisible/assertNotVisible\n 'assertCondition',\n]);\n\nconst NAVIGATION_COMMANDS = new Set([\n 'launchApp', 'killApp', 'stopApp', 'clearState', 'openLink', 'back',\n 'clearKeychain',\n]);\n\nconst DEVICE_COMMANDS = new Set([\n 'setAirplaneMode', 'toggleAirplaneMode', 'setLocation', 'setOrientation',\n 'setPermissions', 'addMedia', 'travel',\n]);\n\nconst MEDIA_COMMANDS = new Set([\n 'takeScreenshot', 'startRecording', 'stopRecording',\n]);\n\nconst SCRIPT_COMMANDS = new Set([\n 'runScript', 'evalScript',\n]);\n\nconst FLOW_CONTROL_COMMANDS = new Set([\n 'runFlow', 'repeat', 'retry', 'waitForAnimationToEnd', 'extendedWaitUntil',\n]);\n\nconst AI_COMMANDS = new Set([\n 'assertWithAI', 'assertNoDefectsWithAI', 'extractTextWithAI',\n]);\n\nexport function categorizeCommand(command: string): MaestroCommandCategory {\n if (AI_COMMANDS.has(command)) return 'ai';\n if (ASSERTION_COMMANDS.has(command)) return 'assertion';\n if (INTERACTION_COMMANDS.has(command)) return 'interaction';\n if (NAVIGATION_COMMANDS.has(command)) return 'navigation';\n if (DEVICE_COMMANDS.has(command)) return 'device';\n if (MEDIA_COMMANDS.has(command)) return 'media';\n if (SCRIPT_COMMANDS.has(command)) return 'script';\n if (FLOW_CONTROL_COMMANDS.has(command)) return 'flow_control';\n return 'other';\n}\n\n/**\n * Maestro 2.x format: each entry has a nested \"command\" object whose single\n * key is the command type (e.g. \"launchAppCommand\") and a \"metadata\" object\n * with status, timestamp (Unix ms), and duration.\n */\ninterface MaestroCommandMetadata {\n status?: string;\n timestamp?: number;\n duration?: number;\n sequenceNumber?: number;\n}\n\ninterface RawCommandEntry {\n /** Either a plain string (legacy) or a nested {commandType: params} object (Maestro 2.x) */\n command?: string | Record<string, unknown>;\n commandName?: string;\n name?: string;\n status?: string;\n duration?: number;\n durationMs?: number;\n timestamp?: string;\n time?: string;\n error?: string;\n errorMessage?: string;\n selector?: string;\n metadata?: MaestroCommandMetadata;\n [key: string]: unknown;\n}\n\n/**\n * Maestro internal command key names → user-facing Maestro command names.\n * Only entries that differ after stripping the \"Command\" suffix are listed.\n */\nconst MAESTRO_COMMAND_NAME_MAP: Record<string, string> = {\n tapOnElement: 'tapOn',\n assertCondition: 'assertVisible',\n backPress: 'back',\n applyConfiguration: 'applyConfiguration',\n defineVariables: 'defineVariables',\n};\n\n/**\n * Normalise the raw key from Maestro's commands JSON.\n * e.g. \"launchAppCommand\" → \"launchApp\", \"tapOnElement\" → \"tapOn\"\n */\nfunction normalizeMaestroCommandName(key: string): string {\n const withoutSuffix = key.replace(/Command$/, '');\n return MAESTRO_COMMAND_NAME_MAP[withoutSuffix] ?? withoutSuffix;\n}\n\n/**\n * Extract a human-readable selector/target string from the nested Maestro\n * command params object (used to enrich the step title in the timeline).\n */\nfunction extractMaestroSelector(params: Record<string, unknown>): string | undefined {\n // tapOn: { selector: { textRegex, id, text, ... } }\n if (params.selector && typeof params.selector === 'object') {\n const sel = params.selector as Record<string, unknown>;\n if (typeof sel.textRegex === 'string') return sel.textRegex;\n if (typeof sel.text === 'string') return sel.text;\n if (typeof sel.id === 'string') return `#${sel.id}`;\n }\n // assertCondition: { condition: { visible: { textRegex, text, ... } } }\n if (params.condition && typeof params.condition === 'object') {\n const cond = params.condition as Record<string, unknown>;\n const inner = (cond.visible ?? cond.notVisible) as Record<string, unknown> | undefined;\n if (inner) {\n if (typeof inner.textRegex === 'string') return inner.textRegex;\n if (typeof inner.text === 'string') return inner.text;\n }\n }\n // screenshot/recording: { path: \"...\" }\n if (typeof params.path === 'string') return params.path;\n return undefined;\n}\n\nfunction parseRawCommand(raw: RawCommandEntry, index: number, baseTimestamp: string): MaestroCommandStep {\n let command: string;\n let selector: string | undefined;\n\n if (typeof raw.command === 'object' && raw.command !== null && !Array.isArray(raw.command)) {\n // Maestro 2.x nested format: { \"launchAppCommand\": { ...params } }\n const commandObj = raw.command as Record<string, unknown>;\n const rawKey = Object.keys(commandObj)[0] ?? `step-${index}`;\n command = normalizeMaestroCommandName(rawKey);\n const params = (commandObj[rawKey] as Record<string, unknown>) ?? {};\n selector = extractMaestroSelector(params);\n } else {\n command = (raw.command as string | undefined) ?? raw.commandName ?? raw.name ?? `step-${index}`;\n selector = raw.selector ?? undefined;\n }\n\n // Prefer Maestro's metadata block for status/duration/timestamp\n const meta = raw.metadata ?? {};\n const status = mapStatus((meta.status as string | undefined) ?? raw.status);\n const duration = (meta.duration as number | undefined) ?? raw.duration ?? raw.durationMs ?? 0;\n\n // Maestro stores timestamps as Unix-millisecond integers\n let timestamp: string;\n if (typeof meta.timestamp === 'number') {\n timestamp = new Date(meta.timestamp).toISOString();\n } else {\n timestamp = raw.timestamp ?? raw.time ?? baseTimestamp;\n }\n\n const error = raw.error ?? raw.errorMessage ?? undefined;\n\n return {\n command,\n category: categorizeCommand(command),\n status,\n duration,\n timestamp,\n ...(selector ? { selector } : {}),\n ...(error ? { error } : {}),\n };\n}\n\nfunction mapStatus(status?: string): MaestroCommandStep['status'] {\n if (!status) return 'completed';\n const lower = status.toLowerCase();\n if (lower === 'failed' || lower === 'error') return 'failed';\n if (lower === 'skipped') return 'skipped';\n return 'completed';\n}\n\nexport function parseCommandsJson(jsonContent: string, baseTimestamp?: string): MaestroCommandStep[] {\n const base = baseTimestamp ?? new Date().toISOString();\n try {\n const parsed: unknown = JSON.parse(jsonContent);\n if (Array.isArray(parsed)) {\n return (parsed as RawCommandEntry[]).map((entry, i) => parseRawCommand(entry, i, base));\n }\n if (typeof parsed === 'object' && parsed !== null) {\n const obj = parsed as Record<string, unknown>;\n const commands = obj.commands ?? obj.steps ?? obj.data;\n if (Array.isArray(commands)) {\n return (commands as RawCommandEntry[]).map((entry, i) => parseRawCommand(entry, i, base));\n }\n }\n return [];\n } catch {\n return [];\n }\n}\n\nexport function parseCommandsFile(filePath: string, baseTimestamp?: string): MaestroCommandStep[] {\n try {\n const content = readFileSync(filePath, 'utf-8');\n return parseCommandsJson(content, baseTimestamp);\n } catch {\n return [];\n }\n}\n\nexport function discoverCommandFiles(artifactsDir: string): string[] {\n if (!existsSync(artifactsDir)) return [];\n try {\n return readdirSync(artifactsDir, { recursive: true })\n .map(String)\n .filter((f) => basename(f).startsWith('commands') && f.endsWith('.json'))\n .map((f) => join(artifactsDir, f));\n } catch {\n return [];\n }\n}\n","/**\n * Flow YAML header parser.\n *\n * Extracts metadata from Maestro Flow YAML files:\n * appId, name, tags, properties, env, hooks, and subflow references.\n */\n\nimport { readFileSync, readdirSync, existsSync } from 'node:fs';\nimport { join, extname } from 'node:path';\nimport { parseDocument } from 'yaml';\nimport type { MaestroFlowMetadata } from '../types.js';\n\nfunction extractSubflowRefs(body: unknown[]): string[] {\n const refs: string[] = [];\n for (const item of body) {\n if (typeof item === 'object' && item !== null) {\n const entry = item as Record<string, unknown>;\n if (typeof entry.runFlow === 'string') {\n refs.push(entry.runFlow);\n } else if (typeof entry.runFlow === 'object' && entry.runFlow !== null) {\n const flow = entry.runFlow as Record<string, unknown>;\n if (typeof flow.file === 'string') refs.push(flow.file);\n }\n }\n }\n return refs;\n}\n\nfunction extractHookRefs(hooks: unknown): string[] {\n if (!hooks) return [];\n const refs: string[] = [];\n const items = Array.isArray(hooks) ? hooks : [hooks];\n for (const item of items) {\n if (typeof item === 'string') {\n refs.push(item);\n } else if (typeof item === 'object' && item !== null) {\n const entry = item as Record<string, unknown>;\n if (typeof entry.runFlow === 'string') refs.push(entry.runFlow);\n else if (typeof entry.runFlow === 'object' && entry.runFlow !== null) {\n const flow = entry.runFlow as Record<string, unknown>;\n if (typeof flow.file === 'string') refs.push(flow.file);\n }\n if (typeof entry.runScript === 'string') refs.push(entry.runScript);\n }\n }\n return refs;\n}\n\nexport function parseFlowYaml(content: string, filePath: string): MaestroFlowMetadata {\n const parts = content.split(/^---\\s*$/m, 2);\n const headerContent = parts[0] ?? '';\n const bodyContent = parts[1] ?? '';\n\n let header: Record<string, unknown> = {};\n try {\n const doc = parseDocument(headerContent);\n const parsed = doc.toJS();\n if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {\n header = parsed as Record<string, unknown>;\n }\n } catch {\n // Malformed YAML header — return empty metadata\n }\n\n let bodyCommands: unknown[] = [];\n if (bodyContent.trim()) {\n try {\n const bodyDoc = parseDocument(bodyContent);\n const parsed = bodyDoc.toJS();\n if (Array.isArray(parsed)) bodyCommands = parsed;\n } catch {\n // Malformed body — ignore\n }\n }\n\n const appId = typeof header.appId === 'string' ? header.appId : null;\n const name = typeof header.name === 'string' ? header.name : null;\n\n const tags: string[] = [];\n if (Array.isArray(header.tags)) {\n for (const t of header.tags) {\n if (typeof t === 'string') tags.push(t);\n }\n }\n\n const env: Record<string, string> = {};\n if (typeof header.env === 'object' && header.env !== null && !Array.isArray(header.env)) {\n for (const [k, v] of Object.entries(header.env as Record<string, unknown>)) {\n env[k] = String(v);\n }\n }\n\n const properties: Record<string, string> = {};\n if (typeof header.properties === 'object' && header.properties !== null && !Array.isArray(header.properties)) {\n for (const [k, v] of Object.entries(header.properties as Record<string, unknown>)) {\n properties[k] = String(v);\n }\n }\n\n const onFlowStart = extractHookRefs(header.onFlowStart);\n const onFlowComplete = extractHookRefs(header.onFlowComplete);\n const subflowRefs = extractSubflowRefs(bodyCommands);\n\n return {\n appId,\n name,\n tags,\n env,\n properties,\n onFlowStart,\n onFlowComplete,\n subflowRefs,\n filePath,\n };\n}\n\nexport function parseFlowFile(filePath: string): MaestroFlowMetadata {\n const content = readFileSync(filePath, 'utf-8');\n return parseFlowYaml(content, filePath);\n}\n\nexport function discoverFlowFiles(dir: string): string[] {\n if (!existsSync(dir)) return [];\n const results: string[] = [];\n\n function walk(currentDir: string): void {\n try {\n const entries = readdirSync(currentDir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = join(currentDir, entry.name);\n if (entry.isDirectory()) {\n walk(fullPath);\n } else if (entry.isFile()) {\n const ext = extname(entry.name).toLowerCase();\n if (ext === '.yaml' || ext === '.yml') {\n results.push(fullPath);\n }\n }\n }\n } catch {\n // Skip unreadable directories\n }\n }\n\n walk(dir);\n return results;\n}\n","/**\n * Parser for maestro.log files.\n *\n * Extracts structured log entries with timestamps, levels, and messages.\n * Also detects device/platform metadata from log content.\n */\n\nimport { readFileSync, existsSync } from 'node:fs';\nimport { join, basename } from 'node:path';\nimport { readdirSync } from 'node:fs';\nimport type { MaestroLogEntry, MaestroPlatform } from '../types.js';\n\nconst LOG_LINE_REGEX = /^(\\d{4}-\\d{2}-\\d{2}[T ]\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:?\\d{2})?)\\s+(\\w+)\\s+(.+)$/;\nconst TIMESTAMP_ONLY_REGEX = /^\\[?(\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?)\\]?\\s+(\\w+)\\s+(.+)$/;\n\nfunction parseLevel(raw: string): MaestroLogEntry['level'] {\n const upper = raw.toUpperCase();\n if (upper === 'DEBUG' || upper === 'TRACE' || upper === 'VERBOSE') return 'DEBUG';\n if (upper === 'INFO') return 'INFO';\n if (upper === 'WARN' || upper === 'WARNING') return 'WARN';\n if (upper === 'ERROR' || upper === 'FATAL' || upper === 'SEVERE') return 'ERROR';\n return 'INFO';\n}\n\nexport function parseLogContent(content: string): MaestroLogEntry[] {\n const entries: MaestroLogEntry[] = [];\n const lines = content.split('\\n');\n const today = new Date().toISOString().split('T')[0];\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n\n let match = LOG_LINE_REGEX.exec(trimmed);\n if (match) {\n entries.push({\n timestamp: match[1],\n level: parseLevel(match[2]),\n message: match[3],\n source: null,\n });\n continue;\n }\n\n match = TIMESTAMP_ONLY_REGEX.exec(trimmed);\n if (match) {\n entries.push({\n timestamp: `${today}T${match[1]}`,\n level: parseLevel(match[2]),\n message: match[3],\n source: null,\n });\n continue;\n }\n\n if (entries.length > 0) {\n const last = entries[entries.length - 1];\n entries[entries.length - 1] = {\n ...last,\n message: last.message + '\\n' + trimmed,\n };\n }\n }\n\n return entries;\n}\n\nexport function parseLogFile(filePath: string): MaestroLogEntry[] {\n try {\n const content = readFileSync(filePath, 'utf-8');\n return parseLogContent(content);\n } catch {\n return [];\n }\n}\n\nexport function detectPlatformFromLogs(entries: MaestroLogEntry[]): MaestroPlatform {\n for (const entry of entries) {\n const msg = entry.message.toLowerCase();\n if (msg.includes('android') || msg.includes('adb') || msg.includes('uiautomator') || msg.includes('emulator')) {\n return 'android';\n }\n if (msg.includes('ios') || msg.includes('xcuitest') || msg.includes('simulator') || msg.includes('xctest')) {\n return 'ios';\n }\n if (msg.includes('web driver') || msg.includes('chrome') || msg.includes('chromium')) {\n return 'web';\n }\n }\n return 'unknown';\n}\n\nexport function discoverLogFiles(dir: string): string[] {\n if (!existsSync(dir)) return [];\n try {\n return readdirSync(dir, { recursive: true })\n .map(String)\n .filter((f) => basename(f) === 'maestro.log' || f.endsWith('.log'))\n .map((f) => join(dir, f));\n } catch {\n return [];\n }\n}\n","/**\n * Parser for Maestro AI analysis reports.\n *\n * Maestro's --analyze flag generates HTML insight reports identifying\n * UI defects, spelling errors, and i18n issues. This parser extracts\n * structured defect data from those reports.\n */\n\nimport { readFileSync, existsSync, readdirSync } from 'node:fs';\nimport { join, basename, extname } from 'node:path';\nimport type { AIAnalysisReport, AIDefect, AIDefectSeverity } from '../types.js';\n\nconst DEFECT_SECTION_REGEX = /(?:defect|issue|bug|error|warning|problem)/i;\nconst SEVERITY_CRITICAL_REGEX = /(?:critical|severe|major|blocker)/i;\nconst SEVERITY_WARNING_REGEX = /(?:warning|moderate|minor)/i;\n\nconst UI_DEFECT_REGEX = /(?:cut[\\s-]?off|overlap|truncat|misalign|overflow|clip|obscur|hidden\\s+text|broken\\s+layout)/i;\nconst SPELLING_REGEX = /(?:spelling|typo|misspell|grammar)/i;\nconst I18N_REGEX = /(?:i18n|internationali[sz]ation|locali[sz]ation|untranslated|missing\\s+translation)/i;\nconst LAYOUT_REGEX = /(?:layout|spacing|padding|margin|alignment|centering|position)/i;\nconst A11Y_REGEX = /(?:accessib|a11y|contrast|screen\\s*reader|alt\\s*text|aria)/i;\n\nfunction classifyDefectType(text: string): AIDefect['type'] {\n if (SPELLING_REGEX.test(text)) return 'spelling';\n if (I18N_REGEX.test(text)) return 'i18n';\n if (A11Y_REGEX.test(text)) return 'accessibility';\n if (LAYOUT_REGEX.test(text)) return 'layout';\n return 'ui';\n}\n\nfunction classifySeverity(text: string): AIDefectSeverity {\n if (SEVERITY_CRITICAL_REGEX.test(text)) return 'critical';\n if (SEVERITY_WARNING_REGEX.test(text)) return 'warning';\n return 'info';\n}\n\nfunction stripHtmlTags(html: string): string {\n return html.replace(/<[^>]*>/g, ' ').replace(/\\s+/g, ' ').trim();\n}\n\nfunction extractDefectsFromHtml(html: string): AIDefect[] {\n const defects: AIDefect[] = [];\n const plainText = stripHtmlTags(html);\n\n const listItemRegex = /<li[^>]*>([\\s\\S]*?)<\\/li>/gi;\n let match;\n\n while ((match = listItemRegex.exec(html)) !== null) {\n const itemHtml = match[1];\n const itemText = stripHtmlTags(itemHtml);\n if (!itemText || itemText.length < 5) continue;\n\n if (DEFECT_SECTION_REGEX.test(itemText) || UI_DEFECT_REGEX.test(itemText) ||\n SPELLING_REGEX.test(itemText) || I18N_REGEX.test(itemText)) {\n defects.push({\n type: classifyDefectType(itemText),\n severity: classifySeverity(itemText),\n description: itemText,\n location: null,\n screenshot: null,\n });\n }\n }\n\n if (defects.length === 0) {\n const paragraphRegex = /<p[^>]*>([\\s\\S]*?)<\\/p>/gi;\n while ((match = paragraphRegex.exec(html)) !== null) {\n const pText = stripHtmlTags(match[1]);\n if (!pText || pText.length < 10) continue;\n\n if (UI_DEFECT_REGEX.test(pText) || SPELLING_REGEX.test(pText) ||\n I18N_REGEX.test(pText) || LAYOUT_REGEX.test(pText) || A11Y_REGEX.test(pText)) {\n defects.push({\n type: classifyDefectType(pText),\n severity: classifySeverity(pText),\n description: pText,\n location: null,\n screenshot: null,\n });\n }\n }\n }\n\n if (defects.length === 0 && plainText.length > 20) {\n const sentences = plainText.split(/[.!?]+/).filter((s) => s.trim().length > 10);\n for (const sentence of sentences) {\n const trimmed = sentence.trim();\n if (UI_DEFECT_REGEX.test(trimmed) || SPELLING_REGEX.test(trimmed) ||\n I18N_REGEX.test(trimmed) || LAYOUT_REGEX.test(trimmed) || A11Y_REGEX.test(trimmed)) {\n defects.push({\n type: classifyDefectType(trimmed),\n severity: classifySeverity(trimmed),\n description: trimmed,\n location: null,\n screenshot: null,\n });\n }\n }\n }\n\n return defects;\n}\n\nexport function parseAiReportHtml(htmlContent: string): AIAnalysisReport {\n const defects = extractDefectsFromHtml(htmlContent);\n return {\n defects,\n totalDefects: defects.length,\n hasIssues: defects.length > 0,\n rawHtml: htmlContent,\n };\n}\n\nexport function parseAiReportFile(filePath: string): AIAnalysisReport {\n try {\n const content = readFileSync(filePath, 'utf-8');\n return parseAiReportHtml(content);\n } catch {\n return { defects: [], totalDefects: 0, hasIssues: false, rawHtml: null };\n }\n}\n\nexport function discoverAiReports(dir: string): string[] {\n if (!existsSync(dir)) return [];\n try {\n return readdirSync(dir, { recursive: true })\n .map(String)\n .filter((f) => {\n const name = basename(f).toLowerCase();\n const ext = extname(f).toLowerCase();\n return ext === '.html' && (name.includes('insight') || name.includes('ai') || name.includes('analysis'));\n })\n .map((f) => join(dir, f));\n } catch {\n return [];\n }\n}\n","/**\n * Artifact collector for Maestro test output directories.\n *\n * Scans --test-output-dir and --debug-output for screenshots, video,\n * logs, command JSONs, and AI reports.\n */\n\nimport { existsSync, readdirSync, statSync } from 'node:fs';\nimport { join, basename, extname } from 'node:path';\nimport type { CollectedArtifacts } from './types.js';\n\nconst IMAGE_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg', '.webp']);\nconst VIDEO_EXTENSIONS = new Set(['.mp4', '.webm', '.mov']);\n\nfunction walkDir(dir: string): string[] {\n const results: string[] = [];\n if (!existsSync(dir)) return results;\n\n function recurse(current: string): void {\n try {\n const entries = readdirSync(current, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = join(current, entry.name);\n if (entry.isDirectory()) {\n recurse(fullPath);\n } else if (entry.isFile()) {\n results.push(fullPath);\n }\n }\n } catch {\n // Skip unreadable directories\n }\n }\n\n recurse(dir);\n return results;\n}\n\nexport function collectArtifacts(\n testOutputDir?: string,\n debugOutputDir?: string,\n): CollectedArtifacts {\n const screenshotPaths: string[] = [];\n const videoPaths: string[] = [];\n const logPaths: string[] = [];\n const commandJsonPaths: string[] = [];\n const aiReportPaths: string[] = [];\n let junitReportPath: string | null = null;\n\n const allFiles: string[] = [];\n if (testOutputDir) allFiles.push(...walkDir(testOutputDir));\n if (debugOutputDir) allFiles.push(...walkDir(debugOutputDir));\n\n const seen = new Set<string>();\n\n for (const filePath of allFiles) {\n if (seen.has(filePath)) continue;\n seen.add(filePath);\n\n const name = basename(filePath).toLowerCase();\n const ext = extname(filePath).toLowerCase();\n\n if (IMAGE_EXTENSIONS.has(ext)) {\n screenshotPaths.push(filePath);\n continue;\n }\n\n if (VIDEO_EXTENSIONS.has(ext)) {\n videoPaths.push(filePath);\n continue;\n }\n\n if (name === 'maestro.log' || (ext === '.log' && name.includes('maestro'))) {\n logPaths.push(filePath);\n continue;\n }\n\n if (name.startsWith('commands') && ext === '.json') {\n commandJsonPaths.push(filePath);\n continue;\n }\n\n if (ext === '.html' && (name.includes('insight') || name.includes('ai') || name.includes('analysis'))) {\n aiReportPaths.push(filePath);\n continue;\n }\n\n if (ext === '.xml' && (name.includes('report') || name.includes('junit'))) {\n if (!junitReportPath) junitReportPath = filePath;\n continue;\n }\n }\n\n return {\n screenshotPaths,\n videoPaths,\n logPaths,\n commandJsonPaths,\n aiReportPaths,\n junitReportPath,\n };\n}\n\nexport function getArtifactStats(artifacts: CollectedArtifacts): Record<string, number> {\n return {\n screenshots: artifacts.screenshotPaths.length,\n videos: artifacts.videoPaths.length,\n logs: artifacts.logPaths.length,\n commandFiles: artifacts.commandJsonPaths.length,\n aiReports: artifacts.aiReportPaths.length,\n junitReport: artifacts.junitReportPath ? 1 : 0,\n };\n}\n","/**\n * Builds TimelineEntry arrays from parsed Maestro flow results.\n * Maps Maestro data to the @testrelic/core TimelineEntry shape.\n */\n\nimport type {\n TimelineEntry,\n TestResult,\n TestStatus,\n TestArtifacts,\n ActionStep,\n ActionCategory,\n} from '@testrelic/core';\nimport type { MaestroFlowResult, MaestroCommandStep } from './types.js';\n\nfunction mapCommandCategory(category: MaestroCommandStep['category']): ActionCategory {\n switch (category) {\n case 'interaction': return 'ui_action';\n case 'assertion': return 'assertion';\n case 'ai': return 'assertion';\n case 'navigation': return 'navigation';\n case 'device': return 'custom_step';\n case 'media': return 'custom_step';\n case 'script': return 'custom_step';\n case 'flow_control': return 'custom_step';\n case 'recording': return 'custom_step';\n default: return 'custom_step';\n }\n}\n\nfunction commandToAction(cmd: MaestroCommandStep): ActionStep {\n return {\n title: cmd.selector ? `${cmd.command} → ${cmd.selector}` : cmd.command,\n category: mapCommandCategory(cmd.category),\n status: cmd.status === 'failed' ? 'failed' : 'passed',\n duration: cmd.duration,\n timestamp: cmd.timestamp,\n videoOffset: null,\n error: cmd.error ?? null,\n children: [],\n };\n}\n\nfunction buildTestResult(flow: MaestroFlowResult): TestResult {\n const actions: ActionStep[] = [\n ...flow.commands.map(commandToAction),\n ...flow.assertions.map(commandToAction),\n ];\n\n actions.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());\n\n const artifacts: TestArtifacts | null =\n flow.screenshotPaths.length > 0 || flow.videoPath\n ? {\n screenshot: flow.screenshotPaths[0] ?? undefined,\n video: flow.videoPath ?? undefined,\n }\n : null;\n\n const tags = [...flow.tags];\n if (flow.platform !== 'unknown' && !tags.includes(flow.platform)) {\n tags.push(flow.platform);\n }\n\n return {\n title: flow.flowName,\n status: flow.status,\n duration: flow.duration,\n startedAt: flow.startedAt,\n completedAt: flow.completedAt,\n retryCount: 0,\n tags,\n failure: flow.failureMessage\n ? { message: flow.failureMessage, line: null, code: null, stack: null }\n : null,\n testId: `${flow.flowFile}::${flow.flowName}`,\n filePath: flow.flowFile,\n suiteName: flow.appId ?? 'maestro-suite',\n testType: 'mobile',\n isFlaky: false,\n retryStatus: null,\n expectedStatus: 'passed',\n actualStatus: flow.status,\n artifacts,\n networkRequests: null,\n apiCalls: null,\n apiAssertions: null,\n actions: actions.length > 0 ? actions : null,\n consoleLogs: null,\n };\n}\n\nexport function buildTimelineEntry(flow: MaestroFlowResult): TimelineEntry {\n const testResult = buildTestResult(flow);\n\n return {\n url: flow.appId ?? 'maestro-flow',\n navigationType: 'page_load',\n visitedAt: flow.startedAt,\n duration: flow.duration,\n specFile: flow.flowFile,\n domContentLoadedAt: null,\n networkIdleAt: null,\n networkStats: null,\n tests: [testResult],\n };\n}\n\nexport function buildTimeline(flows: MaestroFlowResult[]): TimelineEntry[] {\n return flows.map(buildTimelineEntry);\n}\n","/**\n * Computes Summary stats from TimelineEntry arrays.\n */\n\nimport type { Summary, TimelineEntry } from '@testrelic/core';\n\ntype ApiCallsByStatusRange = Record<'2xx' | '3xx' | '4xx' | '5xx' | 'error', number>;\n\nconst EMPTY_STATUS_RANGE: ApiCallsByStatusRange = {\n '2xx': 0, '3xx': 0, '4xx': 0, '5xx': 0, error: 0,\n};\n\nexport function buildSummary(timeline: readonly TimelineEntry[]): Summary {\n let total = 0;\n let passed = 0;\n let failed = 0;\n let flaky = 0;\n let skipped = 0;\n let timedout = 0;\n let totalAssertions = 0;\n let passedAssertions = 0;\n let failedAssertions = 0;\n const totalNavigations = timeline.length;\n let totalActionSteps = 0;\n const actionCategoryCounts: Record<string, number> = {};\n const uniqueUrls = new Set<string>();\n\n for (const entry of timeline) {\n uniqueUrls.add(entry.url);\n\n for (const test of entry.tests) {\n total++;\n switch (test.status) {\n case 'passed': passed++; break;\n case 'failed': failed++; break;\n case 'flaky': flaky++; break;\n case 'skipped': skipped++; break;\n case 'timedout': timedout++; break;\n }\n\n if (test.actions) {\n for (const action of test.actions) {\n totalActionSteps++;\n if (action.category === 'assertion') {\n totalAssertions++;\n if (action.status === 'passed') passedAssertions++;\n else failedAssertions++;\n } else {\n const cat = action.category;\n actionCategoryCounts[cat] = (actionCategoryCounts[cat] ?? 0) + 1;\n }\n }\n }\n }\n }\n\n return {\n total,\n passed,\n failed,\n flaky,\n skipped,\n timedout,\n totalApiCalls: 0,\n uniqueApiUrls: 0,\n apiCallsByMethod: {},\n apiCallsByStatusRange: EMPTY_STATUS_RANGE,\n apiResponseTime: null,\n totalAssertions,\n passedAssertions,\n failedAssertions,\n totalNavigations,\n uniqueNavigationUrls: uniqueUrls.size,\n totalTimelineSteps: timeline.length,\n totalActionSteps,\n actionStepsByCategory: actionCategoryCounts,\n };\n}\n","/**\n * Client-side CSS for TestRelic Maestro Report.\n *\n * Design tokens copied verbatim from Appium html-css.ts.\n * Maestro-specific extensions (waterfall, progress rings, AI cards,\n * fix playbooks, duration heatmap) are additive — never overriding.\n */\nexport const CSS = `\n/* ── Theme Variables (verbatim from Appium) ── */\n:root,[data-theme=\"dark\"]{\n --bg:#0f1117;--bg-1:#161b22;--bg-2:#1c2128;--bg-3:#21262d;--bg-code:#13111c;\n --fg:#e6edf3;--fg-1:#8b949e;--fg-2:#484f58;--fg-code:#d4d4d4;--fg-err:#f8d7da;\n --bd:rgba(255,255,255,0.06);--bd-s:rgba(255,255,255,0.04);--bd-m:rgba(255,255,255,0.08);--bd-l:rgba(255,255,255,0.1);--bd-xs:rgba(255,255,255,0.03);\n --hvr:rgba(255,255,255,0.025);--hvr-s:rgba(255,255,255,0.02);\n --overlay-bg:rgba(0,0,0,.55);--shadow-c:rgba(0,0,0,.35);\n --lb-bg:rgba(0,0,0,.92);--lb-shadow:rgba(0,0,0,.6);--lb-btn:rgba(255,255,255,.1);--lb-btn-h:rgba(255,255,255,.2);\n --scroll-thumb:#21262d;\n}\n[data-theme=\"light\"]{\n --bg:#ffffff;--bg-1:#f6f8fa;--bg-2:#eaeef2;--bg-3:#d0d7de;--bg-code:#f6f8fa;\n --fg:#1f2328;--fg-1:#656d76;--fg-2:#8b949e;--fg-code:#24292e;--fg-err:#82071e;\n --bd:rgba(0,0,0,0.08);--bd-s:rgba(0,0,0,0.04);--bd-m:rgba(0,0,0,0.12);--bd-l:rgba(0,0,0,0.15);--bd-xs:rgba(0,0,0,0.03);\n --hvr:rgba(0,0,0,0.04);--hvr-s:rgba(0,0,0,0.03);\n --overlay-bg:rgba(0,0,0,.3);--shadow-c:rgba(0,0,0,.1);\n --lb-bg:rgba(0,0,0,.85);--lb-shadow:rgba(0,0,0,.3);--lb-btn:rgba(0,0,0,.12);--lb-btn-h:rgba(0,0,0,.2);\n --scroll-thumb:#d0d7de;\n}\n*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}\nbody{font-family:Inter,system-ui,-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,sans-serif;background:var(--bg);color:var(--fg);line-height:1.5;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}\n\n/* ── Layout ── */\n.wrap{max-width:960px;margin:0 auto;padding:32px 24px 64px}\n\n/* ── Topbar (same as Appium) ── */\n.top-bar{display:flex;align-items:center;gap:14px;margin-bottom:20px;flex-wrap:wrap}\n.top-bar-actions{display:flex;align-items:center;gap:8px;flex-shrink:0;margin-left:auto}\n.brand{display:flex;align-items:center;gap:10px}\n.brand svg{flex-shrink:0}\n.brand-text{display:flex;flex-direction:column}\n.brand-title{font-size:17px;font-weight:700;color:var(--fg);line-height:1.2}\n.brand-sub{font-size:10px;font-weight:600;color:#03b79c;letter-spacing:.06em;text-transform:uppercase;margin-top:1px}\n.run-meta{display:flex;flex-wrap:wrap;gap:4px 14px;font-size:11px;color:var(--fg-1);flex:1;min-width:0}\n.run-meta-item{display:flex;align-items:center;gap:4px}\n.run-meta-label{font-weight:600}\n.run-id-tag{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:10px;background:var(--bg-2);padding:1px 6px;border-radius:4px;color:var(--fg-1)}\n.ci-tag{display:inline-flex;align-items:center;gap:4px;background:rgba(59,130,246,0.12);color:#60a5fa;padding:1px 6px;border-radius:4px;font-size:10px;font-weight:600}\n.platform-tag{display:inline-flex;align-items:center;gap:4px;background:rgba(3,183,156,0.12);color:#2dd4a8;padding:1px 6px;border-radius:4px;font-size:10px;font-weight:600}\n\n/* ── Theme Toggle (same as Appium) ── */\n.theme-toggle{display:inline-flex;border:1px solid var(--bd-m);border-radius:8px;overflow:hidden;background:var(--bg);flex-shrink:0}\n.theme-btn{padding:6px 10px;border:none;background:transparent;color:var(--fg-2);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .15s;font-family:inherit}\n.theme-btn:not(:last-child){border-right:1px solid var(--bd-m)}\n.theme-btn.active{background:var(--bg-3);color:var(--fg)}\n.theme-btn:hover:not(.active){color:var(--fg-1);background:var(--hvr)}\n.theme-btn svg{width:14px;height:14px}\n\n/* ── CTA Button (same as Appium) ── */\n.cta-btn{display:inline-flex;align-items:center;gap:5px;font-size:11px;font-weight:500;color:#e6edf3;background:linear-gradient(135deg,rgba(3,183,156,0.15),rgba(14,165,233,0.15));border:1px solid rgba(3,183,156,0.3);padding:6px 14px;border-radius:8px;text-decoration:none;white-space:nowrap;flex-shrink:0;transition:all .2s;letter-spacing:.01em}\n.cta-btn:hover{background:linear-gradient(135deg,rgba(3,183,156,0.25),rgba(14,165,233,0.25));border-color:rgba(3,183,156,0.5);box-shadow:0 0 16px rgba(3,183,156,0.15);color:#fff}\n.cta-btn strong{font-weight:700;color:#2dd4a8}\n.cta-btn:hover strong{color:#5eead4}\n.cta-icon{width:13px;height:13px;color:#2dd4a8;flex-shrink:0}\n.cta-arrow{width:11px;height:11px;color:#2dd4a8;flex-shrink:0;transition:transform .2s}\n.cta-btn:hover .cta-arrow{transform:translateX(2px)}\n[data-theme=\"light\"] .cta-btn{color:#1f2328;background:linear-gradient(135deg,rgba(3,183,156,0.08),rgba(14,165,233,0.08));border-color:rgba(3,183,156,0.25)}\n[data-theme=\"light\"] .cta-btn:hover{background:linear-gradient(135deg,rgba(3,183,156,0.15),rgba(14,165,233,0.15));border-color:rgba(3,183,156,0.4);color:#0f172a}\n[data-theme=\"light\"] .cta-btn strong{color:#059669}\n[data-theme=\"light\"] .cta-icon,[data-theme=\"light\"] .cta-arrow{color:#059669}\n\n/* ── Hero Strip (Maestro: progress rings + summary chips) ── */\n.hero-strip{border:1px solid var(--bd);border-radius:10px;background:var(--bg-1);padding:20px;margin-bottom:20px}\n.hero-top{display:flex;align-items:center;gap:24px;margin-bottom:16px;flex-wrap:wrap}\n.hero-rings{display:flex;gap:20px;flex-shrink:0}\n.ring-wrap{text-align:center}\n.ring-wrap svg{display:block;margin:0 auto 4px}\n.ring-label{font-size:9px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--fg-2)}\n.hero-chips{display:flex;gap:8px;flex:1;flex-wrap:wrap}\n.summary-chip{flex:1;text-align:center;padding:12px 4px;border-radius:8px;border:1px solid var(--bd-s);min-width:70px;cursor:pointer;transition:border-color .15s}\n.summary-chip:hover{border-color:var(--bd-m)}\n.summary-chip.active{border-color:#03b79c}\n.summary-chip .s-count{font-size:22px;font-weight:800;line-height:1}\n.summary-chip .s-label{font-size:9px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;margin-top:4px;color:var(--fg-2)}\n.s-total{background:rgba(99,102,241,0.06)}.s-total .s-count{color:#818cf8}\n.s-passed{background:rgba(34,197,94,0.06)}.s-passed .s-count{color:#22c55e}\n.s-failed{background:rgba(239,68,68,0.06)}.s-failed .s-count{color:#ef4444}\n.s-flaky{background:rgba(245,158,11,0.06)}.s-flaky .s-count{color:#f59e0b}\n.s-skipped{background:rgba(107,114,128,0.06)}.s-skipped .s-count{color:#6b7280}\n.s-ai{background:rgba(168,85,247,0.06)}.s-ai .s-count{color:#a855f7}\n.hero-footer{display:flex;gap:14px;font-size:11px;color:var(--fg-1);flex-wrap:wrap;padding-top:12px;border-top:1px solid var(--bd-s)}\n\n/* ── Execution Waterfall ── */\n.waterfall-card{border:1px solid var(--bd);border-radius:10px;background:var(--bg-1);padding:16px;margin-bottom:20px}\n.waterfall-title{font-size:11px;font-weight:600;color:var(--fg-2);text-transform:uppercase;letter-spacing:.06em;margin-bottom:10px}\n.waterfall-row{display:flex;align-items:center;gap:8px;padding:3px 0;font-size:11px;cursor:pointer;transition:background .1s;border-radius:4px;padding:3px 6px;margin:0 -6px}\n.waterfall-row:hover{background:var(--hvr)}\n.waterfall-name{width:140px;flex-shrink:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--fg-1);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:10px}\n.waterfall-track{flex:1;height:14px;background:var(--bg-2);border-radius:3px;overflow:hidden;position:relative}\n.waterfall-bar{height:100%;border-radius:3px;min-width:2px;transition:width .3s}\n.wb-passed{background:rgba(34,197,94,0.35)}\n.wb-failed{background:rgba(239,68,68,0.35)}\n.wb-skipped{background:rgba(107,114,128,0.2)}\n.waterfall-dur{width:56px;flex-shrink:0;text-align:right;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:10px;color:var(--fg-2)}\n\n/* ── Filter Bar (same pattern as Appium: search + status chips + filter icon) ── */\n.filter-bar{display:flex;align-items:center;gap:8px;margin-bottom:24px;flex-wrap:wrap}\n.filter-chips{display:flex;gap:5px;flex-wrap:wrap}\n.filter-chip{font-size:12px;font-weight:500;padding:5px 14px;border-radius:9999px;border:1px solid var(--bd-m);background:transparent;color:var(--fg-1);cursor:pointer;transition:all .15s;font-family:inherit;white-space:nowrap}\n.filter-chip:hover{border-color:#03b79c;color:var(--fg)}\n.filter-chip.active{background:rgba(3,183,156,0.12);border-color:#03b79c;color:#2dd4a8}\n.filter-chip--dimmed{opacity:.45}\n.chip-count{font-weight:700;margin-left:3px}\n.filter-clear{font-size:11px;font-weight:600;color:#03b79c;background:none;border:none;cursor:pointer;padding:4px 8px;border-radius:6px;font-family:inherit;transition:background .15s}\n.filter-clear:hover{background:rgba(3,183,156,0.1)}\n.filter-indicator{font-size:11px;color:var(--fg-2)}\n.search-box{position:relative;width:260px;flex-shrink:1;min-width:160px;margin-left:0}\n.search-input{width:100%;padding:6px 10px 6px 32px;border:1px solid var(--bd-m);border-radius:8px;background:var(--bg-1);color:var(--fg);font-size:12px;font-family:inherit;outline:none;transition:border-color .15s}\n.search-input::placeholder{color:var(--fg-2)}\n.search-input:focus{border-color:#03b79c}\n.search-icon{position:absolute;left:10px;top:50%;transform:translateY(-50%);color:var(--fg-2);pointer-events:none}\n\n/* ── Filter Icon Button (opens tag drawer) ── */\n.filter-icon-btn{position:relative;display:flex;align-items:center;justify-content:center;width:34px;height:34px;border-radius:8px;border:1px solid var(--bd-m);background:transparent;color:var(--fg-2);cursor:pointer;transition:all .15s;flex-shrink:0;margin-left:auto}\n.filter-icon-btn:hover{background:var(--hvr);color:var(--fg);border-color:var(--bd-l)}\n.filter-icon-badge{position:absolute;top:5px;right:5px;width:7px;height:7px;border-radius:50%;background:#03b79c}\n\n/* ── Filter Drawer (right panel for tags + file filters) ── */\n.filter-drawer-backdrop{position:fixed;inset:0;background:var(--overlay-bg);z-index:100;opacity:0;pointer-events:none;transition:opacity .25s}\n.filter-drawer-backdrop.open{opacity:1;pointer-events:auto}\n.filter-drawer{position:fixed;top:0;right:0;bottom:0;width:320px;max-width:90vw;z-index:110;background:var(--bg-1);border-left:1px solid var(--bd);transform:translateX(100%);transition:transform .3s cubic-bezier(.4,0,.2,1);display:flex;flex-direction:column;overflow:hidden;box-shadow:-4px 0 24px var(--shadow-c)}\n.filter-drawer.open{transform:translateX(0)}\n.filter-drawer-bar{display:flex;align-items:center;justify-content:space-between;padding:14px 20px;border-bottom:1px solid var(--bd);background:linear-gradient(180deg,rgba(3,183,156,0.04) 0%,transparent 100%);flex-shrink:0}\n.filter-drawer-title{font-size:12px;font-weight:600;color:var(--fg-1);text-transform:uppercase;letter-spacing:.06em}\n.filter-drawer-close{width:32px;height:32px;border-radius:8px;border:1px solid var(--bd-m);background:transparent;color:var(--fg-1);font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .15s,color .15s}\n.filter-drawer-close:hover{background:var(--bd);color:var(--fg)}\n.filter-drawer-body{flex:1;overflow-y:auto;padding:20px;scrollbar-width:thin;scrollbar-color:var(--scroll-thumb) transparent}\n.filter-drawer-body::-webkit-scrollbar{width:5px}\n.filter-drawer-body::-webkit-scrollbar-thumb{background:var(--bg-3);border-radius:3px}\n.filter-drawer-section{margin-bottom:20px}\n.filter-drawer-section-label{display:block;font-size:10px;font-weight:600;color:var(--fg-2);text-transform:uppercase;letter-spacing:.04em;margin-bottom:8px}\n.filter-drawer-actions{display:flex;align-items:center;gap:12px;padding-top:16px;border-top:1px solid var(--bd)}\n\n/* ── Flow List (same pattern as Appium file-group/test-row) ── */\n.file-group{margin-bottom:16px}\n.file-group-header{font-size:11px;font-weight:600;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;color:var(--fg-2);padding:0 4px 6px}\n.file-group-card{border:1px solid var(--bd);border-radius:10px;overflow:hidden;background:var(--bg-1)}\n.test-row{display:flex;align-items:center;gap:12px;padding:12px 18px;cursor:pointer;transition:background .1s;border-bottom:1px solid var(--bd-s)}\n.test-row:last-child{border-bottom:none}\n.test-row:hover{background:var(--hvr)}\n.test-row.active{background:rgba(3,183,156,0.07)}\n.status-indicator{width:10px;height:10px;border-radius:50%;flex-shrink:0}\n.si-passed{background:#22c55e}.si-failed{background:#ef4444}.si-flaky{background:#f59e0b}.si-skipped{background:#6b7280}.si-timedout{background:#f97316}\n.test-row-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}\n.test-row-title{font-size:14px;font-weight:500;color:var(--fg);line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.test-row-badges{display:flex;gap:5px;align-items:center;flex-wrap:wrap}\n.trb{font-size:10px;font-weight:600;padding:1px 7px;border-radius:4px}\n.trb-app{background:rgba(59,130,246,0.12);color:#60a5fa}\n.trb-tag{background:rgba(139,92,246,0.12);color:#a78bfa}\n.trb-platform{background:rgba(3,183,156,0.12);color:#2dd4a8}\n.test-row-dur{font-size:13px;font-weight:700;color:var(--fg-1);flex-shrink:0;white-space:nowrap}\n.test-row-arrow{color:var(--fg-2);flex-shrink:0;transition:color .15s}\n.test-row:hover .test-row-arrow{color:var(--fg-1)}\n.test-row-error{font-size:11px;color:#ef4444;margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.no-tests{text-align:center;padding:48px 20px;color:var(--fg-2);font-size:14px}\n\n/* ── Drawer (same as Appium) ── */\n.drawer-backdrop{position:fixed;inset:0;background:var(--overlay-bg);z-index:100;opacity:0;pointer-events:none;transition:opacity .25s}\n.drawer-backdrop.open{opacity:1;pointer-events:auto}\n.drawer{position:fixed;top:0;right:0;bottom:0;width:50vw;max-width:50vw;z-index:110;background:var(--bg-1);border-left:1px solid var(--bd);transform:translateX(100%);transition:transform .3s cubic-bezier(.4,0,.2,1);display:flex;flex-direction:column;overflow:hidden;box-shadow:-8px 0 40px var(--shadow-c)}\n.drawer.open{transform:translateX(0)}\n.drawer-bar{display:flex;align-items:center;justify-content:space-between;padding:14px 20px;border-bottom:1px solid var(--bd);background:linear-gradient(180deg,rgba(3,183,156,0.04) 0%,transparent 100%);flex-shrink:0}\n.drawer-bar-title{font-size:12px;font-weight:600;color:var(--fg-1);text-transform:uppercase;letter-spacing:.06em}\n.drawer-close{width:32px;height:32px;border-radius:8px;border:1px solid var(--bd-m);background:transparent;color:var(--fg-1);font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .15s,color .15s}\n.drawer-close:hover{background:var(--bd);color:var(--fg)}\n.drawer-body{flex:1;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--scroll-thumb) transparent}\n.drawer-body::-webkit-scrollbar{width:5px}\n.drawer-body::-webkit-scrollbar-track{background:transparent}\n.drawer-body::-webkit-scrollbar-thumb{background:var(--bg-3);border-radius:3px}\n\n/* ── Drawer Tabs (same seg-ctrl as Appium) ── */\n.seg-ctrl{display:flex;gap:2px;padding:8px 12px 0;border-bottom:1px solid var(--bd);background:var(--bg-1);flex-wrap:wrap}\n.seg-btn{display:flex;align-items:center;gap:5px;font-size:11px;padding:5px 12px;border-radius:6px 6px 0 0;border:none;background:none;color:var(--fg-2);cursor:pointer;font-weight:500;transition:all .15s;border-bottom:2px solid transparent;margin-bottom:-1px;font-family:inherit}\n.seg-btn:hover{color:var(--fg-1);background:var(--hvr)}\n.seg-btn.active{color:#03b79c;border-bottom-color:#03b79c;font-weight:600;background:var(--bg)}\n.seg-pane{display:none;padding:20px 24px}\n.seg-pane.active{display:block;animation:trFadeIn .15s ease-out}\n@keyframes trFadeIn{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}\n\n/* ── Overview Tab ── */\n.detail-header{margin-bottom:20px;padding-bottom:16px;border-bottom:1px solid var(--bd)}\n.detail-top{display:flex;align-items:flex-start;gap:12px;margin-bottom:10px}\n.detail-status-badge{font-size:11px;font-weight:700;padding:4px 14px;border-radius:9999px;text-transform:uppercase;letter-spacing:.04em;flex-shrink:0;margin-top:2px}\n.dbg-passed{background:rgba(34,197,94,0.1);color:#22c55e;border:1px solid rgba(34,197,94,0.2)}\n.dbg-failed{background:rgba(239,68,68,0.1);color:#ef4444;border:1px solid rgba(239,68,68,0.2)}\n.dbg-skipped{background:rgba(107,114,128,0.1);color:#6b7280;border:1px solid rgba(107,114,128,0.2)}\n.dbg-flaky{background:rgba(245,158,11,0.1);color:#f59e0b;border:1px solid rgba(245,158,11,0.2)}\n.dbg-timedout{background:rgba(249,115,22,0.1);color:#f97316;border:1px solid rgba(249,115,22,0.2)}\n.detail-title-section{flex:1;min-width:0}\n.detail-title{font-size:16px;font-weight:700;color:var(--fg);line-height:1.35;margin-bottom:6px}\n.detail-meta{display:flex;flex-wrap:wrap;gap:5px;align-items:center}\n.dm-badge{font-size:10px;font-weight:600;padding:2px 8px;border-radius:4px}\n.dm-app{background:rgba(59,130,246,0.12);color:#60a5fa}\n.dm-tag{background:rgba(139,92,246,0.12);color:#a78bfa}\n.dm-platform{background:rgba(3,183,156,0.12);color:#2dd4a8}\n.detail-duration{font-size:24px;font-weight:800;color:var(--fg);flex-shrink:0;white-space:nowrap}\n.detail-props{display:grid;grid-template-columns:auto 1fr;gap:4px 16px;font-size:12px;margin-top:12px}\n.detail-props-k{color:var(--fg-2);font-weight:600;text-transform:uppercase;letter-spacing:.04em;font-size:10px}\n.detail-props-v{color:var(--fg);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n\n/* ── Failure Panel (same as Appium) ── */\n.failure-panel{margin-bottom:20px;border:1px solid rgba(239,68,68,0.2);border-radius:10px;overflow:hidden;background:var(--bg-2)}\n.failure-panel-header{display:flex;align-items:center;gap:8px;padding:10px 14px;background:rgba(239,68,68,0.08);font-size:12px;font-weight:600;color:#ef4444}\n.failure-message{padding:12px 14px;background:var(--bg-code);color:var(--fg-err);font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;white-space:pre-wrap;word-break:break-word;line-height:1.6;position:relative}\n.copy-btn{position:absolute;top:8px;right:8px;font-size:10px;padding:3px 10px;border-radius:4px;border:1px solid var(--bd-m);background:var(--bg-3);color:var(--fg-1);cursor:pointer;font-family:inherit;transition:all .15s}\n.copy-btn:hover{background:var(--bg-2);color:var(--fg)}\n\n/* ── Fix Hint (Maestro-specific, teal accent) ── */\n.fix-hint{margin-bottom:20px;border:1px solid rgba(3,183,156,0.2);border-radius:10px;overflow:hidden;background:var(--bg-2)}\n.fix-hint-header{display:flex;align-items:center;gap:8px;padding:10px 14px;background:rgba(3,183,156,0.06);font-size:12px;font-weight:600;color:#2dd4a8}\n.fix-hint-body{padding:12px 14px;font-size:12px;color:var(--fg-1);line-height:1.7}\n.fix-hint-body ul{margin:4px 0 0 16px;list-style:disc}\n.fix-hint-body li{margin:2px 0}\n.fix-hint-body code{font-size:11px;padding:1px 5px;background:var(--bg-code);border-radius:3px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;color:var(--fg-code)}\n.fix-hint-section{margin-top:10px;padding-top:8px;border-top:1px solid var(--bd-s)}\n.fix-hint-section-title{font-size:10px;font-weight:600;color:var(--fg-2);text-transform:uppercase;letter-spacing:.04em;margin-bottom:4px}\n.fix-code{margin:6px 0;padding:8px 12px;background:var(--bg-code);border:1px solid var(--bd);border-radius:6px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;color:var(--fg-code);white-space:pre-wrap;line-height:1.5}\n\n/* ── Subflow Graph ── */\n.subflow-tree{margin-top:16px;padding:12px;background:var(--bg-2);border:1px solid var(--bd-s);border-radius:8px;font-size:11px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;color:var(--fg-1)}\n.subflow-tree .sf-node{padding:2px 0}\n.subflow-tree .sf-child{padding-left:20px;border-left:1px solid var(--bd-m)}\n\n/* ── Steps Tab ── */\n.steps-header{font-size:11px;font-weight:700;color:var(--fg-1);text-transform:uppercase;letter-spacing:.06em;margin-bottom:12px;display:flex;align-items:center;gap:8px}\n.steps-stat{font-weight:400;color:var(--fg-2);font-size:10px}\n.steps-waterfall{margin-bottom:16px;border:1px solid var(--bd);border-radius:8px;background:var(--bg-2);padding:8px 12px;overflow-x:auto}\n.sw-row{display:flex;align-items:center;gap:6px;padding:2px 0;font-size:10px}\n.sw-name{width:120px;flex-shrink:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--fg-2);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}\n.sw-track{flex:1;height:10px;background:var(--bg-3);border-radius:2px;overflow:hidden}\n.sw-bar{height:100%;border-radius:2px;min-width:1px}\n.sw-bar-green{background:#22c55e}.sw-bar-amber{background:#f59e0b}.sw-bar-red{background:#ef4444}.sw-bar-grey{background:#6b7280}\n.sw-dur{width:52px;flex-shrink:0;text-align:right;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;color:var(--fg-2)}\n.cmd-step{display:flex;align-items:center;gap:8px;padding:6px 12px;border-radius:6px;font-size:12px;transition:background .1s;border-left:3px solid transparent}\n.cmd-step:hover{background:var(--hvr)}\n.cmd-step-failed{background:rgba(239,68,68,0.04);border-left-color:#ef4444}\n.cmd-step-failed:hover{background:rgba(239,68,68,0.08)}\n.cmd-step-slow{border-left-color:#f59e0b;background:rgba(245,158,11,0.03)}\n.cmd-step-slow:hover{background:rgba(245,158,11,0.07)}\n.cmd-step-fast{border-left-color:#22c55e}\n.cmd-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}\n.cmd-dot-pass{background:#22c55e}.cmd-dot-fail{background:#ef4444}\n.cmd-badge{font-size:9px;font-weight:600;padding:2px 7px;border-radius:4px;text-transform:uppercase;letter-spacing:.03em;flex-shrink:0;white-space:nowrap}\n.cmd-badge-ui{background:rgba(59,130,246,0.12);color:#60a5fa}\n.cmd-badge-assert{background:rgba(34,197,94,0.12);color:#22c55e}\n.cmd-badge-nav{background:rgba(168,85,247,0.12);color:#a78bfa}\n.cmd-badge-device{background:rgba(249,115,22,0.12);color:#fb923c}\n.cmd-badge-ai{background:rgba(236,72,153,0.12);color:#f472b6}\n.cmd-badge-cmd{background:var(--bg-3);color:var(--fg-2)}\n.cmd-badge-slow{background:rgba(245,158,11,0.15);color:#f59e0b;margin-left:auto}\n.cmd-title{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--fg);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px}\n.cmd-dur{font-size:10px;color:var(--fg-2);flex-shrink:0;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}\n.cmd-error{font-size:11px;color:#ef4444;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;white-space:pre-wrap;word-break:break-word;line-height:1.5;padding:4px 12px 8px 31px}\n.cmd-connector{width:2px;height:4px;background:var(--bd-m);margin-left:15px}\n.steps-legend{display:flex;flex-wrap:wrap;gap:10px;margin-top:16px;padding-top:12px;border-top:1px solid var(--bd);font-size:10px;color:var(--fg-2)}\n.steps-legend span{display:inline-flex;align-items:center;gap:4px}\n\n/* ── Assertions Tab ── */\n.assert-header{display:flex;align-items:center;gap:16px;margin-bottom:16px;flex-wrap:wrap}\n.assert-ring{flex-shrink:0}\n.assert-stats{font-size:12px;color:var(--fg-1);line-height:1.6}\n.assert-stats strong{font-weight:700}\n.assert-bar-wrap{flex:1;min-width:100px}\n.assert-bar{height:6px;background:var(--bg-3);border-radius:3px;overflow:hidden}\n.assert-bar-fill{height:100%;background:#22c55e;border-radius:3px;transition:width .3s}\n.assert-pct{font-size:11px;font-weight:700;color:var(--fg-1);margin-top:2px;text-align:right}\n.assert-table{width:100%;border-collapse:collapse;font-size:12px}\n.assert-table th{text-align:left;padding:6px 10px;font-size:10px;font-weight:600;color:var(--fg-2);text-transform:uppercase;letter-spacing:.04em;border-bottom:1px solid var(--bd)}\n.assert-table td{padding:6px 10px;border-bottom:1px solid var(--bd-s);vertical-align:top}\n.assert-table tr:hover{background:var(--hvr)}\n.assert-result{font-weight:700;font-size:13px}\n.ar-pass{color:#22c55e}.ar-fail{color:#ef4444}\n.assert-error-row td{padding:2px 10px 8px 40px;border-bottom:1px solid var(--bd-s);color:#ef4444;font-size:11px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}\n\n/* ── Screenshots Tab ── */\n.screenshot-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:12px}\n.screenshot-card{border:1px solid var(--bd);border-radius:8px;overflow:hidden;background:var(--bg-2);cursor:pointer;transition:border-color .15s}\n.screenshot-card:hover{border-color:#03b79c}\n.screenshot-card img{width:100%;display:block;object-fit:contain}\n.screenshot-card-label{padding:6px 10px;font-size:10px;font-weight:600;color:var(--fg-2);border-top:1px solid var(--bd-s)}\n.screenshot-card.failure-shot{border-color:rgba(239,68,68,0.3)}\n.screenshot-card.failure-shot .screenshot-card-label{background:rgba(239,68,68,0.06);color:#ef4444}\n.screenshot-empty{padding:24px 14px;text-align:center;font-size:12px;color:var(--fg-2);font-style:italic;border:1px dashed var(--bd-m);border-radius:10px}\n\n/* ── AI Defects Tab ── */\n.ai-card{border:1px solid var(--bd);border-radius:10px;overflow:hidden;background:var(--bg-2);margin-bottom:12px;border-left:3px solid transparent}\n.ai-card-critical{border-left-color:rgba(239,68,68,0.5)}\n.ai-card-warning{border-left-color:rgba(245,158,11,0.5)}\n.ai-card-info{border-left-color:rgba(59,130,246,0.5)}\n.ai-card-header{display:flex;align-items:center;gap:8px;padding:10px 14px;border-bottom:1px solid var(--bd-s)}\n.ai-severity{font-size:10px;font-weight:700;padding:2px 10px;border-radius:9999px;text-transform:uppercase;letter-spacing:.04em}\n.ai-sev-critical{background:rgba(239,68,68,0.12);color:#ef4444;border:1px solid rgba(239,68,68,0.2)}\n.ai-sev-warning{background:rgba(245,158,11,0.12);color:#f59e0b;border:1px solid rgba(245,158,11,0.2)}\n.ai-sev-info{background:rgba(59,130,246,0.12);color:#60a5fa;border:1px solid rgba(59,130,246,0.2)}\n.ai-type{font-size:11px;font-weight:600;color:var(--fg-1);text-transform:capitalize}\n.ai-desc{padding:10px 14px;font-size:12px;color:var(--fg);line-height:1.6}\n.ai-fix{padding:10px 14px;border-top:1px solid var(--bd-s);background:rgba(3,183,156,0.03)}\n.ai-fix-label{font-size:10px;font-weight:700;color:#2dd4a8;text-transform:uppercase;letter-spacing:.06em;margin-bottom:6px}\n.ai-fix-body{font-size:12px;color:var(--fg-1);line-height:1.7}\n.ai-fix-body ul{margin:0 0 0 16px;list-style:disc}\n.ai-fix-body li{margin:2px 0}\n.ai-fix-code{margin:6px 0;padding:8px 12px;background:var(--bg-code);border:1px solid var(--bd);border-radius:6px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;color:var(--fg-code);white-space:pre-wrap;line-height:1.5}\n.ai-fix-platform{font-size:10px;font-weight:600;color:var(--fg-2);margin:8px 0 2px;text-transform:uppercase;letter-spacing:.04em}\n.ai-empty{text-align:center;padding:32px;color:#22c55e;font-weight:600;font-size:14px}\n\n/* ── Lightbox (same as Appium) ── */\n.lightbox-overlay{position:fixed;inset:0;background:var(--lb-bg);z-index:1000;display:flex;align-items:center;justify-content:center;cursor:zoom-out;animation:trFadeIn .15s}\n.lightbox-overlay img{max-width:92vw;max-height:92vh;border-radius:8px;box-shadow:0 8px 40px var(--lb-shadow)}\n.lightbox-close{position:fixed;top:16px;right:16px;z-index:1001;width:40px;height:40px;border-radius:50%;border:none;background:var(--lb-btn);color:#fff;font-size:22px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .15s;backdrop-filter:blur(8px)}\n.lightbox-close:hover{background:var(--lb-btn-h)}\n.lightbox-nav{position:fixed;top:50%;z-index:1001;width:40px;height:40px;border-radius:50%;border:none;background:var(--lb-btn);color:#fff;font-size:18px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .15s;transform:translateY(-50%);backdrop-filter:blur(8px)}\n.lightbox-nav:hover{background:var(--lb-btn-h)}\n.lightbox-prev{left:16px}\n.lightbox-next{right:16px}\n.lightbox-caption{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);z-index:1001;font-size:12px;color:#fff;background:rgba(0,0,0,.6);padding:4px 16px;border-radius:8px;backdrop-filter:blur(8px)}\n\n/* ── Footer ── */\n.report-footer{text-align:center;padding:24px;font-size:12px;color:var(--fg-2)}\n.report-footer a{color:#03b79c;text-decoration:none}\n.report-footer a:hover{text-decoration:underline}\n\n/* ── Loading (same as Appium) ── */\n.loading-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:var(--bg);z-index:9999;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px}\n.loading-spinner{width:36px;height:36px;border:3px solid var(--bd-l);border-top-color:var(--fg-1);border-radius:50%;animation:spin .8s linear infinite}\n.loading-text{font:13px/1 inherit;color:var(--fg-1)}\n@keyframes spin{to{transform:rotate(360deg)}}\n\n/* ── Scrollbar (same as Appium) ── */\n*{scrollbar-width:thin;scrollbar-color:var(--scroll-thumb) transparent}\n::-webkit-scrollbar{width:6px;height:6px}\n::-webkit-scrollbar-track{background:transparent}\n::-webkit-scrollbar-thumb{background:var(--scroll-thumb);border-radius:3px}\n::-webkit-scrollbar-thumb:hover{background:var(--fg-2)}\n::-webkit-scrollbar-corner{background:transparent}\n\n/* ── Responsive (same breakpoints as Appium) ── */\n@media(max-width:1100px){.drawer{width:60vw;max-width:60vw}}\n@media(max-width:800px){.drawer{width:85vw;max-width:85vw}}\n@media(max-width:640px){\n .wrap{padding:16px 12px 48px}\n .top-bar{flex-direction:column;align-items:flex-start;gap:8px}\n .top-bar-actions{margin-left:0}\n .hero-top{flex-direction:column;align-items:stretch}\n .hero-rings{justify-content:center}\n .hero-chips{flex-wrap:wrap}\n .summary-chip{min-width:60px}\n .filter-bar{flex-direction:column;align-items:stretch}\n .search-box{width:100%;margin-left:0}\n .drawer{width:100%;max-width:100%}\n .filter-drawer{width:100%;max-width:100%}\n .cta-btn{font-size:10px;padding:5px 10px;order:10}\n .detail-title{font-size:14px}\n .detail-duration{font-size:20px}\n .waterfall-name{width:80px}\n}\n\n/* ── Print (same as Appium) ── */\n@media print{\n body{background:#fff;color:#000}\n .drawer-backdrop,.drawer,.lightbox-overlay,.lightbox-close,.lightbox-nav,.lightbox-caption{display:none}\n .test-row-arrow{display:none}\n .summary-chip,.filter-chip,.status-indicator,.detail-status-badge,.dm-badge,.trb,.cmd-badge,.ai-severity{print-color-adjust:exact;-webkit-print-color-adjust:exact}\n}\n`;\n","/**\n * TestRelic Logo SVG — shared brand asset.\n */\n\nexport const LOGO_SVG_RAW = `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 196 247\" fill=\"none\">\n<path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M4.75 1.08009C2.034 2.66209 -0.130999 7.63009 0.610001 10.5821C0.969001 12.0121 3.021 15.2131 5.17 17.6971C14.375 28.3321 18 39.7791 18 58.2101V71.0001H12.434C1.16501 71.0001 0 73.4641 0 97.3051C0 106.858 0.298003 128.922 0.662003 146.337L1.323 178H33.606C65.786 178 65.897 178.007 68.544 180.284C72.228 183.453 72.244 189.21 68.579 192.877L65.957 195.5L33.479 195.801L1 196.103V204.551V213H24.577H48.154L51.077 215.923C55.007 219.853 55.007 224.147 51.077 228.077L48.154 231H26.469H4.783L5.41901 233.534C5.76901 234.927 7.143 238.527 8.472 241.534L10.89 247H40.945H71L71.006 241.75C71.017 230.748 76.027 221.606 84.697 216.767C97.854 209.424 114.086 213.895 121.323 226.857C123.659 231.041 124.418 233.833 124.789 239.607L125.263 247H155.187H185.11L187.528 241.534C188.857 238.527 190.231 234.927 190.581 233.534L191.217 231H169.531H147.846L144.923 228.077C142.928 226.082 142 224.152 142 222C142 219.848 142.928 217.918 144.923 215.923L147.846 213H171.423H195V204.551V196.103L162.521 195.801L130.043 195.5L127.421 192.877C123.991 189.445 123.835 183.869 127.074 180.421L129.349 178H162.013H194.677L195.338 146.337C195.702 128.922 196 106.858 196 97.3051C196 73.4641 194.835 71.0001 183.566 71.0001H178V58.2101C178 39.6501 181.397 28.7731 190.538 18.0651C195.631 12.0971 196.572 9.00809 194.511 5.02109C192.672 1.46509 190.197 9.12233e-05 186.028 9.12233e-05C179.761 9.12233e-05 168.713 14.8831 163.388 30.5001C160.975 37.5771 160.608 40.3751 160.213 54.7501L159.765 71.0001H150.732H141.7L142.286 53.2501C142.904 34.5511 144.727 24.3761 148.938 16.1211C151.823 10.4671 151.628 5.90109 148.364 2.63609C145 -0.726907 140.105 -0.887909 136.596 2.25009C133.481 5.03609 128.686 17.0811 126.507 27.5921C125.569 32.1191 124.617 43.0901 124.28 53.2501L123.69 71.0001H115.345H107V38.4231V5.84609L104.077 2.92309C102.082 0.928088 100.152 9.12233e-05 98 9.12233e-05C95.848 9.12233e-05 93.918 0.928088 91.923 2.92309L89 5.84609V38.4231V71.0001H80.655H72.31L71.72 53.2501C71.383 43.0901 70.431 32.1191 69.493 27.5921C67.314 17.0811 62.519 5.03609 59.404 2.25009C55.998 -0.795909 51.059 -0.710905 47.646 2.45209C44.221 5.62609 44.191 9.92109 47.539 17.4911C51.71 26.9241 53.007 34.4791 53.676 53.2501L54.31 71.0001H45.272H36.235L35.787 54.7501C35.392 40.3751 35.025 37.5771 32.612 30.5001C27.194 14.6091 16.228 -0.02891 9.787 0.03009C7.979 0.04709 5.712 0.519093 4.75 1.08009ZM42.687 108.974C33.431 112.591 20.036 125.024 18.408 131.512C17.476 135.223 20.677 140.453 28.253 147.599C37.495 156.319 44.191 159.471 53.5 159.485C59.317 159.494 61.645 158.953 67.274 156.289C77.634 151.385 88.987 139.161 88.996 132.9C89.004 127.304 76.787 114.707 66.745 109.956C59.16 106.368 50.285 106.006 42.687 108.974ZM129.255 109.904C119.151 114.768 106.996 127.33 107.004 132.9C107.013 139.108 118.562 151.475 128.939 156.389C134.338 158.945 136.744 159.496 142.521 159.498C152.526 159.501 160.369 155.502 169.771 145.605C180.444 134.368 180.278 130.975 168.486 119.388C160.043 111.094 152.727 107.595 143 107.201C136.364 106.933 134.78 107.244 129.255 109.904ZM48.5 125.922C46.3 126.969 43.152 128.945 41.505 130.314L38.511 132.802L40.504 135.005C41.601 136.216 44.434 138.342 46.8 139.728C52.577 143.114 57.36 142.466 64.009 137.395L68.978 133.606L66.756 131.24C60.944 125.054 54.357 123.135 48.5 125.922ZM138.386 125.063C136.674 125.571 133.375 127.677 131.057 129.743L126.841 133.5L131.901 137.343C138.65 142.468 143.407 143.124 149.2 139.728C151.566 138.342 154.351 136.269 155.389 135.122C157.684 132.587 156.742 131.097 150.58 127.511C145.438 124.519 142.329 123.895 138.386 125.063ZM91.923 162.923C89.043 165.804 89 166.008 89 176.973C89 184.805 89.435 188.941 90.47 190.941C92.356 194.589 96.918 196.273 101.03 194.84C105.82 193.17 107 189.638 107 176.973C107 166.008 106.957 165.804 104.077 162.923C102.082 160.928 100.152 160 98 160C95.848 160 93.918 160.928 91.923 162.923ZM94.5 232.155C91.026 234.055 90 236.229 90 241.691V247H98H106V242.082C106 235.732 105.37 234.242 101.928 232.463C98.591 230.737 97.197 230.679 94.5 232.155Z\" fill=\"url(#paint0_linear_55_11)\"/>\n<defs><linearGradient id=\"paint0_linear_55_11\" x1=\"98\" y1=\"0.000244141\" x2=\"98\" y2=\"247\" gradientUnits=\"userSpaceOnUse\">\n<stop stop-color=\"#03B79C\"/><stop offset=\"0.504808\" stop-color=\"#4EDAA4\"/><stop offset=\"0.865285\" stop-color=\"#84F3AA\"/>\n</linearGradient></defs></svg>`;\n\nexport const LOGO_SVG = `<svg width=\"32\" height=\"40\" viewBox=\"0 0 196 247\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n<path fill-rule=\"evenodd\" clip-rule=\"evenodd\" d=\"M4.75 1.08009C2.034 2.66209 -0.130999 7.63009 0.610001 10.5821C0.969001 12.0121 3.021 15.2131 5.17 17.6971C14.375 28.3321 18 39.7791 18 58.2101V71.0001H12.434C1.16501 71.0001 0 73.4641 0 97.3051C0 106.858 0.298003 128.922 0.662003 146.337L1.323 178H33.606C65.786 178 65.897 178.007 68.544 180.284C72.228 183.453 72.244 189.21 68.579 192.877L65.957 195.5L33.479 195.801L1 196.103V204.551V213H24.577H48.154L51.077 215.923C55.007 219.853 55.007 224.147 51.077 228.077L48.154 231H26.469H4.783L5.41901 233.534C5.76901 234.927 7.143 238.527 8.472 241.534L10.89 247H40.945H71L71.006 241.75C71.017 230.748 76.027 221.606 84.697 216.767C97.854 209.424 114.086 213.895 121.323 226.857C123.659 231.041 124.418 233.833 124.789 239.607L125.263 247H155.187H185.11L187.528 241.534C188.857 238.527 190.231 234.927 190.581 233.534L191.217 231H169.531H147.846L144.923 228.077C142.928 226.082 142 224.152 142 222C142 219.848 142.928 217.918 144.923 215.923L147.846 213H171.423H195V204.551V196.103L162.521 195.801L130.043 195.5L127.421 192.877C123.991 189.445 123.835 183.869 127.074 180.421L129.349 178H162.013H194.677L195.338 146.337C195.702 128.922 196 106.858 196 97.3051C196 73.4641 194.835 71.0001 183.566 71.0001H178V58.2101C178 39.6501 181.397 28.7731 190.538 18.0651C195.631 12.0971 196.572 9.00809 194.511 5.02109C192.672 1.46509 190.197 9.12233e-05 186.028 9.12233e-05C179.761 9.12233e-05 168.713 14.8831 163.388 30.5001C160.975 37.5771 160.608 40.3751 160.213 54.7501L159.765 71.0001H150.732H141.7L142.286 53.2501C142.904 34.5511 144.727 24.3761 148.938 16.1211C151.823 10.4671 151.628 5.90109 148.364 2.63609C145 -0.726907 140.105 -0.887909 136.596 2.25009C133.481 5.03609 128.686 17.0811 126.507 27.5921C125.569 32.1191 124.617 43.0901 124.28 53.2501L123.69 71.0001H115.345H107V38.4231V5.84609L104.077 2.92309C102.082 0.928088 100.152 9.12233e-05 98 9.12233e-05C95.848 9.12233e-05 93.918 0.928088 91.923 2.92309L89 5.84609V38.4231V71.0001H80.655H72.31L71.72 53.2501C71.383 43.0901 70.431 32.1191 69.493 27.5921C67.314 17.0811 62.519 5.03609 59.404 2.25009C55.998 -0.795909 51.059 -0.710905 47.646 2.45209C44.221 5.62609 44.191 9.92109 47.539 17.4911C51.71 26.9241 53.007 34.4791 53.676 53.2501L54.31 71.0001H45.272H36.235L35.787 54.7501C35.392 40.3751 35.025 37.5771 32.612 30.5001C27.194 14.6091 16.228 -0.02891 9.787 0.03009C7.979 0.04709 5.712 0.519093 4.75 1.08009ZM42.687 108.974C33.431 112.591 20.036 125.024 18.408 131.512C17.476 135.223 20.677 140.453 28.253 147.599C37.495 156.319 44.191 159.471 53.5 159.485C59.317 159.494 61.645 158.953 67.274 156.289C77.634 151.385 88.987 139.161 88.996 132.9C89.004 127.304 76.787 114.707 66.745 109.956C59.16 106.368 50.285 106.006 42.687 108.974ZM129.255 109.904C119.151 114.768 106.996 127.33 107.004 132.9C107.013 139.108 118.562 151.475 128.939 156.389C134.338 158.945 136.744 159.496 142.521 159.498C152.526 159.501 160.369 155.502 169.771 145.605C180.444 134.368 180.278 130.975 168.486 119.388C160.043 111.094 152.727 107.595 143 107.201C136.364 106.933 134.78 107.244 129.255 109.904ZM48.5 125.922C46.3 126.969 43.152 128.945 41.505 130.314L38.511 132.802L40.504 135.005C41.601 136.216 44.434 138.342 46.8 139.728C52.577 143.114 57.36 142.466 64.009 137.395L68.978 133.606L66.756 131.24C60.944 125.054 54.357 123.135 48.5 125.922ZM138.386 125.063C136.674 125.571 133.375 127.677 131.057 129.743L126.841 133.5L131.901 137.343C138.65 142.468 143.407 143.124 149.2 139.728C151.566 138.342 154.351 136.269 155.389 135.122C157.684 132.587 156.742 131.097 150.58 127.511C145.438 124.519 142.329 123.895 138.386 125.063ZM91.923 162.923C89.043 165.804 89 166.008 89 176.973C89 184.805 89.435 188.941 90.47 190.941C92.356 194.589 96.918 196.273 101.03 194.84C105.82 193.17 107 189.638 107 176.973C107 166.008 106.957 165.804 104.077 162.923C102.082 160.928 100.152 160 98 160C95.848 160 93.918 160.928 91.923 162.923ZM94.5 232.155C91.026 234.055 90 236.229 90 241.691V247H98H106V242.082C106 235.732 105.37 234.242 101.928 232.463C98.591 230.737 97.197 230.679 94.5 232.155Z\" fill=\"url(#paint0_linear_55_11)\"/>\n<defs><linearGradient id=\"paint0_linear_55_11\" x1=\"98\" y1=\"0.000244141\" x2=\"98\" y2=\"247\" gradientUnits=\"userSpaceOnUse\">\n<stop stop-color=\"#03B79C\"/><stop offset=\"0.504808\" stop-color=\"#4EDAA4\"/><stop offset=\"0.865285\" stop-color=\"#84F3AA\"/>\n</linearGradient></defs></svg>`;\n","/**\n * Client-side JS: Data transform, SVG progress rings, summary strip,\n * execution waterfall, and flow list rendering.\n */\nexport const JS_RENDER = `\n/* ── Utilities ── */\nfunction esc(s){if(!s)return '';return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\"/g,'&quot;').replace(/'/g,'&#39;')}\nfunction fmtDur(ms){if(!ms||ms<=0)return '\\\\u2014';if(ms<1000)return Math.round(ms)+'ms';if(ms<60000)return (ms/1000).toFixed(1)+'s';var m=Math.floor(ms/60000),s=Math.round((ms%60000)/1000);return s>0?m+'m '+s+'s':m+'m'}\nfunction fmtDate(iso){try{return new Date(iso).toLocaleString()}catch(e){return iso}}\nfunction $(id){return document.getElementById(id)}\n\n/* ── Data Transform ── */\nvar flows=[];\n(report.timeline||[]).forEach(function(entry,ei){\n if(!entry.tests)return;\n entry.tests.forEach(function(t,ti){\n flows.push({entry:entry,test:t,idx:ei*1000+ti});\n });\n});\nflows.sort(function(a,b){\n if(a.test.filePath!==b.test.filePath)return a.test.filePath<b.test.filePath?-1:1;\n return (a.test.startedAt||'')<(b.test.startedAt||'')?-1:1;\n});\n\nvar summary=report.summary||{};\nvar currentFilter='all';\nvar searchQuery='';\nvar openFlowIdx=-1;\n\nfunction detectPlatform(){\n for(var i=0;i<flows.length;i++){\n var tags=flows[i].test.tags||[];\n for(var j=0;j<tags.length;j++){\n var t=tags[j].toLowerCase();\n if(t==='android'||t==='ios'||t==='web')return t;\n }\n }\n return '';\n}\n\n/* ── SVG Progress Ring ── */\nfunction svgRing(pct,size,stroke,color){\n var r=(size-stroke)/2;\n var c=2*Math.PI*r;\n var offset=c*(1-pct/100);\n return '<svg width=\"'+size+'\" height=\"'+size+'\" viewBox=\"0 0 '+size+' '+size+'\">'\n +'<circle cx=\"'+(size/2)+'\" cy=\"'+(size/2)+'\" r=\"'+r+'\" fill=\"none\" stroke=\"var(--bd-l)\" stroke-width=\"'+stroke+'\"/>'\n +'<circle cx=\"'+(size/2)+'\" cy=\"'+(size/2)+'\" r=\"'+r+'\" fill=\"none\" stroke=\"'+color+'\" stroke-width=\"'+stroke+'\"'\n +' stroke-dasharray=\"'+c+'\" stroke-dashoffset=\"'+offset+'\" stroke-linecap=\"round\" transform=\"rotate(-90 '+(size/2)+' '+(size/2)+')\"/>'\n +'<text x=\"'+(size/2)+'\" y=\"'+(size/2+5)+'\" text-anchor=\"middle\" fill=\"var(--fg)\" font-size=\"'+(size>60?14:12)+'\" font-weight=\"700\">'+Math.round(pct)+'%</text>'\n +'</svg>';\n}\n\n/* ── Render: Run Meta ── */\nfunction renderRunMeta(){\n var el=$('run-meta');if(!el)return;\n var h='';\n h+='<span class=\"run-meta-item\"><span class=\"run-meta-label\">Run</span><span class=\"run-id-tag\">'+esc((report.testRunId||'').substring(0,12))+'</span></span>';\n h+='<span class=\"run-meta-item\"><span class=\"run-meta-label\">Duration</span>'+fmtDur(report.totalDuration)+'</span>';\n h+='<span class=\"run-meta-item\">'+fmtDate(report.startedAt)+'</span>';\n if(report.ci){\n h+='<span class=\"run-meta-item\"><span class=\"ci-tag\">'+esc(report.ci.provider)+'</span></span>';\n if(report.ci.branch)h+='<span class=\"run-meta-item\"><span class=\"run-meta-label\">Branch</span>'+esc(report.ci.branch)+'</span>';\n if(report.ci.commitSha)h+='<span class=\"run-meta-item\"><span class=\"run-meta-label\">Commit</span><span class=\"run-id-tag\">'+esc(report.ci.commitSha.substring(0,8))+'</span></span>';\n }\n var plat=detectPlatform();\n if(plat)h+='<span class=\"platform-tag\">'+esc(plat)+'</span>';\n el.innerHTML=h;\n}\n\n/* ── Render: Hero Strip (rings + chips) ── */\nfunction renderHeroStrip(){\n var el=$('hero-strip');if(!el)return;\n var passRate=summary.total>0?Math.round((summary.passed/summary.total)*100):0;\n var assertRate=summary.totalAssertions>0?Math.round((summary.passedAssertions/summary.totalAssertions)*100):0;\n\n var h='<div class=\"hero-top\">';\n h+='<div class=\"hero-rings\">';\n h+='<div class=\"ring-wrap\">'+svgRing(passRate,64,5,'#22c55e')+'<div class=\"ring-label\">Pass Rate</div></div>';\n h+='<div class=\"ring-wrap\">'+svgRing(assertRate,64,5,'#a78bfa')+'<div class=\"ring-label\">Assert Rate</div></div>';\n h+='</div>';\n\n h+='<div class=\"hero-chips\">';\n var chips=[\n {cls:'s-total',n:summary.total||0,l:'Total',f:'all'},\n {cls:'s-failed',n:summary.failed||0,l:'Failed',f:'failed'},\n {cls:'s-passed',n:summary.passed||0,l:'Passed',f:'passed'},\n {cls:'s-skipped',n:summary.skipped||0,l:'Skipped',f:'skipped'},\n {cls:'s-ai',n:aiDefects.length,l:'AI Defects',f:null}\n ];\n chips.forEach(function(c){\n h+='<div class=\"summary-chip '+c.cls+'\" data-filter=\"'+(c.f||'')+'\">'\n +'<div class=\"s-count\">'+c.n+'</div>'\n +'<div class=\"s-label\">'+c.l+'</div></div>';\n });\n h+='</div></div>';\n\n h+='<div class=\"hero-footer\">';\n h+='<span>Total Duration: <strong>'+fmtDur(report.totalDuration)+'</strong></span>';\n var plat=detectPlatform();\n if(plat)h+='<span>Platform: <strong>'+esc(plat)+'</strong></span>';\n if(summary.totalActionSteps)h+='<span>Steps: <strong>'+summary.totalActionSteps+'</strong></span>';\n if(summary.totalAssertions)h+='<span>Assertions: <strong>'+summary.totalAssertions+'</strong></span>';\n h+='</div>';\n\n el.innerHTML=h;\n\n el.querySelectorAll('.summary-chip[data-filter]').forEach(function(chip){\n chip.addEventListener('click',function(){\n var f=chip.dataset.filter;\n if(f){currentFilter=f;renderFilterBar();renderFlowList()}\n });\n });\n}\n\n/* ── Render: Execution Waterfall ── */\nfunction renderWaterfall(){\n var el=$('waterfall');if(!el)return;\n if(flows.length===0){el.style.display='none';return}\n var maxDur=0;\n flows.forEach(function(f){if(f.test.duration>maxDur)maxDur=f.test.duration});\n if(maxDur===0)maxDur=1;\n\n var h='<div class=\"waterfall-title\">Execution Timeline</div>';\n flows.forEach(function(f){\n var t=f.test;\n var pct=Math.max(1,Math.round((t.duration/maxDur)*100));\n var cls=t.status==='passed'?'wb-passed':t.status==='failed'?'wb-failed':'wb-skipped';\n h+='<div class=\"waterfall-row\" data-wf-idx=\"'+f.idx+'\">'\n +'<div class=\"waterfall-name\" title=\"'+esc(t.title)+'\">'+esc(t.title)+'</div>'\n +'<div class=\"waterfall-track\"><div class=\"waterfall-bar '+cls+'\" style=\"width:'+pct+'%\"></div></div>'\n +'<div class=\"waterfall-dur\">'+fmtDur(t.duration)+'</div>'\n +'</div>';\n });\n el.innerHTML=h;\n}\n\n/* ── Tag filter state ── */\nvar activeTagFilter='';\n\n/* ── Compute tag counts ── */\nfunction getTagCounts(){\n var tags={};\n flows.forEach(function(f){(f.test.tags||[]).forEach(function(t){\n var low=t.toLowerCase();if(low!=='android'&&low!=='ios'&&low!=='web')tags[t]=(tags[t]||0)+1;\n })});\n return tags;\n}\n\n/* ── Render: Filter Bar (clean: search + status pills + filter icon) ── */\nfunction renderFilterBar(){\n var el=$('filter-bar');if(!el)return;\n var h='';\n\n h+='<div class=\"search-box\"><svg class=\"search-icon\" width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"M21 21l-4.35-4.35\"/></svg>'\n +'<input class=\"search-input\" id=\"search-input\" type=\"text\" placeholder=\"Search flows...\" value=\"'+esc(searchQuery)+'\"></div>';\n\n h+='<div class=\"filter-chips\">';\n var statuses=['passed','failed','skipped'];\n var statusLabels={passed:'Passed',failed:'Failed',skipped:'Skipped'};\n statuses.forEach(function(s){\n var cnt=flows.filter(function(f){return f.test.status===s}).length;\n var cls='filter-chip'+(currentFilter===s?' active':'')+(cnt===0?' filter-chip--dimmed':'');\n h+='<button class=\"'+cls+'\" data-status=\"'+s+'\">'+statusLabels[s]+' <span class=\"chip-count\">'+cnt+'</span></button>';\n });\n h+='</div>';\n\n if(currentFilter!=='all'||activeTagFilter){\n var parts=[];\n if(currentFilter!=='all')parts.push(currentFilter);\n if(activeTagFilter)parts.push('#'+activeTagFilter);\n h+='<span class=\"filter-indicator\">Filtering: '+esc(parts.join(' + '))+'</span>';\n h+='<button class=\"filter-clear\">Clear all</button>';\n }\n\n var tagCounts=getTagCounts();\n var hasTagFilter=!!activeTagFilter;\n h+='<button class=\"filter-icon-btn\" title=\"Tags &amp; filters\">'\n +'<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M22 3H2l8 9.46V19l4 2v-8.54L22 3z\"/></svg>'\n +(hasTagFilter?'<span class=\"filter-icon-badge\"></span>':'')\n +'</button>';\n\n el.innerHTML=h;\n\n renderFilterDrawerBody(tagCounts);\n}\n\n/* ── Render: Filter Drawer Body ── */\nfunction renderFilterDrawerBody(tagCounts){\n var el=$('filter-drawer-body');if(!el)return;\n var tags=Object.keys(tagCounts).sort();\n if(tags.length===0){el.innerHTML='<div style=\"font-size:12px;color:var(--fg-2)\">No tags found in flow metadata.</div>';return}\n\n var h='<div class=\"filter-drawer-section\">';\n h+='<span class=\"filter-drawer-section-label\">Tags</span>';\n h+='<div class=\"filter-chips\">';\n tags.forEach(function(t){\n var cnt=tagCounts[t];\n var cls='filter-chip'+(activeTagFilter===t?' active':'');\n h+='<button class=\"'+cls+'\" data-tag=\"'+esc(t)+'\">'+esc(t)+' <span class=\"chip-count\">'+cnt+'</span></button>';\n });\n h+='</div></div>';\n\n var specCounts={};\n flows.forEach(function(f){\n var key=f.test.filePath||f.entry.specFile||'';\n if(key)specCounts[key]=(specCounts[key]||0)+1;\n });\n var specs=Object.keys(specCounts).sort();\n if(specs.length>1){\n h+='<div class=\"filter-drawer-section\">';\n h+='<span class=\"filter-drawer-section-label\">Flow Files</span>';\n h+='<div class=\"filter-chips\">';\n specs.forEach(function(sp){\n var short=sp.split(/[\\\\/\\\\\\\\]/).pop()||sp;\n h+='<button class=\"filter-chip\" data-tag=\"'+esc(short)+'\" title=\"'+esc(sp)+'\">'+esc(short)+' <span class=\"chip-count\">'+specCounts[sp]+'</span></button>';\n });\n h+='</div></div>';\n }\n\n if(activeTagFilter){\n h+='<div class=\"filter-drawer-actions\"><button class=\"filter-clear\">Clear all filters</button></div>';\n }\n\n el.innerHTML=h;\n}\n\n/* ── Render: Flow List ── */\nfunction getFilteredFlows(){\n return flows.filter(function(f){\n if(currentFilter!=='all'&&f.test.status!==currentFilter)return false;\n if(activeTagFilter){\n var tags=(f.test.tags||[]).map(function(t){return t.toLowerCase()});\n if(tags.indexOf(activeTagFilter.toLowerCase())<0)return false;\n }\n if(searchQuery){\n var q=searchQuery.toLowerCase();\n var title=(f.test.title||'').toLowerCase();\n var tagStr=(f.test.tags||[]).join(' ').toLowerCase();\n var file=(f.test.filePath||'').toLowerCase();\n if(title.indexOf(q)<0&&tagStr.indexOf(q)<0&&file.indexOf(q)<0)return false;\n }\n return true;\n });\n}\n\nfunction renderFlowList(){\n var el=$('flow-list');if(!el)return;\n var filtered=getFilteredFlows();\n if(filtered.length===0){el.innerHTML='<div class=\"no-tests\">No flows match the current filter.</div>';return}\n\n var groups={};\n filtered.forEach(function(f){\n var key=f.test.filePath||f.entry.specFile||'flows';\n if(!groups[key])groups[key]=[];\n groups[key].push(f);\n });\n\n var h='';\n for(var key in groups){\n if(!groups.hasOwnProperty(key))continue;\n h+='<div class=\"file-group\">';\n h+='<div class=\"file-group-header\">'+esc(key)+'</div>';\n h+='<div class=\"file-group-card\">';\n groups[key].forEach(function(f){\n var t=f.test;\n var badges='';\n if(f.entry.url&&f.entry.url!=='maestro-flow')badges+='<span class=\"trb trb-app\">'+esc(f.entry.url)+'</span>';\n (t.tags||[]).forEach(function(tag){\n var low=tag.toLowerCase();\n if(low==='android'||low==='ios'||low==='web')badges+='<span class=\"trb trb-platform\">'+esc(tag)+'</span>';\n else badges+='<span class=\"trb trb-tag\">'+esc(tag)+'</span>';\n });\n var activeClass=f.idx===openFlowIdx?' active':'';\n h+='<div class=\"test-row'+activeClass+'\" data-flow-idx=\"'+f.idx+'\">'\n +'<div class=\"status-indicator si-'+esc(t.status)+'\"></div>'\n +'<div class=\"test-row-info\">'\n +'<div class=\"test-row-title\">'+esc(t.title)+'</div>'\n +'<div class=\"test-row-badges\">'+badges+'</div>'\n +(t.failure?'<div class=\"test-row-error\">'+esc(t.failure.message||'')+'</div>':'')\n +'</div>'\n +'<div class=\"test-row-dur\">'+fmtDur(t.duration)+'</div>'\n +'<svg class=\"test-row-arrow\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"m9 18 6-6-6-6\"/></svg>'\n +'</div>';\n });\n h+='</div></div>';\n }\n el.innerHTML=h;\n}\n`;\n","/**\n * Client-side JS: Drawer lifecycle, tab switching, and Overview tab rendering\n * with failure diagnostic panel, root cause analysis, and fix code snippets.\n */\nexport const JS_DRAWER = `\n/* ── Drawer Open/Close ── */\nfunction openDrawer(flowIdx){\n openFlowIdx=flowIdx;\n renderFlowList();\n var flow=flows.find(function(f){return f.idx===flowIdx});\n if(!flow)return;\n $('drawer-backdrop').classList.add('open');\n $('drawer').classList.add('open');\n renderDrawerContent(flow);\n}\nfunction closeDrawer(){\n openFlowIdx=-1;\n renderFlowList();\n $('drawer-backdrop').classList.remove('open');\n $('drawer').classList.remove('open');\n}\n\nfunction renderDrawerContent(flow){\n var tabs=['Overview','Steps','Assertions','Screenshots','AI Defects'];\n var h='<div class=\"seg-ctrl\">';\n tabs.forEach(function(name,i){\n h+='<button class=\"seg-btn'+(i===0?' active':'')+'\" data-seg=\"tab'+i+'\">'+name+'</button>';\n });\n h+='</div>';\n\n h+='<div class=\"seg-pane active\" data-seg-pane=\"tab0\">'+renderOverviewTab(flow)+'</div>';\n h+='<div class=\"seg-pane\" data-seg-pane=\"tab1\">'+renderStepsTab(flow)+'</div>';\n h+='<div class=\"seg-pane\" data-seg-pane=\"tab2\">'+renderAssertionsTab(flow)+'</div>';\n h+='<div class=\"seg-pane\" data-seg-pane=\"tab3\">'+renderScreenshotsTab()+'</div>';\n h+='<div class=\"seg-pane\" data-seg-pane=\"tab4\">'+renderAiDefectsTab()+'</div>';\n\n $('drawer-body').innerHTML=h;\n}\n\n/* ── Overview Tab ── */\nfunction renderOverviewTab(flow){\n var t=flow.test;\n var h='<div class=\"detail-header\"><div class=\"detail-top\">';\n h+='<span class=\"detail-status-badge dbg-'+esc(t.status)+'\">'+esc(t.status)+'</span>';\n h+='<div class=\"detail-title-section\"><div class=\"detail-title\">'+esc(t.title)+'</div>';\n h+='<div class=\"detail-meta\">';\n if(flow.entry.url&&flow.entry.url!=='maestro-flow')h+='<span class=\"dm-badge dm-app\">'+esc(flow.entry.url)+'</span>';\n (t.tags||[]).forEach(function(tag){\n var low=tag.toLowerCase();\n if(low==='android'||low==='ios'||low==='web')h+='<span class=\"dm-badge dm-platform\">'+esc(tag)+'</span>';\n else h+='<span class=\"dm-badge dm-tag\">'+esc(tag)+'</span>';\n });\n h+='</div></div>';\n h+='<div class=\"detail-duration\">'+fmtDur(t.duration)+'</div>';\n h+='</div>';\n\n h+='<div class=\"detail-props\">';\n h+='<span class=\"detail-props-k\">Flow File</span><span class=\"detail-props-v\">'+esc(t.filePath||flow.entry.specFile||'\\\\u2014')+'</span>';\n h+='<span class=\"detail-props-k\">App ID</span><span class=\"detail-props-v\">'+esc(flow.entry.url||'\\\\u2014')+'</span>';\n h+='<span class=\"detail-props-k\">Suite</span><span class=\"detail-props-v\">'+esc(t.suiteName||'\\\\u2014')+'</span>';\n h+='<span class=\"detail-props-k\">Started</span><span class=\"detail-props-v\">'+esc(t.startedAt?fmtDate(t.startedAt):'\\\\u2014')+'</span>';\n h+='<span class=\"detail-props-k\">Test ID</span><span class=\"detail-props-v\">'+esc(t.testId||'\\\\u2014')+'</span>';\n h+='</div></div>';\n\n if(t.failure){\n h+='<div class=\"failure-panel\">';\n h+='<div class=\"failure-panel-header\"><svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"m15 9-6 6M9 9l6 6\"/></svg> Failure Diagnostic</div>';\n h+='<div class=\"failure-message\">'+esc(t.failure.message||'Unknown error')+'<button class=\"copy-btn\" data-copy=\"'+esc(t.failure.message||'')+'\">Copy</button></div>';\n h+='</div>';\n h+=renderRootCauseAnalysis(t,flow);\n }\n\n if(t.tags&&t.tags.length>0){\n var subflows=[];\n (t.actions||[]).forEach(function(a){\n if(a.title&&a.title.indexOf('runFlow')>=0)subflows.push(a.title.replace('runFlow \\\\u2192 ',''));\n });\n if(subflows.length>0){\n h+='<div class=\"subflow-tree\"><div class=\"sf-node\">'+esc(t.filePath||t.title)+'</div>';\n subflows.forEach(function(sf){h+='<div class=\"sf-child\"><div class=\"sf-node\">\\\\u2514\\\\u2500 '+esc(sf)+' (subflow)</div></div>'});\n h+='</div>';\n }\n }\n\n return h;\n}\n\n/* ── Root Cause Analysis ── */\nfunction renderRootCauseAnalysis(t,flow){\n var msg=(t.failure&&t.failure.message)||'';\n var lower=msg.toLowerCase();\n var actions=t.actions||[];\n var failedSteps=actions.filter(function(a){return a.status==='failed'});\n\n var analysis='';\n if(lower.indexOf('not found')>=0||lower.indexOf('not visible')>=0||lower.indexOf('could not find')>=0){\n var prevFailed=failedSteps.length>1;\n analysis+='<p>The test attempted to interact with an element that was not present in the view hierarchy.';\n if(prevFailed)analysis+=' The preceding step also failed, suggesting the target screen did not fully load.';\n analysis+='</p>';\n }else if(lower.indexOf('timeout')>=0||lower.indexOf('timed out')>=0){\n analysis+='<p>The operation exceeded its timeout threshold. This typically indicates a slow network response, a heavy UI render, or the target element appearing after the timeout window.</p>';\n }else if(lower.indexOf('assert')>=0){\n analysis+='<p>An assertion did not match the expected state. This could indicate a UI regression, dynamic content change, or a race condition between the app and the test.</p>';\n }else{\n analysis+='<p>Review the step timeline to identify the exact failing command and its preceding context.</p>';\n }\n\n var h='<div class=\"fix-hint\">';\n h+='<div class=\"fix-hint-header\"><svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 16v-4M12 8h.01\"/></svg> Root Cause Analysis</div>';\n h+='<div class=\"fix-hint-body\">';\n h+='<div class=\"fix-hint-section\"><div class=\"fix-hint-section-title\">What Went Wrong</div>'+analysis+'</div>';\n\n h+='<div class=\"fix-hint-section\"><div class=\"fix-hint-section-title\">Recommended Actions</div><ul>';\n if(lower.indexOf('not found')>=0||lower.indexOf('not visible')>=0){\n h+='<li>Add <code>extendedWaitUntil</code> before the interaction:</li>';\n h+='</ul><div class=\"fix-code\">- extendedWaitUntil:\\\\n visible: \"Pay Now\"\\\\n timeout: 10000</div><ul>';\n h+='<li>Verify the selector with <strong>Maestro Studio</strong> on the target device</li>';\n h+='<li>Add an <code>assertVisible</code> guard before <code>tapOn</code></li>';\n h+='<li>Check if the element requires scrolling into view first</li>';\n h+='<li>Inspect network requests \\\\u2014 the backend API may be slow</li>';\n }else if(lower.indexOf('timeout')>=0||lower.indexOf('timed out')>=0){\n h+='<li>Increase timeout with <code>extendedWaitUntil</code> (default 5s may be too short)</li>';\n h+='<li>Check for loading spinners or network requests blocking the UI</li>';\n h+='<li>Run on a faster emulator or physical device to rule out performance</li>';\n h+='<li>Add <code>waitForAnimationToEnd</code> before the assertion</li>';\n }else if(lower.indexOf('assert')>=0){\n h+='<li>Verify the expected text/element exists on the current screen</li>';\n h+='<li>Check for dynamic content that changes between runs</li>';\n h+='<li>Use <code>assertWithAI</code> for fuzzy visual assertions</li>';\n h+='<li>Add <code>waitForAnimationToEnd</code> before <code>assertVisible</code></li>';\n }else{\n h+='<li>Review the step timeline for the exact failing command</li>';\n h+='<li>Run the flow in <strong>Maestro Studio</strong> to debug interactively</li>';\n h+='<li>Check device logs for crash traces or ANR</li>';\n h+='<li>Try <code>clearState</code> before <code>launchApp</code></li>';\n }\n h+='</ul></div>';\n\n h+='</div></div>';\n return h;\n}\n`;\n","/**\n * Client-side JS: Step waterfall mini-chart and step timeline\n * with duration heatmap colouring and SLOW badges.\n */\nexport const JS_STEPS = `\nfunction renderStepsTab(flow){\n var actions=flow.test.actions||[];\n if(actions.length===0)return '<div class=\"no-tests\">No step data available for this flow.</div>';\n\n var failCount=actions.filter(function(a){return a.status==='failed'}).length;\n var slowCount=actions.filter(function(a){return a.duration>2000}).length;\n var maxDur=0;\n actions.forEach(function(a){if(a.duration>maxDur)maxDur=a.duration});\n\n var h='<div class=\"steps-header\">'+actions.length+' steps'\n +'<span class=\"steps-stat\">'+failCount+' failed</span>'\n +(slowCount?'<span class=\"steps-stat\">'+slowCount+' slow (&gt;2s)</span>':'')\n +'<span class=\"steps-stat\">slowest: '+fmtDur(maxDur)+'</span></div>';\n\n /* Mini waterfall */\n h+='<div class=\"steps-waterfall\">';\n var wfMax=maxDur||1;\n actions.forEach(function(a){\n var pct=Math.max(1,Math.round((a.duration/wfMax)*100));\n var barCls=a.status==='failed'?'sw-bar-red':a.duration>2000?'sw-bar-amber':a.duration>500?'sw-bar-amber':'sw-bar-green';\n var nameStr=a.title||'step';\n if(nameStr.length>18)nameStr=nameStr.substring(0,18)+'\\\\u2026';\n h+='<div class=\"sw-row\">'\n +'<div class=\"sw-name\" title=\"'+esc(a.title)+'\">'+esc(nameStr)+'</div>'\n +'<div class=\"sw-track\"><div class=\"sw-bar '+barCls+'\" style=\"width:'+pct+'%\"></div></div>'\n +'<div class=\"sw-dur\">'+fmtDur(a.duration)+'</div></div>';\n });\n h+='</div>';\n\n /* Step list */\n actions.forEach(function(a,i){\n var isFailed=a.status==='failed';\n var isSlow=a.duration>2000&&!isFailed;\n var isFast=a.duration<=500&&!isFailed;\n var rowCls='cmd-step';\n if(isFailed)rowCls+=' cmd-step-failed';\n else if(isSlow)rowCls+=' cmd-step-slow';\n else if(isFast)rowCls+=' cmd-step-fast';\n var dotCls='cmd-dot '+(isFailed?'cmd-dot-fail':'cmd-dot-pass');\n h+='<div class=\"'+rowCls+'\">';\n h+='<div class=\"'+dotCls+'\"></div>';\n h+=getCmdBadge(a.category);\n h+='<div class=\"cmd-title\" title=\"'+esc(a.title)+'\">'+esc(a.title)+'</div>';\n if(isSlow||isFailed&&a.duration>2000)h+='<span class=\"cmd-badge cmd-badge-slow\">SLOW</span>';\n h+='<div class=\"cmd-dur\">'+fmtDur(a.duration)+'</div>';\n h+='</div>';\n if(a.error)h+='<div class=\"cmd-error\">'+esc(a.error)+'</div>';\n if(i<actions.length-1)h+='<div class=\"cmd-connector\"></div>';\n });\n\n h+='<div class=\"steps-legend\">';\n h+='<span><span class=\"cmd-badge cmd-badge-ui\">UI</span> ui_action</span>';\n h+='<span><span class=\"cmd-badge cmd-badge-assert\">ASSERT</span> assertion</span>';\n h+='<span><span class=\"cmd-badge cmd-badge-ai\">AI</span> ai</span>';\n h+='<span><span class=\"cmd-badge cmd-badge-cmd\">CMD</span> custom_step</span>';\n h+='<span><span class=\"cmd-badge cmd-badge-slow\">SLOW</span> &gt; 2s</span>';\n h+='</div>';\n h+='<div class=\"steps-legend\" style=\"margin-top:4px\">';\n h+='<span>Duration: <span style=\"color:#22c55e;font-weight:700\">&lt;500ms</span></span>';\n h+='<span style=\"color:#f59e0b;font-weight:700\">500ms\\\\u20132s</span>';\n h+='<span style=\"color:#ef4444;font-weight:700\">&gt;2s</span>';\n h+='</div>';\n return h;\n}\n\nfunction getCmdBadge(cat){\n var m={ui_action:['UI','cmd-badge-ui'],assertion:['ASSERT','cmd-badge-assert'],navigation:['NAV','cmd-badge-nav'],custom_step:['CMD','cmd-badge-cmd'],ai:['AI','cmd-badge-ai'],device:['DEV','cmd-badge-device']};\n var pair=m[cat]||['CMD','cmd-badge-cmd'];\n return '<span class=\"cmd-badge '+pair[1]+'\">'+pair[0]+'</span>';\n}\n`;\n","/**\n * Client-side JS: Assertions tab with SVG progress ring and detail table.\n */\nexport const JS_ASSERTIONS = `\nfunction renderAssertionsTab(flow){\n var actions=flow.test.actions||[];\n var asserts=actions.filter(function(a){return a.category==='assertion'});\n if(asserts.length===0)return '<div class=\"no-tests\">No assertions recorded for this flow.</div>';\n\n var passCount=asserts.filter(function(a){return a.status==='passed'}).length;\n var failCount=asserts.length-passCount;\n var pct=Math.round((passCount/asserts.length)*100);\n\n var h='<div class=\"assert-header\">';\n h+='<div class=\"assert-ring\">'+svgRing(pct,56,4,'#22c55e')+'</div>';\n h+='<div class=\"assert-stats\"><strong>'+asserts.length+'</strong> assertions<br>';\n h+='<span style=\"color:#22c55e;font-weight:700\">'+passCount+' passed</span> &middot; ';\n h+='<span style=\"color:#ef4444;font-weight:700\">'+failCount+' failed</span></div>';\n h+='<div class=\"assert-bar-wrap\"><div class=\"assert-bar\"><div class=\"assert-bar-fill\" style=\"width:'+pct+'%\"></div></div>';\n h+='<div class=\"assert-pct\">'+pct+'% pass rate</div></div>';\n h+='</div>';\n\n h+='<table class=\"assert-table\"><thead><tr><th>Result</th><th>Command</th><th>Duration</th></tr></thead><tbody>';\n asserts.forEach(function(a){\n var icon=a.status==='passed'?'\\\\u2713':'\\\\u2717';\n var cls=a.status==='passed'?'ar-pass':'ar-fail';\n h+='<tr><td class=\"assert-result '+cls+'\">'+icon+'</td>';\n h+='<td style=\"font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px\">'+esc(a.title)+'</td>';\n h+='<td class=\"cmd-dur\">'+fmtDur(a.duration)+'</td></tr>';\n if(a.error)h+='<tr class=\"assert-error-row\"><td></td><td colspan=\"2\">'+esc(a.error)+'</td></tr>';\n });\n h+='</tbody></table>';\n return h;\n}\n`;\n","/**\n * Client-side JS: AI defect cards with severity accent stripes\n * and platform-specific fix playbooks.\n */\nexport const JS_AI_DEFECTS = `\nfunction renderAiDefectsTab(){\n if(aiDefects.length===0)return '<div class=\"ai-empty\">\\\\u2714 No AI defects detected.</div>';\n var h='<div style=\"font-size:12px;color:var(--fg-1);margin-bottom:16px\">'+aiDefects.length+' defect'+(aiDefects.length>1?'s':'')+' found by Maestro AI analysis</div>';\n\n aiDefects.forEach(function(d){\n var sev=d.severity||'info';\n var type=(d.type||'ui').toLowerCase();\n h+='<div class=\"ai-card ai-card-'+sev+'\">';\n h+='<div class=\"ai-card-header\"><span class=\"ai-severity ai-sev-'+sev+'\">'+esc(sev)+'</span><span class=\"ai-type\">'+esc(d.type||'UI')+'</span></div>';\n h+='<div class=\"ai-desc\">'+esc(d.description||'')+'</div>';\n h+='<div class=\"ai-fix\"><div class=\"ai-fix-label\">Fix Playbook</div><div class=\"ai-fix-body\">'+getFixPlaybook(type,d)+'</div></div>';\n h+='</div>';\n });\n return h;\n}\n\nfunction getFixPlaybook(type,d){\n if(type==='ui')return uiFixPlaybook(d);\n if(type==='spelling')return spellingFixPlaybook(d);\n if(type==='layout')return layoutFixPlaybook(d);\n if(type==='i18n')return i18nFixPlaybook(d);\n if(type==='accessibility')return a11yFixPlaybook(d);\n return genericFixPlaybook(d);\n}\n\nfunction uiFixPlaybook(d){\n return '<div class=\"ai-fix-platform\">Android (XML)</div>'\n +'<div class=\"ai-fix-code\">android:layout_width=\"wrap_content\"\\\\nandroid:minWidth=\"120dp\"\\\\nandroid:paddingHorizontal=\"16dp\"</div>'\n +'<div class=\"ai-fix-platform\">Jetpack Compose</div>'\n +'<div class=\"ai-fix-code\">Modifier\\\\n .widthIn(min = 120.dp)\\\\n .padding(horizontal = 16.dp)</div>'\n +'<div class=\"ai-fix-platform\">Verify</div>'\n +'<div class=\"ai-fix-code\">maestro test --device Pixel_4a ./flows</div>'\n +'<ul><li>Test on smallest supported screen (360dp width)</li>'\n +'<li>Run <code>assertNoDefectsWithAI</code> to catch regressions</li></ul>';\n}\n\nfunction spellingFixPlaybook(d){\n var desc=d.description||'';\n var match=desc.match(/\"([^\"]+)\"/);\n var typo=match?match[1]:'typo';\n return '<div class=\"ai-fix-platform\">Find &amp; Replace</div>'\n +'<div class=\"ai-fix-code\">grep -r \"'+esc(typo)+'\" --include=\"*.xml\" --include=\"*.strings\"\\\\ngrep -r \"'+esc(typo)+'\" --include=\"*.kt\" --include=\"*.swift\"</div>'\n +'<ul><li>Android: <code>res/values/strings.xml</code></li>'\n +'<li>iOS: <code>Localizable.strings</code></li>'\n +'<li>Add a CI linting rule for common typos</li></ul>';\n}\n\nfunction layoutFixPlaybook(d){\n return '<div class=\"ai-fix-platform\">Android (XML)</div>'\n +'<div class=\"ai-fix-code\">android:maxLines=\"1\"\\\\nandroid:ellipsize=\"end\"</div>'\n +'<div class=\"ai-fix-platform\">iOS (Swift)</div>'\n +'<div class=\"ai-fix-code\">label.lineBreakMode = .byTruncatingTail\\\\nlabel.numberOfLines = 1</div>'\n +'<ul><li>Use ConstraintLayout / Auto Layout to prevent overlap</li>'\n +'<li>Test with long strings (20+ characters)</li></ul>';\n}\n\nfunction i18nFixPlaybook(d){\n return '<ul><li>Audit translation files for missing keys</li>'\n +'<li>Run extraction tool to find untranslated strings</li>'\n +'<li>Verify all locales in CI with locale-specific flows</li></ul>'\n +'<div class=\"ai-fix-platform\">Example Flow</div>'\n +'<div class=\"ai-fix-code\">env:\\\\n LANG: fr_FR\\\\n---\\\\n- launchApp\\\\n- assertVisible: \"Bienvenue\"</div>';\n}\n\nfunction a11yFixPlaybook(d){\n return '<div class=\"ai-fix-platform\">Android</div>'\n +'<div class=\"ai-fix-code\">android:contentDescription=\"1 item in cart\"</div>'\n +'<div class=\"ai-fix-platform\">iOS (Swift)</div>'\n +'<div class=\"ai-fix-code\">cartBadge.accessibilityLabel = \"1 item in cart\"</div>'\n +'<ul><li>WCAG 2.1 SC 1.1.1: Non-text Content</li>'\n +'<li>Ensure 4.5:1 contrast ratio (SC 1.4.3)</li>'\n +'<li>Test with TalkBack (Android) / VoiceOver (iOS)</li></ul>';\n}\n\nfunction genericFixPlaybook(d){\n return '<ul><li>Review the defect description and identify the affected screen</li>'\n +'<li>Reproduce on a physical device</li>'\n +'<li>Add <code>assertNoDefectsWithAI</code> for regression detection</li></ul>';\n}\n`;\n","/**\n * Client-side JS: Event delegation, theme toggle, search,\n * filter chips, lightbox with prev/next/keyboard, and screenshots.\n */\nexport const JS_INTERACTIONS = `\n/* ── Theme ── */\nfunction applyTheme(){\n var pref=localStorage.getItem('tr-theme')||'system';\n var resolved=pref==='system'?(window.matchMedia('(prefers-color-scheme:light)').matches?'light':'dark'):pref;\n document.documentElement.setAttribute('data-theme',resolved);\n document.querySelectorAll('.theme-btn').forEach(function(b){\n b.classList.toggle('active',b.dataset.themeVal===pref);\n });\n}\n\n/* ── Screenshots Tab ── */\nfunction renderScreenshotsTab(){\n if(screenshotPaths.length===0)return '<div class=\"screenshot-empty\">No screenshots captured for this run.</div>';\n var h='<div class=\"screenshot-grid\">';\n screenshotPaths.forEach(function(p,i){\n var name=p.split(/[\\\\/\\\\\\\\]/).pop()||'screenshot';\n var isFail=name.toLowerCase().indexOf('fail')>=0||name.toLowerCase().indexOf('error')>=0;\n h+='<div class=\"screenshot-card'+(isFail?' failure-shot':'')+'\" data-lb-idx=\"'+i+'\">'\n +'<img src=\"'+esc(p)+'\" alt=\"'+esc(name)+'\" loading=\"lazy\" onerror=\"this.parentElement.style.display=\\\\'none\\\\'\"/>'\n +'<div class=\"screenshot-card-label\">'+(isFail?'Failure Screenshot':esc(name))+'</div></div>';\n });\n h+='</div>';\n return h;\n}\n\n/* ── Lightbox ── */\nfunction openLightbox(idx){\n if(idx<0||idx>=screenshotPaths.length)return;\n var existing=document.querySelector('.lightbox-overlay');\n if(existing)existing.remove();\n\n var name=screenshotPaths[idx].split(/[\\\\/\\\\\\\\]/).pop()||'screenshot';\n var div=document.createElement('div');\n div.className='lightbox-overlay';\n div.innerHTML='<img src=\"'+esc(screenshotPaths[idx])+'\"/>'\n +'<button class=\"lightbox-close\">&times;</button>'\n +(idx>0?'<button class=\"lightbox-nav lightbox-prev\">&lsaquo;</button>':'')\n +(idx<screenshotPaths.length-1?'<button class=\"lightbox-nav lightbox-next\">&rsaquo;</button>':'')\n +'<div class=\"lightbox-caption\">'+esc(name)+' ('+(idx+1)+' / '+screenshotPaths.length+')</div>';\n document.body.appendChild(div);\n\n function cleanup(){div.remove();document.removeEventListener('keydown',keyHandler)}\n function keyHandler(e){\n if(e.key==='Escape'){cleanup()}\n if(e.key==='ArrowLeft'&&idx>0){cleanup();openLightbox(idx-1)}\n if(e.key==='ArrowRight'&&idx<screenshotPaths.length-1){cleanup();openLightbox(idx+1)}\n }\n div.querySelector('.lightbox-close').addEventListener('click',cleanup);\n div.addEventListener('click',function(e){if(e.target===div)cleanup()});\n var prev=div.querySelector('.lightbox-prev');\n var next=div.querySelector('.lightbox-next');\n if(prev)prev.addEventListener('click',function(e){e.stopPropagation();cleanup();openLightbox(idx-1)});\n if(next)next.addEventListener('click',function(e){e.stopPropagation();cleanup();openLightbox(idx+1)});\n document.addEventListener('keydown',keyHandler);\n}\n\n/* ── Event Delegation ── */\ndocument.addEventListener('click',function(e){\n var target=e.target;\n\n /* Test row -> open drawer */\n var row=target.closest('.test-row');\n if(row){openDrawer(parseInt(row.dataset.flowIdx));return}\n\n /* Waterfall row -> open drawer */\n var wfRow=target.closest('.waterfall-row');\n if(wfRow){openDrawer(parseInt(wfRow.dataset.wfIdx));return}\n\n /* Drawer close */\n if(target.closest('#drawer-close')){closeDrawer();return}\n if(target.closest('#drawer-backdrop')){closeDrawer();return}\n\n /* Filter chip (status) */\n var statusChip=target.closest('.filter-chip[data-status]');\n if(statusChip){\n var s=statusChip.dataset.status;\n currentFilter=currentFilter===s?'all':s;\n renderFilterBar();renderFlowList();\n return;\n }\n\n /* Filter chip (tag — in filter drawer) */\n var tagChip=target.closest('.filter-chip[data-tag]');\n if(tagChip){\n var tag=tagChip.dataset.tag;\n activeTagFilter=activeTagFilter===tag?'':tag;\n renderFilterBar();renderFlowList();\n return;\n }\n\n /* Filter icon -> open filter drawer */\n if(target.closest('.filter-icon-btn')){\n $('filter-drawer-backdrop').classList.add('open');\n $('filter-drawer').classList.add('open');\n return;\n }\n\n /* Filter drawer close */\n if(target.closest('#filter-drawer-close')||target.closest('#filter-drawer-backdrop')){\n $('filter-drawer-backdrop').classList.remove('open');\n $('filter-drawer').classList.remove('open');\n return;\n }\n\n /* Clear all filters */\n if(target.closest('.filter-clear')){\n currentFilter='all';searchQuery='';activeTagFilter='';\n $('filter-drawer-backdrop').classList.remove('open');\n $('filter-drawer').classList.remove('open');\n renderFilterBar();renderFlowList();\n return;\n }\n\n /* Seg-ctrl tab switching */\n var segBtn=target.closest('.seg-btn');\n if(segBtn){\n var parent=segBtn.parentElement;\n if(parent&&parent.classList.contains('seg-ctrl')){\n var container=parent.parentElement;\n parent.querySelectorAll('.seg-btn').forEach(function(b){b.classList.remove('active')});\n segBtn.classList.add('active');\n container.querySelectorAll('.seg-pane').forEach(function(p){p.classList.remove('active')});\n var pane=container.querySelector('[data-seg-pane=\"'+segBtn.dataset.seg+'\"]');\n if(pane)pane.classList.add('active');\n }\n return;\n }\n\n /* Copy button */\n var copyBtn=target.closest('.copy-btn');\n if(copyBtn){\n e.stopPropagation();\n var text=copyBtn.dataset.copy||'';\n navigator.clipboard.writeText(text).then(function(){copyBtn.textContent='Copied!'});\n setTimeout(function(){copyBtn.textContent='Copy'},1500);\n return;\n }\n\n /* Screenshot lightbox */\n var card=target.closest('.screenshot-card');\n if(card&&card.dataset.lbIdx!==undefined){openLightbox(parseInt(card.dataset.lbIdx));return}\n});\n\n/* Theme buttons */\ndocument.querySelectorAll('.theme-btn').forEach(function(b){\n b.addEventListener('click',function(){\n localStorage.setItem('tr-theme',b.dataset.themeVal);\n applyTheme();\n });\n});\n\n/* Search input */\ndocument.addEventListener('input',function(e){\n if(e.target&&e.target.id==='search-input'){\n searchQuery=e.target.value.toLowerCase();\n renderFlowList();\n }\n});\n\n/* Escape to close drawer */\ndocument.addEventListener('keydown',function(e){\n if(e.key==='Escape'&&$('drawer').classList.contains('open'))closeDrawer();\n});\n`;\n","/**\n * HTML Template for TestRelic Maestro Report.\n *\n * Orchestrator: imports CSS, logo, and JS modules; assembles them\n * into a single self-contained HTML document. Same architecture as\n * the Appium html-template.ts.\n */\n\nimport { CSS } from './html-css.js';\nimport { LOGO_SVG, LOGO_SVG_RAW } from './html-logo.js';\nimport { JS_RENDER } from './html-js-render.js';\nimport { JS_DRAWER } from './html-js-drawer.js';\nimport { JS_STEPS } from './html-js-steps.js';\nimport { JS_ASSERTIONS } from './html-js-assertions.js';\nimport { JS_AI_DEFECTS } from './html-js-ai-defects.js';\nimport { JS_INTERACTIONS } from './html-js-interactions.js';\n\nconst JS = `\n(function(){\n var payload=JSON.parse(document.getElementById('report-data').textContent);\n var report=payload.report;\n var aiDefects=payload.aiDefects||[];\n var screenshotPaths=payload.screenshotPaths||[];\n\n ${JS_RENDER}\n ${JS_DRAWER}\n ${JS_STEPS}\n ${JS_ASSERTIONS}\n ${JS_AI_DEFECTS}\n ${JS_INTERACTIONS}\n\n applyTheme();\n renderRunMeta();\n renderHeroStrip();\n renderWaterfall();\n renderFilterBar();\n renderFlowList();\n\n var _lo=document.getElementById('loading-overlay');if(_lo)_lo.remove();\n})();`;\n\nexport function renderHtmlDocument(reportJson: string): string {\n const safeJson = reportJson.replace(/<\\//g, '<\\\\/');\n return `<!-- TestRelic Maestro Report — self-contained, no external dependencies -->\n<!DOCTYPE html>\n<html lang=\"en\" data-theme=\"\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'none'; style-src 'unsafe-inline'; script-src 'unsafe-inline'; img-src data: blob: file: 'self'; media-src blob: 'self'; connect-src 'self';\">\n<title>TestRelic Maestro Test Report</title>\n<link rel=\"icon\" type=\"image/svg+xml\" href=\"data:image/svg+xml;base64,${Buffer.from(LOGO_SVG_RAW).toString('base64')}\">\n<style>${CSS}</style>\n<script>(function(){var p=localStorage.getItem('tr-theme')||'system';var t=p==='system'?(window.matchMedia('(prefers-color-scheme:light)').matches?'light':'dark'):p;document.documentElement.setAttribute('data-theme',t);})()</script>\n</head>\n<body>\n<div class=\"wrap\">\n <div class=\"top-bar\">\n <div class=\"brand\">\n ${LOGO_SVG}\n <div class=\"brand-text\">\n <span class=\"brand-title\">TestRelic AI</span>\n <span class=\"brand-sub\">Maestro Test Report</span>\n </div>\n </div>\n <div class=\"run-meta\" id=\"run-meta\"></div>\n <div class=\"top-bar-actions\">\n <a class=\"cta-btn\" href=\"https://testrelic.ai\" target=\"_blank\" rel=\"noopener noreferrer\"><svg class=\"cta-icon\" viewBox=\"0 0 16 16\" fill=\"none\"><path d=\"M8 1l1.545 4.955L14.5 7.5l-4.955 1.545L8 14l-1.545-4.955L1.5 7.5l4.955-1.545L8 1z\" fill=\"currentColor\"/></svg>Advanced Insights with AI<span style=\"color:var(--fg-2);margin:0 2px\">&ndash;</span><strong>Get Started</strong><svg class=\"cta-arrow\" viewBox=\"0 0 12 12\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\"><path d=\"M2.5 6h7M6.5 3l3 3-3 3\"/></svg></a>\n <div class=\"theme-toggle\">\n <button class=\"theme-btn\" data-theme-val=\"system\" title=\"System\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><rect x=\"2\" y=\"3\" width=\"20\" height=\"14\" rx=\"2\"/><path d=\"M8 21h8M12 17v4\"/></svg></button>\n <button class=\"theme-btn\" data-theme-val=\"light\" title=\"Light\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"5\"/><path d=\"M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42\"/></svg></button>\n <button class=\"theme-btn\" data-theme-val=\"dark\" title=\"Dark\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z\"/></svg></button>\n </div>\n </div>\n </div>\n\n <div id=\"hero-strip\" class=\"hero-strip\"></div>\n <div id=\"waterfall\" class=\"waterfall-card\"></div>\n <div id=\"filter-bar\" class=\"filter-bar\"></div>\n <div id=\"flow-list\"></div>\n\n <div class=\"report-footer\">\n Generated by <a href=\"https://testrelic.ai\">TestRelic AI</a> &middot; <code>@testrelic/maestro-analytics</code> &middot; <a href=\"https://docs.testrelic.ai\">Documentation</a>\n </div>\n</div>\n\n<div class=\"loading-overlay\" id=\"loading-overlay\"><div class=\"loading-spinner\"></div><div class=\"loading-text\">Loading report...</div></div>\n<div class=\"filter-drawer-backdrop\" id=\"filter-drawer-backdrop\"></div>\n<aside class=\"filter-drawer\" id=\"filter-drawer\">\n <div class=\"filter-drawer-bar\">\n <span class=\"filter-drawer-title\">Filters</span>\n <button class=\"filter-drawer-close\" id=\"filter-drawer-close\">&times;</button>\n </div>\n <div class=\"filter-drawer-body\" id=\"filter-drawer-body\"></div>\n</aside>\n<div class=\"drawer-backdrop\" id=\"drawer-backdrop\"></div>\n<aside class=\"drawer\" id=\"drawer\">\n <div class=\"drawer-bar\">\n <span class=\"drawer-bar-title\">Flow Details</span>\n <button class=\"drawer-close\" id=\"drawer-close\">&times;</button>\n </div>\n <div class=\"drawer-body\" id=\"drawer-body\"></div>\n</aside>\n<script id=\"report-data\" type=\"application/json\">${safeJson}</script>\n<script>${JS}</script>\n</body>\n</html>`;\n}\n","/**\n * Generate and write the HTML report file.\n *\n * Serialises report data + AI defects + screenshot paths to JSON\n * and delegates to renderHtmlDocument() from html-template.ts.\n */\n\nimport { existsSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport type { TestRunReport } from '@testrelic/core';\nimport type { AIDefect } from './types.js';\nimport { renderHtmlDocument } from './html-template.js';\n\nexport function generateHtmlReport(\n report: TestRunReport,\n aiDefects: AIDefect[],\n screenshotPaths: string[],\n outputPath: string,\n): void {\n const htmlPath = resolve(outputPath);\n const htmlDir = dirname(htmlPath);\n if (!existsSync(htmlDir)) mkdirSync(htmlDir, { recursive: true });\n\n const payload = { report, aiDefects, screenshotPaths };\n const reportJson = JSON.stringify(payload);\n const html = renderHtmlDocument(reportJson);\n writeFileSync(htmlPath, html, 'utf-8');\n}\n","/**\n * Formatted console summary output for Maestro test runs.\n */\n\nimport type { Summary } from '@testrelic/core';\nimport type { AIDefect } from './types.js';\n\nconst BOX_WIDTH = 62;\n\nfunction pad(text: string): string {\n const padding = BOX_WIDTH - 2 - text.length;\n return padding > 0 ? text + ' '.repeat(padding) : text;\n}\n\nfunction line(text: string): string {\n return `\\u2502 ${pad(text)} \\u2502\\n`;\n}\n\nexport function printConsoleSummary(\n summary: Summary,\n outputPath: string,\n htmlReportPath: string,\n quiet: boolean,\n aiDefects?: AIDefect[],\n): void {\n if (quiet) return;\n if (summary.total === 0) return;\n\n const top = `\\u250C${'─'.repeat(BOX_WIDTH)}\\u2510\\n`;\n const bottom = `\\u2514${'─'.repeat(BOX_WIDTH)}\\u2518\\n`;\n const blank = line('');\n\n let output = top;\n output += line('TestRelic AI - Maestro Test Report');\n output += blank;\n\n const parts: string[] = [];\n if (summary.passed > 0) parts.push(`${summary.passed} \\u2713`);\n if (summary.failed > 0) parts.push(`${summary.failed} \\u2717`);\n if (summary.flaky > 0) parts.push(`${summary.flaky} \\u26A0`);\n if (summary.skipped > 0) parts.push(`${summary.skipped} skipped`);\n if (summary.timedout > 0) parts.push(`${summary.timedout} timedout`);\n output += line(`Flows: ${summary.total} total (${parts.join(' ')})`);\n\n if (summary.totalAssertions > 0) {\n output += line(\n `Assertions: ${summary.totalAssertions} total (${summary.passedAssertions} \\u2713 ${summary.failedAssertions} \\u2717)`,\n );\n }\n\n if (summary.totalActionSteps > 0) {\n const catParts: string[] = [];\n const catMap = summary.actionStepsByCategory;\n if (catMap['ui_action']) catParts.push(`${catMap['ui_action']} UI`);\n if (catMap['navigation']) catParts.push(`${catMap['navigation']} nav`);\n if (catMap['custom_step']) catParts.push(`${catMap['custom_step']} custom`);\n const catStr = catParts.length > 0 ? ` (${catParts.join(' ')})` : '';\n output += line(`Steps: ${summary.totalActionSteps} total${catStr}`);\n }\n\n if (aiDefects && aiDefects.length > 0) {\n const critical = aiDefects.filter((d) => d.severity === 'critical').length;\n const warning = aiDefects.filter((d) => d.severity === 'warning').length;\n const info = aiDefects.filter((d) => d.severity === 'info').length;\n const defectParts: string[] = [];\n if (critical > 0) defectParts.push(`${critical} critical`);\n if (warning > 0) defectParts.push(`${warning} warning`);\n if (info > 0) defectParts.push(`${info} info`);\n output += line(`AI Defects: ${aiDefects.length} found (${defectParts.join(' ')})`);\n }\n\n output += line(`Report: ${htmlReportPath}`);\n output += line(`Data: ${outputPath}`);\n\n output += bottom;\n output += 'For more information visit us at https://docs.testrelic.ai\\n';\n\n process.stderr.write(output);\n}\n","/**\n * Open a file path in the user's default browser.\n */\n\nimport { exec } from 'node:child_process';\n\nexport function openInBrowser(filePath: string): void {\n const platform = process.platform;\n let command: string;\n\n if (platform === 'darwin') {\n command = `open \"${filePath}\"`;\n } else if (platform === 'win32') {\n command = `start \"\" \"${filePath}\"`;\n } else {\n command = `xdg-open \"${filePath}\"`;\n }\n\n exec(command, () => {\n // Silently ignore errors\n });\n}\n","/**\n * Maestro CLI runner — spawns `maestro test` as a child process\n * with injected flags for report generation and artifact capture.\n */\n\nimport { spawn } from 'node:child_process';\nimport { mkdirSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport { tmpdir } from 'node:os';\nimport { randomBytes } from 'node:crypto';\nimport type { MaestroTestOptions } from './types.js';\n\nexport interface MaestroRunResult {\n readonly exitCode: number;\n readonly junitPath: string;\n readonly testOutputDir: string;\n readonly debugOutputDir: string;\n readonly stdout: string;\n readonly stderr: string;\n}\n\nfunction generateTempDir(): string {\n const id = randomBytes(8).toString('hex');\n return join(tmpdir(), `testrelic-maestro-${id}`);\n}\n\nexport function buildMaestroArgs(options: MaestroTestOptions, tempDir: string): string[] {\n const args: string[] = [];\n\n if (options.platform) {\n args.push('--platform', options.platform);\n }\n if (options.device) {\n args.push('--device', options.device);\n }\n\n args.push('test');\n\n const junitPath = join(tempDir, 'report.xml');\n const testOutputDir = join(tempDir, 'artifacts');\n const debugOutputDir = join(tempDir, 'debug');\n\n args.push('--format', 'junit');\n args.push('--output', junitPath);\n args.push('--test-output-dir', testOutputDir);\n args.push('--debug-output', debugOutputDir);\n\n if (options.analyze) {\n args.push('--analyze');\n }\n if (options.includeTags) {\n args.push('--include-tags', options.includeTags);\n }\n if (options.excludeTags) {\n args.push('--exclude-tags', options.excludeTags);\n }\n if (options.shards && options.shards > 1) {\n args.push('--shards', String(options.shards));\n }\n if (options.config) {\n args.push('--config', options.config);\n }\n if (options.env) {\n for (const [key, value] of Object.entries(options.env)) {\n args.push('-e', `${key}=${value}`);\n }\n }\n\n if (options.maestroArgs) {\n args.push(...options.maestroArgs);\n }\n\n args.push(...options.flowPaths);\n\n return args;\n}\n\nexport async function runMaestro(options: MaestroTestOptions): Promise<MaestroRunResult> {\n const tempDir = options.outputDir ? resolve(options.outputDir) : generateTempDir();\n\n mkdirSync(join(tempDir, 'artifacts'), { recursive: true });\n mkdirSync(join(tempDir, 'debug'), { recursive: true });\n\n const args = buildMaestroArgs(options, tempDir);\n const junitPath = join(tempDir, 'report.xml');\n const testOutputDir = join(tempDir, 'artifacts');\n const debugOutputDir = join(tempDir, 'debug');\n\n return new Promise<MaestroRunResult>((resolvePromise) => {\n const stdoutChunks: Buffer[] = [];\n const stderrChunks: Buffer[] = [];\n\n const proc = spawn('maestro', args, {\n stdio: ['inherit', 'pipe', 'pipe'],\n shell: true,\n });\n\n proc.stdout.on('data', (chunk: Buffer) => {\n stdoutChunks.push(chunk);\n if (!options.quiet) process.stdout.write(chunk);\n });\n\n proc.stderr.on('data', (chunk: Buffer) => {\n stderrChunks.push(chunk);\n if (!options.quiet) process.stderr.write(chunk);\n });\n\n proc.on('close', (code) => {\n resolvePromise({\n exitCode: code ?? 1,\n junitPath,\n testOutputDir,\n debugOutputDir,\n stdout: Buffer.concat(stdoutChunks).toString('utf-8'),\n stderr: Buffer.concat(stderrChunks).toString('utf-8'),\n });\n });\n\n proc.on('error', (err) => {\n resolvePromise({\n exitCode: 127,\n junitPath,\n testOutputDir,\n debugOutputDir,\n stdout: '',\n stderr: `Failed to start maestro CLI: ${err.message}`,\n });\n });\n });\n}\n","/**\n * Cloud authentication — token exchange and refresh.\n * Ported from @testrelic/appium-analytics for Maestro integration.\n */\n\nimport { ErrorCode, createError } from '@testrelic/core';\n\nexport interface TokenExchangeResult {\n readonly accessToken: string;\n readonly refreshToken: string;\n readonly expiresIn: number;\n readonly orgId: string;\n readonly orgName: string;\n readonly userId: string;\n readonly userName: string;\n}\n\nexport interface TokenRefreshResult {\n readonly accessToken: string;\n readonly refreshToken: string;\n readonly expiresIn: number;\n}\n\nexport interface AuthError {\n readonly code: 'invalid_key' | 'expired_key' | 'validation_error' | 'rate_limited' | 'server_error' | 'network_error' | 'timeout';\n readonly message: string;\n readonly statusCode: number | null;\n}\n\nconst LOCALHOST_HOSTS = new Set(['localhost', '127.0.0.1', '0.0.0.0']);\n\nexport function enforceHttps(endpoint: string): void {\n const url = new URL(endpoint);\n if (url.protocol === 'https:') return;\n if (url.protocol === 'http:' && LOCALHOST_HOSTS.has(url.hostname)) return;\n throw createError(\n ErrorCode.CLOUD_CONFIG_INVALID,\n `HTTPS is required for cloud communication. Got: ${url.protocol}//${url.hostname}`,\n );\n}\n\nconst HEALTH_CHECK_TIMEOUT_MS = 3000;\n\nexport async function healthCheck(endpoint: string): Promise<boolean> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), HEALTH_CHECK_TIMEOUT_MS);\n try {\n const response = await fetch(`${endpoint}/health`, {\n method: 'GET',\n signal: controller.signal,\n });\n return response.ok;\n } catch {\n return false;\n } finally {\n clearTimeout(timer);\n }\n}\n\nasync function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function parseCloudError(response: Response): Promise<string> {\n try {\n const body = await response.json() as Record<string, unknown>;\n const error = body.error as Record<string, unknown> | undefined;\n if (error && typeof error.message === 'string') {\n return error.message;\n }\n } catch {\n // not JSON\n }\n return `HTTP ${response.status}`;\n}\n\nexport async function exchangeToken(\n endpoint: string,\n apiKey: string,\n timeout: number,\n): Promise<TokenExchangeResult | AuthError> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeout);\n try {\n const response = await fetch(`${endpoint}/sdk/auth/token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ apiKey }),\n signal: controller.signal,\n });\n\n if (response.ok) {\n const body = await response.json() as Record<string, unknown>;\n return {\n accessToken: body.accessToken as string,\n refreshToken: body.refreshToken as string,\n expiresIn: body.expiresIn as number,\n orgId: body.orgId as string,\n orgName: body.orgName as string,\n userId: body.userId as string,\n userName: body.userName as string,\n };\n }\n\n if (response.status === 429) {\n const retryAfter = response.headers.get('Retry-After');\n const waitMs = retryAfter ? parseInt(retryAfter, 10) * 1000 : 5000;\n if (!isNaN(waitMs) && waitMs > 0) {\n await sleep(waitMs);\n const retryResponse = await fetch(`${endpoint}/sdk/auth/token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ apiKey }),\n signal: controller.signal,\n });\n if (retryResponse.ok) {\n const body = await retryResponse.json() as Record<string, unknown>;\n return {\n accessToken: body.accessToken as string,\n refreshToken: body.refreshToken as string,\n expiresIn: body.expiresIn as number,\n orgId: body.orgId as string,\n orgName: body.orgName as string,\n userId: body.userId as string,\n userName: body.userName as string,\n };\n }\n }\n return { code: 'rate_limited', message: 'Rate limited during token exchange.', statusCode: 429 };\n }\n\n if (response.status === 400) {\n const errorMessage = await parseCloudError(response);\n return { code: 'validation_error', message: errorMessage, statusCode: 400 };\n }\n if (response.status === 401) {\n return { code: 'invalid_key', message: 'API key is invalid or has been revoked.', statusCode: 401 };\n }\n if (response.status === 403) {\n return { code: 'expired_key', message: 'API key has expired.', statusCode: 403 };\n }\n\n const errorMessage = await parseCloudError(response);\n return { code: 'server_error', message: errorMessage, statusCode: response.status };\n } catch (err) {\n if (err instanceof DOMException && err.name === 'AbortError') {\n return { code: 'timeout', message: 'Token exchange timed out.', statusCode: null };\n }\n return { code: 'network_error', message: 'Failed to reach cloud for token exchange.', statusCode: null };\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport function isAuthError(result: TokenExchangeResult | AuthError): result is AuthError {\n return 'code' in result && typeof (result as AuthError).code === 'string'\n && ['invalid_key', 'expired_key', 'validation_error', 'rate_limited', 'server_error', 'network_error', 'timeout'].includes((result as AuthError).code);\n}\n\nexport async function refreshAccessToken(\n endpoint: string,\n currentRefreshToken: string,\n timeout: number,\n): Promise<TokenRefreshResult | null> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeout);\n try {\n let response = await fetch(`${endpoint}/sdk/auth/refresh`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ refreshToken: currentRefreshToken }),\n signal: controller.signal,\n });\n\n if (response.status === 429) {\n const retryAfter = response.headers.get('Retry-After');\n const waitMs = retryAfter ? parseInt(retryAfter, 10) * 1000 : 5000;\n if (!isNaN(waitMs) && waitMs > 0) {\n await sleep(waitMs);\n response = await fetch(`${endpoint}/sdk/auth/refresh`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ refreshToken: currentRefreshToken }),\n signal: controller.signal,\n });\n }\n }\n\n if (!response.ok) return null;\n\n const body = await response.json() as Record<string, unknown>;\n return {\n accessToken: body.accessToken as string,\n refreshToken: body.refreshToken as string,\n expiresIn: body.expiresIn as number,\n };\n } catch {\n return null;\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport interface RepoResolveResult {\n readonly repoId: string;\n readonly displayName: string;\n}\n\nexport async function resolveRepo(\n endpoint: string,\n accessToken: string,\n gitId: string,\n displayName: string,\n timeout: number,\n branch?: string | null,\n): Promise<RepoResolveResult | null> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeout);\n try {\n const requestBody: Record<string, string> = { gitId, displayName };\n if (branch) requestBody.branch = branch;\n const response = await fetch(`${endpoint}/repos/resolve`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${accessToken}`,\n },\n body: JSON.stringify(requestBody),\n signal: controller.signal,\n });\n\n if (!response.ok) return null;\n\n const responseBody = await response.json() as Record<string, unknown>;\n return {\n repoId: responseBody.repoId as string,\n displayName: responseBody.displayName as string,\n };\n } catch {\n return null;\n } finally {\n clearTimeout(timer);\n }\n}\n","/**\n * Git metadata collection and project ID normalization.\n *\n * Collects branch, commit, message, and remote URL using git CLI.\n * Each field fails independently.\n */\n\nimport { execSync } from 'node:child_process';\nimport { readFileSync } from 'node:fs';\nimport { basename, join } from 'node:path';\nimport type { GitMetadata } from '@testrelic/core';\n\nconst GIT_TIMEOUT_MS = 5000;\n\nfunction execGit(command: string, cwd?: string): string | null {\n try {\n const result = execSync(command, {\n cwd,\n timeout: GIT_TIMEOUT_MS,\n encoding: 'utf-8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n return result.trim() || null;\n } catch {\n return null;\n }\n}\n\nexport function collectGitMetadata(cwd?: string): GitMetadata {\n const branch = execGit('git rev-parse --abbrev-ref HEAD', cwd);\n const commitSha = execGit('git rev-parse --short HEAD', cwd);\n const commitMessage = execGit('git log -1 --pretty=%s', cwd);\n const commitAuthor =\n execGit('git log -1 --format=%an', cwd) ??\n execGit('git config user.name', cwd);\n const rawRemoteUrl = getRemoteUrl(cwd);\n const remoteUrl = rawRemoteUrl ? normalizeGitRemoteUrl(rawRemoteUrl) : null;\n\n return { branch, commitSha, commitMessage, commitAuthor, remoteUrl };\n}\n\nfunction getRemoteUrl(cwd?: string): string | null {\n const originUrl = execGit('git remote get-url origin', cwd);\n if (originUrl) return originUrl;\n\n const remotes = execGit('git remote', cwd);\n if (!remotes) return null;\n\n const firstRemote = remotes.split('\\n')[0]?.trim();\n if (!firstRemote) return null;\n\n return execGit(`git remote get-url ${firstRemote}`, cwd);\n}\n\nexport function normalizeGitRemoteUrl(url: string): string {\n let normalized = url.trim();\n normalized = normalized.replace(/^[a-z+]+:\\/\\//, '');\n normalized = normalized.replace(/^[^@]+@/, '');\n normalized = normalized.replace(/:(?!\\d)/, '/');\n normalized = normalized.replace(/\\.git$/, '');\n normalized = normalized.replace(/\\/+$/, '');\n normalized = normalized.replace(/^[^@/]+@/, '');\n return normalized.toLowerCase();\n}\n\nexport function deriveRepoDisplayName(normalizedUrl: string): string {\n const segments = normalizedUrl.split('/').filter(Boolean);\n return segments[segments.length - 1] ?? normalizedUrl;\n}\n\nexport function readPackageJsonName(dirPath: string): string | null {\n try {\n const raw = readFileSync(join(dirPath, 'package.json'), 'utf-8');\n const pkg = JSON.parse(raw) as Record<string, unknown>;\n return typeof pkg.name === 'string' && pkg.name.length > 0 ? pkg.name : null;\n } catch {\n return null;\n }\n}\n\nexport function deriveNonGitProjectId(dirPath: string): string {\n const pkgName = readPackageJsonName(dirPath);\n if (pkgName) return pkgName;\n return `local/${basename(dirPath)}`;\n}\n","/**\n * Cloud queue — offline storage and retry for failed uploads.\n */\n\nimport {\n mkdirSync, writeFileSync, readFileSync, readdirSync,\n unlinkSync, renameSync, statSync,\n} from 'node:fs';\nimport { join } from 'node:path';\nimport type { QueueEntry } from '@testrelic/core';\nimport { isValidQueueEntry } from '@testrelic/core';\n\nconst QUEUE_ENTRY_VERSION = '1.0.0';\nconst FLUSH_BATCH_SIZE = 10;\n\nexport function writeToQueue(\n queueDirectory: string,\n runId: string,\n type: string,\n reason: string,\n targetEndpoint: string,\n method: string,\n payload: Record<string, unknown>,\n headers: Record<string, string>,\n): void {\n try {\n mkdirSync(queueDirectory, { recursive: true });\n const timestamp = Date.now();\n const filename = `${timestamp}-${runId}-${type}.json`;\n const filePath = join(queueDirectory, filename);\n const entry: QueueEntry = { version: QUEUE_ENTRY_VERSION, queuedAt: new Date(timestamp).toISOString(), reason, retryCount: 0, targetEndpoint, method, payload, headers };\n const tmpPath = filePath + '.tmp';\n writeFileSync(tmpPath, JSON.stringify(entry, null, 2), 'utf-8');\n renameSync(tmpPath, filePath);\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOSPC') {\n process.stderr.write('\\u26A0 TestRelic: Disk full \\u2014 unable to queue upload data.\\n');\n } else {\n process.stderr.write('\\u26A0 TestRelic: Unable to queue upload data.\\n');\n }\n }\n}\n\nexport async function flushQueue(queueDirectory: string, endpoint: string, accessToken: string): Promise<void> {\n let files: string[];\n try {\n files = readdirSync(queueDirectory).filter((f) => f.endsWith('.json') && !f.endsWith('.tmp')).sort();\n } catch { return; }\n\n if (files.length === 0) return;\n\n const batches: string[][] = [];\n for (let i = 0; i < files.length; i += FLUSH_BATCH_SIZE) {\n batches.push(files.slice(i, i + FLUSH_BATCH_SIZE));\n }\n\n for (const batch of batches) {\n for (const file of batch) {\n const filePath = join(queueDirectory, file);\n try {\n const stat = statSync(filePath);\n if (!stat.isFile()) continue;\n const raw = readFileSync(filePath, 'utf-8');\n const entry = JSON.parse(raw) as unknown;\n if (!isValidQueueEntry(entry)) {\n unlinkSync(filePath);\n continue;\n }\n const queueEntry = entry as QueueEntry;\n const response = await fetch(queueEntry.targetEndpoint, {\n method: queueEntry.method,\n headers: { ...queueEntry.headers, 'Authorization': `Bearer ${accessToken}` },\n body: JSON.stringify(queueEntry.payload),\n });\n if (response.ok) { unlinkSync(filePath); } else { return; }\n } catch { return; }\n }\n }\n}\n\nexport function cleanupExpiredQueue(queueDirectory: string, maxAge: number): void {\n try {\n const files = readdirSync(queueDirectory).filter((f) => f.endsWith('.json') && !f.endsWith('.tmp'));\n const now = Date.now();\n for (const file of files) {\n const filePath = join(queueDirectory, file);\n try {\n const raw = readFileSync(filePath, 'utf-8');\n const entry = JSON.parse(raw) as Record<string, unknown>;\n const queuedAt = entry.queuedAt;\n if (typeof queuedAt === 'string') {\n const queuedTime = new Date(queuedAt).getTime();\n if (now - queuedTime > maxAge) unlinkSync(filePath);\n }\n } catch {\n try { unlinkSync(filePath); } catch { /* ignore */ }\n }\n }\n } catch { /* queue dir doesn't exist */ }\n}\n","/**\n * CloudClient — manages cloud connectivity lifecycle for Maestro analytics.\n * Handles auth, repo resolution, token refresh, and queue flush.\n */\n\nimport { readFileSync, writeFileSync, mkdirSync, renameSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { createHash } from 'node:crypto';\nimport type { CloudConfig, AuthState, AuthMode, GitMetadata } from '@testrelic/core';\nimport {\n enforceHttps,\n healthCheck,\n exchangeToken,\n refreshAccessToken,\n isAuthError,\n resolveRepo,\n} from './cloud-auth.js';\nimport type { TokenExchangeResult } from './cloud-auth.js';\nimport { collectGitMetadata, normalizeGitRemoteUrl, deriveRepoDisplayName, deriveNonGitProjectId, readPackageJsonName } from './git-metadata.js';\nimport { cleanupExpiredQueue, flushQueue } from './cloud-queue.js';\n\nconst TOKEN_REFRESH_BUFFER_MS = 300_000;\nconst PROJECT_CACHE_TTL_MS = 86_400_000;\nconst HEALTH_CHECK_INTERVAL_MS = 60_000;\nconst FLUSH_TIMEOUT_MS = 30_000;\nconst DASHBOARD_SETTINGS_URL = 'https://platform.testrelic.ai/settings/api-keys';\n\ninterface RepoCache {\n readonly repoId: string;\n readonly gitId: string;\n readonly displayName: string;\n readonly resolvedAt: number;\n readonly apiKeyHash: string;\n}\n\nexport class CloudClient {\n private config: CloudConfig | null;\n private authState: AuthState;\n private gitMetadata: GitMetadata | null = null;\n private repoId: string | null = null;\n private failureReason: string | null = null;\n private flushPromise: Promise<void> | null = null;\n private healthCheckTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(cloudConfig: CloudConfig | null) {\n this.config = cloudConfig;\n this.authState = {\n mode: 'pending',\n accessToken: null,\n refreshToken: null,\n expiresAt: null,\n orgId: null,\n orgName: null,\n userId: null,\n userName: null,\n };\n }\n\n async initialize(): Promise<void> {\n if (!this.config || !this.config.apiKey) {\n this.setLocalMode('no_api_key');\n process.stderr.write('\\u2139 TestRelic: No API key configured. Running in local mode.\\n');\n return;\n }\n\n try {\n enforceHttps(this.config.endpoint);\n this.gitMetadata = collectGitMetadata();\n\n const healthy = await healthCheck(this.config.endpoint);\n if (!healthy) {\n this.setLocalMode('cloud_unreachable');\n process.stderr.write('\\u26A0 TestRelic: Cloud unreachable. Switching to local mode.\\n');\n return;\n }\n\n const result = await exchangeToken(this.config.endpoint, this.config.apiKey, this.config.timeout);\n if (isAuthError(result)) {\n this.handleAuthError(result.code, result.statusCode);\n return;\n }\n\n const tokenResult = result as TokenExchangeResult;\n this.authState = {\n mode: 'cloud',\n accessToken: tokenResult.accessToken,\n refreshToken: tokenResult.refreshToken,\n expiresAt: Date.now() + tokenResult.expiresIn * 1000,\n orgId: tokenResult.orgId,\n orgName: tokenResult.orgName,\n userId: tokenResult.userId,\n userName: tokenResult.userName,\n };\n\n process.stderr.write(`\\u2713 TestRelic: Connected to cloud (${tokenResult.orgName} / ${tokenResult.userName})\\n`);\n\n await this.resolveRepoId();\n\n if (this.config.queueDirectory) {\n cleanupExpiredQueue(this.config.queueDirectory, this.config.queueMaxAge);\n this.startBackgroundFlush();\n }\n\n this.startHealthCheck();\n } catch (err) {\n this.setLocalMode('unexpected_error');\n process.stderr.write(`\\u26A0 TestRelic: Unexpected error during cloud initialization. ${err instanceof Error ? err.message : String(err)}\\n`);\n }\n }\n\n getMode(): AuthMode { return this.authState.mode; }\n isCloudMode(): boolean { return this.authState.mode === 'cloud'; }\n isLocalMode(): boolean { return this.authState.mode === 'local'; }\n getAccessToken(): string | null { return this.authState.accessToken; }\n getRepoId(): string | null { return this.repoId; }\n getGitMetadata(): GitMetadata | null { return this.gitMetadata; }\n getConfig(): CloudConfig | null { return this.config; }\n getFailureReason(): string | null { return this.failureReason; }\n getEndpoint(): string { return this.config?.endpoint ?? 'https://platform.testrelic.ai/api/v1'; }\n\n async ensureValidToken(): Promise<boolean> {\n if (!this.isCloudMode() || !this.authState.accessToken) return false;\n const expiresAt = this.authState.expiresAt ?? 0;\n if (Date.now() + TOKEN_REFRESH_BUFFER_MS < expiresAt) return true;\n if (!this.config || !this.authState.refreshToken) {\n this.switchToLocalMode('token_expired_no_refresh');\n return false;\n }\n try {\n const refreshResult = await refreshAccessToken(this.config.endpoint, this.authState.refreshToken, this.config.timeout);\n if (!refreshResult) {\n this.switchToLocalMode('token_refresh_failed');\n return false;\n }\n this.authState.accessToken = refreshResult.accessToken;\n this.authState.refreshToken = refreshResult.refreshToken;\n this.authState.expiresAt = Date.now() + refreshResult.expiresIn * 1000;\n return true;\n } catch {\n this.switchToLocalMode('token_refresh_error');\n return false;\n }\n }\n\n switchToLocalMode(reason: string): void {\n if (this.authState.mode === 'local') return;\n this.authState.mode = 'local';\n this.failureReason = reason;\n process.stderr.write(`\\u26A0 TestRelic: Switched to local mode (${reason}).\\n`);\n }\n\n async dispose(): Promise<void> {\n if (this.healthCheckTimer) {\n clearInterval(this.healthCheckTimer);\n this.healthCheckTimer = null;\n }\n if (this.flushPromise) {\n try {\n await Promise.race([this.flushPromise, new Promise<void>((resolve) => setTimeout(resolve, FLUSH_TIMEOUT_MS))]);\n } catch { /* ignore */ }\n this.flushPromise = null;\n }\n this.authState = { mode: 'local', accessToken: null, refreshToken: null, expiresAt: null, orgId: null, orgName: null, userId: null, userName: null };\n this.repoId = null;\n this.gitMetadata = null;\n }\n\n private setLocalMode(reason: string): void {\n this.authState.mode = 'local';\n this.failureReason = reason;\n }\n\n private handleAuthError(code: string, statusCode: number | null): void {\n switch (code) {\n case 'invalid_key':\n this.setLocalMode('invalid_api_key');\n process.stderr.write(`\\u26A0 TestRelic: API key is invalid or revoked. Running in local mode.\\n \\u2192 Manage keys: ${DASHBOARD_SETTINGS_URL}\\n`);\n break;\n case 'expired_key':\n this.setLocalMode('expired_api_key');\n process.stderr.write(`\\u26A0 TestRelic: API key has expired. Running in local mode.\\n \\u2192 Generate a new key: ${DASHBOARD_SETTINGS_URL}\\n`);\n break;\n case 'rate_limited':\n this.setLocalMode('rate_limited');\n process.stderr.write('\\u26A0 TestRelic: Rate limited during authentication. Running in local mode.\\n');\n break;\n default:\n this.setLocalMode(`auth_error_${statusCode ?? 'unknown'}`);\n process.stderr.write('\\u26A0 TestRelic: Cloud authentication failed. Running in local mode.\\n');\n break;\n }\n }\n\n private async resolveRepoId(): Promise<void> {\n if (!this.config || !this.authState.accessToken) return;\n const gitId = this.gitMetadata?.remoteUrl ? normalizeGitRemoteUrl(this.gitMetadata.remoteUrl) : null;\n const displayName = gitId ? deriveRepoDisplayName(gitId) : this.config.projectName ?? deriveNonGitProjectId(process.cwd());\n const effectiveGitId = gitId ?? (this.config.projectName ?? deriveNonGitProjectId(process.cwd()));\n\n const cached = this.readRepoCache(effectiveGitId);\n if (cached) { this.repoId = cached.repoId; return; }\n\n try {\n const resolved = await resolveRepo(this.config.endpoint, this.authState.accessToken, effectiveGitId, displayName, this.config.timeout, this.gitMetadata?.branch);\n if (resolved) {\n this.repoId = resolved.repoId;\n this.writeRepoCache(effectiveGitId, resolved.repoId, resolved.displayName);\n }\n } catch { /* non-fatal */ }\n }\n\n private readRepoCache(gitId: string): RepoCache | null {\n if (!this.config) return null;\n const cachePath = join(this.config.queueDirectory, '..', 'cache', 'repo.json');\n try {\n if (!existsSync(cachePath)) return null;\n const raw = readFileSync(cachePath, 'utf-8');\n const cache = JSON.parse(raw) as RepoCache;\n if (cache.gitId !== gitId) return null;\n if (Date.now() - cache.resolvedAt > PROJECT_CACHE_TTL_MS) return null;\n const currentKeyHash = this.hashApiKey();\n if (currentKeyHash && cache.apiKeyHash !== currentKeyHash) return null;\n return cache;\n } catch { return null; }\n }\n\n private writeRepoCache(gitId: string, repoId: string, displayName: string): void {\n if (!this.config) return;\n const cacheDir = join(this.config.queueDirectory, '..', 'cache');\n const cachePath = join(cacheDir, 'repo.json');\n try {\n mkdirSync(cacheDir, { recursive: true });\n const cache: RepoCache = { repoId, gitId, displayName, resolvedAt: Date.now(), apiKeyHash: this.hashApiKey() ?? '' };\n const tmpPath = cachePath + '.tmp';\n writeFileSync(tmpPath, JSON.stringify(cache, null, 2), 'utf-8');\n renameSync(tmpPath, cachePath);\n } catch { /* non-fatal */ }\n }\n\n private hashApiKey(): string | null {\n if (!this.config?.apiKey) return null;\n return createHash('sha256').update(this.config.apiKey).digest('hex').substring(0, 16);\n }\n\n private startBackgroundFlush(): void {\n if (!this.config || !this.authState.accessToken) return;\n const accessToken = this.authState.accessToken;\n const queueDir = this.config.queueDirectory;\n const endpoint = this.config.endpoint;\n this.flushPromise = Promise.race([\n flushQueue(queueDir, endpoint, accessToken),\n new Promise<void>((resolve) => setTimeout(resolve, FLUSH_TIMEOUT_MS)),\n ]).catch(() => { /* non-fatal */ });\n }\n\n private startHealthCheck(): void {\n if (!this.config) return;\n const endpoint = this.config.endpoint;\n this.healthCheckTimer = setInterval(async () => {\n if (this.isCloudMode()) return;\n if (!this.failureReason || !['cloud_unreachable', 'auth_timeout', 'network_error'].includes(this.failureReason)) return;\n try {\n const healthy = await healthCheck(endpoint);\n if (!healthy) return;\n if (this.config?.apiKey) {\n const result = await exchangeToken(this.config.endpoint, this.config.apiKey, this.config.timeout);\n if (!isAuthError(result)) {\n const tokenResult = result as TokenExchangeResult;\n this.authState = { mode: 'cloud', accessToken: tokenResult.accessToken, refreshToken: tokenResult.refreshToken, expiresAt: Date.now() + tokenResult.expiresIn * 1000, orgId: tokenResult.orgId, orgName: tokenResult.orgName, userId: tokenResult.userId, userName: tokenResult.userName };\n this.failureReason = null;\n process.stderr.write('\\u2713 TestRelic: Cloud connectivity restored.\\n');\n this.startBackgroundFlush();\n }\n }\n } catch { /* ignore */ }\n }, HEALTH_CHECK_INTERVAL_MS);\n }\n}\n","/**\n * Cloud upload — batch and realtime upload functions.\n * Adapted from appium-analytics with testFramework: 'maestro'.\n */\n\nimport { gzipSync } from 'node:zlib';\nimport type {\n TestRunReport,\n GitMetadata,\n CIMetadata,\n Summary,\n TimelineEntry,\n TimelineStep,\n} from '@testrelic/core';\n\nexport interface TestResultForUpload {\n readonly testId: string;\n readonly title: string;\n readonly status: string;\n readonly duration: number;\n readonly suiteName: string;\n readonly filePath: string;\n readonly testType: string;\n readonly isFlaky: boolean;\n readonly startedAt: string;\n readonly completedAt: string;\n readonly tags: readonly string[];\n readonly failure: unknown | null;\n readonly retry: number;\n readonly retryCount: number;\n readonly retryStatus: string | null;\n readonly source?: string;\n readonly platform?: string;\n /** Human-readable OS name, e.g. \"Android\", \"iOS\" */\n readonly os?: string;\n /** Device name / serial, e.g. \"emulator-5554\", \"iPhone 15 Pro\" */\n readonly deviceName?: string;\n /**\n * Per-flow console / device log entries. For Maestro this is the slice of\n * `maestro.log` that falls inside the flow's start/end window — the platform\n * surfaces them in the Maestro Session Workspace \"Logs\" tab.\n */\n readonly consoleLogs?: ReadonlyArray<{\n readonly level: string;\n readonly message: string;\n readonly timestamp: string;\n readonly source?: string | null;\n }>;\n}\n\nexport interface RunUploadPayload {\n readonly runId: string;\n readonly repoGitId: string;\n readonly startedAt: string;\n readonly summary: Summary;\n readonly timeline: readonly (TimelineEntry | TimelineStep | Record<string, unknown>)[];\n readonly tests?: readonly TestResultForUpload[];\n readonly branch?: string;\n readonly commit?: string;\n readonly commitMessage?: string;\n readonly commitAuthor?: string;\n readonly finishedAt?: string;\n readonly duration?: number;\n readonly ciProvider?: string;\n readonly ciRunUrl?: string;\n readonly environment?: string;\n readonly testFramework?: string;\n}\n\nexport interface UploadResult {\n readonly success: boolean;\n readonly statusCode: number | null;\n readonly error: string | null;\n}\n\nexport interface UploadFailure {\n readonly success: false;\n readonly reason: string;\n readonly statusCode: number | null;\n readonly payload: Record<string, unknown>;\n readonly targetEndpoint: string;\n readonly method: string;\n}\n\nconst GZIP_THRESHOLD_BYTES = 1_048_576;\nconst RETRY_DELAYS_MS = [1000, 3000, 9000];\nconst MAX_RETRIES = 3;\n\nexport function buildUploadPayload(\n report: TestRunReport,\n repoGitId: string,\n git: GitMetadata | null,\n ci: CIMetadata | null,\n tests?: readonly TestResultForUpload[],\n): RunUploadPayload {\n const environment = ci?.provider && ci.provider !== 'unknown' ? ci.provider : 'local';\n const payload: RunUploadPayload = {\n runId: report.testRunId,\n repoGitId,\n startedAt: report.startedAt,\n summary: report.summary,\n timeline: report.timeline,\n environment,\n testFramework: 'maestro',\n ...(tests && tests.length > 0 ? { tests } : {}),\n ...(git?.branch ? { branch: git.branch } : {}),\n ...(git?.commitSha ? { commit: git.commitSha } : {}),\n ...(git?.commitMessage ? { commitMessage: git.commitMessage } : {}),\n ...(git?.commitAuthor ? { commitAuthor: git.commitAuthor } : {}),\n ...(report.completedAt ? { finishedAt: report.completedAt } : {}),\n ...(report.totalDuration ? { duration: report.totalDuration } : {}),\n ...(ci?.provider ? { ciProvider: ci.provider } : {}),\n ...(ci?.runUrl ? { ciRunUrl: ci.runUrl } : {}),\n };\n return payload;\n}\n\nasync function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function retryWithBackoff(\n url: string,\n init: RequestInit,\n onTokenRefresh?: () => Promise<string | null>,\n): Promise<Response | null> {\n for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {\n try {\n const response = await fetch(url, init);\n\n if (response.ok) return response;\n\n if (response.status === 401 && onTokenRefresh && attempt === 0) {\n const newToken = await onTokenRefresh();\n if (newToken) {\n const refreshedInit = {\n ...init,\n headers: { ...init.headers as Record<string, string>, Authorization: `Bearer ${newToken}` },\n };\n const retryResponse = await fetch(url, refreshedInit);\n if (retryResponse.ok) return retryResponse;\n }\n return null;\n }\n\n if (response.status === 429 && attempt < MAX_RETRIES - 1) {\n const retryAfter = response.headers.get('Retry-After');\n const waitMs = retryAfter ? parseInt(retryAfter, 10) * 1000 : RETRY_DELAYS_MS[attempt];\n if (!isNaN(waitMs) && waitMs > 0) {\n await sleep(waitMs);\n } else {\n await sleep(RETRY_DELAYS_MS[attempt]);\n }\n continue;\n }\n\n if (response.status >= 500 && attempt < MAX_RETRIES - 1) {\n await sleep(RETRY_DELAYS_MS[attempt]);\n continue;\n }\n\n return response;\n } catch {\n if (attempt < MAX_RETRIES - 1) {\n await sleep(RETRY_DELAYS_MS[attempt]);\n continue;\n }\n return null;\n }\n }\n return null;\n}\n\nfunction prepareBody(payload: unknown): { body: Buffer | string; headers: Record<string, string> } {\n const json = JSON.stringify(payload);\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n\n if (Buffer.byteLength(json, 'utf-8') > GZIP_THRESHOLD_BYTES) {\n const compressed = gzipSync(Buffer.from(json, 'utf-8'));\n headers['Content-Encoding'] = 'gzip';\n return { body: compressed, headers };\n }\n\n return { body: json, headers };\n}\n\nexport async function uploadBatchRun(\n endpoint: string,\n accessToken: string,\n payload: RunUploadPayload,\n onTokenRefresh?: () => Promise<string | null>,\n): Promise<UploadResult | UploadFailure> {\n const url = `${endpoint}/runs`;\n const { body, headers } = prepareBody(payload);\n headers['Authorization'] = `Bearer ${accessToken}`;\n\n const response = await retryWithBackoff(url, { method: 'POST', headers, body }, onTokenRefresh);\n\n if (response?.ok) {\n return { success: true, statusCode: response.status, error: null };\n }\n\n if (response?.status === 409) {\n return { success: true, statusCode: 409, error: null };\n }\n\n return {\n success: false,\n reason: response ? `Upload failed with status ${response.status}` : 'Upload failed after retries',\n statusCode: response?.status ?? null,\n payload: payload as unknown as Record<string, unknown>,\n targetEndpoint: url,\n method: 'POST',\n };\n}\n\nexport interface RealtimeInitResult {\n readonly runId: string;\n}\n\nfunction stripNulls(obj: Record<string, unknown>): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(obj)) {\n if (val !== null && val !== undefined) result[key] = val;\n }\n return result;\n}\n\nexport async function initRealtimeRun(\n endpoint: string,\n accessToken: string,\n payload: {\n runId: string;\n repoGitId: string;\n branch: string | null;\n commit: string | null;\n commitMessage?: string | null;\n commitAuthor?: string | null;\n startedAt: string;\n totalTests: number | null;\n ciProvider: string | null;\n ciRunUrl: string | null;\n environment?: string | null;\n testFramework?: string;\n },\n): Promise<RealtimeInitResult | null> {\n const url = `${endpoint}/runs/init`;\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${accessToken}`,\n },\n body: JSON.stringify(stripNulls(payload as unknown as Record<string, unknown>)),\n });\n if (!response.ok) return null;\n const responseBody = await response.json() as Record<string, unknown>;\n return { runId: responseBody.runId as string };\n } catch {\n return null;\n }\n}\n\nexport async function finalizeRun(\n endpoint: string,\n accessToken: string,\n cloudRunId: string,\n payload: {\n finishedAt: string;\n duration: number;\n summary: Summary;\n commitMessage?: string | null;\n },\n): Promise<boolean> {\n const url = `${endpoint}/runs/${cloudRunId}/finalize`;\n const response = await retryWithBackoff(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${accessToken}`,\n },\n body: JSON.stringify(payload),\n });\n return response?.ok ?? false;\n}\n","/**\n * Cloud artifact upload — binary file upload via presigned S3 URLs.\n */\n\nimport { createReadStream, statSync } from 'node:fs';\nimport { basename } from 'node:path';\nimport { Readable } from 'node:stream';\nimport type { ReadableStream as WebReadableStream } from 'node:stream/web';\n\nexport interface ArtifactUploadRequest {\n readonly filePath: string;\n readonly runId: string;\n readonly testId: string;\n readonly type: 'screenshot' | 'video' | 'trace' | 'other';\n}\n\nexport interface ArtifactUploadResult {\n readonly success: boolean;\n readonly storageKey: string | null;\n readonly artifactId: string | null;\n readonly error: string | null;\n}\n\ninterface UploadUrlResponse {\n readonly artifactId: string;\n readonly uploadUrl: string;\n readonly storageKey: string;\n}\n\nconst UPLOAD_CONCURRENCY = 5;\nconst RETRY_DELAYS_MS = [1000, 3000, 9000];\nconst MAX_RETRIES = 3;\n\nconst CONTENT_TYPE_MAP: Record<string, string> = {\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.jpeg': 'image/jpeg',\n '.webp': 'image/webp',\n '.webm': 'video/webm',\n '.mp4': 'video/mp4',\n '.mov': 'video/quicktime',\n '.zip': 'application/zip',\n};\n\nlet activeUploads = 0;\nconst pendingUploads: Array<() => void> = [];\n\nasync function acquireSlot(): Promise<void> {\n if (activeUploads >= UPLOAD_CONCURRENCY) {\n await new Promise<void>((resolve) => pendingUploads.push(resolve));\n }\n activeUploads++;\n}\n\nfunction releaseSlot(): void {\n activeUploads--;\n if (pendingUploads.length > 0) pendingUploads.shift()!();\n}\n\nasync function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction getContentType(filePath: string): string {\n const ext = filePath.substring(filePath.lastIndexOf('.')).toLowerCase();\n return CONTENT_TYPE_MAP[ext] ?? 'application/octet-stream';\n}\n\nfunction getFileSize(filePath: string): number {\n try { return statSync(filePath).size; } catch { return 0; }\n}\n\nasync function requestUploadUrl(\n endpoint: string, accessToken: string, request: ArtifactUploadRequest, sizeBytes: number, contentType: string,\n): Promise<UploadUrlResponse | null> {\n const url = `${endpoint}/artifacts/upload-url`;\n const body = JSON.stringify({ runId: request.runId, testId: request.testId, fileName: basename(request.filePath), contentType, type: request.type, sizeBytes });\n\n for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {\n try {\n const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${accessToken}` }, body });\n if (response.ok) return (await response.json()) as UploadUrlResponse;\n if (response.status >= 500 && attempt < MAX_RETRIES - 1) { await sleep(RETRY_DELAYS_MS[attempt]); continue; }\n return null;\n } catch {\n if (attempt < MAX_RETRIES - 1) { await sleep(RETRY_DELAYS_MS[attempt]); continue; }\n return null;\n }\n }\n return null;\n}\n\nasync function putFileToPresignedUrl(presignedUrl: string, filePath: string, contentType: string, sizeBytes: number): Promise<boolean> {\n for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {\n try {\n const nodeStream = createReadStream(filePath);\n const webStream = Readable.toWeb(nodeStream) as WebReadableStream<Uint8Array>;\n const response = await fetch(presignedUrl, {\n method: 'PUT',\n headers: { 'Content-Type': contentType, 'Content-Length': String(sizeBytes) },\n body: webStream,\n duplex: 'half',\n });\n if (response.ok) return true;\n if (response.status >= 500 && attempt < MAX_RETRIES - 1) { await sleep(RETRY_DELAYS_MS[attempt]); continue; }\n return false;\n } catch {\n if (attempt < MAX_RETRIES - 1) { await sleep(RETRY_DELAYS_MS[attempt]); continue; }\n return false;\n }\n }\n return false;\n}\n\nasync function confirmUpload(endpoint: string, accessToken: string, artifactId: string): Promise<boolean> {\n try {\n const response = await fetch(`${endpoint}/artifacts/confirm`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${accessToken}` },\n body: JSON.stringify({ artifactId }),\n });\n return response.ok;\n } catch { return false; }\n}\n\nexport async function uploadArtifact(endpoint: string, accessToken: string, request: ArtifactUploadRequest, maxSizeMb: number): Promise<ArtifactUploadResult> {\n const sizeBytes = getFileSize(request.filePath);\n if (sizeBytes === 0) return { success: false, storageKey: null, artifactId: null, error: 'file_not_found_or_empty' };\n if (sizeBytes > maxSizeMb * 1024 * 1024) return { success: false, storageKey: null, artifactId: null, error: 'file_too_large' };\n const contentType = getContentType(request.filePath);\n\n await acquireSlot();\n try {\n const urlResult = await requestUploadUrl(endpoint, accessToken, request, sizeBytes, contentType);\n if (!urlResult) return { success: false, storageKey: null, artifactId: null, error: 'upload_url_request_failed' };\n const uploaded = await putFileToPresignedUrl(urlResult.uploadUrl, request.filePath, contentType, sizeBytes);\n if (!uploaded) return { success: false, storageKey: urlResult.storageKey, artifactId: urlResult.artifactId, error: 'presigned_put_failed' };\n const confirmed = await confirmUpload(endpoint, accessToken, urlResult.artifactId);\n return { success: confirmed, storageKey: urlResult.storageKey, artifactId: urlResult.artifactId, error: confirmed ? null : 'confirm_failed' };\n } finally { releaseSlot(); }\n}\n\nexport async function uploadArtifacts(endpoint: string, accessToken: string, requests: readonly ArtifactUploadRequest[], maxSizeMb: number): Promise<Map<string, ArtifactUploadResult>> {\n const results = new Map<string, ArtifactUploadResult>();\n const promises = requests.map(async (req) => {\n const result = await uploadArtifact(endpoint, accessToken, req, maxSizeMb);\n results.set(req.filePath, result);\n });\n await Promise.allSettled(promises);\n return results;\n}\n","/**\n * CI provider auto-detection via environment variables.\n * Strategy pattern: check providers in priority order, first match wins.\n */\n\nimport type { CIMetadata } from '@testrelic/core';\n\ntype DetectFn = (env: Record<string, string | undefined>) => CIMetadata | null;\n\nfunction detectGitHubActions(env: Record<string, string | undefined>): CIMetadata | null {\n if (env.GITHUB_ACTIONS !== 'true') return null;\n return {\n provider: 'github-actions',\n buildId: env.GITHUB_RUN_ID ?? null,\n commitSha: env.GITHUB_SHA ?? null,\n branch: env.GITHUB_REF_NAME ?? null,\n runUrl: env.GITHUB_SERVER_URL && env.GITHUB_REPOSITORY && env.GITHUB_RUN_ID\n ? `${env.GITHUB_SERVER_URL}/${env.GITHUB_REPOSITORY}/actions/runs/${env.GITHUB_RUN_ID}`\n : null,\n };\n}\n\nfunction detectGitLabCI(env: Record<string, string | undefined>): CIMetadata | null {\n if (env.GITLAB_CI !== 'true') return null;\n return {\n provider: 'gitlab-ci',\n buildId: env.CI_PIPELINE_ID ?? null,\n commitSha: env.CI_COMMIT_SHA ?? null,\n branch: env.CI_COMMIT_BRANCH ?? env.CI_COMMIT_REF_NAME ?? null,\n runUrl: env.CI_PIPELINE_URL ?? null,\n };\n}\n\nfunction detectJenkins(env: Record<string, string | undefined>): CIMetadata | null {\n if (!env.JENKINS_URL) return null;\n let branch = env.GIT_BRANCH ?? null;\n if (branch?.startsWith('origin/')) {\n branch = branch.slice('origin/'.length);\n }\n return {\n provider: 'jenkins',\n buildId: env.BUILD_ID ?? null,\n commitSha: env.GIT_COMMIT ?? null,\n branch,\n runUrl: env.BUILD_URL ?? null,\n };\n}\n\nfunction detectCircleCI(env: Record<string, string | undefined>): CIMetadata | null {\n if (env.CIRCLECI !== 'true') return null;\n return {\n provider: 'circleci',\n buildId: env.CIRCLE_BUILD_NUM ?? null,\n commitSha: env.CIRCLE_SHA1 ?? null,\n branch: env.CIRCLE_BRANCH ?? null,\n runUrl: env.CIRCLE_BUILD_URL ?? null,\n };\n}\n\nfunction detectBitbucketPipelines(env: Record<string, string | undefined>): CIMetadata | null {\n if (!env.BITBUCKET_PIPELINE_UUID) return null;\n const runUrl = env.BITBUCKET_WORKSPACE && env.BITBUCKET_REPO_SLUG && env.BITBUCKET_PIPELINE_UUID\n ? `https://bitbucket.org/${env.BITBUCKET_WORKSPACE}/${env.BITBUCKET_REPO_SLUG}/pipelines/results/${env.BITBUCKET_PIPELINE_UUID}`\n : null;\n return {\n provider: 'bitbucket-pipelines',\n buildId: env.BITBUCKET_BUILD_NUMBER ?? null,\n commitSha: env.BITBUCKET_COMMIT ?? null,\n branch: env.BITBUCKET_BRANCH ?? null,\n runUrl,\n };\n}\n\nconst detectors: DetectFn[] = [\n detectGitHubActions,\n detectGitLabCI,\n detectJenkins,\n detectCircleCI,\n detectBitbucketPipelines,\n];\n\nexport function detectCI(env?: Record<string, string | undefined>): CIMetadata | null {\n const envVars = env ?? process.env;\n for (const detect of detectors) {\n const result = detect(envVars);\n if (result) return result;\n }\n return null;\n}\n","/**\n * Cloud reporter orchestration for Maestro analytics.\n * Handles batch upload and artifact upload to the TestRelic platform.\n */\n\nimport type { CloudConfig, TestRunReport, Summary, TimelineEntry, ActionStep } from '@testrelic/core';\nimport { CloudClient } from './cloud-client.js';\nimport { buildUploadPayload, uploadBatchRun, finalizeRun } from './cloud-upload.js';\nimport type { UploadFailure, TestResultForUpload } from './cloud-upload.js';\nimport { uploadArtifacts } from './cloud-artifact-upload.js';\nimport type { ArtifactUploadRequest } from './cloud-artifact-upload.js';\nimport { writeToQueue } from './cloud-queue.js';\nimport { detectCI } from './ci-detector.js';\nimport { normalizeGitRemoteUrl, deriveNonGitProjectId } from './git-metadata.js';\n\nfunction flattenTimelineForCloud(timeline: readonly (TimelineEntry | Record<string, unknown>)[]): Record<string, unknown>[] {\n const flat: Record<string, unknown>[] = [];\n\n for (const entry of timeline) {\n const timelineEntry = entry as TimelineEntry;\n const tests = timelineEntry.tests;\n if (!Array.isArray(tests) || tests.length === 0) {\n flat.push(entry as Record<string, unknown>);\n continue;\n }\n\n for (const test of tests) {\n flat.push({\n timestamp: test.startedAt,\n title: test.title,\n category: 'test',\n navigationType: timelineEntry.navigationType,\n url: timelineEntry.url,\n visitedAt: timelineEntry.visitedAt,\n duration: test.duration,\n status: test.status,\n test_status: test.status,\n test_id: test.testId,\n test_title: test.title,\n specfile: test.filePath,\n suiteName: test.suiteName,\n type: 'test',\n ...(test.failure ? { error: test.failure.message, errorMessage: test.failure.message } : {}),\n });\n\n for (const action of (test.actions ?? []) as ActionStep[]) {\n flat.push({\n timestamp: action.timestamp,\n title: action.title,\n category: action.category,\n status: action.status,\n test_status: test.status,\n duration: action.duration,\n error: action.error,\n test_id: test.testId,\n test_title: test.title,\n specfile: test.filePath,\n url: timelineEntry.url,\n type: action.category === 'assertion' ? 'assert' : 'action',\n });\n }\n }\n }\n\n return flat;\n}\n\nfunction getEffectiveGitId(cloudClient: CloudClient): string {\n const gitMeta = cloudClient.getGitMetadata();\n const remoteUrl = gitMeta?.remoteUrl;\n if (remoteUrl) return normalizeGitRemoteUrl(remoteUrl);\n const projectName = cloudClient.getConfig()?.projectName;\n return projectName ?? deriveNonGitProjectId(process.cwd());\n}\n\n/** A video (or other artifact) explicitly associated with a specific test/flow. */\nexport interface PerTestArtifact {\n readonly path: string;\n readonly testId: string;\n}\n\nexport async function finalizeAndUpload(\n cloudClient: CloudClient,\n cloudConfig: CloudConfig | undefined | null,\n testRunId: string,\n report: TestRunReport,\n completedAt: string,\n totalDuration: number,\n summary: Summary,\n screenshotPaths?: readonly string[],\n videoPaths?: readonly string[],\n tests?: readonly TestResultForUpload[],\n /** Per-flow video artifacts — each is uploaded with its exact flow testId. */\n videoArtifacts?: readonly PerTestArtifact[],\n): Promise<void> {\n try {\n const perTestVideoCount = videoArtifacts?.length ?? 0;\n const hasArtifacts =\n (screenshotPaths?.length ?? 0) +\n (videoPaths?.length ?? 0) +\n perTestVideoCount > 0;\n\n if (cloudClient.isCloudMode() && hasArtifacts) {\n const tokenValid = await cloudClient.ensureValidToken();\n if (tokenValid) {\n const endpoint = cloudClient.getEndpoint();\n const token = cloudClient.getAccessToken()!;\n const maxSizeMb = cloudConfig?.artifactMaxSizeMb ?? 50;\n\n const requests: ArtifactUploadRequest[] = [];\n for (const path of (screenshotPaths ?? [])) {\n requests.push({ filePath: path, runId: testRunId, testId: 'maestro-suite', type: 'screenshot' });\n }\n // Per-flow videos — uploaded with the flow's own testId so the frontend\n // can resolve the correct recording per test case.\n const perTestPaths = new Set<string>();\n for (const va of (videoArtifacts ?? [])) {\n requests.push({ filePath: va.path, runId: testRunId, testId: va.testId, type: 'video' });\n perTestPaths.add(va.path);\n }\n // Any remaining videos (not matched to a specific flow) fall back to the\n // suite-level bucket so they are still visible in the run overview.\n for (const path of (videoPaths ?? [])) {\n if (!perTestPaths.has(path)) {\n requests.push({ filePath: path, runId: testRunId, testId: 'maestro-suite', type: 'video' });\n }\n }\n\n if (requests.length > 0) {\n const results = await uploadArtifacts(endpoint, token, requests, maxSizeMb);\n const resultEntries = Array.from(results.entries());\n const uploaded = resultEntries.filter(([, r]) => r.success).length;\n const failed = resultEntries.filter(([, r]) => !r.success);\n\n if (uploaded > 0) {\n const videoCount = requests.filter(r => r.type === 'video').length;\n const perFlowVideoCount = (videoArtifacts?.length ?? 0);\n const ssCount = requests.filter(r => r.type === 'screenshot').length;\n const parts: string[] = [];\n if (ssCount > 0) parts.push(`${ssCount} screenshot(s)`);\n if (videoCount > 0) {\n const perFlowNote = perFlowVideoCount > 0 ? ` (${perFlowVideoCount} per-flow)` : '';\n parts.push(`${videoCount} video(s)${perFlowNote}`);\n }\n process.stderr.write(`\\u2713 TestRelic: Uploaded ${uploaded} artifact(s) [${parts.join(', ')}] to cloud storage.\\n`);\n }\n\n if (failed.length > 0) {\n process.stderr.write(`\\u26A0 TestRelic: ${failed.length} artifact(s) failed to upload:\\n`);\n for (const [filePath, result] of failed) {\n const req = requests.find((r) => r.filePath === filePath);\n const label = req ? `[${req.type}] ${filePath}` : filePath;\n process.stderr.write(` \\u2717 ${label} \\u2014 reason: ${result.error ?? 'unknown'}\\n`);\n }\n }\n }\n }\n }\n\n if (cloudClient.isCloudMode()) {\n const tokenValid = await cloudClient.ensureValidToken();\n if (tokenValid) {\n const batchGit = cloudClient.getGitMetadata();\n const batchCi = detectCI();\n const batchGitId = getEffectiveGitId(cloudClient);\n\n const flatTimeline = flattenTimelineForCloud(report.timeline as (TimelineEntry | Record<string, unknown>)[]);\n const flatReport = { ...report, timeline: flatTimeline };\n const payload = buildUploadPayload(flatReport as unknown as TestRunReport, batchGitId, batchGit, batchCi, tests);\n\n const result = await uploadBatchRun(\n cloudClient.getEndpoint(),\n cloudClient.getAccessToken()!,\n payload,\n async () => {\n const refreshed = await cloudClient.ensureValidToken();\n return refreshed ? cloudClient.getAccessToken() : null;\n },\n );\n\n if (result.success) {\n process.stderr.write(`\\u2713 TestRelic: Cloud upload succeeded \\u2192 ${cloudClient.getEndpoint()}/runs/${testRunId}\\n`);\n } else {\n const failure = result as UploadFailure;\n const queueDir = cloudConfig?.queueDirectory ?? '.testrelic/queue';\n writeToQueue(queueDir, testRunId, 'batch', failure.reason, failure.targetEndpoint, failure.method, failure.payload, { 'Content-Type': 'application/json' });\n process.stderr.write('\\u26A0 TestRelic: Cloud upload failed, queued for retry.\\n');\n }\n } else {\n const queueDir = cloudConfig?.queueDirectory ?? '.testrelic/queue';\n const queueGit = cloudClient.getGitMetadata();\n const queueCi = detectCI();\n const queueGitId = getEffectiveGitId(cloudClient);\n const queuePayload = buildUploadPayload(report, queueGitId, queueGit, queueCi);\n writeToQueue(queueDir, testRunId, 'batch', cloudClient.getFailureReason() ?? 'token_invalid', `${cloudClient.getEndpoint()}/runs`, 'POST', queuePayload as unknown as Record<string, unknown>, { 'Content-Type': 'application/json' });\n }\n }\n\n await cloudClient.dispose();\n } catch {\n try { await cloudClient.dispose(); } catch { /* ignore */ }\n }\n}\n","/**\n * Report orchestrator — ties all parsers together to produce\n * a TestRunReport from Maestro's output artifacts.\n */\n\nimport { existsSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { dirname, resolve, basename, extname } from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport type { TestRunReport, TimelineEntry } from '@testrelic/core';\nimport type {\n MaestroFlowResult,\n AIDefect,\n ResolvedMaestroConfig,\n CollectedArtifacts,\n MaestroPlatform,\n MaestroLogEntry,\n} from './types.js';\nimport { parseJUnitFile } from './parsers/junit-parser.js';\nimport { parseCommandsFile, discoverCommandFiles } from './parsers/command-parser.js';\nimport { parseFlowFile, discoverFlowFiles } from './parsers/flow-parser.js';\nimport { parseLogFile, detectPlatformFromLogs, discoverLogFiles } from './parsers/log-parser.js';\nimport { parseAiReportFile, discoverAiReports } from './parsers/ai-report-parser.js';\nimport { collectArtifacts } from './artifact-collector.js';\nimport { buildTimeline } from './timeline-builder.js';\nimport { buildSummary } from './summary-builder.js';\nimport { generateHtmlReport } from './html-report.js';\nimport { printConsoleSummary } from './console-summary.js';\nimport { openInBrowser } from './browser-open.js';\nimport { CloudClient } from './cloud-client.js';\nimport { finalizeAndUpload } from './cloud-reporter.js';\nimport type { PerTestArtifact } from './cloud-reporter.js';\nimport type { TestResultForUpload } from './cloud-upload.js';\n\n/**\n * Try to find the recording file that belongs to a specific Maestro flow.\n *\n * Matching strategy (tried in order):\n * 1. Exact: video base name == flow base name (e.g. \"login\" == \"login\")\n * 2. Forward: video filename contains flow base (e.g. \"login-recording.mp4\")\n * 3. Reverse: flow name contains video base (e.g. \"Login Flow\" → \"login\")\n * 4. Commands: the flow's commands JSON has a startRecording entry whose path\n * parameter matches a video filename.\n *\n * Returns `null` when no candidate is found so the caller can fall back.\n */\nfunction matchVideoToFlow(\n flowFile: string,\n videoPaths: readonly string[],\n flowRecordingPath?: string | null,\n): string | null {\n if (videoPaths.length === 0) return null;\n const flowBase = basename(flowFile, extname(flowFile)).toLowerCase();\n\n // Exact: video base name == flow base name\n const exact = videoPaths.find(\n (v) => basename(v, extname(v)).toLowerCase() === flowBase,\n );\n if (exact) return exact;\n\n // Forward: video filename contains flow base\n const forward = videoPaths.find(\n (v) => basename(v).toLowerCase().includes(flowBase),\n );\n if (forward) return forward;\n\n // Reverse: flow name contains or starts with video base name\n const reverse = videoPaths.find((v) => {\n const videoBase = basename(v, extname(v)).toLowerCase();\n return videoBase.length >= 3 && (flowBase.startsWith(videoBase) || flowBase.includes(videoBase));\n });\n if (reverse) return reverse;\n\n // Commands-JSON: match by the startRecording path extracted from the\n // flow's commands file (e.g. startRecording: \"recording\" → \"recording.mp4\")\n if (flowRecordingPath) {\n const recBase = basename(flowRecordingPath, extname(flowRecordingPath)).toLowerCase();\n const byRec = videoPaths.find((v) => basename(v, extname(v)).toLowerCase() === recBase);\n if (byRec) return byRec;\n const byRecPartial = videoPaths.find((v) => basename(v).toLowerCase().includes(recBase));\n if (byRecPartial) return byRecPartial;\n }\n\n return null;\n}\n\n/**\n * Filter screenshots that belong to a specific Maestro flow.\n */\nfunction matchScreenshotsToFlow(flowFile: string, screenshotPaths: readonly string[]): string[] {\n if (screenshotPaths.length === 0) return [];\n const flowBase = basename(flowFile, extname(flowFile)).toLowerCase();\n const matched = screenshotPaths.filter((s) => {\n const name = basename(s).toLowerCase();\n const nameBase = basename(s, extname(s)).toLowerCase();\n if (name.startsWith(flowBase) || name.includes(flowBase)) return true;\n return nameBase.length >= 3 && (flowBase.startsWith(nameBase) || flowBase.includes(nameBase));\n });\n return matched.length > 0 ? matched : screenshotPaths.slice();\n}\n\n/**\n * Extract the `startRecording` path from a list of parsed command steps.\n * Maestro's commands-*.json contains entries like:\n * { command: \"startRecording\", metadata: { path: \"recording\" } }\n * Returns the path string or null if no recording was started.\n */\nfunction extractRecordingPath(\n commands: ReturnType<typeof parseCommandsFile>,\n): string | null {\n for (const cmd of commands) {\n if (cmd.command === 'startRecording') {\n const meta = cmd.metadata as Record<string, unknown> | undefined;\n const path = meta?.path ?? meta?.recording ?? meta?.fileName;\n if (typeof path === 'string') return path;\n // When startRecording uses shorthand (\"startRecording: name\"), the\n // value ends up in the selector or as the command argument itself.\n if (typeof cmd.selector === 'string') return cmd.selector;\n }\n }\n return null;\n}\n\n/**\n * Build a mapping from flow-name → recording path by inspecting the\n * commands JSON files. Commands files are named `commands-{flowName}.json`.\n */\nfunction buildFlowRecordingMap(\n commandSteps: Map<string, ReturnType<typeof parseCommandsFile>>,\n): Map<string, string> {\n const map = new Map<string, string>();\n for (const [filePath, commands] of commandSteps) {\n const name = basename(filePath)\n .replace(/^commands[-_]?/i, '')\n .replace(/\\.json$/i, '')\n .toLowerCase();\n const recPath = extractRecordingPath(commands);\n if (recPath && name) map.set(name, recPath);\n }\n return map;\n}\n\n/**\n * Maestro emits one `commands-{flowBase}.json` per flow. To prevent the\n * Ask-AI / dashboard step timeline from showing other flows' steps inside\n * this flow, match each flow to its own command file by base name. When no\n * per-flow file matches (legacy single-flow runs that emit a generic\n * `commands.json`), fall back to ALL commands so the timeline isn't empty.\n *\n * Match rules, tried in order:\n * 1. Exact: stripped(commandFileBase) === flowBase.\n * 2. Forward: stripped(commandFileBase) contains flowBase (e.g.\n * `commands-login_flow.json` matches flow base `login_flow`).\n * 3. Reverse: flowBase contains stripped(commandFileBase) for short names.\n */\nfunction commandsForFlow(\n commandSteps: Map<string, ReturnType<typeof parseCommandsFile>>,\n flowFile: string,\n): { commands: ReturnType<typeof parseCommandsFile>; matchedAnyFile: boolean } {\n const flowBase = basename(flowFile, extname(flowFile)).toLowerCase();\n if (!flowBase || commandSteps.size === 0) {\n return { commands: [], matchedAnyFile: false };\n }\n\n const stripCmdPrefix = (p: string): string =>\n basename(p, extname(p)).replace(/^commands[-_]?/i, '').toLowerCase();\n\n // Exact match first\n for (const [cmdFile, cmds] of commandSteps) {\n if (stripCmdPrefix(cmdFile) === flowBase) {\n return { commands: cmds, matchedAnyFile: true };\n }\n }\n // Forward: command file name contains the flow base\n for (const [cmdFile, cmds] of commandSteps) {\n const stripped = stripCmdPrefix(cmdFile);\n if (stripped.length >= 3 && stripped.includes(flowBase)) {\n return { commands: cmds, matchedAnyFile: true };\n }\n }\n // Reverse: flow base contains the command file's base (handles\n // `commands-login.json` → flow `login_flow.yaml`).\n for (const [cmdFile, cmds] of commandSteps) {\n const stripped = stripCmdPrefix(cmdFile);\n if (stripped.length >= 3 && flowBase.includes(stripped)) {\n return { commands: cmds, matchedAnyFile: true };\n }\n }\n return { commands: [], matchedAnyFile: false };\n}\n\n/**\n * Map a Maestro platform identifier to a human-readable OS string suitable\n * for display in the dashboard (e.g. 'android' → 'Android').\n */\nfunction platformToOs(platform: MaestroPlatform): string | undefined {\n switch (platform) {\n case 'android': return 'Android';\n case 'ios': return 'iOS';\n case 'web': return 'Web';\n default: return undefined;\n }\n}\n\n/**\n * Convert a MaestroFlowResult into the TestResultForUpload shape expected by\n * the server's batch-upload endpoint. This is what makes individual flows\n * visible as test cases in the TestRelic dashboard.\n */\nfunction flowToTestResult(flow: MaestroFlowResult): TestResultForUpload {\n // Lower-case the level so the platform's frontend Logs tab (which expects\n // 'error' | 'warn' | 'info' | 'debug' | 'log') renders the right chip.\n // The server stores levels verbatim, so do the canonicalisation here.\n const consoleLogs = flow.logEntries.length > 0\n ? flow.logEntries.map((entry) => ({\n level: entry.level.toLowerCase(),\n message: entry.message,\n timestamp: entry.timestamp,\n source: entry.source,\n }))\n : undefined;\n\n return {\n testId: `${flow.flowFile}::${flow.flowName}`,\n title: flow.flowName,\n status: flow.status,\n duration: flow.duration,\n suiteName: flow.appId ?? 'maestro-suite',\n filePath: flow.flowFile,\n testType: 'mobile',\n isFlaky: false,\n startedAt: flow.startedAt,\n completedAt: flow.completedAt,\n tags: flow.tags,\n failure: flow.failureMessage\n ? { message: flow.failureMessage }\n : null,\n retry: 0,\n retryCount: 0,\n retryStatus: null,\n source: 'maestro',\n platform: flow.platform !== 'unknown' ? flow.platform : undefined,\n os: platformToOs(flow.platform),\n deviceName: flow.deviceId,\n ...(consoleLogs ? { consoleLogs } : {}),\n };\n}\n\n/**\n * Slice a set of parsed maestro.log entries into the window\n * `[flowStartedAt, flowCompletedAt]`. Entries with unparseable timestamps are\n * included in every window so the user does not lose context when the log\n * format is ambiguous.\n */\nfunction logEntriesForFlow(\n allEntries: readonly MaestroLogEntry[],\n flowStartedAt: string,\n flowCompletedAt: string,\n): MaestroLogEntry[] {\n if (allEntries.length === 0) return [];\n const startMs = Date.parse(flowStartedAt);\n const endMs = Date.parse(flowCompletedAt);\n if (Number.isNaN(startMs) || Number.isNaN(endMs)) {\n return allEntries.slice();\n }\n return allEntries.filter((entry) => {\n const t = Date.parse(entry.timestamp);\n if (Number.isNaN(t)) return true;\n return t >= startMs && t <= endMs;\n });\n}\n\nexport interface OrchestratorInput {\n readonly junitPath?: string;\n readonly testOutputDir?: string;\n readonly debugOutputDir?: string;\n readonly flowsDir?: string;\n readonly config: ResolvedMaestroConfig;\n /** Device serial / name used during this run, e.g. \"emulator-5554\". Passed through to each flow result and uploaded as deviceName. */\n readonly device?: string;\n /**\n * Explicit mobile platform override ('android' | 'ios').\n * When set, this takes precedence over log-based detection so callers that\n * already know the target platform (e.g. the E2E script using adb) do not\n * rely on heuristic detection that may return 'unknown'.\n */\n readonly platform?: MaestroPlatform;\n}\n\nexport interface OrchestratorResult {\n readonly report: TestRunReport;\n readonly flowResults: MaestroFlowResult[];\n readonly aiDefects: AIDefect[];\n readonly artifacts: CollectedArtifacts;\n}\n\nexport async function orchestrateReport(input: OrchestratorInput): Promise<OrchestratorResult> {\n const { config } = input;\n const startedAt = new Date().toISOString();\n const testRunId = config.testRunId ?? randomUUID();\n\n const artifacts = collectArtifacts(input.testOutputDir, input.debugOutputDir);\n const junitPath = input.junitPath ?? artifacts.junitReportPath;\n\n // Parse every maestro.log file up-front so we can both detect the platform\n // and attach the entries to each flow's result (so the platform's Logs tab\n // shows real log lines instead of an empty state).\n const logFiles = input.debugOutputDir ? discoverLogFiles(input.debugOutputDir) : artifacts.logPaths;\n const allLogEntries: MaestroLogEntry[] = [];\n for (const logFile of logFiles) {\n const entries = parseLogFile(logFile);\n if (entries.length > 0) allLogEntries.push(...entries);\n }\n\n // Caller-supplied platform takes priority; log detection is a fallback.\n let platform: MaestroPlatform = input.platform ?? 'unknown';\n if (platform === 'unknown' && allLogEntries.length > 0) {\n const detected = detectPlatformFromLogs(allLogEntries);\n if (detected !== 'unknown') platform = detected;\n }\n\n const flowMetadataMap = new Map<string, ReturnType<typeof parseFlowFile>>();\n if (input.flowsDir && existsSync(input.flowsDir)) {\n const flowFiles = discoverFlowFiles(input.flowsDir);\n for (const flowFile of flowFiles) {\n const meta = parseFlowFile(flowFile);\n const name = meta.name ?? flowFile;\n flowMetadataMap.set(name, meta);\n }\n }\n\n const commandSteps = new Map<string, ReturnType<typeof parseCommandsFile>>();\n const commandFiles = input.testOutputDir ? discoverCommandFiles(input.testOutputDir) : artifacts.commandJsonPaths;\n for (const cmdFile of commandFiles) {\n commandSteps.set(cmdFile, parseCommandsFile(cmdFile));\n }\n\n const allAiDefects: AIDefect[] = [];\n const aiReportFiles = input.testOutputDir ? discoverAiReports(input.testOutputDir) : artifacts.aiReportPaths;\n for (const aiFile of aiReportFiles) {\n const aiReport = parseAiReportFile(aiFile);\n allAiDefects.push(...aiReport.defects);\n }\n\n // Build a flow-name → recording-path map from the commands JSONs so we can\n // match videos even when filenames don't correlate with flow filenames\n // (e.g. startRecording: \"recording\" in login_flow.yaml → recording.mp4).\n const flowRecordingMap = buildFlowRecordingMap(commandSteps);\n\n const flowResults: MaestroFlowResult[] = [];\n\n if (junitPath && existsSync(junitPath)) {\n const junit = parseJUnitFile(junitPath);\n\n // Suite-wide fallback for single-flow runs that emit a generic commands.json\n // with no flow-name suffix — used only when commandsForFlow finds no match.\n const allCommands = Array.from(commandSteps.values()).flat();\n const allAssertions = allCommands.filter((c) => c.category === 'assertion');\n const allNonAssertions = allCommands.filter((c) => c.category !== 'assertion');\n\n for (const suite of junit.testSuites) {\n for (const testCase of suite.testCases) {\n const name = testCase.name;\n const meta = flowMetadataMap.get(name);\n\n const status = testCase.status === 'SUCCESS' ? 'passed'\n : testCase.status === 'SKIPPED' ? 'skipped'\n : 'failed';\n\n const durationMs = testCase.time * 1000;\n const flowStartedAt = startedAt;\n const flowCompletedAt = new Date(new Date(flowStartedAt).getTime() + durationMs).toISOString();\n\n const flowFile = testCase.classname || name;\n const flowBase = basename(flowFile, extname(flowFile)).toLowerCase();\n const recordingPath = flowRecordingMap.get(flowBase) ?? null;\n\n // Attribute only this flow's own commands/assertions so the Ask-AI\n // timeline / step tools surface the right per-flow steps.\n const { commands: flowCmds, matchedAnyFile } = commandsForFlow(commandSteps, flowFile);\n const perFlowCommands = matchedAnyFile\n ? flowCmds.filter((c) => c.category !== 'assertion')\n : allNonAssertions;\n const perFlowAssertions = matchedAnyFile\n ? flowCmds.filter((c) => c.category === 'assertion')\n : allAssertions;\n\n flowResults.push({\n flowName: name,\n flowFile,\n appId: meta?.appId ?? null,\n platform,\n deviceId: input.device,\n status,\n duration: durationMs,\n startedAt: flowStartedAt,\n completedAt: flowCompletedAt,\n tags: meta?.tags ?? [],\n properties: meta?.properties ?? {},\n commands: perFlowCommands,\n assertions: perFlowAssertions,\n screenshotPaths: matchScreenshotsToFlow(flowFile, artifacts.screenshotPaths),\n videoPath: matchVideoToFlow(flowFile, artifacts.videoPaths, recordingPath),\n aiDefects: allAiDefects,\n logEntries: logEntriesForFlow(allLogEntries, flowStartedAt, flowCompletedAt),\n failureMessage: testCase.failureMessage ?? testCase.errorMessage ?? null,\n failureType: testCase.failureType ?? testCase.errorType ?? null,\n subflowRefs: meta?.subflowRefs ?? [],\n metadata: meta ?? null,\n });\n }\n }\n }\n\n if (flowResults.length === 0 && flowMetadataMap.size > 0) {\n for (const [, meta] of flowMetadataMap) {\n flowResults.push({\n flowName: meta.name ?? meta.filePath,\n flowFile: meta.filePath,\n appId: meta.appId,\n platform,\n deviceId: input.device,\n status: 'passed',\n duration: 0,\n startedAt,\n completedAt: startedAt,\n tags: meta.tags,\n properties: meta.properties,\n commands: [],\n assertions: [],\n screenshotPaths: [],\n videoPath: null,\n aiDefects: [],\n // No JUnit window to bound by; if maestro.log was parsed, surface\n // every entry so the platform's Logs tab is still useful for\n // pre-test setup / runtime failures.\n logEntries: allLogEntries.slice(),\n failureMessage: null,\n failureType: null,\n subflowRefs: meta.subflowRefs,\n metadata: meta,\n });\n }\n }\n\n const timeline: TimelineEntry[] = buildTimeline(flowResults);\n const summary = buildSummary(timeline);\n const completedAt = new Date().toISOString();\n const totalDuration = Date.now() - new Date(startedAt).getTime();\n\n const report: TestRunReport = {\n schemaVersion: '1.0.0',\n testRunId,\n startedAt,\n completedAt,\n totalDuration,\n summary,\n ci: null,\n metadata: config.metadata ?? null,\n timeline,\n shardRunIds: null,\n };\n\n mkdirSync(dirname(resolve(config.outputPath)), { recursive: true });\n writeFileSync(resolve(config.outputPath), JSON.stringify(report, null, 2), 'utf-8');\n\n generateHtmlReport(report, allAiDefects, artifacts.screenshotPaths, resolve(config.htmlReportPath));\n\n printConsoleSummary(summary, config.outputPath, config.htmlReportPath, config.quiet, allAiDefects);\n\n if (config.openReport) {\n openInBrowser(resolve(config.htmlReportPath));\n }\n\n if (config.cloud) {\n const cloudClient = new CloudClient(config.cloud);\n await cloudClient.initialize();\n\n const videoPathsToUpload = config.includeVideo ? artifacts.videoPaths : [];\n\n // Build per-flow video associations so each flow shows its own recording\n // in the cloud platform instead of a shared suite-level clip.\n const videoArtifacts: PerTestArtifact[] = [];\n if (videoPathsToUpload.length > 0) {\n // First pass: try to match videos to flows using all heuristics.\n for (const flow of flowResults) {\n const flowBase = basename(flow.flowFile, extname(flow.flowFile)).toLowerCase();\n const recordingPath = flowRecordingMap.get(flowBase) ?? null;\n const matched = matchVideoToFlow(flow.flowFile, videoPathsToUpload, recordingPath);\n if (matched) {\n const testId = `${flow.flowFile}::${flow.flowName}`;\n if (!videoArtifacts.some((va) => va.path === matched && va.testId === testId)) {\n videoArtifacts.push({ path: matched, testId });\n }\n }\n }\n\n // When only one video exists for the entire suite and some flows were\n // not matched, assign that single video to ALL unmatched flows. This\n // covers the common case of a single startRecording in the first flow\n // that captures the whole suite session.\n if (videoPathsToUpload.length === 1) {\n const singleVideo = videoPathsToUpload[0];\n for (const flow of flowResults) {\n const testId = `${flow.flowFile}::${flow.flowName}`;\n if (!videoArtifacts.some((va) => va.testId === testId)) {\n videoArtifacts.push({ path: singleVideo, testId });\n }\n }\n }\n }\n\n // Videos not matched to any flow are passed via the legacy `videoPaths`\n // param so they still appear under the suite-level umbrella.\n const matchedPaths = new Set(videoArtifacts.map((va) => va.path));\n const unmatchedVideos = videoPathsToUpload.filter((v) => !matchedPaths.has(v));\n\n // Convert each flow result to the TestResultForUpload shape so the server\n // stores them as individual test cases (visible in the dashboard test list).\n const testsForUpload: TestResultForUpload[] = flowResults.map(flowToTestResult);\n\n await finalizeAndUpload(\n cloudClient,\n config.cloud,\n testRunId,\n report,\n completedAt,\n totalDuration,\n summary,\n config.includeScreenshots ? artifacts.screenshotPaths : [],\n unmatchedVideos,\n testsForUpload,\n videoArtifacts,\n );\n }\n\n return { report, flowResults, aiDefects: allAiDefects, artifacts };\n}\n","/**\n * Merge multiple Maestro report JSON files into a single report.\n */\n\nimport { readFileSync, readdirSync, writeFileSync, existsSync } from 'node:fs';\nimport { join, extname } from 'node:path';\nimport type { TestRunReport, TimelineEntry, TimelineStep } from '@testrelic/core';\nimport { buildSummary } from './summary-builder.js';\n\nfunction isTimelineEntry(item: TimelineEntry | TimelineStep): item is TimelineEntry {\n return 'tests' in item && 'visitedAt' in item;\n}\n\nexport function mergeReports(reportPaths: string[]): TestRunReport {\n const allTimeline: (TimelineEntry | TimelineStep)[] = [];\n const timelineEntries: TimelineEntry[] = [];\n let earliestStart = '';\n let latestEnd = '';\n let runId = '';\n\n for (const reportPath of reportPaths) {\n try {\n const content = readFileSync(reportPath, 'utf-8');\n const report = JSON.parse(content) as TestRunReport;\n\n if (!runId) runId = report.testRunId;\n if (!earliestStart || report.startedAt < earliestStart) earliestStart = report.startedAt;\n if (!latestEnd || (report.completedAt && report.completedAt > latestEnd)) latestEnd = report.completedAt;\n\n for (const item of report.timeline) {\n allTimeline.push(item);\n if (isTimelineEntry(item)) timelineEntries.push(item);\n }\n } catch {\n process.stderr.write(`\\u26A0 TestRelic: Unable to read report file: ${reportPath}\\n`);\n }\n }\n\n const summary = buildSummary(timelineEntries);\n const startMs = earliestStart ? new Date(earliestStart).getTime() : Date.now();\n const endMs = latestEnd ? new Date(latestEnd).getTime() : Date.now();\n\n return {\n schemaVersion: '1.0.0',\n testRunId: runId || `maestro-merged-${Date.now()}`,\n startedAt: earliestStart || new Date().toISOString(),\n completedAt: latestEnd || new Date().toISOString(),\n totalDuration: endMs - startMs,\n summary,\n ci: null,\n metadata: null,\n timeline: allTimeline,\n shardRunIds: null,\n };\n}\n\nexport function mergeReportsFromDirectory(dirPath: string, outputPath: string): TestRunReport {\n if (!existsSync(dirPath)) {\n throw new Error(`Directory does not exist: ${dirPath}`);\n }\n\n const reportPaths = readdirSync(dirPath)\n .filter((f) => extname(f) === '.json' && f.includes('testrelic'))\n .map((f) => join(dirPath, f));\n\n const merged = mergeReports(reportPaths);\n writeFileSync(outputPath, JSON.stringify(merged, null, 2), 'utf-8');\n return merged;\n}\n"]}