@testrelic/maestro-analytics 1.2.2 → 1.3.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/network-proxy.ts","../src/device-proxy-setup.ts","../src/maestro-runner.ts","../src/api-redactor.ts","../src/parsers/network-parser.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","DEFAULT_REDACT_HEADERS","DEFAULT_REDACT_BODY_FIELDS","DEFAULT_MAX_BODY_BYTES","resolveNetworkCapture","options","o","envEnabled","hasPrototypePollution","isValidMaestroConfig","input","resolveConfig","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","REDACTED_DETAIL_COMMANDS","MAX_SCRIPT_DETAIL_LENGTH","MAX_AI_PROMPT_LENGTH","categorizeCommand","command","MAESTRO_COMMAND_NAME_MAP","normalizeMaestroCommandName","withoutSuffix","RESERVED_CHILD_KEYS","findInlineCommandKey","extractMaestroSelector","params","sel","cond","inner","truncate","max","redactText","stringifyPoint","point","extractCommandDetails","commandName","details","text","startPoint","endPoint","appId","inlineScript","prompt","v","extractChildCommands","baseTimestamp","candidates","c","entry","parseRawCommand","index","selector","children","commandObj","rawKey","inlineKey","meta","mapStatus","duration","sequenceNumber","timestamp","lower","parseCommandsJson","jsonContent","base","commands","parseCommandsFile","discoverCommandFiles","artifactsDir","readdirSync","f","basename","extractSubflowRefs","body","refs","item","flow","extractHookRefs","hooks","items","parseFlowYaml","parts","headerContent","bodyContent","header","parseDocument","bodyCommands","tags","t","env","k","properties","onFlowStart","onFlowComplete","subflowRefs","parseFlowFile","discoverFlowFiles","dir","results","walk","entries","fullPath","ext","extname","LOG_LINE_REGEX","TIMESTAMP_ONLY_REGEX","parseLevel","upper","buildEntryTimestamp","dateAnchor","hhmmss","m","h","mi","parseLogContent","defaultDate","lines","anchorDate","dateAnchorStr","line","trimmed","ts","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","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","buildStepTitle","cmd","d","commandToAction","flowStartedMs","cmdMs","videoOffset","sortActions","a","b","ta","tb","buildTestResult","apiCalls","orderedCommands","sa","sb","orderedAssertions","actions","buildTimelineEntry","apiCallsByFlow","testId","testResult","buildTimeline","flows","EMPTY_STATUS_RANGE","statusBucket","code","percentile","sorted","idx","buildApiStats","calls","byMethod","byStatus","urls","times","apiResponseTime","n","buildSummary","timeline","total","passed","failed","flaky","timedout","totalAssertions","passedAssertions","failedAssertions","totalNavigations","totalActionSteps","actionCategoryCounts","uniqueUrls","allApiCalls","test","action","cat","apiStats","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","warning","info","defectParts","openInBrowser","platform","exec","resolveAddonPath","here","fileURLToPath","maybeDirname","generateRunDir","randomBytes","tmpdir","isMitmdumpAvailable","resolvePromise","child","spawn","settle","ok","startNetworkProxy","verbose","addon","outputDir","args","err","chunk","r","stop","settled","NOOP_HANDLE","commandExists","spawnSync","logHint","logManualInstructions","proxyHost","proxyPort","adb","deviceId","full","out","setUpAndroidEmulator","opts","skipCertInstall","setResult","xcrun","setUpIosSimulator","caCandidate","udid","res","setUpDeviceProxy","generateTempDir","buildMaestroArgs","tempDir","junitPath","normalisePlatform","runMaestroProc","networkJsonlDir","onClose","stdoutChunks","stderrChunks","proc","runMaestro","extras","proxyHandle","deviceHandle","networkConfig","networkDir","cleanup","REDACTED","redactHeaders","headers","redactList","redactJsonBody","fields","redactBody","redacted","redactApiCall","call","redactHeadersList","redactBodyFields","matchesUrlFilter","url","include","exclude","matches","pat","parseJsonlLine","parseNetworkJsonl","records","rec","parseNetworkJsonlFile","discoverNetworkJsonlFiles","harHeadersToRecord","harBodyToString","postData","isBinaryMime","mime","harEntryToRecord","req","isBinary","parseHar","parseHarFile","bindApiCallsToFlows","buckets","windows","hit","w","best","dist","collectNetworkByFlow","jsonlDir","all","file","reindexFlowCalls","LOCALHOST_HOSTS","enforceHttps","endpoint","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","filename","tmpPath","renameSync","flushQueue","files","batches","batch","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","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","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","normaliseCommandFileName","buildFlowRecordingMap","commandSteps","map","recPath","commandsForFlow","stripCmdPrefix","cmdFile","cmds","stripped","platformToOs","flowToTestResult","consoleLogs","logEntriesForFlow","allEntries","flowStartedAt","flowCompletedAt","startMs","endMs","orchestrateReport","startedAt","randomUUID","logFiles","allLogEntries","logFile","mtimeDate","detected","flowMetadataMap","flowFiles","commandFiles","allAiDefects","aiReportFiles","aiFile","aiReport","flowRecordingMap","flowResults","junit","allCommands","allAssertions","allNonAssertions","testCase","durationMs","recordingPath","flowCmds","matchedAnyFile","perFlowCommands","perFlowAssertions","earliestCmdMs","min","bound","remapped","videoPathsToUpload","singleVideo","matchedPaths","unmatchedVideos","testsForUpload","tlTest","isTimelineEntry","mergeReports","reportPaths","allTimeline","timelineEntries","earliestStart","latestEnd","reportPath","mergeReportsFromDirectory"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;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;AA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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 {\n MaestroReporterConfig,\n ResolvedMaestroConfig,\n NetworkCaptureConfig,\n ResolvedNetworkCaptureConfig,\n} from './types.js';\nimport { discoverConfigFile, parseConfigFile, resolveEnvVars, mergeCloudConfig } from './cloud-config.js';\n\n// Defaults mirror @testrelic/playwright-analytics so the same redaction policy\n// applies to mobile and web captures. Sensitive header / body field names are\n// case-insensitive when applied at capture time.\nconst DEFAULT_REDACT_HEADERS: readonly string[] = [\n 'authorization', 'cookie', 'set-cookie', 'x-api-key',\n];\nconst DEFAULT_REDACT_BODY_FIELDS: readonly string[] = [\n 'password', 'secret', 'token', 'apiKey', 'api_key',\n];\nconst DEFAULT_MAX_BODY_BYTES = 1024 * 1024; // 1 MiB\n\nfunction resolveNetworkCapture(\n options: NetworkCaptureConfig | undefined,\n): ResolvedNetworkCaptureConfig {\n const o = options ?? {};\n const envEnabled = process.env.TESTRELIC_CAPTURE_NETWORK === '1'\n || process.env.TESTRELIC_CAPTURE_NETWORK?.toLowerCase() === 'true';\n return Object.freeze({\n enabled: o.enabled ?? envEnabled ?? false,\n proxyPort: o.proxyPort ?? (process.env.TESTRELIC_PROXY_PORT ? Number.parseInt(process.env.TESTRELIC_PROXY_PORT, 10) : 8080),\n proxyHost: o.proxyHost ?? process.env.TESTRELIC_PROXY_HOST ?? '127.0.0.1',\n harPath: o.harPath ?? process.env.TESTRELIC_HAR_PATH ?? null,\n outputDir: o.outputDir ?? null,\n skipCertInstall: o.skipCertInstall ?? false,\n includeUrls: Object.freeze(o.includeUrls ? [...o.includeUrls] : []),\n excludeUrls: Object.freeze(o.excludeUrls ? [...o.excludeUrls] : []),\n redactHeaders: Object.freeze(o.redactHeaders ? [...o.redactHeaders] : [...DEFAULT_REDACT_HEADERS]),\n redactBodyFields: Object.freeze(o.redactBodyFields ? [...o.redactBodyFields] : [...DEFAULT_REDACT_BODY_FIELDS]),\n maxBodyBytes: o.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES,\n });\n}\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 target.network = resolveNetworkCapture(options?.network);\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\n// Commands whose params should NEVER be surfaced verbatim in the report.\n// `inputText` is here because it often contains usernames / passwords / OTPs;\n// we still capture a length-preserving redaction so timing diagnostics remain useful.\nconst REDACTED_DETAIL_COMMANDS = new Set(['inputText', 'pasteText']);\n\nconst MAX_SCRIPT_DETAIL_LENGTH = 200;\nconst MAX_AI_PROMPT_LENGTH = 400;\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 /** Children for flow-control wrappers (retry/repeat). Maestro stores these as `commands` or `subSteps`. */\n commands?: unknown;\n subSteps?: unknown;\n children?: unknown;\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 * Reserved keys that may appear on a Maestro child entry alongside the\n * command-type key (which is what we actually want).\n */\nconst RESERVED_CHILD_KEYS = new Set([\n 'metadata', 'command', 'commandName', 'name', 'status', 'duration',\n 'durationMs', 'timestamp', 'time', 'error', 'errorMessage', 'selector',\n 'commands', 'subSteps', 'children', 'sequenceNumber',\n]);\n\n/**\n * Find the inline Maestro command-type key on a child entry. Children inside\n * `runFlowCommand.commands` (and retry/repeat) are emitted as bare\n * `{ \"<typeName>\": { ...params } }` objects with no `command:` wrapper. We pick\n * the first non-reserved key that points to an object; everything else is\n * metadata-shaped and shouldn't be treated as the command.\n */\nfunction findInlineCommandKey(raw: Record<string, unknown>): string | undefined {\n for (const key of Object.keys(raw)) {\n if (RESERVED_CHILD_KEYS.has(key)) continue;\n const value = raw[key];\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n return key;\n }\n }\n return undefined;\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 truncate(value: string, max: number): string {\n if (value.length <= max) return value;\n return `${value.slice(0, max)}…`;\n}\n\nfunction redactText(value: string): string {\n // Keep a length-hint without leaking the actual content.\n if (value.length === 0) return '';\n return `[REDACTED ${value.length} chars]`;\n}\n\nfunction stringifyPoint(point: unknown): string | undefined {\n if (!point || typeof point !== 'object') return undefined;\n const p = point as Record<string, unknown>;\n if (typeof p.x === 'number' && typeof p.y === 'number') return `${p.x},${p.y}`;\n return undefined;\n}\n\n/**\n * Extract human-relevant command parameters for the report's step detail view.\n * Returns `undefined` when there's nothing useful to surface (selector is\n * already covered by `extractMaestroSelector`).\n *\n * Sensitive commands (inputText / pasteText) are stored with the text value\n * redacted so diagnostics remain useful without leaking PII to the cloud.\n */\nfunction extractCommandDetails(\n commandName: string,\n params: Record<string, unknown>,\n): Record<string, unknown> | undefined {\n const details: Record<string, unknown> = {};\n\n switch (commandName) {\n case 'inputText':\n case 'pasteText': {\n const text = params.text ?? params.value;\n if (typeof text === 'string') {\n details.text = REDACTED_DETAIL_COMMANDS.has(commandName) ? redactText(text) : text;\n }\n break;\n }\n case 'swipe':\n case 'scroll':\n case 'scrollUntilVisible': {\n if (typeof params.direction === 'string') details.direction = params.direction;\n const startPoint = stringifyPoint(params.start ?? params.startRelative);\n const endPoint = stringifyPoint(params.end ?? params.endRelative);\n if (startPoint) details.start = startPoint;\n if (endPoint) details.end = endPoint;\n if (typeof params.duration === 'number') details.swipeDurationMs = params.duration;\n if (typeof params.timeout === 'number') details.timeoutMs = params.timeout;\n break;\n }\n case 'setLocation': {\n if (typeof params.latitude === 'number') details.latitude = params.latitude;\n if (typeof params.longitude === 'number') details.longitude = params.longitude;\n break;\n }\n case 'pressKey':\n case 'back': {\n if (typeof params.key === 'string') details.key = params.key;\n else if (typeof params.code === 'string') details.key = params.code;\n break;\n }\n case 'setOrientation': {\n if (typeof params.orientation === 'string') details.orientation = params.orientation;\n break;\n }\n case 'setAirplaneMode':\n case 'toggleAirplaneMode': {\n if (typeof params.value === 'string' || typeof params.value === 'boolean') {\n details.value = params.value;\n }\n break;\n }\n case 'setPermissions': {\n if (params.permissions && typeof params.permissions === 'object') {\n details.permissions = params.permissions;\n }\n break;\n }\n case 'addMedia': {\n if (Array.isArray(params.mediaPaths)) details.mediaPaths = params.mediaPaths;\n else if (typeof params.path === 'string') details.mediaPaths = [params.path];\n break;\n }\n case 'launchApp':\n case 'killApp':\n case 'stopApp':\n case 'clearState': {\n const appId = params.appId ?? params.packageName ?? params.bundleId;\n if (typeof appId === 'string') details.appId = appId;\n if (params.arguments) details.arguments = params.arguments;\n break;\n }\n case 'openLink': {\n if (typeof params.url === 'string') details.url = params.url;\n else if (typeof params.link === 'string') details.url = params.link;\n break;\n }\n case 'runScript':\n case 'evalScript': {\n if (typeof params.path === 'string') details.path = params.path;\n // Maestro emits inline JS under `scriptString` for evalScript, `script` for legacy runScript.\n const inlineScript = params.script ?? params.scriptString;\n if (typeof inlineScript === 'string') details.script = truncate(inlineScript, MAX_SCRIPT_DETAIL_LENGTH);\n if (typeof params.sourceDescription === 'string') details.sourceDescription = params.sourceDescription;\n break;\n }\n case 'assertWithAI':\n case 'assertNoDefectsWithAI':\n case 'extractTextWithAI': {\n const prompt = params.assertion ?? params.prompt ?? params.question;\n if (typeof prompt === 'string') details.prompt = truncate(prompt, MAX_AI_PROMPT_LENGTH);\n break;\n }\n case 'extendedWaitUntil':\n case 'waitForAnimationToEnd': {\n if (typeof params.timeout === 'number') details.timeoutMs = params.timeout;\n break;\n }\n case 'repeat':\n case 'retry': {\n if (typeof params.times === 'number') details.times = params.times;\n if (typeof params.maxRetries === 'number') details.maxRetries = params.maxRetries;\n if (typeof params.condition === 'object' && params.condition) details.condition = params.condition;\n break;\n }\n case 'runFlow': {\n if (typeof params.file === 'string') details.file = params.file;\n if (typeof params.flowId === 'string') details.flowId = params.flowId;\n break;\n }\n default:\n // Surface a single useful primitive for unknown commands if present, otherwise drop.\n for (const key of ['text', 'value', 'url']) {\n const v = params[key];\n if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') {\n details[key] = v;\n break;\n }\n }\n }\n\n return Object.keys(details).length > 0 ? details : undefined;\n}\n\n/**\n * Extract child commands from a wrapper command (retry / repeat / runFlow).\n * Maestro 2.x emits the children under `commands` inside the nested params; legacy\n * format may put them under `subSteps` or `children` on the raw entry. Returns an\n * empty array when none are present.\n */\nfunction extractChildCommands(\n params: Record<string, unknown>,\n raw: RawCommandEntry,\n baseTimestamp: string,\n): MaestroCommandStep[] {\n const candidates = [params.commands, raw.commands, raw.subSteps, raw.children];\n for (const c of candidates) {\n if (Array.isArray(c) && c.length > 0) {\n return (c as RawCommandEntry[]).map((entry, i) =>\n parseRawCommand(entry, i, baseTimestamp),\n );\n }\n }\n return [];\n}\n\nfunction parseRawCommand(raw: RawCommandEntry, index: number, baseTimestamp: string): MaestroCommandStep {\n let command: string;\n let selector: string | undefined;\n let details: Record<string, unknown> | undefined;\n let children: MaestroCommandStep[] = [];\n\n if (typeof raw.command === 'object' && raw.command !== null && !Array.isArray(raw.command)) {\n // Maestro 2.x top-level entries: { \"command\": { \"launchAppCommand\": { ...params } }, \"metadata\": {...} }\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 details = extractCommandDetails(command, params);\n children = extractChildCommands(params, raw, baseTimestamp);\n } else {\n // Maestro 2.x children inside runFlow/retry/repeat `commands` arrays are emitted\n // *without* a `command:` wrapper — the command-type key sits directly on the entry\n // (e.g. `{ \"tapOnElement\": { ... } }`). Detect that shape first; fall back to the\n // legacy/string forms only if no inline key is present.\n const inlineKey = findInlineCommandKey(raw);\n if (inlineKey) {\n command = normalizeMaestroCommandName(inlineKey);\n const params = (raw[inlineKey] as Record<string, unknown>) ?? {};\n selector = extractMaestroSelector(params);\n details = extractCommandDetails(command, params);\n children = extractChildCommands(params, raw, baseTimestamp);\n } else {\n command = (raw.command as string | undefined) ?? raw.commandName ?? raw.name ?? `step-${index}`;\n selector = raw.selector ?? undefined;\n children = extractChildCommands({}, raw, baseTimestamp);\n }\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 const sequenceNumber = typeof meta.sequenceNumber === 'number' ? meta.sequenceNumber : undefined;\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 ...(details ? { details } : {}),\n ...(sequenceNumber !== undefined ? { sequenceNumber } : {}),\n ...(children.length > 0 ? { children } : {}),\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\n// Maestro emits log lines in the form\n// \"02:43:56.278 [ INFO] MAESTRO.logSystemInfo: ...message\"\n// Level may be unbracketed (legacy) OR bracketed with optional inner whitespace.\n// We allow either so a future Maestro change to the format doesn't silently\n// regress the consoleLogs upload again.\nconst LOG_LINE_REGEX = /^(\\d{4}-\\d{2}-\\d{2}[T ]\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:?\\d{2})?)\\s+\\[?\\s*(\\w+)\\s*\\]?\\s+(.+)$/;\nconst TIMESTAMP_ONLY_REGEX = /^\\[?(\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?)\\]?\\s+\\[?\\s*(\\w+)\\s*\\]?\\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\n/**\n * Combine the LOCAL-time date components of `dateAnchor` with the\n * LOCAL-time `HH:MM:SS.mmm` from a log line, then return the proper UTC\n * ISO string. This is the only correct way to handle Maestro's log timestamps\n * because Maestro writes wall-clock time in the host's local timezone without\n * an offset — naive concatenation with a UTC date prefix produces an ISO\n * string that's off by the local UTC offset (e.g. 5:30h for IST), causing\n * `logEntriesForFlow` to drop every line.\n */\nfunction buildEntryTimestamp(dateAnchor: Date, hhmmss: string): string {\n const m = /^(\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d{1,3}))?$/.exec(hhmmss);\n if (!m) {\n // Should not happen — caller already matched the timestamp regex.\n return new Date().toISOString();\n }\n const h = Number(m[1]);\n const mi = Number(m[2]);\n const s = Number(m[3]);\n const ms = m[4] ? Number(m[4].padEnd(3, '0')) : 0;\n // `new Date(y, m, d, h, mi, s, ms)` interprets components as LOCAL time\n // and stores the absolute instant — `toISOString()` then yields UTC.\n const local = new Date(\n dateAnchor.getFullYear(),\n dateAnchor.getMonth(),\n dateAnchor.getDate(),\n h, mi, s, ms,\n );\n return local.toISOString();\n}\n\n/**\n * @param defaultDate Either an ISO date string (`YYYY-MM-DD`) or a Date object.\n * Used to anchor log lines that only carry a wall-clock time (`HH:MM:SS`).\n * Callers should pass the log file's mtime as a Date so the local-zone\n * conversion produces a correct UTC ISO timestamp for log entries. When a\n * plain string is passed, the timestamp is constructed without TZ-shift\n * compensation (legacy behaviour — may be off by the local UTC offset).\n */\nexport function parseLogContent(\n content: string,\n defaultDate?: string | Date,\n): MaestroLogEntry[] {\n const entries: MaestroLogEntry[] = [];\n const lines = content.split('\\n');\n const anchorDate: Date | null = defaultDate instanceof Date\n ? defaultDate\n : null;\n const dateAnchorStr: string = typeof defaultDate === 'string'\n ? defaultDate\n : (defaultDate instanceof Date\n ? `${defaultDate.getFullYear()}-${String(defaultDate.getMonth() + 1).padStart(2, '0')}-${String(defaultDate.getDate()).padStart(2, '0')}`\n : 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 // Prefer the TZ-aware local-date construction when the caller passed a\n // real Date; otherwise fall back to the legacy string-concat path.\n const ts = anchorDate\n ? buildEntryTimestamp(anchorDate, match[1])\n : `${dateAnchorStr}T${match[1]}`;\n entries.push({\n timestamp: ts,\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(\n filePath: string,\n defaultDate?: string | Date,\n): MaestroLogEntry[] {\n try {\n const content = readFileSync(filePath, 'utf-8');\n return parseLogContent(content, defaultDate);\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 TestArtifacts,\n ActionStep,\n ActionCategory,\n ApiCallRecord,\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\n/**\n * Build a human-readable title for a command step, enriching with the most\n * useful extracted detail when present. Falls back to the selector (existing\n * behaviour) and finally the bare command name.\n *\n * Examples:\n * tapOn → Login (selector)\n * inputText → \"<value>\" (details.text or the redaction marker)\n * swipe UP (details.direction)\n * setLocation 37.77,-122.42\n * pressKey BACK\n * runScript scripts/foo.js\n * retry × 3 (details.maxRetries or .times)\n */\nfunction buildStepTitle(cmd: MaestroCommandStep): string {\n const d = cmd.details;\n if (d) {\n switch (cmd.command) {\n case 'inputText':\n case 'pasteText':\n if (typeof d.text === 'string') return `${cmd.command} → ${d.text}`;\n break;\n case 'swipe':\n case 'scroll':\n case 'scrollUntilVisible':\n if (typeof d.direction === 'string') return `${cmd.command} ${d.direction}`;\n break;\n case 'setLocation':\n if (typeof d.latitude === 'number' && typeof d.longitude === 'number') {\n return `setLocation ${d.latitude},${d.longitude}`;\n }\n break;\n case 'pressKey':\n if (typeof d.key === 'string') return `pressKey ${d.key}`;\n break;\n case 'setOrientation':\n if (typeof d.orientation === 'string') return `setOrientation ${d.orientation}`;\n break;\n case 'launchApp':\n case 'killApp':\n case 'stopApp':\n case 'clearState':\n if (typeof d.appId === 'string') return `${cmd.command} ${d.appId}`;\n break;\n case 'openLink':\n if (typeof d.url === 'string') return `openLink ${d.url}`;\n break;\n case 'runScript':\n case 'evalScript':\n if (typeof d.path === 'string') return `${cmd.command} ${d.path}`;\n if (typeof d.sourceDescription === 'string') return `${cmd.command} ${d.sourceDescription}`;\n if (typeof d.script === 'string') return `${cmd.command} ${d.script}`;\n break;\n case 'assertWithAI':\n case 'assertNoDefectsWithAI':\n case 'extractTextWithAI':\n if (typeof d.prompt === 'string') return `${cmd.command} → ${d.prompt}`;\n break;\n case 'retry':\n if (typeof d.maxRetries === 'number') return `retry × ${d.maxRetries}`;\n break;\n case 'repeat':\n if (typeof d.times === 'number') return `repeat × ${d.times}`;\n break;\n case 'runFlow':\n if (typeof d.file === 'string') return `runFlow ${d.file}`;\n break;\n }\n }\n return cmd.selector ? `${cmd.command} → ${cmd.selector}` : cmd.command;\n}\n\n/**\n * Convert a parsed Maestro command into a core `ActionStep`, recursing into\n * child commands (retry/repeat/runFlow wrappers). `videoOffset` is computed\n * from the flow's start time so the cloud platform can sync each step to the\n * recorded video without re-deriving offsets.\n */\nfunction commandToAction(cmd: MaestroCommandStep, flowStartedMs: number): ActionStep {\n const cmdMs = Date.parse(cmd.timestamp);\n const videoOffset = Number.isFinite(cmdMs) && Number.isFinite(flowStartedMs)\n ? Math.max(0, (cmdMs - flowStartedMs) / 1000)\n : null;\n\n const children: ActionStep[] = (cmd.children ?? []).map((c) => commandToAction(c, flowStartedMs));\n\n return {\n title: buildStepTitle(cmd),\n category: mapCommandCategory(cmd.category),\n status: cmd.status === 'failed' ? 'failed' : 'passed',\n duration: cmd.duration,\n timestamp: cmd.timestamp,\n videoOffset,\n error: cmd.error ?? null,\n children,\n };\n}\n\n/**\n * Stable sort key for action ordering. Primary: timestamp (ms). Tiebreaker:\n * Maestro's metadata.sequenceNumber when both sides have one, so back-to-back\n * commands emitted in the same millisecond preserve emission order.\n */\nfunction sortActions(a: ActionStep, b: ActionStep): number {\n const ta = Date.parse(a.timestamp);\n const tb = Date.parse(b.timestamp);\n if (Number.isFinite(ta) && Number.isFinite(tb) && ta !== tb) return ta - tb;\n return 0;\n}\n\nfunction buildTestResult(flow: MaestroFlowResult, apiCalls: readonly ApiCallRecord[]): TestResult {\n const flowStartedMs = Date.parse(flow.startedAt);\n\n // Sort underlying commands by sequenceNumber when timestamps tie, BEFORE mapping\n // to ActionStep (ActionStep doesn't carry sequenceNumber).\n const orderedCommands = [...flow.commands].sort((a, b) => {\n const ta = Date.parse(a.timestamp);\n const tb = Date.parse(b.timestamp);\n if (Number.isFinite(ta) && Number.isFinite(tb) && ta !== tb) return ta - tb;\n const sa = a.sequenceNumber ?? 0;\n const sb = b.sequenceNumber ?? 0;\n return sa - sb;\n });\n const orderedAssertions = [...flow.assertions].sort((a, b) => {\n const ta = Date.parse(a.timestamp);\n const tb = Date.parse(b.timestamp);\n if (Number.isFinite(ta) && Number.isFinite(tb) && ta !== tb) return ta - tb;\n const sa = a.sequenceNumber ?? 0;\n const sb = b.sequenceNumber ?? 0;\n return sa - sb;\n });\n\n const actions: ActionStep[] = [\n ...orderedCommands.map((c) => commandToAction(c, flowStartedMs)),\n ...orderedAssertions.map((c) => commandToAction(c, flowStartedMs)),\n ];\n\n actions.sort(sortActions);\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: apiCalls.length > 0 ? [...apiCalls] : null,\n apiAssertions: null,\n actions: actions.length > 0 ? actions : null,\n consoleLogs: null,\n };\n}\n\n/**\n * Build a TimelineEntry for a single flow. Optionally accepts an\n * `apiCallsByFlow` map keyed by `testId` (`${flowFile}::${flowName}`) so the\n * orchestrator can plumb network capture results in without re-shaping flows.\n */\nexport function buildTimelineEntry(\n flow: MaestroFlowResult,\n apiCallsByFlow?: ReadonlyMap<string, readonly ApiCallRecord[]>,\n): TimelineEntry {\n const testId = `${flow.flowFile}::${flow.flowName}`;\n const apiCalls = apiCallsByFlow?.get(testId) ?? [];\n const testResult = buildTestResult(flow, apiCalls);\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(\n flows: MaestroFlowResult[],\n apiCallsByFlow?: ReadonlyMap<string, readonly ApiCallRecord[]>,\n): TimelineEntry[] {\n return flows.map((f) => buildTimelineEntry(f, apiCallsByFlow));\n}\n","/**\n * Computes Summary stats from TimelineEntry arrays.\n */\n\nimport type { Summary, TimelineEntry, ApiCallRecord } 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\nfunction statusBucket(code: number | null): keyof ApiCallsByStatusRange {\n if (code === null) return 'error';\n if (code >= 200 && code < 300) return '2xx';\n if (code >= 300 && code < 400) return '3xx';\n if (code >= 400 && code < 500) return '4xx';\n if (code >= 500 && code < 600) return '5xx';\n return 'error';\n}\n\nfunction percentile(sorted: number[], p: number): number | null {\n if (sorted.length === 0) return null;\n const idx = Math.min(sorted.length - 1, Math.floor((p / 100) * sorted.length));\n return sorted[idx];\n}\n\nfunction buildApiStats(calls: ApiCallRecord[]): {\n totalApiCalls: number;\n uniqueApiUrls: number;\n apiCallsByMethod: Record<string, number>;\n apiCallsByStatusRange: ApiCallsByStatusRange;\n apiResponseTime: Summary['apiResponseTime'];\n} {\n if (calls.length === 0) {\n return {\n totalApiCalls: 0,\n uniqueApiUrls: 0,\n apiCallsByMethod: {},\n apiCallsByStatusRange: { ...EMPTY_STATUS_RANGE },\n apiResponseTime: null,\n };\n }\n\n const byMethod: Record<string, number> = {};\n const byStatus: ApiCallsByStatusRange = { ...EMPTY_STATUS_RANGE };\n const urls = new Set<string>();\n const times: number[] = [];\n\n for (const c of calls) {\n byMethod[c.method] = (byMethod[c.method] ?? 0) + 1;\n byStatus[statusBucket(c.responseStatusCode)] += 1;\n urls.add(c.url);\n if (Number.isFinite(c.responseTimeMs) && c.responseTimeMs > 0) {\n times.push(c.responseTimeMs);\n }\n }\n\n times.sort((a, b) => a - b);\n const apiResponseTime: Summary['apiResponseTime'] = times.length > 0 ? {\n p50: percentile(times, 50) ?? 0,\n p95: percentile(times, 95) ?? 0,\n p99: percentile(times, 99) ?? 0,\n avg: times.reduce((s, n) => s + n, 0) / times.length,\n min: times[0],\n max: times[times.length - 1],\n } : null;\n\n return {\n totalApiCalls: calls.length,\n uniqueApiUrls: urls.size,\n apiCallsByMethod: byMethod,\n apiCallsByStatusRange: byStatus,\n apiResponseTime,\n };\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 const allApiCalls: ApiCallRecord[] = [];\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 if (test.apiCalls && test.apiCalls.length > 0) {\n allApiCalls.push(...test.apiCalls);\n }\n }\n }\n\n const apiStats = buildApiStats(allApiCalls);\n\n return {\n total,\n passed,\n failed,\n flaky,\n skipped,\n timedout,\n ...apiStats,\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 * mitmproxy lifecycle wrapper for Maestro network capture.\n *\n * Strategy:\n * 1. Check `mitmdump` is on PATH. If not, log a hint and disable capture\n * (caller falls back to HAR import or no capture).\n * 2. Resolve the bundled Python addon (`proxy-addon/testrelic_capture.py`).\n * In dev it lives alongside the TS source; after `tsup` build it's copied\n * under `dist/proxy-addon/`.\n * 3. Spawn `mitmdump` with addon flags + redaction options translated into\n * `--set` switches the addon understands.\n * 4. Return a handle the runner uses to stop the proxy after Maestro exits.\n *\n * No platform-specific paths here — all device-side setup (CA install, proxy\n * route) lives in `device-proxy-setup.ts`.\n */\n\nimport { spawn, type ChildProcess } from 'node:child_process';\nimport { mkdirSync, existsSync } from 'node:fs';\nimport { join, resolve, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { tmpdir } from 'node:os';\nimport { randomBytes } from 'node:crypto';\nimport type { ResolvedNetworkCaptureConfig } from './types.js';\n\nexport interface NetworkProxyHandle {\n /** Absolute path of the JSONL output the addon is writing to. */\n readonly outputPath: string;\n /** Directory containing the JSONL output (passed to discoverNetworkJsonlFiles). */\n readonly outputDir: string;\n /** Listening host. */\n readonly host: string;\n /** Listening port. */\n readonly port: number;\n /** Stop the proxy gracefully. Resolves when the child process has exited. */\n stop(): Promise<void>;\n}\n\nfunction resolveAddonPath(): string | null {\n // After tsup build the addon is at `<pkg>/dist/proxy-addon/testrelic_capture.py`.\n // In dev the addon lives next to the TS source.\n const candidates: string[] = [];\n try {\n // ESM resolution\n const here = dirname(fileURLToPath(import.meta.url));\n candidates.push(join(here, 'proxy-addon', 'testrelic_capture.py'));\n candidates.push(join(here, '..', 'src', 'proxy-addon', 'testrelic_capture.py'));\n } catch {\n /* not ESM — fall through */\n }\n // CJS / tsup-cjs fallback via __dirname (only present when bundled CJS).\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const maybeDirname: string | undefined = (globalThis as any).__dirname;\n if (maybeDirname) {\n candidates.push(join(maybeDirname, 'proxy-addon', 'testrelic_capture.py'));\n }\n for (const c of candidates) {\n if (existsSync(c)) return resolve(c);\n }\n return null;\n}\n\nfunction generateRunDir(base?: string): string {\n if (base) {\n mkdirSync(base, { recursive: true });\n return base;\n }\n const id = randomBytes(6).toString('hex');\n const dir = join(tmpdir(), `testrelic-maestro-network-${id}`);\n mkdirSync(dir, { recursive: true });\n return dir;\n}\n\nasync function isMitmdumpAvailable(): Promise<boolean> {\n return new Promise((resolvePromise) => {\n const child = spawn('mitmdump', ['--version'], { shell: true });\n let resolved = false;\n const settle = (ok: boolean): void => {\n if (resolved) return;\n resolved = true;\n resolvePromise(ok);\n };\n child.on('error', () => settle(false));\n child.on('close', (code) => settle(code === 0));\n // Defensive 5s timeout — mitmdump --version should respond instantly.\n setTimeout(() => {\n try { child.kill(); } catch { /* ignore */ }\n settle(false);\n }, 5_000);\n });\n}\n\nexport interface StartNetworkProxyOptions {\n readonly config: ResolvedNetworkCaptureConfig;\n /** Directory where the JSONL file should be written. */\n readonly outputDir?: string;\n /** When true, log spawn errors to stderr (default: true). Quieted by `--quiet`. */\n readonly verbose?: boolean;\n}\n\n/**\n * Spawn `mitmdump` with the TestRelic addon. Returns null when:\n * - capture is disabled,\n * - `mitmdump` is not on PATH,\n * - the addon couldn't be located (broken install).\n *\n * Never throws — the caller treats `null` as \"no capture this run.\"\n */\nexport async function startNetworkProxy(\n options: StartNetworkProxyOptions,\n): Promise<NetworkProxyHandle | null> {\n const { config, verbose = true } = options;\n if (!config.enabled) return null;\n\n if (!(await isMitmdumpAvailable())) {\n if (verbose) {\n process.stderr.write(\n '⚠ TestRelic: --capture-network requested but `mitmdump` is not on PATH.\\n'\n + ' Install mitmproxy (`brew install mitmproxy` / `pipx install mitmproxy`)\\n'\n + ' or supply --har-path to import a HAR file instead.\\n',\n );\n }\n return null;\n }\n\n const addon = resolveAddonPath();\n if (!addon) {\n if (verbose) {\n process.stderr.write('⚠ TestRelic: network-capture addon not found in package; skipping capture.\\n');\n }\n return null;\n }\n\n const outputDir = generateRunDir(options.outputDir ?? config.outputDir ?? undefined);\n const outputPath = join(outputDir, 'network.jsonl');\n\n const args: string[] = [\n '--listen-host', config.proxyHost,\n '--listen-port', String(config.proxyPort),\n '-s', addon,\n '--set', `testrelic_output=${outputPath}`,\n '--set', `testrelic_redact_headers=${config.redactHeaders.join(',')}`,\n '--set', `testrelic_redact_body_fields=${config.redactBodyFields.join(',')}`,\n '--set', `testrelic_max_body_bytes=${config.maxBodyBytes}`,\n // Reduce noise; the addon writes its own log lines on warn.\n '--set', 'console_eventlog_verbosity=warn',\n '--set', 'termlog_verbosity=warn',\n ];\n\n let child: ChildProcess;\n try {\n child = spawn('mitmdump', args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n shell: true,\n });\n } catch (err) {\n if (verbose) {\n process.stderr.write(`⚠ TestRelic: failed to spawn mitmdump: ${(err as Error).message}\\n`);\n }\n return null;\n }\n\n // Drain output so the buffer never fills, but only echo on verbose.\n child.stdout?.on('data', (chunk: Buffer) => {\n if (verbose) process.stderr.write(`[mitmdump] ${chunk.toString('utf-8')}`);\n });\n child.stderr?.on('data', (chunk: Buffer) => {\n if (verbose) process.stderr.write(`[mitmdump] ${chunk.toString('utf-8')}`);\n });\n\n // Tiny boot delay — give mitmdump a moment to bind the port before the\n // device starts routing traffic through it. Not load-bearing, but it\n // prevents the first 1-2 requests from racing the listener.\n await new Promise((r) => setTimeout(r, 750));\n\n const stop = (): Promise<void> => new Promise<void>((resolvePromise) => {\n if (child.killed || child.exitCode !== null) {\n resolvePromise();\n return;\n }\n let settled = false;\n const settle = (): void => {\n if (settled) return;\n settled = true;\n resolvePromise();\n };\n child.once('close', settle);\n child.once('exit', settle);\n try {\n child.kill('SIGINT');\n } catch {\n try { child.kill(); } catch { /* ignore */ }\n }\n // Fallback hard-stop after 4s so a misbehaving mitmdump doesn't hang the\n // entire run cleanup.\n setTimeout(() => {\n if (!settled) {\n try { child.kill('SIGKILL'); } catch { /* ignore */ }\n settle();\n }\n }, 4_000);\n });\n\n return {\n outputPath,\n outputDir,\n host: config.proxyHost,\n port: config.proxyPort,\n stop,\n };\n}\n","/**\n * Best-effort device-side setup for routing mobile app HTTP traffic through\n * a host-side mitmproxy.\n *\n * This module is **best-effort by design**. Real-device cert installs and\n * proxy plumbing vary widely across OS versions, manufacturers, and emulator\n * images. Goals here:\n *\n * - Cover the 80% case automatically: Android emulator + iOS simulator.\n * - For real devices, print precise manual instructions and return without\n * erroring — the proxy still runs, so users who pre-configured the device\n * get capture immediately.\n * - Never leave a device in a half-configured state. `tearDownDeviceProxy`\n * reverts whatever `setUpDeviceProxy` changed, idempotently.\n *\n * No throws: failures degrade to \"capture won't see this device's traffic\"\n * with a stderr hint. The runner continues so the user's tests still execute.\n */\n\nimport { spawn, spawnSync } from 'node:child_process';\nimport { join } from 'node:path';\nimport { tmpdir } from 'node:os';\nimport { writeFileSync, existsSync, unlinkSync } from 'node:fs';\nimport type { MaestroPlatform } from './types.js';\n\nexport interface DeviceProxyOptions {\n readonly platform: MaestroPlatform;\n readonly deviceId?: string;\n readonly proxyHost: string;\n readonly proxyPort: number;\n readonly skipCertInstall?: boolean;\n readonly quiet?: boolean;\n}\n\nexport interface DeviceProxyHandle {\n /** Reverts any device-side proxy / cert changes. Safe to call multiple times. */\n teardown(): Promise<void>;\n}\n\nconst NOOP_HANDLE: DeviceProxyHandle = { teardown: async () => { /* no-op */ } };\n\nfunction commandExists(cmd: string): boolean {\n // `cmd --version` works for adb, xcrun, networksetup, etc. We don't care\n // about the exit code — only that the binary resolved without ENOENT.\n const result = spawnSync(cmd, ['--version'], { shell: true, stdio: 'ignore' });\n return result.error === undefined || (result.error as NodeJS.ErrnoException).code !== 'ENOENT';\n}\n\nfunction logHint(quiet: boolean | undefined, msg: string): void {\n if (!quiet) process.stderr.write(`${msg}\\n`);\n}\n\nfunction logManualInstructions(quiet: boolean | undefined, platform: MaestroPlatform, proxyHost: string, proxyPort: number): void {\n if (quiet) return;\n if (platform === 'android') {\n process.stderr.write(\n 'ℹ TestRelic: real-device Android capture — set the device proxy manually:\\n'\n + ` Settings → Wi-Fi → (long-press your network) → Modify → Proxy: Manual\\n`\n + ` Host: ${proxyHost} Port: ${proxyPort}\\n`\n + ' Then install mitmproxy CA: open http://mitm.it from the device after the proxy is reachable.\\n',\n );\n } else if (platform === 'ios') {\n process.stderr.write(\n 'ℹ TestRelic: real-device iOS capture — set the device proxy manually:\\n'\n + ` Settings → Wi-Fi → (i) on your network → Configure Proxy → Manual\\n`\n + ` Server: ${proxyHost} Port: ${proxyPort}\\n`\n + ' Then install + TRUST the mitmproxy CA:\\n'\n + ' 1. Open http://mitm.it from Safari on the device → install profile.\\n'\n + ' 2. Settings → General → About → Certificate Trust Settings → enable mitmproxy.\\n',\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Android emulator\n// ---------------------------------------------------------------------------\n\nasync function adb(args: string[], deviceId: string | undefined): Promise<{ code: number; stdout: string; stderr: string }> {\n return new Promise((resolvePromise) => {\n const full = deviceId ? ['-s', deviceId, ...args] : args;\n const child = spawn('adb', full, { shell: true, stdio: ['ignore', 'pipe', 'pipe'] });\n let out = '';\n let err = '';\n child.stdout?.on('data', (c: Buffer) => { out += c.toString(); });\n child.stderr?.on('data', (c: Buffer) => { err += c.toString(); });\n child.on('error', () => resolvePromise({ code: 127, stdout: out, stderr: err || 'adb not on PATH' }));\n child.on('close', (code) => resolvePromise({ code: code ?? 1, stdout: out, stderr: err }));\n });\n}\n\nasync function setUpAndroidEmulator(opts: DeviceProxyOptions): Promise<DeviceProxyHandle> {\n const { deviceId, proxyHost, proxyPort, quiet, skipCertInstall } = opts;\n if (!commandExists('adb')) {\n logHint(quiet, '⚠ TestRelic: `adb` not on PATH; skipping automatic Android proxy setup. See manual instructions:');\n logManualInstructions(quiet, 'android', proxyHost, proxyPort);\n return NOOP_HANDLE;\n }\n\n // 1. Set proxy via global settings (works on emulators without root).\n const setResult = await adb(['shell', 'settings', 'put', 'global', 'http_proxy', `${proxyHost}:${proxyPort}`], deviceId);\n if (setResult.code !== 0) {\n logHint(quiet, `⚠ TestRelic: adb set proxy failed: ${setResult.stderr.trim() || 'unknown error'}`);\n logManualInstructions(quiet, 'android', proxyHost, proxyPort);\n return NOOP_HANDLE;\n }\n logHint(quiet, `ℹ TestRelic: Android proxy set to ${proxyHost}:${proxyPort} on ${deviceId ?? 'default device'}.`);\n\n // 2. Cert install is intentionally NOT attempted automatically — system CA\n // install requires `adb root` + remount, which only works on Google API\n // emulator images and breaks Play-store images. Print a hint instead.\n if (!skipCertInstall) {\n logHint(quiet,\n 'ℹ TestRelic: if HTTPS calls fail, install the mitmproxy CA on the emulator:\\n'\n + ' open http://mitm.it from Chrome on the emulator and follow prompts,\\n'\n + ' OR for a system-level install:\\n'\n + ' adb root && adb remount && adb push <mitm-ca>.0 /system/etc/security/cacerts/ && adb reboot\\n'\n + ' Set --skip-cert-install to silence this hint.',\n );\n }\n\n const teardown = async (): Promise<void> => {\n // Clear the proxy. `put` with `:0` or empty string both work on most images;\n // `:0` is the documented unset value.\n await adb(['shell', 'settings', 'put', 'global', 'http_proxy', ':0'], deviceId);\n };\n return { teardown };\n}\n\n// ---------------------------------------------------------------------------\n// iOS simulator\n// ---------------------------------------------------------------------------\n\nasync function xcrun(args: string[]): Promise<{ code: number; stdout: string; stderr: string }> {\n return new Promise((resolvePromise) => {\n const child = spawn('xcrun', args, { shell: true, stdio: ['ignore', 'pipe', 'pipe'] });\n let out = '';\n let err = '';\n child.stdout?.on('data', (c: Buffer) => { out += c.toString(); });\n child.stderr?.on('data', (c: Buffer) => { err += c.toString(); });\n child.on('error', () => resolvePromise({ code: 127, stdout: out, stderr: err || 'xcrun not on PATH' }));\n child.on('close', (code) => resolvePromise({ code: code ?? 1, stdout: out, stderr: err }));\n });\n}\n\nasync function setUpIosSimulator(opts: DeviceProxyOptions): Promise<DeviceProxyHandle> {\n const { proxyHost, proxyPort, quiet, skipCertInstall } = opts;\n\n // iOS simulator shares the host Mac's network stack, so a host-side proxy\n // automatically catches anything that respects system proxy settings — but\n // many apps (NSURLSession with custom configurations, anything using their\n // own resolver) ignore it. Realistically we tell the user to install the\n // mitmproxy profile in the simulator and they're set.\n if (process.platform !== 'darwin') {\n logHint(quiet, '⚠ TestRelic: iOS simulator capture requires macOS; the host is not Darwin. Skipping setup.');\n logManualInstructions(quiet, 'ios', proxyHost, proxyPort);\n return NOOP_HANDLE;\n }\n if (!commandExists('xcrun')) {\n logHint(quiet, '⚠ TestRelic: `xcrun` not on PATH; skipping automatic iOS simulator setup.');\n logManualInstructions(quiet, 'ios', proxyHost, proxyPort);\n return NOOP_HANDLE;\n }\n\n if (!skipCertInstall) {\n // Try to fetch mitmproxy's CA so we can push it into the simulator's keychain.\n // The CA lives at ~/.mitmproxy/mitmproxy-ca-cert.pem after the first mitmdump run.\n const caCandidate = join(process.env.HOME ?? '', '.mitmproxy', 'mitmproxy-ca-cert.pem');\n if (existsSync(caCandidate)) {\n // `simctl keychain <udid|booted> add-root-cert <path>` is supported on Xcode 11.4+\n const udid = opts.deviceId ?? 'booted';\n const res = await xcrun(['simctl', 'keychain', udid, 'add-root-cert', caCandidate]);\n if (res.code === 0) {\n logHint(quiet, `ℹ TestRelic: installed mitmproxy CA on iOS simulator (${udid}).`);\n } else {\n logHint(quiet, `⚠ TestRelic: failed to add-root-cert via simctl: ${res.stderr.trim()}`);\n logManualInstructions(quiet, 'ios', proxyHost, proxyPort);\n }\n } else {\n logHint(quiet,\n `⚠ TestRelic: mitmproxy CA not found at ${caCandidate}. Run mitmdump once to generate it,\\n`\n + ' then re-run with --capture-network. Falling back to manual instructions:',\n );\n logManualInstructions(quiet, 'ios', proxyHost, proxyPort);\n }\n }\n\n // Setting the simulator's proxy programmatically requires editing the\n // network plist for that runtime, which is fragile across Xcode versions.\n // We deliberately do NOT touch the system network settings (`networksetup`)\n // because that affects the user's whole machine. Instead we rely on either:\n // (a) the simulator inheriting the host proxy if the user configured one, or\n // (b) explicit manual proxy entry in Wi-Fi settings inside the simulator.\n logHint(quiet,\n `ℹ TestRelic: in the iOS simulator, configure Wi-Fi → (i) → Manual proxy:\\n`\n + ` Server: ${proxyHost} Port: ${proxyPort}\\n`\n + ' (We do not set host-wide `networksetup` proxy automatically; see docs.)',\n );\n\n return NOOP_HANDLE;\n}\n\n// ---------------------------------------------------------------------------\n// Public entry point\n// ---------------------------------------------------------------------------\n\nexport async function setUpDeviceProxy(options: DeviceProxyOptions): Promise<DeviceProxyHandle> {\n switch (options.platform) {\n case 'android':\n return setUpAndroidEmulator(options);\n case 'ios':\n return setUpIosSimulator(options);\n case 'web':\n logHint(options.quiet, 'ℹ TestRelic: --capture-network ignored for Maestro web flows (browser already handles HTTP).');\n return NOOP_HANDLE;\n case 'unknown':\n default:\n logHint(options.quiet, '⚠ TestRelic: target platform unknown; printing manual proxy setup instructions:');\n logManualInstructions(options.quiet, 'android', options.proxyHost, options.proxyPort);\n logManualInstructions(options.quiet, 'ios', options.proxyHost, options.proxyPort);\n return NOOP_HANDLE;\n }\n}\n\n// ---------------------------------------------------------------------------\n// PAC file emission (optional helper for custom proxy autoconfiguration)\n// ---------------------------------------------------------------------------\n\n/** Write a tiny proxy autoconfig (PAC) file so iOS users can paste a single URL into Auto Proxy. */\nexport function emitPacFile(proxyHost: string, proxyPort: number): string {\n const path = join(tmpdir(), `testrelic-proxy-${Date.now()}.pac`);\n const content = `function FindProxyForURL(url, host) { return \"PROXY ${proxyHost}:${proxyPort}; DIRECT\"; }`;\n writeFileSync(path, content, 'utf-8');\n return path;\n}\n\nexport function cleanupPacFile(path: string): void {\n try { if (existsSync(path)) unlinkSync(path); } catch { /* ignore */ }\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, MaestroPlatform, ResolvedNetworkCaptureConfig } from './types.js';\nimport { startNetworkProxy, type NetworkProxyHandle } from './network-proxy.js';\nimport { setUpDeviceProxy, type DeviceProxyHandle } from './device-proxy-setup.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 /** Directory where the network proxy wrote JSONL records (null when capture was disabled or failed to start). */\n readonly networkJsonlDir: string | null;\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\n/**\n * Map the user-supplied platform string to the internal MaestroPlatform enum.\n * Used for device proxy setup so we route to the right adb/xcrun helper.\n */\nfunction normalisePlatform(p: string | undefined): MaestroPlatform {\n switch ((p ?? '').toLowerCase()) {\n case 'android': return 'android';\n case 'ios': return 'ios';\n case 'web': return 'web';\n default: return 'unknown';\n }\n}\n\nexport interface RunMaestroExtras {\n /** Optional network-capture config. When `enabled`, mitmproxy is spawned and the device is configured to route through it. */\n readonly network?: ResolvedNetworkCaptureConfig;\n}\n\nfunction runMaestroProc(\n args: string[],\n junitPath: string,\n testOutputDir: string,\n debugOutputDir: string,\n networkJsonlDir: string | null,\n quiet: boolean | undefined,\n onClose: () => Promise<void>,\n): Promise<MaestroRunResult> {\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 (!quiet) process.stdout.write(chunk);\n });\n\n proc.stderr.on('data', (chunk: Buffer) => {\n stderrChunks.push(chunk);\n if (!quiet) process.stderr.write(chunk);\n });\n\n proc.on('close', async (code) => {\n // Run the user-supplied cleanup hook *before* resolving so any artifacts\n // the proxy/device still need to write get a chance to flush.\n try { await onClose(); } catch { /* ignore */ }\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 networkJsonlDir,\n });\n });\n\n proc.on('error', async (err) => {\n try { await onClose(); } catch { /* ignore */ }\n resolvePromise({\n exitCode: 127,\n junitPath,\n testOutputDir,\n debugOutputDir,\n stdout: '',\n stderr: `Failed to start maestro CLI: ${err.message}`,\n networkJsonlDir,\n });\n });\n });\n}\n\nexport async function runMaestro(\n options: MaestroTestOptions,\n extras: RunMaestroExtras = {},\n): 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 // Start mitmproxy + device proxy setup BEFORE maestro spawns so the very\n // first HTTP call from the app under test lands in the JSONL. When capture\n // is disabled or unavailable, both handles are null/no-op.\n let proxyHandle: NetworkProxyHandle | null = null;\n let deviceHandle: DeviceProxyHandle | null = null;\n const networkConfig = extras.network;\n if (networkConfig?.enabled) {\n const networkDir = join(tempDir, 'network');\n proxyHandle = await startNetworkProxy({\n config: networkConfig,\n outputDir: networkDir,\n verbose: !options.quiet,\n });\n if (proxyHandle) {\n deviceHandle = await setUpDeviceProxy({\n platform: normalisePlatform(options.platform),\n deviceId: options.device,\n proxyHost: proxyHandle.host,\n proxyPort: proxyHandle.port,\n skipCertInstall: networkConfig.skipCertInstall,\n quiet: options.quiet,\n });\n }\n }\n\n const networkJsonlDir = proxyHandle?.outputDir ?? null;\n\n const cleanup = async (): Promise<void> => {\n // Small drain window so in-flight responses get flushed by the addon.\n if (proxyHandle) {\n await new Promise((r) => setTimeout(r, 500));\n }\n if (deviceHandle) {\n try { await deviceHandle.teardown(); } catch { /* ignore */ }\n }\n if (proxyHandle) {\n try { await proxyHandle.stop(); } catch { /* ignore */ }\n }\n };\n\n return runMaestroProc(args, junitPath, testOutputDir, debugOutputDir, networkJsonlDir, options.quiet, cleanup);\n}\n","/**\n * Lightweight inline redactor for captured API calls.\n *\n * Mirrors the policy used by `@testrelic/playwright-analytics` (case-insensitive\n * header match, recursive body-field match at any JSON depth). Kept inline here\n * to avoid a cross-package change in this PR — when both packages converge we'll\n * promote a shared implementation to `@testrelic/core/api-redactor`.\n */\n\nimport type { ApiCallRecord } from '@testrelic/core';\n\nconst REDACTED = '[REDACTED]';\n\nfunction redactHeaders(\n headers: Record<string, string> | null,\n redactList: readonly string[],\n): Record<string, string> | null {\n if (!headers) return null;\n const lower = new Set(redactList.map((h) => h.toLowerCase()));\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(headers)) {\n out[k] = lower.has(k.toLowerCase()) ? REDACTED : v;\n }\n return out;\n}\n\nfunction redactJsonBody(value: unknown, fields: ReadonlySet<string>): unknown {\n if (value === null || typeof value !== 'object') return value;\n if (Array.isArray(value)) return value.map((v) => redactJsonBody(v, fields));\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n out[k] = fields.has(k) ? REDACTED : redactJsonBody(v, fields);\n }\n return out;\n}\n\nfunction redactBody(\n body: string | null,\n fields: readonly string[],\n): string | null {\n if (!body || fields.length === 0) return body;\n try {\n const parsed = JSON.parse(body);\n const redacted = redactJsonBody(parsed, new Set(fields));\n return JSON.stringify(redacted);\n } catch {\n // Non-JSON body — return verbatim. Form-encoded redaction is a follow-up.\n return body;\n }\n}\n\nexport function redactApiCall(\n call: ApiCallRecord,\n redactHeadersList: readonly string[],\n redactBodyFields: readonly string[],\n): ApiCallRecord {\n return {\n ...call,\n requestHeaders: redactHeaders(call.requestHeaders, redactHeadersList),\n responseHeaders: redactHeaders(call.responseHeaders, redactHeadersList),\n requestBody: redactBody(call.requestBody, redactBodyFields),\n responseBody: call.isBinary ? call.responseBody : redactBody(call.responseBody, redactBodyFields),\n };\n}\n\nexport function matchesUrlFilter(\n url: string,\n include: readonly (string | RegExp)[],\n exclude: readonly (string | RegExp)[],\n): boolean {\n const matches = (pat: string | RegExp): boolean =>\n typeof pat === 'string' ? url.includes(pat) : pat.test(url);\n if (exclude.some(matches)) return false;\n if (include.length === 0) return true;\n return include.some(matches);\n}\n","/**\n * Parsers for captured network traffic.\n *\n * Two input formats are supported:\n *\n * 1. mitmproxy JSONL — emitted by the bundled `testrelic_capture.py` addon\n * (`<outputDir>/network/<flow-or-suite>.jsonl`). Each line is already an\n * ApiCallRecord (the addon does its own redaction so secrets never touch\n * disk).\n *\n * 2. HAR — user-supplied (Charles, Proxyman, mitmweb, etc.). Mapped from\n * `entries[].request` + `entries[].response` into ApiCallRecord. Useful\n * for environments where spawning mitmproxy is impractical (TLS-pinned\n * apps, locked-down devices, CI runners without sudo).\n *\n * Both parsers tolerate malformed input — they skip the offending entry\n * rather than throwing, because losing one record shouldn't break the whole\n * report.\n */\n\nimport { readFileSync, existsSync, readdirSync } from 'node:fs';\nimport { join, basename, extname } from 'node:path';\nimport type { ApiCallRecord } from '@testrelic/core';\nimport { redactApiCall, matchesUrlFilter } from '../api-redactor.js';\nimport type { ResolvedNetworkCaptureConfig } from '../types.js';\n\n// ---------------------------------------------------------------------------\n// mitmproxy JSONL\n// ---------------------------------------------------------------------------\n\nfunction parseJsonlLine(line: string): ApiCallRecord | null {\n const trimmed = line.trim();\n if (!trimmed) return null;\n try {\n const obj = JSON.parse(trimmed) as Partial<ApiCallRecord>;\n if (typeof obj.url !== 'string' || typeof obj.method !== 'string') return null;\n return {\n id: obj.id ?? `api-call-${Date.now()}`,\n timestamp: obj.timestamp ?? new Date().toISOString(),\n method: obj.method,\n url: obj.url,\n requestHeaders: obj.requestHeaders ?? null,\n requestBody: obj.requestBody ?? null,\n responseStatusCode: obj.responseStatusCode ?? null,\n responseStatusText: obj.responseStatusText ?? null,\n responseHeaders: obj.responseHeaders ?? null,\n responseBody: obj.responseBody ?? null,\n responseTimeMs: typeof obj.responseTimeMs === 'number' ? obj.responseTimeMs : 0,\n isBinary: obj.isBinary ?? false,\n error: obj.error ?? null,\n };\n } catch {\n return null;\n }\n}\n\nexport function parseNetworkJsonl(\n content: string,\n config: ResolvedNetworkCaptureConfig,\n): ApiCallRecord[] {\n const records: ApiCallRecord[] = [];\n for (const line of content.split(/\\r?\\n/)) {\n const rec = parseJsonlLine(line);\n if (!rec) continue;\n if (!matchesUrlFilter(rec.url, config.includeUrls, config.excludeUrls)) continue;\n records.push(redactApiCall(rec, config.redactHeaders, config.redactBodyFields));\n }\n return records;\n}\n\nexport function parseNetworkJsonlFile(\n filePath: string,\n config: ResolvedNetworkCaptureConfig,\n): ApiCallRecord[] {\n try {\n return parseNetworkJsonl(readFileSync(filePath, 'utf-8'), config);\n } catch {\n return [];\n }\n}\n\nexport function discoverNetworkJsonlFiles(dir: string): string[] {\n if (!existsSync(dir)) return [];\n try {\n return readdirSync(dir, { recursive: true })\n .map(String)\n .filter((f) => f.endsWith('.jsonl'))\n .map((f) => join(dir, f));\n } catch {\n return [];\n }\n}\n\n// ---------------------------------------------------------------------------\n// HAR\n// ---------------------------------------------------------------------------\n\ninterface HarHeader { name: string; value: string }\ninterface HarPostData { mimeType?: string; text?: string; params?: { name: string; value?: string }[] }\ninterface HarRequest { method?: string; url?: string; headers?: HarHeader[]; postData?: HarPostData }\ninterface HarContent { size?: number; mimeType?: string; text?: string; encoding?: string }\ninterface HarResponse { status?: number; statusText?: string; headers?: HarHeader[]; content?: HarContent }\ninterface HarEntry {\n startedDateTime?: string;\n time?: number;\n request?: HarRequest;\n response?: HarResponse;\n _resourceType?: string;\n}\ninterface HarLog { entries?: HarEntry[] }\ninterface HarFile { log?: HarLog }\n\nfunction harHeadersToRecord(headers: HarHeader[] | undefined): Record<string, string> | null {\n if (!headers || headers.length === 0) return null;\n const out: Record<string, string> = {};\n for (const h of headers) {\n if (typeof h.name === 'string' && typeof h.value === 'string') out[h.name] = h.value;\n }\n return Object.keys(out).length > 0 ? out : null;\n}\n\nfunction harBodyToString(postData: HarPostData | undefined): string | null {\n if (!postData) return null;\n if (typeof postData.text === 'string') return postData.text;\n if (Array.isArray(postData.params)) {\n return postData.params\n .map((p) => `${encodeURIComponent(p.name)}=${encodeURIComponent(p.value ?? '')}`)\n .join('&');\n }\n return null;\n}\n\nfunction isBinaryMime(mime: string | undefined): boolean {\n if (!mime) return false;\n return /^(image|audio|video|application\\/(octet-stream|pdf|zip|protobuf))/.test(mime);\n}\n\nfunction harEntryToRecord(entry: HarEntry, index: number): ApiCallRecord | null {\n const req = entry.request;\n const res = entry.response;\n if (!req?.method || !req.url) return null;\n\n const content = res?.content;\n const isBinary = isBinaryMime(content?.mimeType);\n return {\n id: `har-call-${index}`,\n timestamp: entry.startedDateTime ?? new Date().toISOString(),\n method: req.method,\n url: req.url,\n requestHeaders: harHeadersToRecord(req.headers),\n requestBody: harBodyToString(req.postData),\n responseStatusCode: typeof res?.status === 'number' && res.status > 0 ? res.status : null,\n responseStatusText: res?.statusText ?? null,\n responseHeaders: harHeadersToRecord(res?.headers),\n responseBody: content?.text ?? null,\n responseTimeMs: typeof entry.time === 'number' ? entry.time : 0,\n isBinary,\n error: null,\n };\n}\n\nexport function parseHar(\n content: string,\n config: ResolvedNetworkCaptureConfig,\n): ApiCallRecord[] {\n let parsed: HarFile;\n try {\n parsed = JSON.parse(content) as HarFile;\n } catch {\n return [];\n }\n const entries = parsed.log?.entries ?? [];\n const records: ApiCallRecord[] = [];\n entries.forEach((entry, i) => {\n const rec = harEntryToRecord(entry, i);\n if (!rec) return;\n if (!matchesUrlFilter(rec.url, config.includeUrls, config.excludeUrls)) return;\n records.push(redactApiCall(rec, config.redactHeaders, config.redactBodyFields));\n });\n return records;\n}\n\nexport function parseHarFile(\n filePath: string,\n config: ResolvedNetworkCaptureConfig,\n): ApiCallRecord[] {\n try {\n return parseHar(readFileSync(filePath, 'utf-8'), config);\n } catch {\n return [];\n }\n}\n\n// ---------------------------------------------------------------------------\n// Per-flow binding\n// ---------------------------------------------------------------------------\n\nexport interface FlowWindow {\n readonly testId: string;\n readonly startedAt: string;\n readonly completedAt: string;\n}\n\n/**\n * Bind a flat list of captured API calls to per-flow buckets by timestamp\n * window. Calls whose timestamp falls outside every window are bucketed\n * against the closest flow so the data isn't silently lost.\n */\nexport function bindApiCallsToFlows(\n calls: readonly ApiCallRecord[],\n flows: readonly FlowWindow[],\n): Map<string, ApiCallRecord[]> {\n const buckets = new Map<string, ApiCallRecord[]>();\n for (const f of flows) buckets.set(f.testId, []);\n if (calls.length === 0 || flows.length === 0) return buckets;\n\n const windows = flows.map((f) => ({\n testId: f.testId,\n startMs: Date.parse(f.startedAt),\n endMs: Date.parse(f.completedAt),\n }));\n\n for (const call of calls) {\n const t = Date.parse(call.timestamp);\n if (!Number.isFinite(t)) continue;\n\n // Direct hit first.\n const hit = windows.find((w) => Number.isFinite(w.startMs) && Number.isFinite(w.endMs) && t >= w.startMs && t <= w.endMs);\n if (hit) {\n buckets.get(hit.testId)!.push(call);\n continue;\n }\n\n // Closest-window fallback.\n let best: { testId: string; dist: number } | null = null;\n for (const w of windows) {\n if (!Number.isFinite(w.startMs) || !Number.isFinite(w.endMs)) continue;\n const dist = t < w.startMs ? w.startMs - t : t - w.endMs;\n if (!best || dist < best.dist) best = { testId: w.testId, dist };\n }\n if (best) buckets.get(best.testId)!.push(call);\n }\n\n return buckets;\n}\n\n/**\n * Resolve all captured network records into a map keyed by flow `testId`.\n * Reads from (in order): mitmproxy JSONL files, an optional HAR file.\n * Returns an empty map when network capture is disabled or nothing was found.\n */\nexport function collectNetworkByFlow(\n jsonlDir: string | null,\n config: ResolvedNetworkCaptureConfig,\n flows: readonly FlowWindow[],\n): Map<string, ApiCallRecord[]> {\n if (!config.enabled && !config.harPath) return new Map();\n\n const all: ApiCallRecord[] = [];\n\n if (jsonlDir) {\n for (const file of discoverNetworkJsonlFiles(jsonlDir)) {\n all.push(...parseNetworkJsonlFile(file, config));\n }\n }\n if (config.harPath && existsSync(config.harPath)) {\n all.push(...parseHarFile(config.harPath, config));\n }\n\n if (all.length === 0) return new Map();\n return bindApiCallsToFlows(all, flows);\n}\n\n/** Re-indexes IDs sequentially per-flow for predictable cloud-side ordering. */\nexport function reindexFlowCalls(calls: readonly ApiCallRecord[]): ApiCallRecord[] {\n return calls.map((c, i) => ({ ...c, id: `api-call-${i}` }));\n}\n\n// Suppress unused-import warning when a downstream consumer doesn't use `basename`/`extname`.\nvoid basename; void extname;\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 ApiCallRecord,\n ActionStep,\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 * Per-flow captured API calls. The cloud platform's `normalizeApiCall` (in\n * `server/src/utils/artifact-normalizer.ts`) accepts this shape directly and\n * stores it as `network.json`. Surfaced in the Test Detail Network panel and\n * via Ask AI's `query_network_logs` tool — works for free, no cloud changes.\n */\n readonly apiCalls?: readonly ApiCallRecord[];\n /**\n * Per-flow ActionStep array (mapped from Maestro `commands` + `assertions`,\n * sorted by timestamp + sequenceNumber, with `retry`/`repeat` children\n * nested under their wrapper and `videoOffset` computed from\n * `flow.startedAt`). The cloud server's per-test ingest path\n * (`server/src/services/run.service.ts:uploadTest` → `enrichTimeline` →\n * OpenObserve `timeline_{orgId}` + RustFS `actions.json`) consumes this\n * and powers the Session Workspace's Steps + Assertions tabs and Ask AI's\n * `query_test_steps` tool.\n *\n * The same array also rides on `timeline[i].tests[j].actions` in the\n * upload's `timeline` field — but the cloud's per-test ingest path keys off\n * `tests[].actions`. Without this field, Maestro test detail pages show\n * \"No step data available\" even though the run summary reports correct step\n * counts. See issue #77.\n */\n readonly actions?: readonly ActionStep[];\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, ApiCallRecord, ActionStep } 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 { collectNetworkByFlow, reindexFlowCalls, type FlowWindow } from './parsers/network-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 */\n/**\n * Strip Maestro's filename decoration. The CLI writes\n * commands-(Launch app and verify catalog).json\n * → we want `launch app and verify catalog` so the key matches `flowBase`\n * (which is derived from JUnit's `classname` / flow name in `commandsForFlow`).\n */\nfunction normaliseCommandFileName(filePath: string): string {\n return basename(filePath)\n .replace(/^commands[-_]?/i, '')\n .replace(/\\.json$/i, '')\n .replace(/^\\(/, '')\n .replace(/\\)$/, '')\n .toLowerCase();\n}\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 = normaliseCommandFileName(filePath);\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 => normaliseCommandFileName(p);\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 parsed Maestro flow into the `TestResultForUpload` shape the\n * cloud's per-test ingest path (`POST /runs/:runId/tests` → OpenObserve\n * `timeline_{orgId}` + RustFS `actions.json`) expects. This is what makes\n * individual flows visible as test cases — with their step / log / network\n * artifacts — in the TestRelic Session Workspace.\n *\n * `actions` is read from the already-built timeline TestResult (sorted +\n * children-nested + videoOffset-stamped). The same array is referenced from\n * both `timeline[i].tests[0].actions` and `tests[].actions` in the upload\n * payload — no shape diverges, no duplicated build logic. See issue #77.\n *\n * Exported only for unit tests; not part of the package's stable surface.\n */\nexport function flowToTestResult(\n flow: MaestroFlowResult,\n apiCalls: readonly ApiCallRecord[] | undefined,\n actions: readonly ActionStep[] | null | undefined,\n): 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 ...(apiCalls && apiCalls.length > 0 ? { apiCalls } : {}),\n ...(actions && actions.length > 0 ? { actions } : {}),\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 * Directory containing JSONL files emitted by `network-proxy.ts` (mitmproxy\n * addon). When omitted, the orchestrator still picks up `config.network.harPath`\n * if set. Unrelated to Maestro's own debug output.\n */\n readonly networkJsonlDir?: string | null;\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 // Anchor wall-clock-only log timestamps to the file's mtime date when\n // available, so logs from a previous day's run don't get re-stamped to today.\n for (const logFile of logFiles) {\n // Pass the file mtime as a Date so parseLogContent can produce TZ-correct\n // UTC ISO timestamps for `HH:MM:SS` lines (Maestro writes local time\n // without an offset; naive string-concat would shift every entry by the\n // local UTC offset and bucket them outside every flow window).\n let mtimeDate: Date | undefined;\n try {\n const { statSync } = await import('node:fs');\n mtimeDate = statSync(logFile).mtime;\n } catch { /* parser falls back to today */ }\n const entries = parseLogFile(logFile, mtimeDate);\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\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 // Derive the flow window from the EARLIEST command timestamp when\n // available. The orchestrator's own `startedAt` is the moment the\n // report runner started — typically *after* maestro finished, so\n // using it for the flow window bounds the log bucketer (and\n // ActionStep videoOffset clamp) to a window that excludes every real\n // log line / step. We fall back to `startedAt + 0` only when the\n // commands JSON had no parseable timestamps (e.g. legacy fixtures).\n const allFlowCmds = [...perFlowCommands, ...perFlowAssertions];\n const earliestCmdMs = allFlowCmds.reduce<number>((min, c) => {\n const t = Date.parse(c.timestamp);\n return Number.isFinite(t) && t < min ? t : min;\n }, Number.POSITIVE_INFINITY);\n const flowStartedAt = Number.isFinite(earliestCmdMs)\n ? new Date(earliestCmdMs).toISOString()\n : startedAt;\n const flowCompletedAt = new Date(new Date(flowStartedAt).getTime() + durationMs).toISOString();\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 // Bind captured network traffic (mitmproxy JSONL and/or HAR) to each flow\n // by timestamp window so the cloud platform's Test Detail Network panel and\n // Ask AI's query_network_logs tool see the right per-flow API calls.\n const apiCallsByFlow = (() => {\n if (!config.network?.enabled && !config.network?.harPath) return undefined;\n const windows: FlowWindow[] = flowResults.map((f) => ({\n testId: `${f.flowFile}::${f.flowName}`,\n startedAt: f.startedAt,\n completedAt: f.completedAt,\n }));\n const bound = collectNetworkByFlow(input.networkJsonlDir ?? null, config.network, windows);\n if (bound.size === 0) return undefined;\n const remapped = new Map<string, readonly ApiCallRecord[]>();\n for (const [k, v] of bound) remapped.set(k, reindexFlowCalls(v));\n return remapped;\n })();\n\n const timeline: TimelineEntry[] = buildTimeline(flowResults, apiCallsByFlow);\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 //\n // Actions are sourced from the already-built timeline so the\n // sorting/children-nesting/videoOffset stays consistent between the\n // `timeline[i].tests[j].actions` and `tests[].actions` paths in the\n // upload (issue #77). Maestro emits one flow per TimelineEntry / per\n // testCase, so the i-th flow corresponds to timeline[i].tests[0].\n const testsForUpload: TestResultForUpload[] = flowResults.map((f, i) => {\n const testId = `${f.flowFile}::${f.flowName}`;\n const tlTest = timeline[i]?.tests?.[0];\n return flowToTestResult(f, apiCallsByFlow?.get(testId), tlTest?.actions ?? null);\n });\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/network-proxy.ts","../src/device-proxy-setup.ts","../src/maestro-runner.ts","../src/api-redactor.ts","../src/parsers/network-parser.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","DEFAULT_REDACT_HEADERS","DEFAULT_REDACT_BODY_FIELDS","DEFAULT_MAX_BODY_BYTES","resolveNetworkCapture","options","o","envEnabled","hasPrototypePollution","isValidMaestroConfig","input","resolveConfig","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","REDACTED_DETAIL_COMMANDS","MAX_SCRIPT_DETAIL_LENGTH","MAX_AI_PROMPT_LENGTH","categorizeCommand","command","MAESTRO_COMMAND_NAME_MAP","normalizeMaestroCommandName","withoutSuffix","RESERVED_CHILD_KEYS","findInlineCommandKey","extractMaestroSelector","params","sel","cond","inner","truncate","max","redactText","stringifyPoint","point","extractCommandDetails","commandName","details","text","startPoint","endPoint","appId","inlineScript","prompt","v","extractChildCommands","baseTimestamp","candidates","c","entry","parseRawCommand","index","selector","children","commandObj","rawKey","inlineKey","meta","mapStatus","duration","sequenceNumber","timestamp","lower","parseCommandsJson","jsonContent","base","commands","parseCommandsFile","discoverCommandFiles","artifactsDir","readdirSync","f","basename","extractSubflowRefs","body","refs","item","flow","extractHookRefs","hooks","items","parseFlowYaml","parts","headerContent","bodyContent","header","parseDocument","bodyCommands","tags","t","env","k","properties","onFlowStart","onFlowComplete","subflowRefs","parseFlowFile","discoverFlowFiles","dir","results","walk","entries","fullPath","ext","extname","LOG_LINE_REGEX","TIMESTAMP_ONLY_REGEX","parseLevel","upper","buildEntryTimestamp","dateAnchor","hhmmss","m","h","mi","parseLogContent","defaultDate","lines","anchorDate","dateAnchorStr","line","trimmed","ts","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","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","buildStepTitle","cmd","d","commandToAction","flowStartedMs","cmdMs","videoOffset","sortActions","a","b","ta","tb","buildTestResult","apiCalls","orderedCommands","sa","sb","orderedAssertions","actions","buildTimelineEntry","apiCallsByFlow","testId","testResult","buildTimeline","flows","EMPTY_STATUS_RANGE","statusBucket","code","percentile","sorted","idx","buildApiStats","calls","byMethod","byStatus","urls","times","apiResponseTime","n","buildSummary","timeline","total","passed","failed","flaky","timedout","totalAssertions","passedAssertions","failedAssertions","totalNavigations","totalActionSteps","actionCategoryCounts","uniqueUrls","allApiCalls","test","action","cat","apiStats","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","warning","info","defectParts","openInBrowser","platform","exec","resolveAddonPath","here","fileURLToPath","maybeDirname","generateRunDir","randomBytes","tmpdir","isMitmdumpAvailable","resolvePromise","child","spawn","settle","ok","startNetworkProxy","verbose","addon","outputDir","args","err","chunk","r","stop","settled","NOOP_HANDLE","commandExists","spawnSync","logHint","logManualInstructions","proxyHost","proxyPort","adb","deviceId","full","out","setUpAndroidEmulator","opts","skipCertInstall","setResult","xcrun","setUpIosSimulator","caCandidate","udid","res","setUpDeviceProxy","generateTempDir","buildMaestroArgs","tempDir","junitPath","normalisePlatform","runMaestroProc","networkJsonlDir","onClose","stdoutChunks","stderrChunks","proc","runMaestro","extras","proxyHandle","deviceHandle","networkConfig","networkDir","cleanup","REDACTED","redactHeaders","headers","redactList","redactJsonBody","fields","redactBody","redacted","redactApiCall","call","redactHeadersList","redactBodyFields","matchesUrlFilter","url","include","exclude","matches","pat","parseJsonlLine","parseNetworkJsonl","records","rec","parseNetworkJsonlFile","discoverNetworkJsonlFiles","harHeadersToRecord","harBodyToString","postData","isBinaryMime","mime","harEntryToRecord","req","isBinary","parseHar","parseHarFile","bindApiCallsToFlows","buckets","windows","hit","w","best","dist","collectNetworkByFlow","jsonlDir","all","file","reindexFlowCalls","LOCALHOST_HOSTS","enforceHttps","endpoint","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","filename","tmpPath","renameSync","flushQueue","files","batches","batch","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","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","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","normaliseCommandFileName","buildFlowRecordingMap","commandSteps","map","recPath","commandsForFlow","stripCmdPrefix","cmdFile","cmds","stripped","platformToOs","flowToTestResult","consoleLogs","logEntriesForFlow","allEntries","flowStartedAt","flowCompletedAt","startMs","endMs","orchestrateReport","startedAt","randomUUID","logFiles","allLogEntries","logFile","mtimeDate","detected","flowMetadataMap","flowFiles","commandFiles","allAiDefects","aiReportFiles","aiFile","aiReport","flowRecordingMap","flowResults","junit","allCommands","allAssertions","allNonAssertions","testCase","durationMs","recordingPath","flowCmds","matchedAnyFile","perFlowCommands","perFlowAssertions","earliestCmdMs","min","bound","remapped","videoPathsToUpload","singleVideo","matchedPaths","unmatchedVideos","testsForUpload","tlTest","isTimelineEntry","mergeReports","reportPaths","allTimeline","timelineEntries","earliestStart","latestEnd","reportPath","mergeReportsFromDirectory"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;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;AA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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 {\n MaestroReporterConfig,\n ResolvedMaestroConfig,\n NetworkCaptureConfig,\n ResolvedNetworkCaptureConfig,\n} from './types.js';\nimport { discoverConfigFile, parseConfigFile, resolveEnvVars, mergeCloudConfig } from './cloud-config.js';\n\n// Defaults mirror @testrelic/playwright-analytics so the same redaction policy\n// applies to mobile and web captures. Sensitive header / body field names are\n// case-insensitive when applied at capture time.\nconst DEFAULT_REDACT_HEADERS: readonly string[] = [\n 'authorization', 'cookie', 'set-cookie', 'x-api-key',\n];\nconst DEFAULT_REDACT_BODY_FIELDS: readonly string[] = [\n 'password', 'secret', 'token', 'apiKey', 'api_key',\n];\nconst DEFAULT_MAX_BODY_BYTES = 1024 * 1024; // 1 MiB\n\nfunction resolveNetworkCapture(\n options: NetworkCaptureConfig | undefined,\n): ResolvedNetworkCaptureConfig {\n const o = options ?? {};\n const envEnabled = process.env.TESTRELIC_CAPTURE_NETWORK === '1'\n || process.env.TESTRELIC_CAPTURE_NETWORK?.toLowerCase() === 'true';\n return Object.freeze({\n enabled: o.enabled ?? envEnabled ?? false,\n proxyPort: o.proxyPort ?? (process.env.TESTRELIC_PROXY_PORT ? Number.parseInt(process.env.TESTRELIC_PROXY_PORT, 10) : 8080),\n proxyHost: o.proxyHost ?? process.env.TESTRELIC_PROXY_HOST ?? '127.0.0.1',\n harPath: o.harPath ?? process.env.TESTRELIC_HAR_PATH ?? null,\n outputDir: o.outputDir ?? null,\n skipCertInstall: o.skipCertInstall ?? false,\n includeUrls: Object.freeze(o.includeUrls ? [...o.includeUrls] : []),\n excludeUrls: Object.freeze(o.excludeUrls ? [...o.excludeUrls] : []),\n redactHeaders: Object.freeze(o.redactHeaders ? [...o.redactHeaders] : [...DEFAULT_REDACT_HEADERS]),\n redactBodyFields: Object.freeze(o.redactBodyFields ? [...o.redactBodyFields] : [...DEFAULT_REDACT_BODY_FIELDS]),\n maxBodyBytes: o.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES,\n });\n}\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 target.network = resolveNetworkCapture(options?.network);\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\n// Commands whose params should NEVER be surfaced verbatim in the report.\n// `inputText` is here because it often contains usernames / passwords / OTPs;\n// we still capture a length-preserving redaction so timing diagnostics remain useful.\nconst REDACTED_DETAIL_COMMANDS = new Set(['inputText', 'pasteText']);\n\nconst MAX_SCRIPT_DETAIL_LENGTH = 200;\nconst MAX_AI_PROMPT_LENGTH = 400;\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 /** Children for flow-control wrappers (retry/repeat). Maestro stores these as `commands` or `subSteps`. */\n commands?: unknown;\n subSteps?: unknown;\n children?: unknown;\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 * Reserved keys that may appear on a Maestro child entry alongside the\n * command-type key (which is what we actually want).\n */\nconst RESERVED_CHILD_KEYS = new Set([\n 'metadata', 'command', 'commandName', 'name', 'status', 'duration',\n 'durationMs', 'timestamp', 'time', 'error', 'errorMessage', 'selector',\n 'commands', 'subSteps', 'children', 'sequenceNumber',\n]);\n\n/**\n * Find the inline Maestro command-type key on a child entry. Children inside\n * `runFlowCommand.commands` (and retry/repeat) are emitted as bare\n * `{ \"<typeName>\": { ...params } }` objects with no `command:` wrapper. We pick\n * the first non-reserved key that points to an object; everything else is\n * metadata-shaped and shouldn't be treated as the command.\n */\nfunction findInlineCommandKey(raw: Record<string, unknown>): string | undefined {\n for (const key of Object.keys(raw)) {\n if (RESERVED_CHILD_KEYS.has(key)) continue;\n const value = raw[key];\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n return key;\n }\n }\n return undefined;\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 truncate(value: string, max: number): string {\n if (value.length <= max) return value;\n return `${value.slice(0, max)}…`;\n}\n\nfunction redactText(value: string): string {\n // Keep a length-hint without leaking the actual content.\n if (value.length === 0) return '';\n return `[REDACTED ${value.length} chars]`;\n}\n\nfunction stringifyPoint(point: unknown): string | undefined {\n if (!point || typeof point !== 'object') return undefined;\n const p = point as Record<string, unknown>;\n if (typeof p.x === 'number' && typeof p.y === 'number') return `${p.x},${p.y}`;\n return undefined;\n}\n\n/**\n * Extract human-relevant command parameters for the report's step detail view.\n * Returns `undefined` when there's nothing useful to surface (selector is\n * already covered by `extractMaestroSelector`).\n *\n * Sensitive commands (inputText / pasteText) are stored with the text value\n * redacted so diagnostics remain useful without leaking PII to the cloud.\n */\nfunction extractCommandDetails(\n commandName: string,\n params: Record<string, unknown>,\n): Record<string, unknown> | undefined {\n const details: Record<string, unknown> = {};\n\n switch (commandName) {\n case 'inputText':\n case 'pasteText': {\n const text = params.text ?? params.value;\n if (typeof text === 'string') {\n details.text = REDACTED_DETAIL_COMMANDS.has(commandName) ? redactText(text) : text;\n }\n break;\n }\n case 'swipe':\n case 'scroll':\n case 'scrollUntilVisible': {\n if (typeof params.direction === 'string') details.direction = params.direction;\n const startPoint = stringifyPoint(params.start ?? params.startRelative);\n const endPoint = stringifyPoint(params.end ?? params.endRelative);\n if (startPoint) details.start = startPoint;\n if (endPoint) details.end = endPoint;\n if (typeof params.duration === 'number') details.swipeDurationMs = params.duration;\n if (typeof params.timeout === 'number') details.timeoutMs = params.timeout;\n break;\n }\n case 'setLocation': {\n if (typeof params.latitude === 'number') details.latitude = params.latitude;\n if (typeof params.longitude === 'number') details.longitude = params.longitude;\n break;\n }\n case 'pressKey':\n case 'back': {\n if (typeof params.key === 'string') details.key = params.key;\n else if (typeof params.code === 'string') details.key = params.code;\n break;\n }\n case 'setOrientation': {\n if (typeof params.orientation === 'string') details.orientation = params.orientation;\n break;\n }\n case 'setAirplaneMode':\n case 'toggleAirplaneMode': {\n if (typeof params.value === 'string' || typeof params.value === 'boolean') {\n details.value = params.value;\n }\n break;\n }\n case 'setPermissions': {\n if (params.permissions && typeof params.permissions === 'object') {\n details.permissions = params.permissions;\n }\n break;\n }\n case 'addMedia': {\n if (Array.isArray(params.mediaPaths)) details.mediaPaths = params.mediaPaths;\n else if (typeof params.path === 'string') details.mediaPaths = [params.path];\n break;\n }\n case 'launchApp':\n case 'killApp':\n case 'stopApp':\n case 'clearState': {\n const appId = params.appId ?? params.packageName ?? params.bundleId;\n if (typeof appId === 'string') details.appId = appId;\n if (params.arguments) details.arguments = params.arguments;\n break;\n }\n case 'openLink': {\n if (typeof params.url === 'string') details.url = params.url;\n else if (typeof params.link === 'string') details.url = params.link;\n break;\n }\n case 'runScript':\n case 'evalScript': {\n if (typeof params.path === 'string') details.path = params.path;\n // Maestro emits inline JS under `scriptString` for evalScript, `script` for legacy runScript.\n const inlineScript = params.script ?? params.scriptString;\n if (typeof inlineScript === 'string') details.script = truncate(inlineScript, MAX_SCRIPT_DETAIL_LENGTH);\n if (typeof params.sourceDescription === 'string') details.sourceDescription = params.sourceDescription;\n break;\n }\n case 'assertWithAI':\n case 'assertNoDefectsWithAI':\n case 'extractTextWithAI': {\n const prompt = params.assertion ?? params.prompt ?? params.question;\n if (typeof prompt === 'string') details.prompt = truncate(prompt, MAX_AI_PROMPT_LENGTH);\n break;\n }\n case 'extendedWaitUntil':\n case 'waitForAnimationToEnd': {\n if (typeof params.timeout === 'number') details.timeoutMs = params.timeout;\n break;\n }\n case 'repeat':\n case 'retry': {\n if (typeof params.times === 'number') details.times = params.times;\n if (typeof params.maxRetries === 'number') details.maxRetries = params.maxRetries;\n if (typeof params.condition === 'object' && params.condition) details.condition = params.condition;\n break;\n }\n case 'runFlow': {\n if (typeof params.file === 'string') details.file = params.file;\n if (typeof params.flowId === 'string') details.flowId = params.flowId;\n break;\n }\n default:\n // Surface a single useful primitive for unknown commands if present, otherwise drop.\n for (const key of ['text', 'value', 'url']) {\n const v = params[key];\n if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') {\n details[key] = v;\n break;\n }\n }\n }\n\n return Object.keys(details).length > 0 ? details : undefined;\n}\n\n/**\n * Extract child commands from a wrapper command (retry / repeat / runFlow).\n * Maestro 2.x emits the children under `commands` inside the nested params; legacy\n * format may put them under `subSteps` or `children` on the raw entry. Returns an\n * empty array when none are present.\n */\nfunction extractChildCommands(\n params: Record<string, unknown>,\n raw: RawCommandEntry,\n baseTimestamp: string,\n): MaestroCommandStep[] {\n const candidates = [params.commands, raw.commands, raw.subSteps, raw.children];\n for (const c of candidates) {\n if (Array.isArray(c) && c.length > 0) {\n return (c as RawCommandEntry[]).map((entry, i) =>\n parseRawCommand(entry, i, baseTimestamp),\n );\n }\n }\n return [];\n}\n\nfunction parseRawCommand(raw: RawCommandEntry, index: number, baseTimestamp: string): MaestroCommandStep {\n let command: string;\n let selector: string | undefined;\n let details: Record<string, unknown> | undefined;\n let children: MaestroCommandStep[] = [];\n\n if (typeof raw.command === 'object' && raw.command !== null && !Array.isArray(raw.command)) {\n // Maestro 2.x top-level entries: { \"command\": { \"launchAppCommand\": { ...params } }, \"metadata\": {...} }\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 details = extractCommandDetails(command, params);\n children = extractChildCommands(params, raw, baseTimestamp);\n } else {\n // Maestro 2.x children inside runFlow/retry/repeat `commands` arrays are emitted\n // *without* a `command:` wrapper — the command-type key sits directly on the entry\n // (e.g. `{ \"tapOnElement\": { ... } }`). Detect that shape first; fall back to the\n // legacy/string forms only if no inline key is present.\n const inlineKey = findInlineCommandKey(raw);\n if (inlineKey) {\n command = normalizeMaestroCommandName(inlineKey);\n const params = (raw[inlineKey] as Record<string, unknown>) ?? {};\n selector = extractMaestroSelector(params);\n details = extractCommandDetails(command, params);\n children = extractChildCommands(params, raw, baseTimestamp);\n } else {\n command = (raw.command as string | undefined) ?? raw.commandName ?? raw.name ?? `step-${index}`;\n selector = raw.selector ?? undefined;\n children = extractChildCommands({}, raw, baseTimestamp);\n }\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 const sequenceNumber = typeof meta.sequenceNumber === 'number' ? meta.sequenceNumber : undefined;\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 ...(details ? { details } : {}),\n ...(sequenceNumber !== undefined ? { sequenceNumber } : {}),\n ...(children.length > 0 ? { children } : {}),\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\n// Maestro emits log lines in the form\n// \"02:43:56.278 [ INFO] MAESTRO.logSystemInfo: ...message\"\n// Level may be unbracketed (legacy) OR bracketed with optional inner whitespace.\n// We allow either so a future Maestro change to the format doesn't silently\n// regress the consoleLogs upload again.\nconst LOG_LINE_REGEX = /^(\\d{4}-\\d{2}-\\d{2}[T ]\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:?\\d{2})?)\\s+\\[?\\s*(\\w+)\\s*\\]?\\s+(.+)$/;\nconst TIMESTAMP_ONLY_REGEX = /^\\[?(\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?)\\]?\\s+\\[?\\s*(\\w+)\\s*\\]?\\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\n/**\n * Combine the LOCAL-time date components of `dateAnchor` with the\n * LOCAL-time `HH:MM:SS.mmm` from a log line, then return the proper UTC\n * ISO string. This is the only correct way to handle Maestro's log timestamps\n * because Maestro writes wall-clock time in the host's local timezone without\n * an offset — naive concatenation with a UTC date prefix produces an ISO\n * string that's off by the local UTC offset (e.g. 5:30h for IST), causing\n * `logEntriesForFlow` to drop every line.\n */\nfunction buildEntryTimestamp(dateAnchor: Date, hhmmss: string): string {\n const m = /^(\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d{1,3}))?$/.exec(hhmmss);\n if (!m) {\n // Should not happen — caller already matched the timestamp regex.\n return new Date().toISOString();\n }\n const h = Number(m[1]);\n const mi = Number(m[2]);\n const s = Number(m[3]);\n const ms = m[4] ? Number(m[4].padEnd(3, '0')) : 0;\n // `new Date(y, m, d, h, mi, s, ms)` interprets components as LOCAL time\n // and stores the absolute instant — `toISOString()` then yields UTC.\n const local = new Date(\n dateAnchor.getFullYear(),\n dateAnchor.getMonth(),\n dateAnchor.getDate(),\n h, mi, s, ms,\n );\n return local.toISOString();\n}\n\n/**\n * @param defaultDate Either an ISO date string (`YYYY-MM-DD`) or a Date object.\n * Used to anchor log lines that only carry a wall-clock time (`HH:MM:SS`).\n * Callers should pass the log file's mtime as a Date so the local-zone\n * conversion produces a correct UTC ISO timestamp for log entries. When a\n * plain string is passed, the timestamp is constructed without TZ-shift\n * compensation (legacy behaviour — may be off by the local UTC offset).\n */\nexport function parseLogContent(\n content: string,\n defaultDate?: string | Date,\n): MaestroLogEntry[] {\n const entries: MaestroLogEntry[] = [];\n const lines = content.split('\\n');\n const anchorDate: Date | null = defaultDate instanceof Date\n ? defaultDate\n : null;\n const dateAnchorStr: string = typeof defaultDate === 'string'\n ? defaultDate\n : (defaultDate instanceof Date\n ? `${defaultDate.getFullYear()}-${String(defaultDate.getMonth() + 1).padStart(2, '0')}-${String(defaultDate.getDate()).padStart(2, '0')}`\n : 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 // Prefer the TZ-aware local-date construction when the caller passed a\n // real Date; otherwise fall back to the legacy string-concat path.\n const ts = anchorDate\n ? buildEntryTimestamp(anchorDate, match[1])\n : `${dateAnchorStr}T${match[1]}`;\n entries.push({\n timestamp: ts,\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(\n filePath: string,\n defaultDate?: string | Date,\n): MaestroLogEntry[] {\n try {\n const content = readFileSync(filePath, 'utf-8');\n return parseLogContent(content, defaultDate);\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 TestArtifacts,\n ActionStep,\n ActionCategory,\n ApiCallRecord,\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\n/**\n * Build a human-readable title for a command step, enriching with the most\n * useful extracted detail when present. Falls back to the selector (existing\n * behaviour) and finally the bare command name.\n *\n * Examples:\n * tapOn → Login (selector)\n * inputText → \"<value>\" (details.text or the redaction marker)\n * swipe UP (details.direction)\n * setLocation 37.77,-122.42\n * pressKey BACK\n * runScript scripts/foo.js\n * retry × 3 (details.maxRetries or .times)\n */\nfunction buildStepTitle(cmd: MaestroCommandStep): string {\n const d = cmd.details;\n if (d) {\n switch (cmd.command) {\n case 'inputText':\n case 'pasteText':\n if (typeof d.text === 'string') return `${cmd.command} → ${d.text}`;\n break;\n case 'swipe':\n case 'scroll':\n case 'scrollUntilVisible':\n if (typeof d.direction === 'string') return `${cmd.command} ${d.direction}`;\n break;\n case 'setLocation':\n if (typeof d.latitude === 'number' && typeof d.longitude === 'number') {\n return `setLocation ${d.latitude},${d.longitude}`;\n }\n break;\n case 'pressKey':\n if (typeof d.key === 'string') return `pressKey ${d.key}`;\n break;\n case 'setOrientation':\n if (typeof d.orientation === 'string') return `setOrientation ${d.orientation}`;\n break;\n case 'launchApp':\n case 'killApp':\n case 'stopApp':\n case 'clearState':\n if (typeof d.appId === 'string') return `${cmd.command} ${d.appId}`;\n break;\n case 'openLink':\n if (typeof d.url === 'string') return `openLink ${d.url}`;\n break;\n case 'runScript':\n case 'evalScript':\n if (typeof d.path === 'string') return `${cmd.command} ${d.path}`;\n if (typeof d.sourceDescription === 'string') return `${cmd.command} ${d.sourceDescription}`;\n if (typeof d.script === 'string') return `${cmd.command} ${d.script}`;\n break;\n case 'assertWithAI':\n case 'assertNoDefectsWithAI':\n case 'extractTextWithAI':\n if (typeof d.prompt === 'string') return `${cmd.command} → ${d.prompt}`;\n break;\n case 'retry':\n if (typeof d.maxRetries === 'number') return `retry × ${d.maxRetries}`;\n break;\n case 'repeat':\n if (typeof d.times === 'number') return `repeat × ${d.times}`;\n break;\n case 'runFlow':\n if (typeof d.file === 'string') return `runFlow ${d.file}`;\n break;\n }\n }\n return cmd.selector ? `${cmd.command} → ${cmd.selector}` : cmd.command;\n}\n\n/**\n * Convert a parsed Maestro command into a core `ActionStep`, recursing into\n * child commands (retry/repeat/runFlow wrappers). `videoOffset` is computed\n * from the flow's start time so the cloud platform can sync each step to the\n * recorded video without re-deriving offsets.\n */\nfunction commandToAction(cmd: MaestroCommandStep, flowStartedMs: number): ActionStep {\n const cmdMs = Date.parse(cmd.timestamp);\n const videoOffset = Number.isFinite(cmdMs) && Number.isFinite(flowStartedMs)\n ? Math.max(0, (cmdMs - flowStartedMs) / 1000)\n : null;\n\n const children: ActionStep[] = (cmd.children ?? []).map((c) => commandToAction(c, flowStartedMs));\n\n return {\n title: buildStepTitle(cmd),\n category: mapCommandCategory(cmd.category),\n status: cmd.status === 'failed' ? 'failed' : 'passed',\n duration: cmd.duration,\n timestamp: cmd.timestamp,\n videoOffset,\n error: cmd.error ?? null,\n children,\n };\n}\n\n/**\n * Stable sort key for action ordering. Primary: timestamp (ms). Tiebreaker:\n * Maestro's metadata.sequenceNumber when both sides have one, so back-to-back\n * commands emitted in the same millisecond preserve emission order.\n */\nfunction sortActions(a: ActionStep, b: ActionStep): number {\n const ta = Date.parse(a.timestamp);\n const tb = Date.parse(b.timestamp);\n if (Number.isFinite(ta) && Number.isFinite(tb) && ta !== tb) return ta - tb;\n return 0;\n}\n\nfunction buildTestResult(flow: MaestroFlowResult, apiCalls: readonly ApiCallRecord[]): TestResult {\n const flowStartedMs = Date.parse(flow.startedAt);\n\n // Sort underlying commands by sequenceNumber when timestamps tie, BEFORE mapping\n // to ActionStep (ActionStep doesn't carry sequenceNumber).\n const orderedCommands = [...flow.commands].sort((a, b) => {\n const ta = Date.parse(a.timestamp);\n const tb = Date.parse(b.timestamp);\n if (Number.isFinite(ta) && Number.isFinite(tb) && ta !== tb) return ta - tb;\n const sa = a.sequenceNumber ?? 0;\n const sb = b.sequenceNumber ?? 0;\n return sa - sb;\n });\n const orderedAssertions = [...flow.assertions].sort((a, b) => {\n const ta = Date.parse(a.timestamp);\n const tb = Date.parse(b.timestamp);\n if (Number.isFinite(ta) && Number.isFinite(tb) && ta !== tb) return ta - tb;\n const sa = a.sequenceNumber ?? 0;\n const sb = b.sequenceNumber ?? 0;\n return sa - sb;\n });\n\n const actions: ActionStep[] = [\n ...orderedCommands.map((c) => commandToAction(c, flowStartedMs)),\n ...orderedAssertions.map((c) => commandToAction(c, flowStartedMs)),\n ];\n\n actions.sort(sortActions);\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: apiCalls.length > 0 ? [...apiCalls] : null,\n apiAssertions: null,\n actions: actions.length > 0 ? actions : null,\n consoleLogs: null,\n };\n}\n\n/**\n * Build a TimelineEntry for a single flow. Optionally accepts an\n * `apiCallsByFlow` map keyed by `testId` (`${flowFile}::${flowName}`) so the\n * orchestrator can plumb network capture results in without re-shaping flows.\n */\nexport function buildTimelineEntry(\n flow: MaestroFlowResult,\n apiCallsByFlow?: ReadonlyMap<string, readonly ApiCallRecord[]>,\n): TimelineEntry {\n const testId = `${flow.flowFile}::${flow.flowName}`;\n const apiCalls = apiCallsByFlow?.get(testId) ?? [];\n const testResult = buildTestResult(flow, apiCalls);\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(\n flows: MaestroFlowResult[],\n apiCallsByFlow?: ReadonlyMap<string, readonly ApiCallRecord[]>,\n): TimelineEntry[] {\n return flows.map((f) => buildTimelineEntry(f, apiCallsByFlow));\n}\n","/**\n * Computes Summary stats from TimelineEntry arrays.\n */\n\nimport type { Summary, TimelineEntry, ApiCallRecord } 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\nfunction statusBucket(code: number | null): keyof ApiCallsByStatusRange {\n if (code === null) return 'error';\n if (code >= 200 && code < 300) return '2xx';\n if (code >= 300 && code < 400) return '3xx';\n if (code >= 400 && code < 500) return '4xx';\n if (code >= 500 && code < 600) return '5xx';\n return 'error';\n}\n\nfunction percentile(sorted: number[], p: number): number | null {\n if (sorted.length === 0) return null;\n const idx = Math.min(sorted.length - 1, Math.floor((p / 100) * sorted.length));\n return sorted[idx];\n}\n\nfunction buildApiStats(calls: ApiCallRecord[]): {\n totalApiCalls: number;\n uniqueApiUrls: number;\n apiCallsByMethod: Record<string, number>;\n apiCallsByStatusRange: ApiCallsByStatusRange;\n apiResponseTime: Summary['apiResponseTime'];\n} {\n if (calls.length === 0) {\n return {\n totalApiCalls: 0,\n uniqueApiUrls: 0,\n apiCallsByMethod: {},\n apiCallsByStatusRange: { ...EMPTY_STATUS_RANGE },\n apiResponseTime: null,\n };\n }\n\n const byMethod: Record<string, number> = {};\n const byStatus: ApiCallsByStatusRange = { ...EMPTY_STATUS_RANGE };\n const urls = new Set<string>();\n const times: number[] = [];\n\n for (const c of calls) {\n byMethod[c.method] = (byMethod[c.method] ?? 0) + 1;\n byStatus[statusBucket(c.responseStatusCode)] += 1;\n urls.add(c.url);\n if (Number.isFinite(c.responseTimeMs) && c.responseTimeMs > 0) {\n times.push(c.responseTimeMs);\n }\n }\n\n times.sort((a, b) => a - b);\n const apiResponseTime: Summary['apiResponseTime'] = times.length > 0 ? {\n p50: percentile(times, 50) ?? 0,\n p95: percentile(times, 95) ?? 0,\n p99: percentile(times, 99) ?? 0,\n avg: times.reduce((s, n) => s + n, 0) / times.length,\n min: times[0],\n max: times[times.length - 1],\n } : null;\n\n return {\n totalApiCalls: calls.length,\n uniqueApiUrls: urls.size,\n apiCallsByMethod: byMethod,\n apiCallsByStatusRange: byStatus,\n apiResponseTime,\n };\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 const allApiCalls: ApiCallRecord[] = [];\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 if (test.apiCalls && test.apiCalls.length > 0) {\n allApiCalls.push(...test.apiCalls);\n }\n }\n }\n\n const apiStats = buildApiStats(allApiCalls);\n\n return {\n total,\n passed,\n failed,\n flaky,\n skipped,\n timedout,\n ...apiStats,\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 * mitmproxy lifecycle wrapper for Maestro network capture.\n *\n * Strategy:\n * 1. Check `mitmdump` is on PATH. If not, log a hint and disable capture\n * (caller falls back to HAR import or no capture).\n * 2. Resolve the bundled Python addon (`proxy-addon/testrelic_capture.py`).\n * In dev it lives alongside the TS source; after `tsup` build it's copied\n * under `dist/proxy-addon/`.\n * 3. Spawn `mitmdump` with addon flags + redaction options translated into\n * `--set` switches the addon understands.\n * 4. Return a handle the runner uses to stop the proxy after Maestro exits.\n *\n * No platform-specific paths here — all device-side setup (CA install, proxy\n * route) lives in `device-proxy-setup.ts`.\n */\n\nimport { spawn, type ChildProcess } from 'node:child_process';\nimport { mkdirSync, existsSync } from 'node:fs';\nimport { join, resolve, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { tmpdir } from 'node:os';\nimport { randomBytes } from 'node:crypto';\nimport type { ResolvedNetworkCaptureConfig } from './types.js';\n\nexport interface NetworkProxyHandle {\n /** Absolute path of the JSONL output the addon is writing to. */\n readonly outputPath: string;\n /** Directory containing the JSONL output (passed to discoverNetworkJsonlFiles). */\n readonly outputDir: string;\n /** Listening host. */\n readonly host: string;\n /** Listening port. */\n readonly port: number;\n /** Stop the proxy gracefully. Resolves when the child process has exited. */\n stop(): Promise<void>;\n}\n\nfunction resolveAddonPath(): string | null {\n // After tsup build the addon is at `<pkg>/dist/proxy-addon/testrelic_capture.py`.\n // In dev the addon lives next to the TS source.\n const candidates: string[] = [];\n try {\n // ESM resolution\n const here = dirname(fileURLToPath(import.meta.url));\n candidates.push(join(here, 'proxy-addon', 'testrelic_capture.py'));\n candidates.push(join(here, '..', 'src', 'proxy-addon', 'testrelic_capture.py'));\n } catch {\n /* not ESM — fall through */\n }\n // CJS / tsup-cjs fallback via __dirname (only present when bundled CJS).\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const maybeDirname: string | undefined = (globalThis as any).__dirname;\n if (maybeDirname) {\n candidates.push(join(maybeDirname, 'proxy-addon', 'testrelic_capture.py'));\n }\n for (const c of candidates) {\n if (existsSync(c)) return resolve(c);\n }\n return null;\n}\n\nfunction generateRunDir(base?: string): string {\n if (base) {\n mkdirSync(base, { recursive: true });\n return base;\n }\n const id = randomBytes(6).toString('hex');\n const dir = join(tmpdir(), `testrelic-maestro-network-${id}`);\n mkdirSync(dir, { recursive: true });\n return dir;\n}\n\nasync function isMitmdumpAvailable(): Promise<boolean> {\n return new Promise((resolvePromise) => {\n const child = spawn('mitmdump', ['--version'], { shell: true });\n let resolved = false;\n const settle = (ok: boolean): void => {\n if (resolved) return;\n resolved = true;\n resolvePromise(ok);\n };\n child.on('error', () => settle(false));\n child.on('close', (code) => settle(code === 0));\n // Defensive 5s timeout — mitmdump --version should respond instantly.\n setTimeout(() => {\n try { child.kill(); } catch { /* ignore */ }\n settle(false);\n }, 5_000);\n });\n}\n\nexport interface StartNetworkProxyOptions {\n readonly config: ResolvedNetworkCaptureConfig;\n /** Directory where the JSONL file should be written. */\n readonly outputDir?: string;\n /** When true, log spawn errors to stderr (default: true). Quieted by `--quiet`. */\n readonly verbose?: boolean;\n}\n\n/**\n * Spawn `mitmdump` with the TestRelic addon. Returns null when:\n * - capture is disabled,\n * - `mitmdump` is not on PATH,\n * - the addon couldn't be located (broken install).\n *\n * Never throws — the caller treats `null` as \"no capture this run.\"\n */\nexport async function startNetworkProxy(\n options: StartNetworkProxyOptions,\n): Promise<NetworkProxyHandle | null> {\n const { config, verbose = true } = options;\n if (!config.enabled) return null;\n\n if (!(await isMitmdumpAvailable())) {\n if (verbose) {\n process.stderr.write(\n '⚠ TestRelic: --capture-network requested but `mitmdump` is not on PATH.\\n'\n + ' Install mitmproxy (`brew install mitmproxy` / `pipx install mitmproxy`)\\n'\n + ' or supply --har-path to import a HAR file instead.\\n',\n );\n }\n return null;\n }\n\n const addon = resolveAddonPath();\n if (!addon) {\n if (verbose) {\n process.stderr.write('⚠ TestRelic: network-capture addon not found in package; skipping capture.\\n');\n }\n return null;\n }\n\n const outputDir = generateRunDir(options.outputDir ?? config.outputDir ?? undefined);\n const outputPath = join(outputDir, 'network.jsonl');\n\n const args: string[] = [\n '--listen-host', config.proxyHost,\n '--listen-port', String(config.proxyPort),\n '-s', addon,\n '--set', `testrelic_output=${outputPath}`,\n '--set', `testrelic_redact_headers=${config.redactHeaders.join(',')}`,\n '--set', `testrelic_redact_body_fields=${config.redactBodyFields.join(',')}`,\n '--set', `testrelic_max_body_bytes=${config.maxBodyBytes}`,\n // Reduce noise; the addon writes its own log lines on warn.\n '--set', 'console_eventlog_verbosity=warn',\n '--set', 'termlog_verbosity=warn',\n ];\n\n let child: ChildProcess;\n try {\n child = spawn('mitmdump', args, {\n stdio: ['ignore', 'pipe', 'pipe'],\n shell: true,\n });\n } catch (err) {\n if (verbose) {\n process.stderr.write(`⚠ TestRelic: failed to spawn mitmdump: ${(err as Error).message}\\n`);\n }\n return null;\n }\n\n // Drain output so the buffer never fills, but only echo on verbose.\n child.stdout?.on('data', (chunk: Buffer) => {\n if (verbose) process.stderr.write(`[mitmdump] ${chunk.toString('utf-8')}`);\n });\n child.stderr?.on('data', (chunk: Buffer) => {\n if (verbose) process.stderr.write(`[mitmdump] ${chunk.toString('utf-8')}`);\n });\n\n // Tiny boot delay — give mitmdump a moment to bind the port before the\n // device starts routing traffic through it. Not load-bearing, but it\n // prevents the first 1-2 requests from racing the listener.\n await new Promise((r) => setTimeout(r, 750));\n\n const stop = (): Promise<void> => new Promise<void>((resolvePromise) => {\n if (child.killed || child.exitCode !== null) {\n resolvePromise();\n return;\n }\n let settled = false;\n const settle = (): void => {\n if (settled) return;\n settled = true;\n resolvePromise();\n };\n child.once('close', settle);\n child.once('exit', settle);\n try {\n child.kill('SIGINT');\n } catch {\n try { child.kill(); } catch { /* ignore */ }\n }\n // Fallback hard-stop after 4s so a misbehaving mitmdump doesn't hang the\n // entire run cleanup.\n setTimeout(() => {\n if (!settled) {\n try { child.kill('SIGKILL'); } catch { /* ignore */ }\n settle();\n }\n }, 4_000);\n });\n\n return {\n outputPath,\n outputDir,\n host: config.proxyHost,\n port: config.proxyPort,\n stop,\n };\n}\n","/**\n * Best-effort device-side setup for routing mobile app HTTP traffic through\n * a host-side mitmproxy.\n *\n * This module is **best-effort by design**. Real-device cert installs and\n * proxy plumbing vary widely across OS versions, manufacturers, and emulator\n * images. Goals here:\n *\n * - Cover the 80% case automatically: Android emulator + iOS simulator.\n * - For real devices, print precise manual instructions and return without\n * erroring — the proxy still runs, so users who pre-configured the device\n * get capture immediately.\n * - Never leave a device in a half-configured state. `tearDownDeviceProxy`\n * reverts whatever `setUpDeviceProxy` changed, idempotently.\n *\n * No throws: failures degrade to \"capture won't see this device's traffic\"\n * with a stderr hint. The runner continues so the user's tests still execute.\n */\n\nimport { spawn, spawnSync } from 'node:child_process';\nimport { join } from 'node:path';\nimport { tmpdir } from 'node:os';\nimport { writeFileSync, existsSync, unlinkSync } from 'node:fs';\nimport type { MaestroPlatform } from './types.js';\n\nexport interface DeviceProxyOptions {\n readonly platform: MaestroPlatform;\n readonly deviceId?: string;\n readonly proxyHost: string;\n readonly proxyPort: number;\n readonly skipCertInstall?: boolean;\n readonly quiet?: boolean;\n}\n\nexport interface DeviceProxyHandle {\n /** Reverts any device-side proxy / cert changes. Safe to call multiple times. */\n teardown(): Promise<void>;\n}\n\nconst NOOP_HANDLE: DeviceProxyHandle = { teardown: async () => { /* no-op */ } };\n\nfunction commandExists(cmd: string): boolean {\n // `cmd --version` works for adb, xcrun, networksetup, etc. We don't care\n // about the exit code — only that the binary resolved without ENOENT.\n const result = spawnSync(cmd, ['--version'], { shell: true, stdio: 'ignore' });\n return result.error === undefined || (result.error as NodeJS.ErrnoException).code !== 'ENOENT';\n}\n\nfunction logHint(quiet: boolean | undefined, msg: string): void {\n if (!quiet) process.stderr.write(`${msg}\\n`);\n}\n\nfunction logManualInstructions(quiet: boolean | undefined, platform: MaestroPlatform, proxyHost: string, proxyPort: number): void {\n if (quiet) return;\n if (platform === 'android') {\n process.stderr.write(\n 'ℹ TestRelic: real-device Android capture — set the device proxy manually:\\n'\n + ` Settings → Wi-Fi → (long-press your network) → Modify → Proxy: Manual\\n`\n + ` Host: ${proxyHost} Port: ${proxyPort}\\n`\n + ' Then install mitmproxy CA: open http://mitm.it from the device after the proxy is reachable.\\n',\n );\n } else if (platform === 'ios') {\n process.stderr.write(\n 'ℹ TestRelic: real-device iOS capture — set the device proxy manually:\\n'\n + ` Settings → Wi-Fi → (i) on your network → Configure Proxy → Manual\\n`\n + ` Server: ${proxyHost} Port: ${proxyPort}\\n`\n + ' Then install + TRUST the mitmproxy CA:\\n'\n + ' 1. Open http://mitm.it from Safari on the device → install profile.\\n'\n + ' 2. Settings → General → About → Certificate Trust Settings → enable mitmproxy.\\n',\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Android emulator\n// ---------------------------------------------------------------------------\n\nasync function adb(args: string[], deviceId: string | undefined): Promise<{ code: number; stdout: string; stderr: string }> {\n return new Promise((resolvePromise) => {\n const full = deviceId ? ['-s', deviceId, ...args] : args;\n const child = spawn('adb', full, { shell: true, stdio: ['ignore', 'pipe', 'pipe'] });\n let out = '';\n let err = '';\n child.stdout?.on('data', (c: Buffer) => { out += c.toString(); });\n child.stderr?.on('data', (c: Buffer) => { err += c.toString(); });\n child.on('error', () => resolvePromise({ code: 127, stdout: out, stderr: err || 'adb not on PATH' }));\n child.on('close', (code) => resolvePromise({ code: code ?? 1, stdout: out, stderr: err }));\n });\n}\n\nasync function setUpAndroidEmulator(opts: DeviceProxyOptions): Promise<DeviceProxyHandle> {\n const { deviceId, proxyHost, proxyPort, quiet, skipCertInstall } = opts;\n if (!commandExists('adb')) {\n logHint(quiet, '⚠ TestRelic: `adb` not on PATH; skipping automatic Android proxy setup. See manual instructions:');\n logManualInstructions(quiet, 'android', proxyHost, proxyPort);\n return NOOP_HANDLE;\n }\n\n // 1. Set proxy via global settings (works on emulators without root).\n const setResult = await adb(['shell', 'settings', 'put', 'global', 'http_proxy', `${proxyHost}:${proxyPort}`], deviceId);\n if (setResult.code !== 0) {\n logHint(quiet, `⚠ TestRelic: adb set proxy failed: ${setResult.stderr.trim() || 'unknown error'}`);\n logManualInstructions(quiet, 'android', proxyHost, proxyPort);\n return NOOP_HANDLE;\n }\n logHint(quiet, `ℹ TestRelic: Android proxy set to ${proxyHost}:${proxyPort} on ${deviceId ?? 'default device'}.`);\n\n // 2. Cert install is intentionally NOT attempted automatically — system CA\n // install requires `adb root` + remount, which only works on Google API\n // emulator images and breaks Play-store images. Print a hint instead.\n if (!skipCertInstall) {\n logHint(quiet,\n 'ℹ TestRelic: if HTTPS calls fail, install the mitmproxy CA on the emulator:\\n'\n + ' open http://mitm.it from Chrome on the emulator and follow prompts,\\n'\n + ' OR for a system-level install:\\n'\n + ' adb root && adb remount && adb push <mitm-ca>.0 /system/etc/security/cacerts/ && adb reboot\\n'\n + ' Set --skip-cert-install to silence this hint.',\n );\n }\n\n const teardown = async (): Promise<void> => {\n // Clear the proxy. `put` with `:0` or empty string both work on most images;\n // `:0` is the documented unset value.\n await adb(['shell', 'settings', 'put', 'global', 'http_proxy', ':0'], deviceId);\n };\n return { teardown };\n}\n\n// ---------------------------------------------------------------------------\n// iOS simulator\n// ---------------------------------------------------------------------------\n\nasync function xcrun(args: string[]): Promise<{ code: number; stdout: string; stderr: string }> {\n return new Promise((resolvePromise) => {\n const child = spawn('xcrun', args, { shell: true, stdio: ['ignore', 'pipe', 'pipe'] });\n let out = '';\n let err = '';\n child.stdout?.on('data', (c: Buffer) => { out += c.toString(); });\n child.stderr?.on('data', (c: Buffer) => { err += c.toString(); });\n child.on('error', () => resolvePromise({ code: 127, stdout: out, stderr: err || 'xcrun not on PATH' }));\n child.on('close', (code) => resolvePromise({ code: code ?? 1, stdout: out, stderr: err }));\n });\n}\n\nasync function setUpIosSimulator(opts: DeviceProxyOptions): Promise<DeviceProxyHandle> {\n const { proxyHost, proxyPort, quiet, skipCertInstall } = opts;\n\n // iOS simulator shares the host Mac's network stack, so a host-side proxy\n // automatically catches anything that respects system proxy settings — but\n // many apps (NSURLSession with custom configurations, anything using their\n // own resolver) ignore it. Realistically we tell the user to install the\n // mitmproxy profile in the simulator and they're set.\n if (process.platform !== 'darwin') {\n logHint(quiet, '⚠ TestRelic: iOS simulator capture requires macOS; the host is not Darwin. Skipping setup.');\n logManualInstructions(quiet, 'ios', proxyHost, proxyPort);\n return NOOP_HANDLE;\n }\n if (!commandExists('xcrun')) {\n logHint(quiet, '⚠ TestRelic: `xcrun` not on PATH; skipping automatic iOS simulator setup.');\n logManualInstructions(quiet, 'ios', proxyHost, proxyPort);\n return NOOP_HANDLE;\n }\n\n if (!skipCertInstall) {\n // Try to fetch mitmproxy's CA so we can push it into the simulator's keychain.\n // The CA lives at ~/.mitmproxy/mitmproxy-ca-cert.pem after the first mitmdump run.\n const caCandidate = join(process.env.HOME ?? '', '.mitmproxy', 'mitmproxy-ca-cert.pem');\n if (existsSync(caCandidate)) {\n // `simctl keychain <udid|booted> add-root-cert <path>` is supported on Xcode 11.4+\n const udid = opts.deviceId ?? 'booted';\n const res = await xcrun(['simctl', 'keychain', udid, 'add-root-cert', caCandidate]);\n if (res.code === 0) {\n logHint(quiet, `ℹ TestRelic: installed mitmproxy CA on iOS simulator (${udid}).`);\n } else {\n logHint(quiet, `⚠ TestRelic: failed to add-root-cert via simctl: ${res.stderr.trim()}`);\n logManualInstructions(quiet, 'ios', proxyHost, proxyPort);\n }\n } else {\n logHint(quiet,\n `⚠ TestRelic: mitmproxy CA not found at ${caCandidate}. Run mitmdump once to generate it,\\n`\n + ' then re-run with --capture-network. Falling back to manual instructions:',\n );\n logManualInstructions(quiet, 'ios', proxyHost, proxyPort);\n }\n }\n\n // Setting the simulator's proxy programmatically requires editing the\n // network plist for that runtime, which is fragile across Xcode versions.\n // We deliberately do NOT touch the system network settings (`networksetup`)\n // because that affects the user's whole machine. Instead we rely on either:\n // (a) the simulator inheriting the host proxy if the user configured one, or\n // (b) explicit manual proxy entry in Wi-Fi settings inside the simulator.\n logHint(quiet,\n `ℹ TestRelic: in the iOS simulator, configure Wi-Fi → (i) → Manual proxy:\\n`\n + ` Server: ${proxyHost} Port: ${proxyPort}\\n`\n + ' (We do not set host-wide `networksetup` proxy automatically; see docs.)',\n );\n\n return NOOP_HANDLE;\n}\n\n// ---------------------------------------------------------------------------\n// Public entry point\n// ---------------------------------------------------------------------------\n\nexport async function setUpDeviceProxy(options: DeviceProxyOptions): Promise<DeviceProxyHandle> {\n switch (options.platform) {\n case 'android':\n return setUpAndroidEmulator(options);\n case 'ios':\n return setUpIosSimulator(options);\n case 'web':\n logHint(options.quiet, 'ℹ TestRelic: --capture-network ignored for Maestro web flows (browser already handles HTTP).');\n return NOOP_HANDLE;\n case 'unknown':\n default:\n logHint(options.quiet, '⚠ TestRelic: target platform unknown; printing manual proxy setup instructions:');\n logManualInstructions(options.quiet, 'android', options.proxyHost, options.proxyPort);\n logManualInstructions(options.quiet, 'ios', options.proxyHost, options.proxyPort);\n return NOOP_HANDLE;\n }\n}\n\n// ---------------------------------------------------------------------------\n// PAC file emission (optional helper for custom proxy autoconfiguration)\n// ---------------------------------------------------------------------------\n\n/** Write a tiny proxy autoconfig (PAC) file so iOS users can paste a single URL into Auto Proxy. */\nexport function emitPacFile(proxyHost: string, proxyPort: number): string {\n const path = join(tmpdir(), `testrelic-proxy-${Date.now()}.pac`);\n const content = `function FindProxyForURL(url, host) { return \"PROXY ${proxyHost}:${proxyPort}; DIRECT\"; }`;\n writeFileSync(path, content, 'utf-8');\n return path;\n}\n\nexport function cleanupPacFile(path: string): void {\n try { if (existsSync(path)) unlinkSync(path); } catch { /* ignore */ }\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, MaestroPlatform, ResolvedNetworkCaptureConfig } from './types.js';\nimport { startNetworkProxy, type NetworkProxyHandle } from './network-proxy.js';\nimport { setUpDeviceProxy, type DeviceProxyHandle } from './device-proxy-setup.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 /** Directory where the network proxy wrote JSONL records (null when capture was disabled or failed to start). */\n readonly networkJsonlDir: string | null;\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\n/**\n * Map the user-supplied platform string to the internal MaestroPlatform enum.\n * Used for device proxy setup so we route to the right adb/xcrun helper.\n */\nfunction normalisePlatform(p: string | undefined): MaestroPlatform {\n switch ((p ?? '').toLowerCase()) {\n case 'android': return 'android';\n case 'ios': return 'ios';\n case 'web': return 'web';\n default: return 'unknown';\n }\n}\n\nexport interface RunMaestroExtras {\n /** Optional network-capture config. When `enabled`, mitmproxy is spawned and the device is configured to route through it. */\n readonly network?: ResolvedNetworkCaptureConfig;\n}\n\nfunction runMaestroProc(\n args: string[],\n junitPath: string,\n testOutputDir: string,\n debugOutputDir: string,\n networkJsonlDir: string | null,\n quiet: boolean | undefined,\n onClose: () => Promise<void>,\n): Promise<MaestroRunResult> {\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 (!quiet) process.stdout.write(chunk);\n });\n\n proc.stderr.on('data', (chunk: Buffer) => {\n stderrChunks.push(chunk);\n if (!quiet) process.stderr.write(chunk);\n });\n\n proc.on('close', async (code) => {\n // Run the user-supplied cleanup hook *before* resolving so any artifacts\n // the proxy/device still need to write get a chance to flush.\n try { await onClose(); } catch { /* ignore */ }\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 networkJsonlDir,\n });\n });\n\n proc.on('error', async (err) => {\n try { await onClose(); } catch { /* ignore */ }\n resolvePromise({\n exitCode: 127,\n junitPath,\n testOutputDir,\n debugOutputDir,\n stdout: '',\n stderr: `Failed to start maestro CLI: ${err.message}`,\n networkJsonlDir,\n });\n });\n });\n}\n\nexport async function runMaestro(\n options: MaestroTestOptions,\n extras: RunMaestroExtras = {},\n): 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 // Start mitmproxy + device proxy setup BEFORE maestro spawns so the very\n // first HTTP call from the app under test lands in the JSONL. When capture\n // is disabled or unavailable, both handles are null/no-op.\n let proxyHandle: NetworkProxyHandle | null = null;\n let deviceHandle: DeviceProxyHandle | null = null;\n const networkConfig = extras.network;\n if (networkConfig?.enabled) {\n const networkDir = join(tempDir, 'network');\n proxyHandle = await startNetworkProxy({\n config: networkConfig,\n outputDir: networkDir,\n verbose: !options.quiet,\n });\n if (proxyHandle) {\n deviceHandle = await setUpDeviceProxy({\n platform: normalisePlatform(options.platform),\n deviceId: options.device,\n proxyHost: proxyHandle.host,\n proxyPort: proxyHandle.port,\n skipCertInstall: networkConfig.skipCertInstall,\n quiet: options.quiet,\n });\n }\n }\n\n const networkJsonlDir = proxyHandle?.outputDir ?? null;\n\n const cleanup = async (): Promise<void> => {\n // Small drain window so in-flight responses get flushed by the addon.\n if (proxyHandle) {\n await new Promise((r) => setTimeout(r, 500));\n }\n if (deviceHandle) {\n try { await deviceHandle.teardown(); } catch { /* ignore */ }\n }\n if (proxyHandle) {\n try { await proxyHandle.stop(); } catch { /* ignore */ }\n }\n };\n\n return runMaestroProc(args, junitPath, testOutputDir, debugOutputDir, networkJsonlDir, options.quiet, cleanup);\n}\n","/**\n * Lightweight inline redactor for captured API calls.\n *\n * Mirrors the policy used by `@testrelic/playwright-analytics` (case-insensitive\n * header match, recursive body-field match at any JSON depth). Kept inline here\n * to avoid a cross-package change in this PR — when both packages converge we'll\n * promote a shared implementation to `@testrelic/core/api-redactor`.\n */\n\nimport type { ApiCallRecord } from '@testrelic/core';\n\nconst REDACTED = '[REDACTED]';\n\nfunction redactHeaders(\n headers: Record<string, string> | null,\n redactList: readonly string[],\n): Record<string, string> | null {\n if (!headers) return null;\n const lower = new Set(redactList.map((h) => h.toLowerCase()));\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(headers)) {\n out[k] = lower.has(k.toLowerCase()) ? REDACTED : v;\n }\n return out;\n}\n\nfunction redactJsonBody(value: unknown, fields: ReadonlySet<string>): unknown {\n if (value === null || typeof value !== 'object') return value;\n if (Array.isArray(value)) return value.map((v) => redactJsonBody(v, fields));\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n out[k] = fields.has(k) ? REDACTED : redactJsonBody(v, fields);\n }\n return out;\n}\n\nfunction redactBody(\n body: string | null,\n fields: readonly string[],\n): string | null {\n if (!body || fields.length === 0) return body;\n try {\n const parsed = JSON.parse(body);\n const redacted = redactJsonBody(parsed, new Set(fields));\n return JSON.stringify(redacted);\n } catch {\n // Non-JSON body — return verbatim. Form-encoded redaction is a follow-up.\n return body;\n }\n}\n\nexport function redactApiCall(\n call: ApiCallRecord,\n redactHeadersList: readonly string[],\n redactBodyFields: readonly string[],\n): ApiCallRecord {\n return {\n ...call,\n requestHeaders: redactHeaders(call.requestHeaders, redactHeadersList),\n responseHeaders: redactHeaders(call.responseHeaders, redactHeadersList),\n requestBody: redactBody(call.requestBody, redactBodyFields),\n responseBody: call.isBinary ? call.responseBody : redactBody(call.responseBody, redactBodyFields),\n };\n}\n\nexport function matchesUrlFilter(\n url: string,\n include: readonly (string | RegExp)[],\n exclude: readonly (string | RegExp)[],\n): boolean {\n const matches = (pat: string | RegExp): boolean =>\n typeof pat === 'string' ? url.includes(pat) : pat.test(url);\n if (exclude.some(matches)) return false;\n if (include.length === 0) return true;\n return include.some(matches);\n}\n","/**\n * Parsers for captured network traffic.\n *\n * Two input formats are supported:\n *\n * 1. mitmproxy JSONL — emitted by the bundled `testrelic_capture.py` addon\n * (`<outputDir>/network/<flow-or-suite>.jsonl`). Each line is already an\n * ApiCallRecord (the addon does its own redaction so secrets never touch\n * disk).\n *\n * 2. HAR — user-supplied (Charles, Proxyman, mitmweb, etc.). Mapped from\n * `entries[].request` + `entries[].response` into ApiCallRecord. Useful\n * for environments where spawning mitmproxy is impractical (TLS-pinned\n * apps, locked-down devices, CI runners without sudo).\n *\n * Both parsers tolerate malformed input — they skip the offending entry\n * rather than throwing, because losing one record shouldn't break the whole\n * report.\n */\n\nimport { readFileSync, existsSync, readdirSync } from 'node:fs';\nimport { join, basename, extname } from 'node:path';\nimport type { ApiCallRecord } from '@testrelic/core';\nimport { redactApiCall, matchesUrlFilter } from '../api-redactor.js';\nimport type { ResolvedNetworkCaptureConfig } from '../types.js';\n\n// ---------------------------------------------------------------------------\n// mitmproxy JSONL\n// ---------------------------------------------------------------------------\n\nfunction parseJsonlLine(line: string): ApiCallRecord | null {\n const trimmed = line.trim();\n if (!trimmed) return null;\n try {\n const obj = JSON.parse(trimmed) as Partial<ApiCallRecord>;\n if (typeof obj.url !== 'string' || typeof obj.method !== 'string') return null;\n return {\n id: obj.id ?? `api-call-${Date.now()}`,\n timestamp: obj.timestamp ?? new Date().toISOString(),\n method: obj.method,\n url: obj.url,\n requestHeaders: obj.requestHeaders ?? null,\n requestBody: obj.requestBody ?? null,\n responseStatusCode: obj.responseStatusCode ?? null,\n responseStatusText: obj.responseStatusText ?? null,\n responseHeaders: obj.responseHeaders ?? null,\n responseBody: obj.responseBody ?? null,\n responseTimeMs: typeof obj.responseTimeMs === 'number' ? obj.responseTimeMs : 0,\n isBinary: obj.isBinary ?? false,\n error: obj.error ?? null,\n };\n } catch {\n return null;\n }\n}\n\nexport function parseNetworkJsonl(\n content: string,\n config: ResolvedNetworkCaptureConfig,\n): ApiCallRecord[] {\n const records: ApiCallRecord[] = [];\n for (const line of content.split(/\\r?\\n/)) {\n const rec = parseJsonlLine(line);\n if (!rec) continue;\n if (!matchesUrlFilter(rec.url, config.includeUrls, config.excludeUrls)) continue;\n records.push(redactApiCall(rec, config.redactHeaders, config.redactBodyFields));\n }\n return records;\n}\n\nexport function parseNetworkJsonlFile(\n filePath: string,\n config: ResolvedNetworkCaptureConfig,\n): ApiCallRecord[] {\n try {\n return parseNetworkJsonl(readFileSync(filePath, 'utf-8'), config);\n } catch {\n return [];\n }\n}\n\nexport function discoverNetworkJsonlFiles(dir: string): string[] {\n if (!existsSync(dir)) return [];\n try {\n return readdirSync(dir, { recursive: true })\n .map(String)\n .filter((f) => f.endsWith('.jsonl'))\n .map((f) => join(dir, f));\n } catch {\n return [];\n }\n}\n\n// ---------------------------------------------------------------------------\n// HAR\n// ---------------------------------------------------------------------------\n\ninterface HarHeader { name: string; value: string }\ninterface HarPostData { mimeType?: string; text?: string; params?: { name: string; value?: string }[] }\ninterface HarRequest { method?: string; url?: string; headers?: HarHeader[]; postData?: HarPostData }\ninterface HarContent { size?: number; mimeType?: string; text?: string; encoding?: string }\ninterface HarResponse { status?: number; statusText?: string; headers?: HarHeader[]; content?: HarContent }\ninterface HarEntry {\n startedDateTime?: string;\n time?: number;\n request?: HarRequest;\n response?: HarResponse;\n _resourceType?: string;\n}\ninterface HarLog { entries?: HarEntry[] }\ninterface HarFile { log?: HarLog }\n\nfunction harHeadersToRecord(headers: HarHeader[] | undefined): Record<string, string> | null {\n if (!headers || headers.length === 0) return null;\n const out: Record<string, string> = {};\n for (const h of headers) {\n if (typeof h.name === 'string' && typeof h.value === 'string') out[h.name] = h.value;\n }\n return Object.keys(out).length > 0 ? out : null;\n}\n\nfunction harBodyToString(postData: HarPostData | undefined): string | null {\n if (!postData) return null;\n if (typeof postData.text === 'string') return postData.text;\n if (Array.isArray(postData.params)) {\n return postData.params\n .map((p) => `${encodeURIComponent(p.name)}=${encodeURIComponent(p.value ?? '')}`)\n .join('&');\n }\n return null;\n}\n\nfunction isBinaryMime(mime: string | undefined): boolean {\n if (!mime) return false;\n return /^(image|audio|video|application\\/(octet-stream|pdf|zip|protobuf))/.test(mime);\n}\n\nfunction harEntryToRecord(entry: HarEntry, index: number): ApiCallRecord | null {\n const req = entry.request;\n const res = entry.response;\n if (!req?.method || !req.url) return null;\n\n const content = res?.content;\n const isBinary = isBinaryMime(content?.mimeType);\n return {\n id: `har-call-${index}`,\n timestamp: entry.startedDateTime ?? new Date().toISOString(),\n method: req.method,\n url: req.url,\n requestHeaders: harHeadersToRecord(req.headers),\n requestBody: harBodyToString(req.postData),\n responseStatusCode: typeof res?.status === 'number' && res.status > 0 ? res.status : null,\n responseStatusText: res?.statusText ?? null,\n responseHeaders: harHeadersToRecord(res?.headers),\n responseBody: content?.text ?? null,\n responseTimeMs: typeof entry.time === 'number' ? entry.time : 0,\n isBinary,\n error: null,\n };\n}\n\nexport function parseHar(\n content: string,\n config: ResolvedNetworkCaptureConfig,\n): ApiCallRecord[] {\n let parsed: HarFile;\n try {\n parsed = JSON.parse(content) as HarFile;\n } catch {\n return [];\n }\n const entries = parsed.log?.entries ?? [];\n const records: ApiCallRecord[] = [];\n entries.forEach((entry, i) => {\n const rec = harEntryToRecord(entry, i);\n if (!rec) return;\n if (!matchesUrlFilter(rec.url, config.includeUrls, config.excludeUrls)) return;\n records.push(redactApiCall(rec, config.redactHeaders, config.redactBodyFields));\n });\n return records;\n}\n\nexport function parseHarFile(\n filePath: string,\n config: ResolvedNetworkCaptureConfig,\n): ApiCallRecord[] {\n try {\n return parseHar(readFileSync(filePath, 'utf-8'), config);\n } catch {\n return [];\n }\n}\n\n// ---------------------------------------------------------------------------\n// Per-flow binding\n// ---------------------------------------------------------------------------\n\nexport interface FlowWindow {\n readonly testId: string;\n readonly startedAt: string;\n readonly completedAt: string;\n}\n\n/**\n * Bind a flat list of captured API calls to per-flow buckets by timestamp\n * window. Calls whose timestamp falls outside every window are bucketed\n * against the closest flow so the data isn't silently lost.\n */\nexport function bindApiCallsToFlows(\n calls: readonly ApiCallRecord[],\n flows: readonly FlowWindow[],\n): Map<string, ApiCallRecord[]> {\n const buckets = new Map<string, ApiCallRecord[]>();\n for (const f of flows) buckets.set(f.testId, []);\n if (calls.length === 0 || flows.length === 0) return buckets;\n\n const windows = flows.map((f) => ({\n testId: f.testId,\n startMs: Date.parse(f.startedAt),\n endMs: Date.parse(f.completedAt),\n }));\n\n for (const call of calls) {\n const t = Date.parse(call.timestamp);\n if (!Number.isFinite(t)) continue;\n\n // Direct hit first.\n const hit = windows.find((w) => Number.isFinite(w.startMs) && Number.isFinite(w.endMs) && t >= w.startMs && t <= w.endMs);\n if (hit) {\n buckets.get(hit.testId)!.push(call);\n continue;\n }\n\n // Closest-window fallback.\n let best: { testId: string; dist: number } | null = null;\n for (const w of windows) {\n if (!Number.isFinite(w.startMs) || !Number.isFinite(w.endMs)) continue;\n const dist = t < w.startMs ? w.startMs - t : t - w.endMs;\n if (!best || dist < best.dist) best = { testId: w.testId, dist };\n }\n if (best) buckets.get(best.testId)!.push(call);\n }\n\n return buckets;\n}\n\n/**\n * Resolve all captured network records into a map keyed by flow `testId`.\n * Reads from (in order): mitmproxy JSONL files, an optional HAR file.\n * Returns an empty map when network capture is disabled or nothing was found.\n */\nexport function collectNetworkByFlow(\n jsonlDir: string | null,\n config: ResolvedNetworkCaptureConfig,\n flows: readonly FlowWindow[],\n): Map<string, ApiCallRecord[]> {\n if (!config.enabled && !config.harPath) return new Map();\n\n const all: ApiCallRecord[] = [];\n\n if (jsonlDir) {\n for (const file of discoverNetworkJsonlFiles(jsonlDir)) {\n all.push(...parseNetworkJsonlFile(file, config));\n }\n }\n if (config.harPath && existsSync(config.harPath)) {\n all.push(...parseHarFile(config.harPath, config));\n }\n\n if (all.length === 0) return new Map();\n return bindApiCallsToFlows(all, flows);\n}\n\n/** Re-indexes IDs sequentially per-flow for predictable cloud-side ordering. */\nexport function reindexFlowCalls(calls: readonly ApiCallRecord[]): ApiCallRecord[] {\n return calls.map((c, i) => ({ ...c, id: `api-call-${i}` }));\n}\n\n// Suppress unused-import warning when a downstream consumer doesn't use `basename`/`extname`.\nvoid basename; void extname;\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 ApiCallRecord,\n ActionStep,\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 * Per-flow captured API calls. The cloud platform's `normalizeApiCall` (in\n * `server/src/utils/artifact-normalizer.ts`) accepts this shape directly and\n * stores it as `network.json`. Surfaced in the Test Detail Network panel and\n * via Ask AI's `query_network_logs` tool — works for free, no cloud changes.\n */\n readonly apiCalls?: readonly ApiCallRecord[];\n /**\n * Per-flow ActionStep array (mapped from Maestro `commands` + `assertions`,\n * sorted by timestamp + sequenceNumber, with `retry`/`repeat` children\n * nested under their wrapper and `videoOffset` computed from\n * `flow.startedAt`). The cloud server's per-test ingest path\n * (`server/src/services/run.service.ts:uploadTest` → `enrichTimeline` →\n * OpenObserve `timeline_{orgId}` + RustFS `actions.json`) consumes this\n * and powers the Session Workspace's Steps + Assertions tabs and Ask AI's\n * `query_test_steps` tool.\n *\n * The same array also rides on `timeline[i].tests[j].actions` in the\n * upload's `timeline` field — but the cloud's per-test ingest path keys off\n * `tests[].actions`. Without this field, Maestro test detail pages show\n * \"No step data available\" even though the run summary reports correct step\n * counts. See issue #77.\n */\n readonly actions?: readonly ActionStep[];\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 /** Run-level test type — always 'mobile' for Maestro runs. */\n readonly testType?: 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 testType: 'mobile',\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 testType?: 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 testType?: string;\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, ApiCallRecord, ActionStep } 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 { collectNetworkByFlow, reindexFlowCalls, type FlowWindow } from './parsers/network-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 */\n/**\n * Strip Maestro's filename decoration. The CLI writes\n * commands-(Launch app and verify catalog).json\n * → we want `launch app and verify catalog` so the key matches `flowBase`\n * (which is derived from JUnit's `classname` / flow name in `commandsForFlow`).\n */\nfunction normaliseCommandFileName(filePath: string): string {\n return basename(filePath)\n .replace(/^commands[-_]?/i, '')\n .replace(/\\.json$/i, '')\n .replace(/^\\(/, '')\n .replace(/\\)$/, '')\n .toLowerCase();\n}\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 = normaliseCommandFileName(filePath);\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 => normaliseCommandFileName(p);\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 parsed Maestro flow into the `TestResultForUpload` shape the\n * cloud's per-test ingest path (`POST /runs/:runId/tests` → OpenObserve\n * `timeline_{orgId}` + RustFS `actions.json`) expects. This is what makes\n * individual flows visible as test cases — with their step / log / network\n * artifacts — in the TestRelic Session Workspace.\n *\n * `actions` is read from the already-built timeline TestResult (sorted +\n * children-nested + videoOffset-stamped). The same array is referenced from\n * both `timeline[i].tests[0].actions` and `tests[].actions` in the upload\n * payload — no shape diverges, no duplicated build logic. See issue #77.\n *\n * Exported only for unit tests; not part of the package's stable surface.\n */\nexport function flowToTestResult(\n flow: MaestroFlowResult,\n apiCalls: readonly ApiCallRecord[] | undefined,\n actions: readonly ActionStep[] | null | undefined,\n): 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 ...(apiCalls && apiCalls.length > 0 ? { apiCalls } : {}),\n ...(actions && actions.length > 0 ? { actions } : {}),\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 * Directory containing JSONL files emitted by `network-proxy.ts` (mitmproxy\n * addon). When omitted, the orchestrator still picks up `config.network.harPath`\n * if set. Unrelated to Maestro's own debug output.\n */\n readonly networkJsonlDir?: string | null;\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 // Anchor wall-clock-only log timestamps to the file's mtime date when\n // available, so logs from a previous day's run don't get re-stamped to today.\n for (const logFile of logFiles) {\n // Pass the file mtime as a Date so parseLogContent can produce TZ-correct\n // UTC ISO timestamps for `HH:MM:SS` lines (Maestro writes local time\n // without an offset; naive string-concat would shift every entry by the\n // local UTC offset and bucket them outside every flow window).\n let mtimeDate: Date | undefined;\n try {\n const { statSync } = await import('node:fs');\n mtimeDate = statSync(logFile).mtime;\n } catch { /* parser falls back to today */ }\n const entries = parseLogFile(logFile, mtimeDate);\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\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 // Derive the flow window from the EARLIEST command timestamp when\n // available. The orchestrator's own `startedAt` is the moment the\n // report runner started — typically *after* maestro finished, so\n // using it for the flow window bounds the log bucketer (and\n // ActionStep videoOffset clamp) to a window that excludes every real\n // log line / step. We fall back to `startedAt + 0` only when the\n // commands JSON had no parseable timestamps (e.g. legacy fixtures).\n const allFlowCmds = [...perFlowCommands, ...perFlowAssertions];\n const earliestCmdMs = allFlowCmds.reduce<number>((min, c) => {\n const t = Date.parse(c.timestamp);\n return Number.isFinite(t) && t < min ? t : min;\n }, Number.POSITIVE_INFINITY);\n const flowStartedAt = Number.isFinite(earliestCmdMs)\n ? new Date(earliestCmdMs).toISOString()\n : startedAt;\n const flowCompletedAt = new Date(new Date(flowStartedAt).getTime() + durationMs).toISOString();\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 // Bind captured network traffic (mitmproxy JSONL and/or HAR) to each flow\n // by timestamp window so the cloud platform's Test Detail Network panel and\n // Ask AI's query_network_logs tool see the right per-flow API calls.\n const apiCallsByFlow = (() => {\n if (!config.network?.enabled && !config.network?.harPath) return undefined;\n const windows: FlowWindow[] = flowResults.map((f) => ({\n testId: `${f.flowFile}::${f.flowName}`,\n startedAt: f.startedAt,\n completedAt: f.completedAt,\n }));\n const bound = collectNetworkByFlow(input.networkJsonlDir ?? null, config.network, windows);\n if (bound.size === 0) return undefined;\n const remapped = new Map<string, readonly ApiCallRecord[]>();\n for (const [k, v] of bound) remapped.set(k, reindexFlowCalls(v));\n return remapped;\n })();\n\n const timeline: TimelineEntry[] = buildTimeline(flowResults, apiCallsByFlow);\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 //\n // Actions are sourced from the already-built timeline so the\n // sorting/children-nesting/videoOffset stays consistent between the\n // `timeline[i].tests[j].actions` and `tests[].actions` paths in the\n // upload (issue #77). Maestro emits one flow per TimelineEntry / per\n // testCase, so the i-th flow corresponds to timeline[i].tests[0].\n const testsForUpload: TestResultForUpload[] = flowResults.map((f, i) => {\n const testId = `${f.flowFile}::${f.flowName}`;\n const tlTest = timeline[i]?.tests?.[0];\n return flowToTestResult(f, apiCallsByFlow?.get(testId), tlTest?.actions ?? null);\n });\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"]}