@resolveio/server-lib 22.3.84 → 22.3.85

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@resolveio/server-lib",
3
- "version": "22.3.84",
3
+ "version": "22.3.85",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "package": "./build_package.sh",
@@ -27,8 +27,8 @@ function buildResolveIORunnerQaAuthBootstrapScript(options) {
27
27
  'const viewportHeight = Number(process.env.RESOLVEIO_RUNNER_QA_VIEWPORT_HEIGHT || process.env.RESOLVEIO_SUPPORT_QA_VIEWPORT_HEIGHT || 1080);',
28
28
  'const startupTimeoutMs = Math.max(1000, Number(process.env.RESOLVEIO_RUNNER_QA_ANGULAR_STARTUP_TIMEOUT_SECONDS || process.env.RESOLVEIO_SUPPORT_QA_ANGULAR_STARTUP_TIMEOUT_SECONDS || 900) * 1000);',
29
29
  'const resultPath = path.join(artifactDir, "auth-bootstrap-result.json");',
30
- 'const readyScreenshotPath = path.join(artifactDir, "auth-bootstrap-ready.png");',
31
- 'const failureScreenshotPath = path.join(artifactDir, "auth-bootstrap-failed.png");',
30
+ 'const readyScreenshotPath = path.join(artifactDir, "auth-bootstrap-ready.jpg");',
31
+ 'const failureScreenshotPath = path.join(artifactDir, "auth-bootstrap-failed.jpg");',
32
32
  '',
33
33
  'function stripTrailingSlash(value) {',
34
34
  ' return String(value || "").replace(/\\/+$/, "");',
@@ -643,12 +643,12 @@ function buildResolveIORunnerQaAuthBootstrapScript(options) {
643
643
  '',
644
644
  '(async () => {',
645
645
  ' fs.mkdirSync(artifactDir, { recursive: true });',
646
+ ' await waitForHttpReady(clientUrl, "QA client");',
647
+ ' await waitForHttpReady(serverUrl, "QA server");',
646
648
  ' const puppeteer = requirePuppeteer();',
647
649
  ' const browser = await launchBrowser(puppeteer);',
648
650
  ' let page;',
649
651
  ' try {',
650
- ' await waitForHttpReady(clientUrl, "QA client");',
651
- ' await waitForHttpReady(serverUrl, "QA server");',
652
652
  ' page = await browser.newPage();',
653
653
  ' await page.setViewport({ width: viewportWidth, height: viewportHeight });',
654
654
  ' page.on("console", (msg) => {',
@@ -665,7 +665,7 @@ function buildResolveIORunnerQaAuthBootstrapScript(options) {
665
665
  ' await waitForAuthenticatedApp(page);',
666
666
  ' const finalPage = await pageSummary(page);',
667
667
  ' await assertSummaryOnTargetRoute(finalPage);',
668
- ' await page.screenshot({ path: readyScreenshotPath, fullPage: true });',
668
+ ' await page.screenshot({ path: readyScreenshotPath, type: "jpeg", quality: 82, fullPage: false });',
669
669
  ' const summary = {',
670
670
  ' status: "pass",',
671
671
  ' clientUrl,',
@@ -682,7 +682,7 @@ function buildResolveIORunnerQaAuthBootstrapScript(options) {
682
682
  ' let summary = { status: "fail", clientUrl, serverUrl, targetRoute, screenshot: failureScreenshotPath, error: error && error.stack || String(error) };',
683
683
  ' try {',
684
684
  ' if (page) {',
685
- ' await page.screenshot({ path: failureScreenshotPath, fullPage: true });',
685
+ ' await page.screenshot({ path: failureScreenshotPath, type: "jpeg", quality: 82, fullPage: false });',
686
686
  ' summary.page = await pageSummary(page);',
687
687
  ' }',
688
688
  ' } catch (screenshotError) {',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/util/ai-runner-qa-auth.ts"],"names":[],"mappings":"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","file":"ai-runner-qa-auth.js","sourcesContent":["export interface ResolveIORunnerQaAuthBootstrapScriptOptions {\n\tdefaultUsername?: string;\n\tdefaultPassword?: string;\n}\n\nexport function buildResolveIORunnerQaAuthBootstrapScript(options: ResolveIORunnerQaAuthBootstrapScriptOptions = {}): string {\n\tconst defaultUsername = options.defaultUsername || 'admin';\n\tconst defaultPassword = options.defaultPassword || '';\n\treturn [\n\t\t'#!/usr/bin/env node',\n\t\t\"'use strict';\",\n\t\t'',\n\t\t'const fs = require(\"fs\");',\n\t\t'const crypto = require(\"crypto\");',\n\t\t'const http = require(\"http\");',\n\t\t'const https = require(\"https\");',\n\t\t'const path = require(\"path\");',\n\t\t'',\n\t\t'const projectRoot = path.resolve(process.argv[2] || process.cwd());',\n\t\t'const routeArg = process.argv[3] || process.env.RESOLVEIO_RUNNER_QA_TARGET_ROUTE || process.env.RESOLVEIO_SUPPORT_QA_TARGET_ROUTE || process.env.RESOLVEIO_SUPPORT_QA_LAST_URL || \"/\";',\n\t\t'const targetRoute = routeArg.startsWith(\"/\") ? routeArg : `/${routeArg}`;',\n\t\t'const clientUrl = stripTrailingSlash(process.env.RESOLVEIO_RUNNER_QA_CLIENT_URL || process.env.RESOLVEIO_SUPPORT_QA_CLIENT_URL || `http://localhost:${process.env.RESOLVEIO_SUPPORT_QA_CLIENT_PORT || \"4200\"}`);',\n\t\t'const serverUrl = stripTrailingSlash(process.env.RESOLVEIO_RUNNER_QA_SERVER_URL || process.env.RESOLVEIO_SUPPORT_QA_SERVER_URL || \"http://localhost:8080\");',\n\t\t`const username = process.env.RESOLVEIO_RUNNER_QA_USERNAME || process.env.RESOLVEIO_SUPPORT_QA_USERNAME || ${JSON.stringify(defaultUsername)};`,\n\t\t`const password = process.env.RESOLVEIO_RUNNER_QA_PASSWORD || process.env.RESOLVEIO_SUPPORT_QA_PASSWORD || ${JSON.stringify(defaultPassword)};`,\n\t\t'const artifactDir = path.resolve(process.env.RESOLVEIO_RUNNER_QA_ARTIFACT_DIR || process.env.RESOLVEIO_SUPPORT_QA_ARTIFACT_DIR || path.join(projectRoot, \"qa-artifacts\"));',\n\t\t'const viewportWidth = Number(process.env.RESOLVEIO_RUNNER_QA_VIEWPORT_WIDTH || process.env.RESOLVEIO_SUPPORT_QA_VIEWPORT_WIDTH || 1920);',\n\t\t'const viewportHeight = Number(process.env.RESOLVEIO_RUNNER_QA_VIEWPORT_HEIGHT || process.env.RESOLVEIO_SUPPORT_QA_VIEWPORT_HEIGHT || 1080);',\n\t\t'const startupTimeoutMs = Math.max(1000, Number(process.env.RESOLVEIO_RUNNER_QA_ANGULAR_STARTUP_TIMEOUT_SECONDS || process.env.RESOLVEIO_SUPPORT_QA_ANGULAR_STARTUP_TIMEOUT_SECONDS || 900) * 1000);',\n\t\t'const resultPath = path.join(artifactDir, \"auth-bootstrap-result.json\");',\n\t\t'const readyScreenshotPath = path.join(artifactDir, \"auth-bootstrap-ready.png\");',\n\t\t'const failureScreenshotPath = path.join(artifactDir, \"auth-bootstrap-failed.png\");',\n\t\t'',\n\t\t'function stripTrailingSlash(value) {',\n\t\t'\treturn String(value || \"\").replace(/\\\\/+$/, \"\");',\n\t\t'}',\n\t\t'',\n\t\t'function isLocalhostUrl(value) {',\n\t\t'\ttry {',\n\t\t'\t\tconst parsed = new URL(value);',\n\t\t'\t\treturn [\"localhost\", \"127.0.0.1\", \"::1\"].includes(parsed.hostname);',\n\t\t'\t}',\n\t\t'\tcatch (error) {',\n\t\t'\t\treturn false;',\n\t\t'\t}',\n\t\t'}',\n\t\t'',\n\t\t'function resolveLocalMongoUrl() {',\n\t\t'\tconst localQaMongoUrl = process.env.RESOLVEIO_RUNNER_QA_MONGO_URL || process.env.RESOLVEIO_SUPPORT_QA_MONGO_URL || `mongodb://127.0.0.1:${process.env.RESOLVEIO_SUPPORT_QA_MONGO_PORT || \"3001\"}/resolveio?directConnection=true`;',\n\t\t'\tif (isLocalhostUrl(serverUrl)) {',\n\t\t'\t\treturn localQaMongoUrl;',\n\t\t'\t}',\n\t\t'\treturn process.env.MONGO_URL || localQaMongoUrl;',\n\t\t'}',\n\t\t'',\n\t\t'function requireMongoClient() {',\n\t\t'\tconst candidates = [',\n\t\t'\t\tpath.join(projectRoot, \"server\", \"node_modules\", \"mongodb\"),',\n\t\t'\t\tpath.join(projectRoot, \"node_modules\", \"mongodb\"),',\n\t\t'\t\tpath.join(process.cwd(), \"server\", \"node_modules\", \"mongodb\"),',\n\t\t'\t\tpath.join(process.cwd(), \"node_modules\", \"mongodb\"),',\n\t\t'\t\t\"mongodb\"',\n\t\t'\t];',\n\t\t'\tfor (const candidate of candidates) {',\n\t\t'\t\ttry { return require(candidate).MongoClient; }',\n\t\t'\t\tcatch (error) {}',\n\t\t'\t}',\n\t\t'\tthrow new Error(\"Unable to require mongodb from project/server node_modules or global resolution\");',\n\t\t'}',\n\t\t'',\n\t\t'function buildDefaultQaUserSettings() {',\n\t\t'\treturn {',\n\t\t'\t\ttable_color: \"#3b3ee3\",',\n\t\t'\t\ttable_font_color: \"#ffffff\",',\n\t\t'\t\tsecondary_table_color: \"#87ceeb\",',\n\t\t'\t\tsecondary_table_font_color: \"#000000\",',\n\t\t'\t\ttertiary_table_color: \"#ff4500\",',\n\t\t'\t\ttertiary_table_font_color: \"#000000\",',\n\t\t'\t\tfont_size: 12,',\n\t\t'\t\tcollapsable_menu: true,',\n\t\t'\t\tentries_per_page: \"25\",',\n\t\t'\t\twarning_color: \"#ffc107\",',\n\t\t'\t\twarning_font_color: \"#000000\",',\n\t\t'\t\twarning_hover_color: \"#e0a800\",',\n\t\t'\t\tsuccess_color: \"#28a745\",',\n\t\t'\t\tsuccess_font_color: \"#ffffff\",',\n\t\t'\t\tsuccess_hover_color: \"#218838\",',\n\t\t'\t\tdanger_color: \"#dc3545\",',\n\t\t'\t\tdanger_font_color: \"#ffffff\",',\n\t\t'\t\tdanger_hover_color: \"#c82333\",',\n\t\t'\t\tinfo_color: \"#17a2b8\",',\n\t\t'\t\tinfo_font_color: \"#ffffff\",',\n\t\t'\t\tinfo_hover_color: \"#138496\",',\n\t\t'\t\tprimary_color: \"#007bff\",',\n\t\t'\t\tprimary_font_color: \"#ffffff\",',\n\t\t'\t\tprimary_hover_color: \"#0069d9\",',\n\t\t'\t\tsecondary_color: \"#868e96\",',\n\t\t'\t\tsecondary_font_color: \"#ffffff\",',\n\t\t'\t\tsecondary_hover_color: \"#5a6268\",',\n\t\t'\t\trouting_preference: \"\",',\n\t\t'\t\topening_route: targetRoute,',\n\t\t'\t\trio_select_search_mode: \"exact\"',\n\t\t'\t};',\n\t\t'}',\n\t\t'',\n\t\t'function normalizeQaUserForBrowser(user) {',\n\t\t'\tconst normalized = { ...(user || {}) };',\n\t\t'\tnormalized.other = {',\n\t\t'\t\t...(normalized.other || {}),',\n\t\t'\t\tyards: Array.isArray(normalized.other && normalized.other.yards) ? normalized.other.yards : [],',\n\t\t'\t\ttour_completed: true,',\n\t\t'\t\ttook_tour: true,',\n\t\t'\t\tcore_tour_completed: true,',\n\t\t'\t\twelcome_tour_completed: true,',\n\t\t'\t\ttop_navigation_tour_completed: true,',\n\t\t'\t\tuser_settings_tour_completed: true',\n\t\t'\t};',\n\t\t'\tnormalized.settings = {',\n\t\t'\t\t...buildDefaultQaUserSettings(),',\n\t\t'\t\t...(normalized.settings || {}),',\n\t\t'\t\topening_route: targetRoute',\n\t\t'\t};',\n\t\t'\treturn normalized;',\n\t\t'}',\n\t\t'',\n\t\t'async function ensureLocalQaUser() {',\n\t\t'\tif (!isLocalhostUrl(serverUrl) || process.env.RESOLVEIO_RUNNER_QA_DISABLE_LOCAL_USER_REPAIR === \"true\" || process.env.RESOLVEIO_SUPPORT_QA_DISABLE_LOCAL_USER_REPAIR === \"true\") {',\n\t\t'\t\treturn false;',\n\t\t'\t}',\n\t\t'\tconst MongoClient = requireMongoClient();',\n\t\t'\tconst client = new MongoClient(resolveLocalMongoUrl());',\n\t\t'\tawait client.connect();',\n\t\t'\ttry {',\n\t\t'\t\tconst db = client.db();',\n\t\t'\t\tconst users = db.collection(\"users\");',\n\t\t'\t\tconst now = new Date();',\n\t\t'\t\tconst salt = crypto.randomBytes(32).toString(\"hex\");',\n\t\t'\t\tconst hash = crypto.pbkdf2Sync(password, salt, 25000, 512, \"sha256\").toString(\"hex\");',\n\t\t'\t\tconst existing = await users.findOne({ $or: [{ username }, { email: username }] }, { projection: { _id: 1 } });',\n\t\t'\t\tconst defaultSettings = buildDefaultQaUserSettings();',\n\t\t'\t\tconst defaultOther = { yards: [], date_picker_day_start: \"S\", tour_completed: true, took_tour: true, core_tour_completed: true, welcome_tour_completed: true, top_navigation_tour_completed: true, user_settings_tour_completed: true, tour_completed_at: now.toISOString() };',\n\t\t'\t\tconst baseUser = {',\n\t\t'\t\t\t__v: 0,',\n\t\t'\t\t\tusername,',\n\t\t'\t\t\temail: username.includes(\"@\") ? username : \"dev@resolveio.com\",',\n\t\t'\t\t\tfullname: \"QA Admin\",',\n\t\t'\t\t\troles: { super_admin: true, approvals: [], groups: [], notifications: [], miscs: [] },',\n\t\t'\t\t\tactive: true,',\n\t\t'\t\t\tother: defaultOther,',\n\t\t'\t\t\tsettings: defaultSettings,',\n\t\t'\t\t\tphonenumber: \"\",',\n\t\t'\t\t\treadonly: false,',\n\t\t'\t\t\tis_customer: false,',\n\t\t'\t\t\thash,',\n\t\t'\t\t\tsalt,',\n\t\t'\t\t\tattempts: 0,',\n\t\t'\t\t\tservices: {},',\n\t\t'\t\t\tupdatedAt: now',\n\t\t'\t\t};',\n\t\t'\t\tconst qaUserFilter = {',\n\t\t'\t\t\t$or: [',\n\t\t'\t\t\t\t{ username },',\n\t\t'\t\t\t\t{ email: username },',\n\t\t'\t\t\t\t{ username: \"admin\" },',\n\t\t'\t\t\t\t{ email: \"admin\" },',\n\t\t'\t\t\t\t{ email: \"dev@resolveio.com\" }',\n\t\t'\t\t\t]',\n\t\t'\t\t};',\n\t\t'\t\tawait users.updateMany(qaUserFilter, {',\n\t\t'\t\t\t$set: {',\n\t\t'\t\t\t\tactive: true,',\n\t\t'\t\t\t\troles: baseUser.roles,',\n\t\t'\t\t\t\tother: defaultOther,',\n\t\t'\t\t\t\tsettings: defaultSettings,',\n\t\t'\t\t\t\thash,',\n\t\t'\t\t\t\tsalt,',\n\t\t'\t\t\t\tattempts: 0,',\n\t\t'\t\t\t\treadonly: false,',\n\t\t'\t\t\t\tis_customer: false,',\n\t\t'\t\t\t\tupdatedAt: now',\n\t\t'\t\t\t}',\n\t\t'\t\t});',\n\t\t'\t\tif (existing && existing._id) {',\n\t\t'\t\t\tawait users.updateOne({ _id: existing._id }, { $set: baseUser });',\n\t\t'\t\t}',\n\t\t'\t\telse {',\n\t\t'\t\t\tawait users.insertOne({ _id: crypto.randomBytes(12).toString(\"hex\"), createdAt: now, ...baseUser });',\n\t\t'\t\t}',\n\t\t'\t\tconst repaired = await users.findOne(qaUserFilter, { projection: { _id: 1 } });',\n\t\t'\t\tif (repaired && repaired._id) {',\n\t\t'\t\t\tawait users.updateOne({ _id: repaired._id }, {',\n\t\t'\t\t\t\t$set: {',\n\t\t'\t\t\t\t\tactive: true,',\n\t\t'\t\t\t\t\troles: baseUser.roles,',\n\t\t'\t\t\t\t\tother: defaultOther,',\n\t\t'\t\t\t\t\tsettings: defaultSettings,',\n\t\t'\t\t\t\t\thash,',\n\t\t'\t\t\t\t\tsalt,',\n\t\t'\t\t\t\t\tattempts: 0,',\n\t\t'\t\t\t\t\treadonly: false,',\n\t\t'\t\t\t\t\tis_customer: false,',\n\t\t'\t\t\t\t\tupdatedAt: now',\n\t\t'\t\t\t\t}',\n\t\t'\t\t\t});',\n\t\t'\t\t}',\n\t\t'\t\treturn true;',\n\t\t'\t}',\n\t\t'\tfinally {',\n\t\t'\t\tawait client.close().catch(() => undefined);',\n\t\t'\t}',\n\t\t'}',\n\t\t'',\n\t\t'async function refreshAuthAndSeed(page) {',\n\t\t'\tconst auth = await login();',\n\t\t'\tawait seedAuth(page, auth);',\n\t\t'\treturn auth;',\n\t\t'}',\n\t\t'',\n\t\t'function writeResult(payload) {',\n\t\t'\tfs.mkdirSync(artifactDir, { recursive: true });',\n\t\t'\tfs.writeFileSync(resultPath, JSON.stringify(payload, null, 2));',\n\t\t'}',\n\t\t'',\n\t\t'function requestJson(url, payload) {',\n\t\t'\treturn new Promise((resolve, reject) => {',\n\t\t'\t\tconst body = JSON.stringify(payload || {});',\n\t\t'\t\tconst parsed = new URL(url);',\n\t\t'\t\tconst mod = parsed.protocol === \"https:\" ? https : http;',\n\t\t'\t\tconst req = mod.request(parsed, {',\n\t\t'\t\t\tmethod: \"POST\",',\n\t\t'\t\t\ttimeout: 20000,',\n\t\t'\t\t\theaders: {',\n\t\t'\t\t\t\t\"content-type\": \"application/json\",',\n\t\t'\t\t\t\t\"content-length\": Buffer.byteLength(body),',\n\t\t'\t\t\t\t\"origin\": clientUrl',\n\t\t'\t\t\t}',\n\t\t'\t\t}, (res) => {',\n\t\t'\t\t\tlet raw = \"\";',\n\t\t'\t\t\tres.setEncoding(\"utf8\");',\n\t\t'\t\t\tres.on(\"data\", (chunk) => { raw += chunk; });',\n\t\t'\t\t\tres.on(\"end\", () => {',\n\t\t'\t\t\t\tlet json = null;',\n\t\t'\t\t\t\ttry { json = raw ? JSON.parse(raw) : {}; }',\n\t\t'\t\t\t\tcatch (error) {',\n\t\t'\t\t\t\t\treject(new Error(`${url} returned non-JSON HTTP ${res.statusCode}: ${raw.slice(0, 300)}`));',\n\t\t'\t\t\t\t\treturn;',\n\t\t'\t\t\t\t}',\n\t\t'\t\t\t\tif (!res.statusCode || res.statusCode >= 400) {',\n\t\t'\t\t\t\t\treject(new Error(`${url} returned HTTP ${res.statusCode}: ${JSON.stringify(json).slice(0, 500)}`));',\n\t\t'\t\t\t\t\treturn;',\n\t\t'\t\t\t\t}',\n\t\t'\t\t\t\tresolve(json);',\n\t\t'\t\t\t});',\n\t\t'\t\t});',\n\t\t'\t\treq.on(\"timeout\", () => req.destroy(new Error(`${url} timed out`)));',\n\t\t'\t\treq.on(\"error\", reject);',\n\t\t'\t\treq.write(body);',\n\t\t'\t\treq.end();',\n\t\t'\t});',\n\t\t'}',\n\t\t'',\n\t\t'function requestReady(url) {',\n\t\t'\treturn new Promise((resolve) => {',\n\t\t'\t\ttry {',\n\t\t'\t\t\tconst parsed = new URL(url);',\n\t\t'\t\t\tconst mod = parsed.protocol === \"https:\" ? https : http;',\n\t\t'\t\t\tconst req = mod.get(parsed, { timeout: 2500 }, (res) => {',\n\t\t'\t\t\t\tres.resume();',\n\t\t'\t\t\t\tresolve(Boolean(res.statusCode && res.statusCode < 500));',\n\t\t'\t\t\t});',\n\t\t'\t\t\treq.on(\"timeout\", () => req.destroy(new Error(\"timeout\")));',\n\t\t'\t\t\treq.on(\"error\", () => resolve(false));',\n\t\t'\t\t}',\n\t\t'\t\tcatch (error) {',\n\t\t'\t\t\tresolve(false);',\n\t\t'\t\t}',\n\t\t'\t});',\n\t\t'}',\n\t\t'',\n\t\t'async function waitForHttpReady(url, label) {',\n\t\t'\tconst deadline = Date.now() + startupTimeoutMs;',\n\t\t'\twhile (Date.now() < deadline) {',\n\t\t'\t\tif (await requestReady(url)) {',\n\t\t'\t\t\treturn;',\n\t\t'\t\t}',\n\t\t'\t\tawait delay(3000);',\n\t\t'\t}',\n\t\t'\tthrow new Error(`${label} did not become ready at ${url} within ${Math.round(startupTimeoutMs / 1000)}s`);',\n\t\t'}',\n\t\t'',\n\t\t'function requirePuppeteer() {',\n\t\t'\tconst candidates = [',\n\t\t'\t\tpath.join(projectRoot, \"server\", \"node_modules\", \"puppeteer\", \"lib\", \"cjs\", \"puppeteer\", \"puppeteer.js\"),',\n\t\t'\t\tpath.join(projectRoot, \"node_modules\", \"puppeteer\", \"lib\", \"cjs\", \"puppeteer\", \"puppeteer.js\"),',\n\t\t'\t\tpath.join(process.cwd(), \"server\", \"node_modules\", \"puppeteer\", \"lib\", \"cjs\", \"puppeteer\", \"puppeteer.js\"),',\n\t\t'\t\tpath.join(process.cwd(), \"node_modules\", \"puppeteer\", \"lib\", \"cjs\", \"puppeteer\", \"puppeteer.js\"),',\n\t\t'\t\t\"puppeteer\"',\n\t\t'\t];',\n\t\t'\tfor (const candidate of candidates) {',\n\t\t'\t\ttry { return require(candidate); }',\n\t\t'\t\tcatch (error) {}',\n\t\t'\t}',\n\t\t'\tthrow new Error(\"Unable to require puppeteer from project/server node_modules or global resolution\");',\n\t\t'}',\n\t\t'',\n\t\t'async function login() {',\n\t\t'\tif (!password) {',\n\t\t'\t\tthrow new Error(\"QA password is empty; source .resolveio-support-tools/env.sh or set RESOLVEIO_RUNNER_QA_PASSWORD before auth bootstrap\");',\n\t\t'\t}',\n\t\t'\tawait waitForHttpReady(serverUrl, \"QA server\");',\n\t\t'\tawait ensureLocalQaUser();',\n\t\t'\tlet loginJson = await requestJson(`${serverUrl}/login`, { username, password });',\n\t\t'\tif ((loginJson && loginJson.error) && /Invalid Username And Password|Too Many Attempts/i.test(String(loginJson.result || \"\"))) {',\n\t\t'\t\tconst repaired = await ensureLocalQaUser();',\n\t\t'\t\tif (repaired) {',\n\t\t'\t\t\tloginJson = await requestJson(`${serverUrl}/login`, { username, password });',\n\t\t'\t\t}',\n\t\t'\t}',\n\t\t'\tconst refreshToken = loginJson && loginJson.result && loginJson.result.token;',\n\t\t'\tif (loginJson.error || !refreshToken) {',\n\t\t'\t\tthrow new Error(`Login failed: ${JSON.stringify(loginJson).slice(0, 800)}`);',\n\t\t'\t}',\n\t\t'\tconst accessJson = await requestJson(`${serverUrl}/accessToken`, { refreshToken });',\n\t\t'\tconst accessToken = accessJson && accessJson.result && accessJson.result.token;',\n\t\t'\tconst user = accessJson && accessJson.result && accessJson.result.user;',\n\t\t'\tif (accessJson.error || !accessToken || !user) {',\n\t\t'\t\tthrow new Error(`Access token failed: ${JSON.stringify(accessJson).slice(0, 800)}`);',\n\t\t'\t}',\n\t\t'\treturn { refreshToken, accessToken, user: normalizeQaUserForBrowser(user) };',\n\t\t'}',\n\t\t'',\n\t\t'async function launchBrowser(puppeteer) {',\n\t\t'\tconst browserUrl = process.env.RESOLVEIO_RUNNER_QA_BROWSER_URL || process.env.RESOLVEIO_SUPPORT_QA_BROWSER_URL || \"\";',\n\t\t'\tif (browserUrl) {',\n\t\t'\t\treturn puppeteer.connect({ browserURL: browserUrl, protocolTimeout: 30000, defaultViewport: null });',\n\t\t'\t}',\n\t\t'\tconst launchOptions = {',\n\t\t'\t\theadless: true,',\n\t\t'\t\tdefaultViewport: { width: viewportWidth, height: viewportHeight },',\n\t\t'\t\targs: [\"--no-sandbox\", \"--disable-setuid-sandbox\", \"--disable-dev-shm-usage\", `--window-size=${viewportWidth},${viewportHeight}`]',\n\t\t'\t};',\n\t\t'\tif (process.env.PUPPETEER_EXECUTABLE_PATH || process.env.CHROME_BIN) {',\n\t\t'\t\tlaunchOptions.executablePath = process.env.PUPPETEER_EXECUTABLE_PATH || process.env.CHROME_BIN;',\n\t\t'\t}',\n\t\t'\treturn puppeteer.launch(launchOptions);',\n\t\t'}',\n\t\t'',\n\t\t'async function resetBrowserState(page) {',\n\t\t'\tawait waitForHttpReady(clientUrl, \"QA client\");',\n\t\t'\tawait page.goto(clientUrl, { waitUntil: \"domcontentloaded\", timeout: 45000 });',\n\t\t'\tawait page.evaluate(async () => {',\n\t\t'\t\ttry {',\n\t\t'\t\t\tconst registrations = await navigator.serviceWorker.getRegistrations();',\n\t\t'\t\t\tawait Promise.all(registrations.map((registration) => registration.unregister().catch(() => false)));',\n\t\t'\t\t} catch (error) {}',\n\t\t'\t\ttry {',\n\t\t'\t\t\tif (window.caches && window.caches.keys) {',\n\t\t'\t\t\t\tconst keys = await window.caches.keys();',\n\t\t'\t\t\t\tawait Promise.all(keys.map((key) => window.caches.delete(key).catch(() => false)));',\n\t\t'\t\t\t}',\n\t\t'\t\t} catch (error) {}',\n\t\t'\t\ttry {',\n\t\t'\t\t\tif (window.indexedDB && window.indexedDB.databases) {',\n\t\t'\t\t\t\tconst databases = await window.indexedDB.databases();',\n\t\t'\t\t\t\tawait Promise.all(databases.filter((database) => database && database.name).map((database) => new Promise((resolve) => {',\n\t\t'\t\t\t\t\tconst request = window.indexedDB.deleteDatabase(database.name);',\n\t\t'\t\t\t\t\trequest.onsuccess = () => resolve(true);',\n\t\t'\t\t\t\t\trequest.onerror = () => resolve(false);',\n\t\t'\t\t\t\t\trequest.onblocked = () => resolve(false);',\n\t\t'\t\t\t\t})));',\n\t\t'\t\t\t}',\n\t\t'\t\t} catch (error) {}',\n\t\t'\t\tlocalStorage.clear();',\n\t\t'\t\tsessionStorage.clear();',\n\t\t'\t});',\n\t\t'}',\n\t\t'',\n\t\t'async function seedAuth(page, auth) {',\n\t\t'\tauth.user = normalizeQaUserForBrowser(auth.user);',\n\t\t'\tawait page.evaluate((payload) => {',\n\t\t'\t\tlocalStorage.setItem(\"refreshToken\", payload.refreshToken);',\n\t\t'\t\tlocalStorage.setItem(\"accessToken\", payload.accessToken);',\n\t\t'\t\tlocalStorage.setItem(\"user\", JSON.stringify(payload.user));',\n\t\t'\t\tlocalStorage.setItem(\"lastURL\", payload.lastURL);',\n\t\t'\t\tlocalStorage.setItem(\"resolveio.runnerQaAuthBootstrappedAt\", payload.bootstrappedAt);',\n\t\t'\t}, { ...auth, lastURL: targetRoute, bootstrappedAt: new Date().toISOString() });',\n\t\t'}',\n\t\t'',\n\t\t'async function patchBrowserQaUser(page) {',\n\t\t'\tawait page.evaluate((nextRoute) => {',\n\t\t'\t\tlet user = {};',\n\t\t'\t\ttry { user = JSON.parse(localStorage.getItem(\"user\") || \"{}\"); } catch (error) { user = {}; }',\n\t\t'\t\tconst settings = {',\n\t\t'\t\t\ttable_color: \"#3b3ee3\",',\n\t\t'\t\t\ttable_font_color: \"#ffffff\",',\n\t\t'\t\t\tsecondary_table_color: \"#87ceeb\",',\n\t\t'\t\t\tsecondary_table_font_color: \"#000000\",',\n\t\t'\t\t\ttertiary_table_color: \"#ff4500\",',\n\t\t'\t\t\ttertiary_table_font_color: \"#000000\",',\n\t\t'\t\t\tfont_size: 12,',\n\t\t'\t\t\tcollapsable_menu: true,',\n\t\t'\t\t\tentries_per_page: \"25\",',\n\t\t'\t\t\twarning_color: \"#ffc107\",',\n\t\t'\t\t\twarning_font_color: \"#000000\",',\n\t\t'\t\t\twarning_hover_color: \"#e0a800\",',\n\t\t'\t\t\tsuccess_color: \"#28a745\",',\n\t\t'\t\t\tsuccess_font_color: \"#ffffff\",',\n\t\t'\t\t\tsuccess_hover_color: \"#218838\",',\n\t\t'\t\t\tdanger_color: \"#dc3545\",',\n\t\t'\t\t\tdanger_font_color: \"#ffffff\",',\n\t\t'\t\t\tdanger_hover_color: \"#c82333\",',\n\t\t'\t\t\tinfo_color: \"#17a2b8\",',\n\t\t'\t\t\tinfo_font_color: \"#ffffff\",',\n\t\t'\t\t\tinfo_hover_color: \"#138496\",',\n\t\t'\t\t\tprimary_color: \"#007bff\",',\n\t\t'\t\t\tprimary_font_color: \"#ffffff\",',\n\t\t'\t\t\tprimary_hover_color: \"#0069d9\",',\n\t\t'\t\t\tsecondary_color: \"#868e96\",',\n\t\t'\t\t\tsecondary_font_color: \"#ffffff\",',\n\t\t'\t\t\tsecondary_hover_color: \"#5a6268\",',\n\t\t'\t\t\trouting_preference: \"\",',\n\t\t'\t\t\topening_route: nextRoute,',\n\t\t'\t\t\trio_select_search_mode: \"exact\"',\n\t\t'\t\t};',\n\t\t'\t\tuser = {',\n\t\t'\t\t\t...user,',\n\t\t'\t\t\tother: {',\n\t\t'\t\t\t\t...(user.other || {}),',\n\t\t'\t\t\t\tyards: Array.isArray(user.other && user.other.yards) ? user.other.yards : [],',\n\t\t'\t\t\t\ttour_completed: true,',\n\t\t'\t\t\t\ttook_tour: true,',\n\t\t'\t\t\t\tcore_tour_completed: true,',\n\t\t'\t\t\t\twelcome_tour_completed: true,',\n\t\t'\t\t\t\ttop_navigation_tour_completed: true,',\n\t\t'\t\t\t\tuser_settings_tour_completed: true',\n\t\t'\t\t\t},',\n\t\t'\t\t\tsettings: { ...settings, ...(user.settings || {}), opening_route: nextRoute }',\n\t\t'\t\t};',\n\t\t'\t\tlocalStorage.setItem(\"user\", JSON.stringify(user));',\n\t\t'\t\tlocalStorage.setItem(\"lastURL\", nextRoute);',\n\t\t'\t\tlocalStorage.setItem(\"resolveio.runnerQaPostLoginGateRepairedAt\", new Date().toISOString());',\n\t\t'\t});',\n\t\t'}',\n\t\t'',\n\t\t'async function dismissVisibleTourOrSetupGate(page) {',\n\t\t'\treturn page.evaluate(() => {',\n\t\t'\t\tconst controls = Array.from(document.querySelectorAll(\"button, a, [role=\\'button\\']\"));',\n\t\t'\t\tconst control = controls.find((candidate) => {',\n\t\t'\t\t\tconst text = (candidate.textContent || \"\").replace(/\\\\s+/g, \" \").trim();',\n\t\t'\t\t\treturn /^(skip|finish|done|close)$/i.test(text);',\n\t\t'\t\t});',\n\t\t'\t\tif (control && typeof control.click === \"function\") {',\n\t\t'\t\t\tcontrol.click();',\n\t\t'\t\t\treturn true;',\n\t\t'\t\t}',\n\t\t'\t\treturn false;',\n\t\t'\t});',\n\t\t'}',\n\t\t'',\n\t\t'function isPostLoginSetupOrTourGate(currentRoute, summary) {',\n\t\t'\tconst text = String(summary && summary.bodyTextSnippet || \"\");',\n\t\t'\treturn currentRoute === \"/user-settings/settings\" || /Top Navigation Step \\\\d+ of \\\\d+|\\\\bSkip\\\\b|User Settings/i.test(text);',\n\t\t'}',\n\t\t'',\n\t\t'async function repairPostLoginSetupOrTourGate(page, expectedRoute) {',\n\t\t'\tif (process.env.RESOLVEIO_RUNNER_QA_DISABLE_POST_LOGIN_ROUTE_REPAIR === \"true\" || process.env.RESOLVEIO_SUPPORT_QA_DISABLE_POST_LOGIN_ROUTE_REPAIR === \"true\") {',\n\t\t'\t\treturn false;',\n\t\t'\t}',\n\t\t'\tconst current = normalizeRoutePath(await page.evaluate(() => location.href));',\n\t\t'\tconst summary = await pageSummary(page).catch(() => ({}));',\n\t\t'\tif (!isPostLoginSetupOrTourGate(current, summary)) {',\n\t\t'\t\treturn false;',\n\t\t'\t}',\n\t\t'\tawait ensureLocalQaUser().catch(() => false);',\n\t\t'\tawait refreshAuthAndSeed(page).catch(() => undefined);',\n\t\t'\tawait dismissVisibleTourOrSetupGate(page).catch(() => false);',\n\t\t'\tawait patchBrowserQaUser(page);',\n\t\t'\tawait page.goto(`${clientUrl}${expectedRoute}`, { waitUntil: \"domcontentloaded\", timeout: 60000 });',\n\t\t'\tawait delay(1500);',\n\t\t'\tawait dismissVisibleTourOrSetupGate(page).catch(() => false);',\n\t\t'\tawait patchBrowserQaUser(page);',\n\t\t'\tawait delay(500);',\n\t\t'\tconst repairedCurrent = normalizeRoutePath(await page.evaluate(() => location.href));',\n\t\t'\treturn repairedCurrent === expectedRoute;',\n\t\t'}',\n\t\t'',\n\t\t'function delay(ms) {',\n\t\t'\treturn new Promise((resolve) => setTimeout(resolve, ms));',\n\t\t'}',\n\t\t'',\n\t\t'function normalizeRoutePath(value) {',\n\t\t'\ttry {',\n\t\t'\t\t\tconst parsed = new URL(value, clientUrl);',\n\t\t'\t\t\tlet pathname = parsed.pathname || \"/\";',\n\t\t'\t\t\tpathname = pathname.replace(/\\\\/+$/, \"\") || \"/\";',\n\t\t'\t\t\treturn pathname;',\n\t\t'\t}',\n\t\t'\tcatch (error) {',\n\t\t'\t\t\treturn String(value || \"/\").split(\"?\")[0].replace(/\\\\/+$/, \"\") || \"/\";',\n\t\t'\t}',\n\t\t'}',\n\t\t'',\n\t\t'function isGenericAuthTargetRoute(routePath) {',\n\t\t'\treturn [\"/\", \"/screen\", \"/home\"].includes(normalizeRoutePath(routePath));',\n\t\t'}',\n\t\t'',\n\t\t'function isAuthenticatedNonSetupRoute(summary) {',\n\t\t'\tconst current = normalizeRoutePath(summary && summary.url || \"\");',\n\t\t'\treturn !!summary',\n\t\t'\t\t&& !!summary.hasRefreshToken',\n\t\t'\t\t&& !!summary.hasAccessToken',\n\t\t'\t\t&& !!summary.hasUser',\n\t\t'\t\t&& !summary.hasLoginText',\n\t\t'\t\t&& !summary.hasOfflineModeText',\n\t\t'\t\t&& current !== \"/user-settings/settings\";',\n\t\t'}',\n\t\t'',\n\t\t'async function assertTargetRoute(page) {',\n\t\t'\tconst expected = normalizeRoutePath(targetRoute);',\n\t\t'\tif (expected === \"/\") {',\n\t\t'\t\treturn;',\n\t\t'\t}',\n\t\t'\tconst current = normalizeRoutePath(await page.evaluate(() => location.href));',\n\t\t'\tif (current !== expected) {',\n\t\t'\t\tconst repaired = await repairPostLoginSetupOrTourGate(page, expected);',\n\t\t'\t\tif (repaired) {',\n\t\t'\t\t\treturn;',\n\t\t'\t\t}',\n\t\t'\t\tconst summary = await pageSummary(page).catch(() => ({}));',\n\t\t'\t\tif (isGenericAuthTargetRoute(expected) && isAuthenticatedNonSetupRoute(summary)) {',\n\t\t'\t\t\treturn;',\n\t\t'\t\t}',\n\t\t'\t\tthrow new Error(`QA auth bootstrap reached ${current}, not requested target route ${expected}. This is a route blocker; do not continue browser QA until the runner/app can reach the requested screen. Page summary: ${JSON.stringify(summary).slice(0, 1200)}`);',\n\t\t'\t}',\n\t\t'}',\n\t\t'',\n\t\t'async function waitForStableTargetRoute(page) {',\n\t\t'\tconst expected = normalizeRoutePath(targetRoute);',\n\t\t'\tif (isGenericAuthTargetRoute(expected)) {',\n\t\t'\t\treturn;',\n\t\t'\t}',\n\t\t'\tconst deadline = Date.now() + Number(process.env.RESOLVEIO_RUNNER_QA_ROUTE_STABILITY_TIMEOUT_MS || process.env.RESOLVEIO_SUPPORT_QA_ROUTE_STABILITY_TIMEOUT_MS || 7000);',\n\t\t'\tlet stableSince = 0;',\n\t\t'\tlet lastCurrent = \"\";',\n\t\t'\twhile (Date.now() < deadline) {',\n\t\t'\t\tconst current = normalizeRoutePath(await page.evaluate(() => location.href));',\n\t\t'\t\tlastCurrent = current;',\n\t\t'\t\tif (current === expected) {',\n\t\t'\t\t\tstableSince = stableSince || Date.now();',\n\t\t'\t\t\tif (Date.now() - stableSince >= Number(process.env.RESOLVEIO_RUNNER_QA_ROUTE_STABLE_MS || process.env.RESOLVEIO_SUPPORT_QA_ROUTE_STABLE_MS || 2500)) {',\n\t\t'\t\t\t\treturn;',\n\t\t'\t\t\t}',\n\t\t'\t\t}',\n\t\t'\t\telse {',\n\t\t'\t\t\tstableSince = 0;',\n\t\t'\t\t}',\n\t\t'\t\tawait delay(250);',\n\t\t'\t}',\n\t\t'\tconst summary = await pageSummary(page).catch(() => ({}));',\n\t\t'\tthrow new Error(`QA auth bootstrap route was not stable on requested target ${expected}; last route was ${lastCurrent || \"unknown\"}. This is a route blocker; do not continue browser QA until the runner/app can remain on the requested screen. Page summary: ${JSON.stringify(summary).slice(0, 1200)}`);',\n\t\t'}',\n\t\t'',\n\t\t'async function dismissNavigationOverlays(page) {',\n\t\t'\ttry {',\n\t\t'\t\tawait page.keyboard.press(\"Escape\");',\n\t\t'\t} catch (error) {}',\n\t\t'\ttry {',\n\t\t'\t\tawait page.mouse.move(16, Math.max(120, viewportHeight - 24));',\n\t\t'\t} catch (error) {}',\n\t\t'\ttry {',\n\t\t'\t\tawait page.evaluate(() => {',\n\t\t'\t\t\tif (document.activeElement && typeof document.activeElement.blur === \"function\") document.activeElement.blur();',\n\t\t'\t\t\tdocument.body && document.body.dispatchEvent(new MouseEvent(\"mousemove\", { bubbles: true, clientX: 16, clientY: Math.max(120, window.innerHeight - 24) }));',\n\t\t'\t\t});',\n\t\t'\t} catch (error) {}',\n\t\t'\tawait delay(350);',\n\t\t'}',\n\t\t'',\n\t\t'async function assertSummaryOnTargetRoute(summary) {',\n\t\t'\tconst expected = normalizeRoutePath(targetRoute);',\n\t\t'\tif (expected === \"/\") {',\n\t\t'\t\treturn;',\n\t\t'\t}',\n\t\t'\tconst current = normalizeRoutePath(summary && summary.url || \"\");',\n\t\t'\tif (current !== expected) {',\n\t\t'\t\tif (isGenericAuthTargetRoute(expected) && isAuthenticatedNonSetupRoute(summary)) {',\n\t\t'\t\t\treturn;',\n\t\t'\t\t}',\n\t\t'\t\tthrow new Error(`QA auth bootstrap final summary is on ${current}, not requested target route ${expected}. This is a route blocker; refusing to write a false pass. Page summary: ${JSON.stringify(summary).slice(0, 1200)}`);',\n\t\t'\t}',\n\t\t'}',\n\t\t'',\n\t\t'async function logoutExistingBrowserSession(page) {',\n\t\t'\ttry {',\n\t\t'\t\tawait page.goto(clientUrl, { waitUntil: \"domcontentloaded\", timeout: 45000 });',\n\t\t'\t\tawait delay(500);',\n\t\t'\t\tawait page.evaluate(() => {',\n\t\t'\t\t\tconst controls = Array.from(document.querySelectorAll(\"button, a, [role=\\'button\\']\"));',\n\t\t'\t\t\tconst logoutControl = controls.find((control) => /(^|\\\\s)logout(\\\\s|$)/i.test((control.textContent || \"\").trim()));',\n\t\t'\t\t\tif (logoutControl && typeof logoutControl.click === \"function\") {',\n\t\t'\t\t\t\tlogoutControl.click();',\n\t\t'\t\t\t\treturn true;',\n\t\t'\t\t\t}',\n\t\t'\t\t\treturn false;',\n\t\t'\t\t});',\n\t\t'\t\tawait delay(1000);',\n\t\t'\t}',\n\t\t'\tcatch (error) {}',\n\t\t'}',\n\t\t'',\n\t\t'async function waitForAuthenticatedApp(page) {',\n\t\t'\tconst url = `${clientUrl}${targetRoute}`;',\n\t\t'\tawait page.goto(url, { waitUntil: \"domcontentloaded\", timeout: 60000 });',\n\t\t'\tawait page.waitForFunction(() => {',\n\t\t'\t\tconst text = (document.body && document.body.innerText || \"\").replace(/\\\\s+/g, \" \").trim();',\n\t\t'\t\tconst hasTokens = !!localStorage.getItem(\"refreshToken\") && !!localStorage.getItem(\"accessToken\") && !!localStorage.getItem(\"user\");',\n\t\t'\t\tconst hasLogin = /Employee\\\\/Customer Login|Employee Sign In|Customer Access|Unable to sign in/i.test(text);',\n\t\t'\t\tconst hasOffline = text.includes(\"*** OFFLINE MODE ***\");',\n\t\t'\t\treturn hasTokens && !hasLogin && !hasOffline && text.length > 40;',\n\t\t'\t}, { timeout: Number(process.env.RESOLVEIO_RUNNER_QA_AUTH_TIMEOUT_MS || process.env.RESOLVEIO_SUPPORT_QA_AUTH_TIMEOUT_MS || 60000) });',\n\t\t'\tawait delay(1000);',\n\t\t'\tawait assertTargetRoute(page);',\n\t\t'\tawait waitForStableTargetRoute(page);',\n\t\t'\tawait dismissNavigationOverlays(page);',\n\t\t'}',\n\t\t'',\n\t\t'async function pageSummary(page) {',\n\t\t'\treturn page.evaluate(() => {',\n\t\t'\t\tconst bodyText = (document.body && document.body.innerText || \"\").replace(/\\\\s+/g, \" \").trim();',\n\t\t'\t\treturn {',\n\t\t'\t\t\turl: location.href,',\n\t\t'\t\t\ttitle: document.title,',\n\t\t'\t\t\thasAngularDebug: !!window.ng,',\n\t\t'\t\t\thasRefreshToken: !!localStorage.getItem(\"refreshToken\"),',\n\t\t'\t\t\thasAccessToken: !!localStorage.getItem(\"accessToken\"),',\n\t\t'\t\t\thasUser: !!localStorage.getItem(\"user\"),',\n\t\t'\t\t\thasOfflineModeText: bodyText.includes(\"*** OFFLINE MODE ***\"),',\n\t\t'\t\t\thasLoginText: /Employee\\\\/Customer Login|Employee Sign In|Customer Access|Unable to sign in/i.test(bodyText),',\n\t\t'\t\t\tbodyTextSnippet: bodyText.slice(0, 800),',\n\t\t'\t\t\tlocalStorageKeys: Object.keys(localStorage).sort()',\n\t\t'\t\t};',\n\t\t'\t});',\n\t\t'}',\n\t\t'',\n\t\t'(async () => {',\n\t\t'\tfs.mkdirSync(artifactDir, { recursive: true });',\n\t\t'\tconst puppeteer = requirePuppeteer();',\n\t\t'\tconst browser = await launchBrowser(puppeteer);',\n\t\t'\tlet page;',\n\t\t'\ttry {',\n\t\t'\t\tawait waitForHttpReady(clientUrl, \"QA client\");',\n\t\t'\t\tawait waitForHttpReady(serverUrl, \"QA server\");',\n\t\t'\t\tpage = await browser.newPage();',\n\t\t'\t\tawait page.setViewport({ width: viewportWidth, height: viewportHeight });',\n\t\t'\t\tpage.on(\"console\", (msg) => {',\n\t\t'\t\t\tconst text = msg.text();',\n\t\t'\t\t\tif ([\"error\", \"warning\"].includes(msg.type()) || /error/i.test(text)) {',\n\t\t'\t\t\t\tconsole.log(\"[browser console]\", msg.type(), text);',\n\t\t'\t\t\t}',\n\t\t'\t\t});',\n\t\t'\t\tpage.on(\"pageerror\", (error) => console.log(\"[pageerror]\", error.message));',\n\t\t'\t\tawait logoutExistingBrowserSession(page);',\n\t\t'\t\tawait resetBrowserState(page);',\n\t\t'\t\tconst auth = await login();',\n\t\t'\t\tawait seedAuth(page, auth);',\n\t\t'\t\tawait waitForAuthenticatedApp(page);',\n\t\t'\t\tconst finalPage = await pageSummary(page);',\n\t\t'\t\tawait assertSummaryOnTargetRoute(finalPage);',\n\t\t'\t\tawait page.screenshot({ path: readyScreenshotPath, fullPage: true });',\n\t\t'\t\tconst summary = {',\n\t\t'\t\t\tstatus: \"pass\",',\n\t\t'\t\t\tclientUrl,',\n\t\t'\t\t\tserverUrl,',\n\t\t'\t\t\ttargetRoute,',\n\t\t'\t\t\tscreenshot: readyScreenshotPath,',\n\t\t'\t\t\tuser: { _id: auth.user && auth.user._id, username: auth.user && auth.user.username, fullname: auth.user && auth.user.fullname },',\n\t\t'\t\t\tpage: finalPage',\n\t\t'\t\t};',\n\t\t'\t\twriteResult(summary);',\n\t\t'\t\tconsole.log(JSON.stringify(summary, null, 2));',\n\t\t'\t}',\n\t\t'\tcatch (error) {',\n\t\t'\t\tlet summary = { status: \"fail\", clientUrl, serverUrl, targetRoute, screenshot: failureScreenshotPath, error: error && error.stack || String(error) };',\n\t\t'\t\ttry {',\n\t\t'\t\t\tif (page) {',\n\t\t'\t\t\t\tawait page.screenshot({ path: failureScreenshotPath, fullPage: true });',\n\t\t'\t\t\t\tsummary.page = await pageSummary(page);',\n\t\t'\t\t\t}',\n\t\t'\t\t} catch (screenshotError) {',\n\t\t'\t\t\tsummary.screenshotError = screenshotError && screenshotError.stack || String(screenshotError);',\n\t\t'\t\t}',\n\t\t'\t\twriteResult(summary);',\n\t\t'\t\tconsole.error(JSON.stringify(summary, null, 2));',\n\t\t'\t\tprocess.exitCode = 1;',\n\t\t'\t}',\n\t\t'\tfinally {',\n\t\t'\t\tif (browser && typeof browser.close === \"function\") {',\n\t\t'\t\t\tawait browser.close().catch(() => undefined);',\n\t\t'\t\t}',\n\t\t'\t\tprocess.exit(process.exitCode || 0);',\n\t\t'\t}',\n\t\t'})();',\n\t\t''\n\t].join('\\n');\n}\n"]}
1
+ {"version":3,"sources":["../../src/util/ai-runner-qa-auth.ts"],"names":[],"mappings":"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","file":"ai-runner-qa-auth.js","sourcesContent":["export interface ResolveIORunnerQaAuthBootstrapScriptOptions {\n\tdefaultUsername?: string;\n\tdefaultPassword?: string;\n}\n\nexport function buildResolveIORunnerQaAuthBootstrapScript(options: ResolveIORunnerQaAuthBootstrapScriptOptions = {}): string {\n\tconst defaultUsername = options.defaultUsername || 'admin';\n\tconst defaultPassword = options.defaultPassword || '';\n\treturn [\n\t\t'#!/usr/bin/env node',\n\t\t\"'use strict';\",\n\t\t'',\n\t\t'const fs = require(\"fs\");',\n\t\t'const crypto = require(\"crypto\");',\n\t\t'const http = require(\"http\");',\n\t\t'const https = require(\"https\");',\n\t\t'const path = require(\"path\");',\n\t\t'',\n\t\t'const projectRoot = path.resolve(process.argv[2] || process.cwd());',\n\t\t'const routeArg = process.argv[3] || process.env.RESOLVEIO_RUNNER_QA_TARGET_ROUTE || process.env.RESOLVEIO_SUPPORT_QA_TARGET_ROUTE || process.env.RESOLVEIO_SUPPORT_QA_LAST_URL || \"/\";',\n\t\t'const targetRoute = routeArg.startsWith(\"/\") ? routeArg : `/${routeArg}`;',\n\t\t'const clientUrl = stripTrailingSlash(process.env.RESOLVEIO_RUNNER_QA_CLIENT_URL || process.env.RESOLVEIO_SUPPORT_QA_CLIENT_URL || `http://localhost:${process.env.RESOLVEIO_SUPPORT_QA_CLIENT_PORT || \"4200\"}`);',\n\t\t'const serverUrl = stripTrailingSlash(process.env.RESOLVEIO_RUNNER_QA_SERVER_URL || process.env.RESOLVEIO_SUPPORT_QA_SERVER_URL || \"http://localhost:8080\");',\n\t\t`const username = process.env.RESOLVEIO_RUNNER_QA_USERNAME || process.env.RESOLVEIO_SUPPORT_QA_USERNAME || ${JSON.stringify(defaultUsername)};`,\n\t\t`const password = process.env.RESOLVEIO_RUNNER_QA_PASSWORD || process.env.RESOLVEIO_SUPPORT_QA_PASSWORD || ${JSON.stringify(defaultPassword)};`,\n\t\t'const artifactDir = path.resolve(process.env.RESOLVEIO_RUNNER_QA_ARTIFACT_DIR || process.env.RESOLVEIO_SUPPORT_QA_ARTIFACT_DIR || path.join(projectRoot, \"qa-artifacts\"));',\n\t\t'const viewportWidth = Number(process.env.RESOLVEIO_RUNNER_QA_VIEWPORT_WIDTH || process.env.RESOLVEIO_SUPPORT_QA_VIEWPORT_WIDTH || 1920);',\n\t\t'const viewportHeight = Number(process.env.RESOLVEIO_RUNNER_QA_VIEWPORT_HEIGHT || process.env.RESOLVEIO_SUPPORT_QA_VIEWPORT_HEIGHT || 1080);',\n\t\t'const startupTimeoutMs = Math.max(1000, Number(process.env.RESOLVEIO_RUNNER_QA_ANGULAR_STARTUP_TIMEOUT_SECONDS || process.env.RESOLVEIO_SUPPORT_QA_ANGULAR_STARTUP_TIMEOUT_SECONDS || 900) * 1000);',\n\t\t'const resultPath = path.join(artifactDir, \"auth-bootstrap-result.json\");',\n\t\t'const readyScreenshotPath = path.join(artifactDir, \"auth-bootstrap-ready.jpg\");',\n\t\t'const failureScreenshotPath = path.join(artifactDir, \"auth-bootstrap-failed.jpg\");',\n\t\t'',\n\t\t'function stripTrailingSlash(value) {',\n\t\t'\treturn String(value || \"\").replace(/\\\\/+$/, \"\");',\n\t\t'}',\n\t\t'',\n\t\t'function isLocalhostUrl(value) {',\n\t\t'\ttry {',\n\t\t'\t\tconst parsed = new URL(value);',\n\t\t'\t\treturn [\"localhost\", \"127.0.0.1\", \"::1\"].includes(parsed.hostname);',\n\t\t'\t}',\n\t\t'\tcatch (error) {',\n\t\t'\t\treturn false;',\n\t\t'\t}',\n\t\t'}',\n\t\t'',\n\t\t'function resolveLocalMongoUrl() {',\n\t\t'\tconst localQaMongoUrl = process.env.RESOLVEIO_RUNNER_QA_MONGO_URL || process.env.RESOLVEIO_SUPPORT_QA_MONGO_URL || `mongodb://127.0.0.1:${process.env.RESOLVEIO_SUPPORT_QA_MONGO_PORT || \"3001\"}/resolveio?directConnection=true`;',\n\t\t'\tif (isLocalhostUrl(serverUrl)) {',\n\t\t'\t\treturn localQaMongoUrl;',\n\t\t'\t}',\n\t\t'\treturn process.env.MONGO_URL || localQaMongoUrl;',\n\t\t'}',\n\t\t'',\n\t\t'function requireMongoClient() {',\n\t\t'\tconst candidates = [',\n\t\t'\t\tpath.join(projectRoot, \"server\", \"node_modules\", \"mongodb\"),',\n\t\t'\t\tpath.join(projectRoot, \"node_modules\", \"mongodb\"),',\n\t\t'\t\tpath.join(process.cwd(), \"server\", \"node_modules\", \"mongodb\"),',\n\t\t'\t\tpath.join(process.cwd(), \"node_modules\", \"mongodb\"),',\n\t\t'\t\t\"mongodb\"',\n\t\t'\t];',\n\t\t'\tfor (const candidate of candidates) {',\n\t\t'\t\ttry { return require(candidate).MongoClient; }',\n\t\t'\t\tcatch (error) {}',\n\t\t'\t}',\n\t\t'\tthrow new Error(\"Unable to require mongodb from project/server node_modules or global resolution\");',\n\t\t'}',\n\t\t'',\n\t\t'function buildDefaultQaUserSettings() {',\n\t\t'\treturn {',\n\t\t'\t\ttable_color: \"#3b3ee3\",',\n\t\t'\t\ttable_font_color: \"#ffffff\",',\n\t\t'\t\tsecondary_table_color: \"#87ceeb\",',\n\t\t'\t\tsecondary_table_font_color: \"#000000\",',\n\t\t'\t\ttertiary_table_color: \"#ff4500\",',\n\t\t'\t\ttertiary_table_font_color: \"#000000\",',\n\t\t'\t\tfont_size: 12,',\n\t\t'\t\tcollapsable_menu: true,',\n\t\t'\t\tentries_per_page: \"25\",',\n\t\t'\t\twarning_color: \"#ffc107\",',\n\t\t'\t\twarning_font_color: \"#000000\",',\n\t\t'\t\twarning_hover_color: \"#e0a800\",',\n\t\t'\t\tsuccess_color: \"#28a745\",',\n\t\t'\t\tsuccess_font_color: \"#ffffff\",',\n\t\t'\t\tsuccess_hover_color: \"#218838\",',\n\t\t'\t\tdanger_color: \"#dc3545\",',\n\t\t'\t\tdanger_font_color: \"#ffffff\",',\n\t\t'\t\tdanger_hover_color: \"#c82333\",',\n\t\t'\t\tinfo_color: \"#17a2b8\",',\n\t\t'\t\tinfo_font_color: \"#ffffff\",',\n\t\t'\t\tinfo_hover_color: \"#138496\",',\n\t\t'\t\tprimary_color: \"#007bff\",',\n\t\t'\t\tprimary_font_color: \"#ffffff\",',\n\t\t'\t\tprimary_hover_color: \"#0069d9\",',\n\t\t'\t\tsecondary_color: \"#868e96\",',\n\t\t'\t\tsecondary_font_color: \"#ffffff\",',\n\t\t'\t\tsecondary_hover_color: \"#5a6268\",',\n\t\t'\t\trouting_preference: \"\",',\n\t\t'\t\topening_route: targetRoute,',\n\t\t'\t\trio_select_search_mode: \"exact\"',\n\t\t'\t};',\n\t\t'}',\n\t\t'',\n\t\t'function normalizeQaUserForBrowser(user) {',\n\t\t'\tconst normalized = { ...(user || {}) };',\n\t\t'\tnormalized.other = {',\n\t\t'\t\t...(normalized.other || {}),',\n\t\t'\t\tyards: Array.isArray(normalized.other && normalized.other.yards) ? normalized.other.yards : [],',\n\t\t'\t\ttour_completed: true,',\n\t\t'\t\ttook_tour: true,',\n\t\t'\t\tcore_tour_completed: true,',\n\t\t'\t\twelcome_tour_completed: true,',\n\t\t'\t\ttop_navigation_tour_completed: true,',\n\t\t'\t\tuser_settings_tour_completed: true',\n\t\t'\t};',\n\t\t'\tnormalized.settings = {',\n\t\t'\t\t...buildDefaultQaUserSettings(),',\n\t\t'\t\t...(normalized.settings || {}),',\n\t\t'\t\topening_route: targetRoute',\n\t\t'\t};',\n\t\t'\treturn normalized;',\n\t\t'}',\n\t\t'',\n\t\t'async function ensureLocalQaUser() {',\n\t\t'\tif (!isLocalhostUrl(serverUrl) || process.env.RESOLVEIO_RUNNER_QA_DISABLE_LOCAL_USER_REPAIR === \"true\" || process.env.RESOLVEIO_SUPPORT_QA_DISABLE_LOCAL_USER_REPAIR === \"true\") {',\n\t\t'\t\treturn false;',\n\t\t'\t}',\n\t\t'\tconst MongoClient = requireMongoClient();',\n\t\t'\tconst client = new MongoClient(resolveLocalMongoUrl());',\n\t\t'\tawait client.connect();',\n\t\t'\ttry {',\n\t\t'\t\tconst db = client.db();',\n\t\t'\t\tconst users = db.collection(\"users\");',\n\t\t'\t\tconst now = new Date();',\n\t\t'\t\tconst salt = crypto.randomBytes(32).toString(\"hex\");',\n\t\t'\t\tconst hash = crypto.pbkdf2Sync(password, salt, 25000, 512, \"sha256\").toString(\"hex\");',\n\t\t'\t\tconst existing = await users.findOne({ $or: [{ username }, { email: username }] }, { projection: { _id: 1 } });',\n\t\t'\t\tconst defaultSettings = buildDefaultQaUserSettings();',\n\t\t'\t\tconst defaultOther = { yards: [], date_picker_day_start: \"S\", tour_completed: true, took_tour: true, core_tour_completed: true, welcome_tour_completed: true, top_navigation_tour_completed: true, user_settings_tour_completed: true, tour_completed_at: now.toISOString() };',\n\t\t'\t\tconst baseUser = {',\n\t\t'\t\t\t__v: 0,',\n\t\t'\t\t\tusername,',\n\t\t'\t\t\temail: username.includes(\"@\") ? username : \"dev@resolveio.com\",',\n\t\t'\t\t\tfullname: \"QA Admin\",',\n\t\t'\t\t\troles: { super_admin: true, approvals: [], groups: [], notifications: [], miscs: [] },',\n\t\t'\t\t\tactive: true,',\n\t\t'\t\t\tother: defaultOther,',\n\t\t'\t\t\tsettings: defaultSettings,',\n\t\t'\t\t\tphonenumber: \"\",',\n\t\t'\t\t\treadonly: false,',\n\t\t'\t\t\tis_customer: false,',\n\t\t'\t\t\thash,',\n\t\t'\t\t\tsalt,',\n\t\t'\t\t\tattempts: 0,',\n\t\t'\t\t\tservices: {},',\n\t\t'\t\t\tupdatedAt: now',\n\t\t'\t\t};',\n\t\t'\t\tconst qaUserFilter = {',\n\t\t'\t\t\t$or: [',\n\t\t'\t\t\t\t{ username },',\n\t\t'\t\t\t\t{ email: username },',\n\t\t'\t\t\t\t{ username: \"admin\" },',\n\t\t'\t\t\t\t{ email: \"admin\" },',\n\t\t'\t\t\t\t{ email: \"dev@resolveio.com\" }',\n\t\t'\t\t\t]',\n\t\t'\t\t};',\n\t\t'\t\tawait users.updateMany(qaUserFilter, {',\n\t\t'\t\t\t$set: {',\n\t\t'\t\t\t\tactive: true,',\n\t\t'\t\t\t\troles: baseUser.roles,',\n\t\t'\t\t\t\tother: defaultOther,',\n\t\t'\t\t\t\tsettings: defaultSettings,',\n\t\t'\t\t\t\thash,',\n\t\t'\t\t\t\tsalt,',\n\t\t'\t\t\t\tattempts: 0,',\n\t\t'\t\t\t\treadonly: false,',\n\t\t'\t\t\t\tis_customer: false,',\n\t\t'\t\t\t\tupdatedAt: now',\n\t\t'\t\t\t}',\n\t\t'\t\t});',\n\t\t'\t\tif (existing && existing._id) {',\n\t\t'\t\t\tawait users.updateOne({ _id: existing._id }, { $set: baseUser });',\n\t\t'\t\t}',\n\t\t'\t\telse {',\n\t\t'\t\t\tawait users.insertOne({ _id: crypto.randomBytes(12).toString(\"hex\"), createdAt: now, ...baseUser });',\n\t\t'\t\t}',\n\t\t'\t\tconst repaired = await users.findOne(qaUserFilter, { projection: { _id: 1 } });',\n\t\t'\t\tif (repaired && repaired._id) {',\n\t\t'\t\t\tawait users.updateOne({ _id: repaired._id }, {',\n\t\t'\t\t\t\t$set: {',\n\t\t'\t\t\t\t\tactive: true,',\n\t\t'\t\t\t\t\troles: baseUser.roles,',\n\t\t'\t\t\t\t\tother: defaultOther,',\n\t\t'\t\t\t\t\tsettings: defaultSettings,',\n\t\t'\t\t\t\t\thash,',\n\t\t'\t\t\t\t\tsalt,',\n\t\t'\t\t\t\t\tattempts: 0,',\n\t\t'\t\t\t\t\treadonly: false,',\n\t\t'\t\t\t\t\tis_customer: false,',\n\t\t'\t\t\t\t\tupdatedAt: now',\n\t\t'\t\t\t\t}',\n\t\t'\t\t\t});',\n\t\t'\t\t}',\n\t\t'\t\treturn true;',\n\t\t'\t}',\n\t\t'\tfinally {',\n\t\t'\t\tawait client.close().catch(() => undefined);',\n\t\t'\t}',\n\t\t'}',\n\t\t'',\n\t\t'async function refreshAuthAndSeed(page) {',\n\t\t'\tconst auth = await login();',\n\t\t'\tawait seedAuth(page, auth);',\n\t\t'\treturn auth;',\n\t\t'}',\n\t\t'',\n\t\t'function writeResult(payload) {',\n\t\t'\tfs.mkdirSync(artifactDir, { recursive: true });',\n\t\t'\tfs.writeFileSync(resultPath, JSON.stringify(payload, null, 2));',\n\t\t'}',\n\t\t'',\n\t\t'function requestJson(url, payload) {',\n\t\t'\treturn new Promise((resolve, reject) => {',\n\t\t'\t\tconst body = JSON.stringify(payload || {});',\n\t\t'\t\tconst parsed = new URL(url);',\n\t\t'\t\tconst mod = parsed.protocol === \"https:\" ? https : http;',\n\t\t'\t\tconst req = mod.request(parsed, {',\n\t\t'\t\t\tmethod: \"POST\",',\n\t\t'\t\t\ttimeout: 20000,',\n\t\t'\t\t\theaders: {',\n\t\t'\t\t\t\t\"content-type\": \"application/json\",',\n\t\t'\t\t\t\t\"content-length\": Buffer.byteLength(body),',\n\t\t'\t\t\t\t\"origin\": clientUrl',\n\t\t'\t\t\t}',\n\t\t'\t\t}, (res) => {',\n\t\t'\t\t\tlet raw = \"\";',\n\t\t'\t\t\tres.setEncoding(\"utf8\");',\n\t\t'\t\t\tres.on(\"data\", (chunk) => { raw += chunk; });',\n\t\t'\t\t\tres.on(\"end\", () => {',\n\t\t'\t\t\t\tlet json = null;',\n\t\t'\t\t\t\ttry { json = raw ? JSON.parse(raw) : {}; }',\n\t\t'\t\t\t\tcatch (error) {',\n\t\t'\t\t\t\t\treject(new Error(`${url} returned non-JSON HTTP ${res.statusCode}: ${raw.slice(0, 300)}`));',\n\t\t'\t\t\t\t\treturn;',\n\t\t'\t\t\t\t}',\n\t\t'\t\t\t\tif (!res.statusCode || res.statusCode >= 400) {',\n\t\t'\t\t\t\t\treject(new Error(`${url} returned HTTP ${res.statusCode}: ${JSON.stringify(json).slice(0, 500)}`));',\n\t\t'\t\t\t\t\treturn;',\n\t\t'\t\t\t\t}',\n\t\t'\t\t\t\tresolve(json);',\n\t\t'\t\t\t});',\n\t\t'\t\t});',\n\t\t'\t\treq.on(\"timeout\", () => req.destroy(new Error(`${url} timed out`)));',\n\t\t'\t\treq.on(\"error\", reject);',\n\t\t'\t\treq.write(body);',\n\t\t'\t\treq.end();',\n\t\t'\t});',\n\t\t'}',\n\t\t'',\n\t\t'function requestReady(url) {',\n\t\t'\treturn new Promise((resolve) => {',\n\t\t'\t\ttry {',\n\t\t'\t\t\tconst parsed = new URL(url);',\n\t\t'\t\t\tconst mod = parsed.protocol === \"https:\" ? https : http;',\n\t\t'\t\t\tconst req = mod.get(parsed, { timeout: 2500 }, (res) => {',\n\t\t'\t\t\t\tres.resume();',\n\t\t'\t\t\t\tresolve(Boolean(res.statusCode && res.statusCode < 500));',\n\t\t'\t\t\t});',\n\t\t'\t\t\treq.on(\"timeout\", () => req.destroy(new Error(\"timeout\")));',\n\t\t'\t\t\treq.on(\"error\", () => resolve(false));',\n\t\t'\t\t}',\n\t\t'\t\tcatch (error) {',\n\t\t'\t\t\tresolve(false);',\n\t\t'\t\t}',\n\t\t'\t});',\n\t\t'}',\n\t\t'',\n\t\t'async function waitForHttpReady(url, label) {',\n\t\t'\tconst deadline = Date.now() + startupTimeoutMs;',\n\t\t'\twhile (Date.now() < deadline) {',\n\t\t'\t\tif (await requestReady(url)) {',\n\t\t'\t\t\treturn;',\n\t\t'\t\t}',\n\t\t'\t\tawait delay(3000);',\n\t\t'\t}',\n\t\t'\tthrow new Error(`${label} did not become ready at ${url} within ${Math.round(startupTimeoutMs / 1000)}s`);',\n\t\t'}',\n\t\t'',\n\t\t'function requirePuppeteer() {',\n\t\t'\tconst candidates = [',\n\t\t'\t\tpath.join(projectRoot, \"server\", \"node_modules\", \"puppeteer\", \"lib\", \"cjs\", \"puppeteer\", \"puppeteer.js\"),',\n\t\t'\t\tpath.join(projectRoot, \"node_modules\", \"puppeteer\", \"lib\", \"cjs\", \"puppeteer\", \"puppeteer.js\"),',\n\t\t'\t\tpath.join(process.cwd(), \"server\", \"node_modules\", \"puppeteer\", \"lib\", \"cjs\", \"puppeteer\", \"puppeteer.js\"),',\n\t\t'\t\tpath.join(process.cwd(), \"node_modules\", \"puppeteer\", \"lib\", \"cjs\", \"puppeteer\", \"puppeteer.js\"),',\n\t\t'\t\t\"puppeteer\"',\n\t\t'\t];',\n\t\t'\tfor (const candidate of candidates) {',\n\t\t'\t\ttry { return require(candidate); }',\n\t\t'\t\tcatch (error) {}',\n\t\t'\t}',\n\t\t'\tthrow new Error(\"Unable to require puppeteer from project/server node_modules or global resolution\");',\n\t\t'}',\n\t\t'',\n\t\t'async function login() {',\n\t\t'\tif (!password) {',\n\t\t'\t\tthrow new Error(\"QA password is empty; source .resolveio-support-tools/env.sh or set RESOLVEIO_RUNNER_QA_PASSWORD before auth bootstrap\");',\n\t\t'\t}',\n\t\t'\tawait waitForHttpReady(serverUrl, \"QA server\");',\n\t\t'\tawait ensureLocalQaUser();',\n\t\t'\tlet loginJson = await requestJson(`${serverUrl}/login`, { username, password });',\n\t\t'\tif ((loginJson && loginJson.error) && /Invalid Username And Password|Too Many Attempts/i.test(String(loginJson.result || \"\"))) {',\n\t\t'\t\tconst repaired = await ensureLocalQaUser();',\n\t\t'\t\tif (repaired) {',\n\t\t'\t\t\tloginJson = await requestJson(`${serverUrl}/login`, { username, password });',\n\t\t'\t\t}',\n\t\t'\t}',\n\t\t'\tconst refreshToken = loginJson && loginJson.result && loginJson.result.token;',\n\t\t'\tif (loginJson.error || !refreshToken) {',\n\t\t'\t\tthrow new Error(`Login failed: ${JSON.stringify(loginJson).slice(0, 800)}`);',\n\t\t'\t}',\n\t\t'\tconst accessJson = await requestJson(`${serverUrl}/accessToken`, { refreshToken });',\n\t\t'\tconst accessToken = accessJson && accessJson.result && accessJson.result.token;',\n\t\t'\tconst user = accessJson && accessJson.result && accessJson.result.user;',\n\t\t'\tif (accessJson.error || !accessToken || !user) {',\n\t\t'\t\tthrow new Error(`Access token failed: ${JSON.stringify(accessJson).slice(0, 800)}`);',\n\t\t'\t}',\n\t\t'\treturn { refreshToken, accessToken, user: normalizeQaUserForBrowser(user) };',\n\t\t'}',\n\t\t'',\n\t\t'async function launchBrowser(puppeteer) {',\n\t\t'\tconst browserUrl = process.env.RESOLVEIO_RUNNER_QA_BROWSER_URL || process.env.RESOLVEIO_SUPPORT_QA_BROWSER_URL || \"\";',\n\t\t'\tif (browserUrl) {',\n\t\t'\t\treturn puppeteer.connect({ browserURL: browserUrl, protocolTimeout: 30000, defaultViewport: null });',\n\t\t'\t}',\n\t\t'\tconst launchOptions = {',\n\t\t'\t\theadless: true,',\n\t\t'\t\tdefaultViewport: { width: viewportWidth, height: viewportHeight },',\n\t\t'\t\targs: [\"--no-sandbox\", \"--disable-setuid-sandbox\", \"--disable-dev-shm-usage\", `--window-size=${viewportWidth},${viewportHeight}`]',\n\t\t'\t};',\n\t\t'\tif (process.env.PUPPETEER_EXECUTABLE_PATH || process.env.CHROME_BIN) {',\n\t\t'\t\tlaunchOptions.executablePath = process.env.PUPPETEER_EXECUTABLE_PATH || process.env.CHROME_BIN;',\n\t\t'\t}',\n\t\t'\treturn puppeteer.launch(launchOptions);',\n\t\t'}',\n\t\t'',\n\t\t'async function resetBrowserState(page) {',\n\t\t'\tawait waitForHttpReady(clientUrl, \"QA client\");',\n\t\t'\tawait page.goto(clientUrl, { waitUntil: \"domcontentloaded\", timeout: 45000 });',\n\t\t'\tawait page.evaluate(async () => {',\n\t\t'\t\ttry {',\n\t\t'\t\t\tconst registrations = await navigator.serviceWorker.getRegistrations();',\n\t\t'\t\t\tawait Promise.all(registrations.map((registration) => registration.unregister().catch(() => false)));',\n\t\t'\t\t} catch (error) {}',\n\t\t'\t\ttry {',\n\t\t'\t\t\tif (window.caches && window.caches.keys) {',\n\t\t'\t\t\t\tconst keys = await window.caches.keys();',\n\t\t'\t\t\t\tawait Promise.all(keys.map((key) => window.caches.delete(key).catch(() => false)));',\n\t\t'\t\t\t}',\n\t\t'\t\t} catch (error) {}',\n\t\t'\t\ttry {',\n\t\t'\t\t\tif (window.indexedDB && window.indexedDB.databases) {',\n\t\t'\t\t\t\tconst databases = await window.indexedDB.databases();',\n\t\t'\t\t\t\tawait Promise.all(databases.filter((database) => database && database.name).map((database) => new Promise((resolve) => {',\n\t\t'\t\t\t\t\tconst request = window.indexedDB.deleteDatabase(database.name);',\n\t\t'\t\t\t\t\trequest.onsuccess = () => resolve(true);',\n\t\t'\t\t\t\t\trequest.onerror = () => resolve(false);',\n\t\t'\t\t\t\t\trequest.onblocked = () => resolve(false);',\n\t\t'\t\t\t\t})));',\n\t\t'\t\t\t}',\n\t\t'\t\t} catch (error) {}',\n\t\t'\t\tlocalStorage.clear();',\n\t\t'\t\tsessionStorage.clear();',\n\t\t'\t});',\n\t\t'}',\n\t\t'',\n\t\t'async function seedAuth(page, auth) {',\n\t\t'\tauth.user = normalizeQaUserForBrowser(auth.user);',\n\t\t'\tawait page.evaluate((payload) => {',\n\t\t'\t\tlocalStorage.setItem(\"refreshToken\", payload.refreshToken);',\n\t\t'\t\tlocalStorage.setItem(\"accessToken\", payload.accessToken);',\n\t\t'\t\tlocalStorage.setItem(\"user\", JSON.stringify(payload.user));',\n\t\t'\t\tlocalStorage.setItem(\"lastURL\", payload.lastURL);',\n\t\t'\t\tlocalStorage.setItem(\"resolveio.runnerQaAuthBootstrappedAt\", payload.bootstrappedAt);',\n\t\t'\t}, { ...auth, lastURL: targetRoute, bootstrappedAt: new Date().toISOString() });',\n\t\t'}',\n\t\t'',\n\t\t'async function patchBrowserQaUser(page) {',\n\t\t'\tawait page.evaluate((nextRoute) => {',\n\t\t'\t\tlet user = {};',\n\t\t'\t\ttry { user = JSON.parse(localStorage.getItem(\"user\") || \"{}\"); } catch (error) { user = {}; }',\n\t\t'\t\tconst settings = {',\n\t\t'\t\t\ttable_color: \"#3b3ee3\",',\n\t\t'\t\t\ttable_font_color: \"#ffffff\",',\n\t\t'\t\t\tsecondary_table_color: \"#87ceeb\",',\n\t\t'\t\t\tsecondary_table_font_color: \"#000000\",',\n\t\t'\t\t\ttertiary_table_color: \"#ff4500\",',\n\t\t'\t\t\ttertiary_table_font_color: \"#000000\",',\n\t\t'\t\t\tfont_size: 12,',\n\t\t'\t\t\tcollapsable_menu: true,',\n\t\t'\t\t\tentries_per_page: \"25\",',\n\t\t'\t\t\twarning_color: \"#ffc107\",',\n\t\t'\t\t\twarning_font_color: \"#000000\",',\n\t\t'\t\t\twarning_hover_color: \"#e0a800\",',\n\t\t'\t\t\tsuccess_color: \"#28a745\",',\n\t\t'\t\t\tsuccess_font_color: \"#ffffff\",',\n\t\t'\t\t\tsuccess_hover_color: \"#218838\",',\n\t\t'\t\t\tdanger_color: \"#dc3545\",',\n\t\t'\t\t\tdanger_font_color: \"#ffffff\",',\n\t\t'\t\t\tdanger_hover_color: \"#c82333\",',\n\t\t'\t\t\tinfo_color: \"#17a2b8\",',\n\t\t'\t\t\tinfo_font_color: \"#ffffff\",',\n\t\t'\t\t\tinfo_hover_color: \"#138496\",',\n\t\t'\t\t\tprimary_color: \"#007bff\",',\n\t\t'\t\t\tprimary_font_color: \"#ffffff\",',\n\t\t'\t\t\tprimary_hover_color: \"#0069d9\",',\n\t\t'\t\t\tsecondary_color: \"#868e96\",',\n\t\t'\t\t\tsecondary_font_color: \"#ffffff\",',\n\t\t'\t\t\tsecondary_hover_color: \"#5a6268\",',\n\t\t'\t\t\trouting_preference: \"\",',\n\t\t'\t\t\topening_route: nextRoute,',\n\t\t'\t\t\trio_select_search_mode: \"exact\"',\n\t\t'\t\t};',\n\t\t'\t\tuser = {',\n\t\t'\t\t\t...user,',\n\t\t'\t\t\tother: {',\n\t\t'\t\t\t\t...(user.other || {}),',\n\t\t'\t\t\t\tyards: Array.isArray(user.other && user.other.yards) ? user.other.yards : [],',\n\t\t'\t\t\t\ttour_completed: true,',\n\t\t'\t\t\t\ttook_tour: true,',\n\t\t'\t\t\t\tcore_tour_completed: true,',\n\t\t'\t\t\t\twelcome_tour_completed: true,',\n\t\t'\t\t\t\ttop_navigation_tour_completed: true,',\n\t\t'\t\t\t\tuser_settings_tour_completed: true',\n\t\t'\t\t\t},',\n\t\t'\t\t\tsettings: { ...settings, ...(user.settings || {}), opening_route: nextRoute }',\n\t\t'\t\t};',\n\t\t'\t\tlocalStorage.setItem(\"user\", JSON.stringify(user));',\n\t\t'\t\tlocalStorage.setItem(\"lastURL\", nextRoute);',\n\t\t'\t\tlocalStorage.setItem(\"resolveio.runnerQaPostLoginGateRepairedAt\", new Date().toISOString());',\n\t\t'\t});',\n\t\t'}',\n\t\t'',\n\t\t'async function dismissVisibleTourOrSetupGate(page) {',\n\t\t'\treturn page.evaluate(() => {',\n\t\t'\t\tconst controls = Array.from(document.querySelectorAll(\"button, a, [role=\\'button\\']\"));',\n\t\t'\t\tconst control = controls.find((candidate) => {',\n\t\t'\t\t\tconst text = (candidate.textContent || \"\").replace(/\\\\s+/g, \" \").trim();',\n\t\t'\t\t\treturn /^(skip|finish|done|close)$/i.test(text);',\n\t\t'\t\t});',\n\t\t'\t\tif (control && typeof control.click === \"function\") {',\n\t\t'\t\t\tcontrol.click();',\n\t\t'\t\t\treturn true;',\n\t\t'\t\t}',\n\t\t'\t\treturn false;',\n\t\t'\t});',\n\t\t'}',\n\t\t'',\n\t\t'function isPostLoginSetupOrTourGate(currentRoute, summary) {',\n\t\t'\tconst text = String(summary && summary.bodyTextSnippet || \"\");',\n\t\t'\treturn currentRoute === \"/user-settings/settings\" || /Top Navigation Step \\\\d+ of \\\\d+|\\\\bSkip\\\\b|User Settings/i.test(text);',\n\t\t'}',\n\t\t'',\n\t\t'async function repairPostLoginSetupOrTourGate(page, expectedRoute) {',\n\t\t'\tif (process.env.RESOLVEIO_RUNNER_QA_DISABLE_POST_LOGIN_ROUTE_REPAIR === \"true\" || process.env.RESOLVEIO_SUPPORT_QA_DISABLE_POST_LOGIN_ROUTE_REPAIR === \"true\") {',\n\t\t'\t\treturn false;',\n\t\t'\t}',\n\t\t'\tconst current = normalizeRoutePath(await page.evaluate(() => location.href));',\n\t\t'\tconst summary = await pageSummary(page).catch(() => ({}));',\n\t\t'\tif (!isPostLoginSetupOrTourGate(current, summary)) {',\n\t\t'\t\treturn false;',\n\t\t'\t}',\n\t\t'\tawait ensureLocalQaUser().catch(() => false);',\n\t\t'\tawait refreshAuthAndSeed(page).catch(() => undefined);',\n\t\t'\tawait dismissVisibleTourOrSetupGate(page).catch(() => false);',\n\t\t'\tawait patchBrowserQaUser(page);',\n\t\t'\tawait page.goto(`${clientUrl}${expectedRoute}`, { waitUntil: \"domcontentloaded\", timeout: 60000 });',\n\t\t'\tawait delay(1500);',\n\t\t'\tawait dismissVisibleTourOrSetupGate(page).catch(() => false);',\n\t\t'\tawait patchBrowserQaUser(page);',\n\t\t'\tawait delay(500);',\n\t\t'\tconst repairedCurrent = normalizeRoutePath(await page.evaluate(() => location.href));',\n\t\t'\treturn repairedCurrent === expectedRoute;',\n\t\t'}',\n\t\t'',\n\t\t'function delay(ms) {',\n\t\t'\treturn new Promise((resolve) => setTimeout(resolve, ms));',\n\t\t'}',\n\t\t'',\n\t\t'function normalizeRoutePath(value) {',\n\t\t'\ttry {',\n\t\t'\t\t\tconst parsed = new URL(value, clientUrl);',\n\t\t'\t\t\tlet pathname = parsed.pathname || \"/\";',\n\t\t'\t\t\tpathname = pathname.replace(/\\\\/+$/, \"\") || \"/\";',\n\t\t'\t\t\treturn pathname;',\n\t\t'\t}',\n\t\t'\tcatch (error) {',\n\t\t'\t\t\treturn String(value || \"/\").split(\"?\")[0].replace(/\\\\/+$/, \"\") || \"/\";',\n\t\t'\t}',\n\t\t'}',\n\t\t'',\n\t\t'function isGenericAuthTargetRoute(routePath) {',\n\t\t'\treturn [\"/\", \"/screen\", \"/home\"].includes(normalizeRoutePath(routePath));',\n\t\t'}',\n\t\t'',\n\t\t'function isAuthenticatedNonSetupRoute(summary) {',\n\t\t'\tconst current = normalizeRoutePath(summary && summary.url || \"\");',\n\t\t'\treturn !!summary',\n\t\t'\t\t&& !!summary.hasRefreshToken',\n\t\t'\t\t&& !!summary.hasAccessToken',\n\t\t'\t\t&& !!summary.hasUser',\n\t\t'\t\t&& !summary.hasLoginText',\n\t\t'\t\t&& !summary.hasOfflineModeText',\n\t\t'\t\t&& current !== \"/user-settings/settings\";',\n\t\t'}',\n\t\t'',\n\t\t'async function assertTargetRoute(page) {',\n\t\t'\tconst expected = normalizeRoutePath(targetRoute);',\n\t\t'\tif (expected === \"/\") {',\n\t\t'\t\treturn;',\n\t\t'\t}',\n\t\t'\tconst current = normalizeRoutePath(await page.evaluate(() => location.href));',\n\t\t'\tif (current !== expected) {',\n\t\t'\t\tconst repaired = await repairPostLoginSetupOrTourGate(page, expected);',\n\t\t'\t\tif (repaired) {',\n\t\t'\t\t\treturn;',\n\t\t'\t\t}',\n\t\t'\t\tconst summary = await pageSummary(page).catch(() => ({}));',\n\t\t'\t\tif (isGenericAuthTargetRoute(expected) && isAuthenticatedNonSetupRoute(summary)) {',\n\t\t'\t\t\treturn;',\n\t\t'\t\t}',\n\t\t'\t\tthrow new Error(`QA auth bootstrap reached ${current}, not requested target route ${expected}. This is a route blocker; do not continue browser QA until the runner/app can reach the requested screen. Page summary: ${JSON.stringify(summary).slice(0, 1200)}`);',\n\t\t'\t}',\n\t\t'}',\n\t\t'',\n\t\t'async function waitForStableTargetRoute(page) {',\n\t\t'\tconst expected = normalizeRoutePath(targetRoute);',\n\t\t'\tif (isGenericAuthTargetRoute(expected)) {',\n\t\t'\t\treturn;',\n\t\t'\t}',\n\t\t'\tconst deadline = Date.now() + Number(process.env.RESOLVEIO_RUNNER_QA_ROUTE_STABILITY_TIMEOUT_MS || process.env.RESOLVEIO_SUPPORT_QA_ROUTE_STABILITY_TIMEOUT_MS || 7000);',\n\t\t'\tlet stableSince = 0;',\n\t\t'\tlet lastCurrent = \"\";',\n\t\t'\twhile (Date.now() < deadline) {',\n\t\t'\t\tconst current = normalizeRoutePath(await page.evaluate(() => location.href));',\n\t\t'\t\tlastCurrent = current;',\n\t\t'\t\tif (current === expected) {',\n\t\t'\t\t\tstableSince = stableSince || Date.now();',\n\t\t'\t\t\tif (Date.now() - stableSince >= Number(process.env.RESOLVEIO_RUNNER_QA_ROUTE_STABLE_MS || process.env.RESOLVEIO_SUPPORT_QA_ROUTE_STABLE_MS || 2500)) {',\n\t\t'\t\t\t\treturn;',\n\t\t'\t\t\t}',\n\t\t'\t\t}',\n\t\t'\t\telse {',\n\t\t'\t\t\tstableSince = 0;',\n\t\t'\t\t}',\n\t\t'\t\tawait delay(250);',\n\t\t'\t}',\n\t\t'\tconst summary = await pageSummary(page).catch(() => ({}));',\n\t\t'\tthrow new Error(`QA auth bootstrap route was not stable on requested target ${expected}; last route was ${lastCurrent || \"unknown\"}. This is a route blocker; do not continue browser QA until the runner/app can remain on the requested screen. Page summary: ${JSON.stringify(summary).slice(0, 1200)}`);',\n\t\t'}',\n\t\t'',\n\t\t'async function dismissNavigationOverlays(page) {',\n\t\t'\ttry {',\n\t\t'\t\tawait page.keyboard.press(\"Escape\");',\n\t\t'\t} catch (error) {}',\n\t\t'\ttry {',\n\t\t'\t\tawait page.mouse.move(16, Math.max(120, viewportHeight - 24));',\n\t\t'\t} catch (error) {}',\n\t\t'\ttry {',\n\t\t'\t\tawait page.evaluate(() => {',\n\t\t'\t\t\tif (document.activeElement && typeof document.activeElement.blur === \"function\") document.activeElement.blur();',\n\t\t'\t\t\tdocument.body && document.body.dispatchEvent(new MouseEvent(\"mousemove\", { bubbles: true, clientX: 16, clientY: Math.max(120, window.innerHeight - 24) }));',\n\t\t'\t\t});',\n\t\t'\t} catch (error) {}',\n\t\t'\tawait delay(350);',\n\t\t'}',\n\t\t'',\n\t\t'async function assertSummaryOnTargetRoute(summary) {',\n\t\t'\tconst expected = normalizeRoutePath(targetRoute);',\n\t\t'\tif (expected === \"/\") {',\n\t\t'\t\treturn;',\n\t\t'\t}',\n\t\t'\tconst current = normalizeRoutePath(summary && summary.url || \"\");',\n\t\t'\tif (current !== expected) {',\n\t\t'\t\tif (isGenericAuthTargetRoute(expected) && isAuthenticatedNonSetupRoute(summary)) {',\n\t\t'\t\t\treturn;',\n\t\t'\t\t}',\n\t\t'\t\tthrow new Error(`QA auth bootstrap final summary is on ${current}, not requested target route ${expected}. This is a route blocker; refusing to write a false pass. Page summary: ${JSON.stringify(summary).slice(0, 1200)}`);',\n\t\t'\t}',\n\t\t'}',\n\t\t'',\n\t\t'async function logoutExistingBrowserSession(page) {',\n\t\t'\ttry {',\n\t\t'\t\tawait page.goto(clientUrl, { waitUntil: \"domcontentloaded\", timeout: 45000 });',\n\t\t'\t\tawait delay(500);',\n\t\t'\t\tawait page.evaluate(() => {',\n\t\t'\t\t\tconst controls = Array.from(document.querySelectorAll(\"button, a, [role=\\'button\\']\"));',\n\t\t'\t\t\tconst logoutControl = controls.find((control) => /(^|\\\\s)logout(\\\\s|$)/i.test((control.textContent || \"\").trim()));',\n\t\t'\t\t\tif (logoutControl && typeof logoutControl.click === \"function\") {',\n\t\t'\t\t\t\tlogoutControl.click();',\n\t\t'\t\t\t\treturn true;',\n\t\t'\t\t\t}',\n\t\t'\t\t\treturn false;',\n\t\t'\t\t});',\n\t\t'\t\tawait delay(1000);',\n\t\t'\t}',\n\t\t'\tcatch (error) {}',\n\t\t'}',\n\t\t'',\n\t\t'async function waitForAuthenticatedApp(page) {',\n\t\t'\tconst url = `${clientUrl}${targetRoute}`;',\n\t\t'\tawait page.goto(url, { waitUntil: \"domcontentloaded\", timeout: 60000 });',\n\t\t'\tawait page.waitForFunction(() => {',\n\t\t'\t\tconst text = (document.body && document.body.innerText || \"\").replace(/\\\\s+/g, \" \").trim();',\n\t\t'\t\tconst hasTokens = !!localStorage.getItem(\"refreshToken\") && !!localStorage.getItem(\"accessToken\") && !!localStorage.getItem(\"user\");',\n\t\t'\t\tconst hasLogin = /Employee\\\\/Customer Login|Employee Sign In|Customer Access|Unable to sign in/i.test(text);',\n\t\t'\t\tconst hasOffline = text.includes(\"*** OFFLINE MODE ***\");',\n\t\t'\t\treturn hasTokens && !hasLogin && !hasOffline && text.length > 40;',\n\t\t'\t}, { timeout: Number(process.env.RESOLVEIO_RUNNER_QA_AUTH_TIMEOUT_MS || process.env.RESOLVEIO_SUPPORT_QA_AUTH_TIMEOUT_MS || 60000) });',\n\t\t'\tawait delay(1000);',\n\t\t'\tawait assertTargetRoute(page);',\n\t\t'\tawait waitForStableTargetRoute(page);',\n\t\t'\tawait dismissNavigationOverlays(page);',\n\t\t'}',\n\t\t'',\n\t\t'async function pageSummary(page) {',\n\t\t'\treturn page.evaluate(() => {',\n\t\t'\t\tconst bodyText = (document.body && document.body.innerText || \"\").replace(/\\\\s+/g, \" \").trim();',\n\t\t'\t\treturn {',\n\t\t'\t\t\turl: location.href,',\n\t\t'\t\t\ttitle: document.title,',\n\t\t'\t\t\thasAngularDebug: !!window.ng,',\n\t\t'\t\t\thasRefreshToken: !!localStorage.getItem(\"refreshToken\"),',\n\t\t'\t\t\thasAccessToken: !!localStorage.getItem(\"accessToken\"),',\n\t\t'\t\t\thasUser: !!localStorage.getItem(\"user\"),',\n\t\t'\t\t\thasOfflineModeText: bodyText.includes(\"*** OFFLINE MODE ***\"),',\n\t\t'\t\t\thasLoginText: /Employee\\\\/Customer Login|Employee Sign In|Customer Access|Unable to sign in/i.test(bodyText),',\n\t\t'\t\t\tbodyTextSnippet: bodyText.slice(0, 800),',\n\t\t'\t\t\tlocalStorageKeys: Object.keys(localStorage).sort()',\n\t\t'\t\t};',\n\t\t'\t});',\n\t\t'}',\n\t\t'',\n\t\t'(async () => {',\n\t\t'\tfs.mkdirSync(artifactDir, { recursive: true });',\n\t\t'\tawait waitForHttpReady(clientUrl, \"QA client\");',\n\t\t'\tawait waitForHttpReady(serverUrl, \"QA server\");',\n\t\t'\tconst puppeteer = requirePuppeteer();',\n\t\t'\tconst browser = await launchBrowser(puppeteer);',\n\t\t'\tlet page;',\n\t\t'\ttry {',\n\t\t'\t\tpage = await browser.newPage();',\n\t\t'\t\tawait page.setViewport({ width: viewportWidth, height: viewportHeight });',\n\t\t'\t\tpage.on(\"console\", (msg) => {',\n\t\t'\t\t\tconst text = msg.text();',\n\t\t'\t\t\tif ([\"error\", \"warning\"].includes(msg.type()) || /error/i.test(text)) {',\n\t\t'\t\t\t\tconsole.log(\"[browser console]\", msg.type(), text);',\n\t\t'\t\t\t}',\n\t\t'\t\t});',\n\t\t'\t\tpage.on(\"pageerror\", (error) => console.log(\"[pageerror]\", error.message));',\n\t\t'\t\tawait logoutExistingBrowserSession(page);',\n\t\t'\t\tawait resetBrowserState(page);',\n\t\t'\t\tconst auth = await login();',\n\t\t'\t\tawait seedAuth(page, auth);',\n\t\t'\t\tawait waitForAuthenticatedApp(page);',\n\t\t'\t\tconst finalPage = await pageSummary(page);',\n\t\t'\t\tawait assertSummaryOnTargetRoute(finalPage);',\n\t\t'\t\tawait page.screenshot({ path: readyScreenshotPath, type: \"jpeg\", quality: 82, fullPage: false });',\n\t\t'\t\tconst summary = {',\n\t\t'\t\t\tstatus: \"pass\",',\n\t\t'\t\t\tclientUrl,',\n\t\t'\t\t\tserverUrl,',\n\t\t'\t\t\ttargetRoute,',\n\t\t'\t\t\tscreenshot: readyScreenshotPath,',\n\t\t'\t\t\tuser: { _id: auth.user && auth.user._id, username: auth.user && auth.user.username, fullname: auth.user && auth.user.fullname },',\n\t\t'\t\t\tpage: finalPage',\n\t\t'\t\t};',\n\t\t'\t\twriteResult(summary);',\n\t\t'\t\tconsole.log(JSON.stringify(summary, null, 2));',\n\t\t'\t}',\n\t\t'\tcatch (error) {',\n\t\t'\t\tlet summary = { status: \"fail\", clientUrl, serverUrl, targetRoute, screenshot: failureScreenshotPath, error: error && error.stack || String(error) };',\n\t\t'\t\ttry {',\n\t\t'\t\t\tif (page) {',\n\t\t'\t\t\t\tawait page.screenshot({ path: failureScreenshotPath, type: \"jpeg\", quality: 82, fullPage: false });',\n\t\t'\t\t\t\tsummary.page = await pageSummary(page);',\n\t\t'\t\t\t}',\n\t\t'\t\t} catch (screenshotError) {',\n\t\t'\t\t\tsummary.screenshotError = screenshotError && screenshotError.stack || String(screenshotError);',\n\t\t'\t\t}',\n\t\t'\t\twriteResult(summary);',\n\t\t'\t\tconsole.error(JSON.stringify(summary, null, 2));',\n\t\t'\t\tprocess.exitCode = 1;',\n\t\t'\t}',\n\t\t'\tfinally {',\n\t\t'\t\tif (browser && typeof browser.close === \"function\") {',\n\t\t'\t\t\tawait browser.close().catch(() => undefined);',\n\t\t'\t\t}',\n\t\t'\t\tprocess.exit(process.exitCode || 0);',\n\t\t'\t}',\n\t\t'})();',\n\t\t''\n\t].join('\\n');\n}\n"]}
@@ -348,6 +348,14 @@ function buildResolveIORunnerLocalQaScript() {
348
348
  '}',
349
349
  'trap cleanup EXIT',
350
350
  'detach_keepalive() {',
351
+ ' if ! probe_url "$CLIENT_URL"; then',
352
+ ' echo "ResolveIO AI runner QA keepalive refused: client URL is no longer reachable at $CLIENT_URL." | tee -a "$ARTIFACT_DIR/runner.log"',
353
+ ' return 1',
354
+ ' fi',
355
+ ' if [ "${SERVER_REQUIRED:-0}" = "1" ] && ! probe_url "$SERVER_URL"; then',
356
+ ' echo "ResolveIO AI runner QA keepalive refused: server URL is no longer reachable at $SERVER_URL." | tee -a "$ARTIFACT_DIR/runner.log"',
357
+ ' return 4',
358
+ ' fi',
351
359
  ' janitor_update_manifest_status ready_keepalive',
352
360
  ' janitor_write_cleanup_status ready_keepalive 0',
353
361
  ' rm -rf "$LOCK_DIR" >/dev/null 2>&1 || true',
@@ -500,7 +508,7 @@ function buildResolveIORunnerLocalQaScript() {
500
508
  ' echo "ResolveIO AI runner QA local app ready at $CLIENT_URL";',
501
509
  ' echo "$SERVER_PID" > "$ARTIFACT_DIR/server.pid";',
502
510
  ' echo "$CLIENT_PID" > "$ARTIFACT_DIR/client.pid";',
503
- ' if truthy "$KEEPALIVE"; then detach_keepalive; fi;',
511
+ ' if truthy "$KEEPALIVE"; then detach_keepalive || exit $?; fi;',
504
512
  ' exit 0',
505
513
  ' ;;',
506
514
  ' 2) echo "ResolveIO AI runner QA client process exited before $CLIENT_URL became ready. See $ARTIFACT_DIR/client.log"; exit 2 ;;',