@resolveio/server-lib 22.3.84 → 22.3.86
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
|
@@ -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.
|
|
31
|
-
'const failureScreenshotPath = path.join(artifactDir, "auth-bootstrap-failed.
|
|
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:
|
|
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:
|
|
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
|
|
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 ;;',
|
|
@@ -717,6 +725,16 @@ function buildResolveIORunnerQaLiveDataSeederScript() {
|
|
|
717
725
|
' try { return JSON.parse(fs.readFileSync(filePath, "utf8")); } catch (error) { return null; }',
|
|
718
726
|
'}',
|
|
719
727
|
'',
|
|
728
|
+
'function preserveExistingSeedResult(reason) {',
|
|
729
|
+
' const existing = readJsonIfExists(resultPath);',
|
|
730
|
+
' const status = String(existing && existing.status || "").toLowerCase();',
|
|
731
|
+
' if (["pass", "needs-data"].includes(status)) {',
|
|
732
|
+
' const preserved = { ...existing, reused_existing: true, reuse_reason: reason, checked_at: new Date().toISOString() };',
|
|
733
|
+
' console.log(JSON.stringify(preserved, null, 2));',
|
|
734
|
+
' process.exit(0);',
|
|
735
|
+
' }',
|
|
736
|
+
'}',
|
|
737
|
+
'',
|
|
720
738
|
'function resolveRuntimeSource() {',
|
|
721
739
|
' const envUri = process.env.RESOLVEIO_QA_LIVE_MONGO_URL || process.env.RESOLVEIO_SUPPORT_QA_LIVE_MONGO_URL || process.env.RESOLVEIO_RUNNER_QA_LIVE_MONGO_URL || "";',
|
|
722
740
|
' const envDb = process.env.RESOLVEIO_QA_LIVE_MONGO_DB || process.env.RESOLVEIO_SUPPORT_QA_LIVE_MONGO_DB || process.env.RESOLVEIO_RUNNER_QA_LIVE_MONGO_DB || "";',
|
|
@@ -996,7 +1014,10 @@ function buildResolveIORunnerQaLiveDataSeederScript() {
|
|
|
996
1014
|
' const { MongoClient } = requireMongo();',
|
|
997
1015
|
' const source = resolveRuntimeSource();',
|
|
998
1016
|
' const targetUri = process.env.RESOLVEIO_SUPPORT_QA_MONGO_URL || process.env.RESOLVEIO_RUNNER_QA_MONGO_URL || process.env.MONGO_URL || "mongodb://127.0.0.1:3001/resolveio?directConnection=true";',
|
|
999
|
-
' if (!source.uri)
|
|
1017
|
+
' if (!source.uri) {',
|
|
1018
|
+
' preserveExistingSeedResult("missing_live_mongo_uri");',
|
|
1019
|
+
' writeResult({ status: isLiveDataRequired() ? "failed" : "skipped", reason: "missing_live_mongo_uri", required: isLiveDataRequired(), result_path: resultPath }, isLiveDataRequired() ? 5 : 0);',
|
|
1020
|
+
' }',
|
|
1000
1021
|
' if (!isLocalMongoUri(targetUri)) writeResult({ status: "failed", reason: "target_mongo_must_be_localhost", target_uri_redacted: redactUri(targetUri), result_path: resultPath }, 3);',
|
|
1001
1022
|
' if (String(source.uri) === String(targetUri)) writeResult({ status: "failed", reason: "source_and_target_mongo_match", result_path: resultPath }, 3);',
|
|
1002
1023
|
' const sourceClient = new MongoClient(source.uri, { readPreference: "secondaryPreferred", serverSelectionTimeoutMS: 15000 });',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/util/ai-runner-qa-tools.ts"],"names":[],"mappings":"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","file":"ai-runner-qa-tools.js","sourcesContent":["import { buildRunnerProcessJanitorShellLibrary } from './runner-process-janitor';\n\nexport interface ResolveIORunnerQaToolBundleOptions {\n\tmode?: 'support' | 'runner';\n\tqaClientPort?: number | string;\n\tdefaultUsername?: string;\n\tdefaultPassword?: string;\n\tjobId?: string;\n\townerId?: string;\n\trunnerToken?: string;\n\tmongoRuntimePath?: string;\n\tqaLiveDataRequired?: boolean;\n\ttoolsBinPath?: string;\n\tbrowserslistPath?: string;\n\tmongodbBinaryCachePath?: string;\n\ttmpRoot?: string;\n\thomeRoot?: string;\n}\n\nfunction shellDoubleQuote(value: string): string {\n\treturn String(value || '').replace(/[\"\\\\$`]/g, '\\\\$&');\n}\n\nfunction normalizePort(value: number | string | undefined, fallback: string): string {\n\tconst parsed = Number.parseInt(String(value || ''), 10);\n\treturn Number.isFinite(parsed) && parsed > 0 ? String(parsed) : fallback;\n}\n\nfunction envVar(mode: 'support' | 'runner', suffix: string): string {\n\treturn mode === 'support' ? `RESOLVEIO_SUPPORT_QA_${suffix}` : `RESOLVEIO_RUNNER_QA_${suffix}`;\n}\n\nexport function buildResolveIORunnerQaEnvScript(options: ResolveIORunnerQaToolBundleOptions = {}): string {\n\tconst mode = options.mode || 'runner';\n\tconst altMode = mode === 'support' ? 'runner' : 'support';\n\tconst tmpRoot = options.tmpRoot || (mode === 'support' ? '/tmp/resolveio-support-qa' : '/tmp/resolveio-ai-runner-qa');\n\tconst homeRoot = options.homeRoot || `${tmpRoot}/home`;\n\tconst defaultPort = normalizePort(options.qaClientPort, '4200');\n\tconst username = shellDoubleQuote(options.defaultUsername || 'admin');\n\tconst password = shellDoubleQuote(options.defaultPassword || '');\n\tconst jobId = shellDoubleQuote(options.jobId || '');\n\tconst ownerId = shellDoubleQuote(options.ownerId || '');\n\tconst runnerToken = shellDoubleQuote(options.runnerToken || '');\n\tconst mongoRuntimePath = shellDoubleQuote(options.mongoRuntimePath || '');\n\tconst qaLiveDataRequired = options.qaLiveDataRequired === true ? 'true' : 'false';\n\tconst toolsBinPath = options.toolsBinPath || '';\n\tconst browserslistPath = options.browserslistPath || '';\n\tconst mongodbBinaryCachePath = options.mongodbBinaryCachePath || '';\n\tconst clientPortVar = envVar(mode, 'CLIENT_PORT');\n\tconst altClientPortVar = envVar(altMode, 'CLIENT_PORT');\n\tconst clientUrlVar = envVar(mode, 'CLIENT_URL');\n\tconst altClientUrlVar = envVar(altMode, 'CLIENT_URL');\n\tconst serverUrlVar = envVar(mode, 'SERVER_URL');\n\tconst altServerUrlVar = envVar(altMode, 'SERVER_URL');\n\tconst usernameVar = envVar(mode, 'USERNAME');\n\tconst altUsernameVar = envVar(altMode, 'USERNAME');\n\tconst passwordVar = envVar(mode, 'PASSWORD');\n\tconst altPasswordVar = envVar(altMode, 'PASSWORD');\n\tconst viewportWidthVar = envVar(mode, 'VIEWPORT_WIDTH');\n\tconst altViewportWidthVar = envVar(altMode, 'VIEWPORT_WIDTH');\n\tconst viewportHeightVar = envVar(mode, 'VIEWPORT_HEIGHT');\n\tconst altViewportHeightVar = envVar(altMode, 'VIEWPORT_HEIGHT');\n\tconst timeoutVar = envVar(mode, 'ANGULAR_STARTUP_TIMEOUT_SECONDS');\n\tconst altTimeoutVar = envVar(altMode, 'ANGULAR_STARTUP_TIMEOUT_SECONDS');\n\tconst prebundleVar = envVar(mode, 'ANGULAR_PREBUNDLE');\n\tconst altPrebundleVar = envVar(altMode, 'ANGULAR_PREBUNDLE');\n\tconst reuseVar = envVar(mode, 'REUSE_RUNNING');\n\tconst altReuseVar = envVar(altMode, 'REUSE_RUNNING');\n\tconst keepaliveVar = envVar(mode, 'KEEPALIVE');\n\tconst altKeepaliveVar = envVar(altMode, 'KEEPALIVE');\n\tconst mongoPortVar = envVar(mode, 'MONGO_PORT');\n\tconst altMongoPortVar = envVar(altMode, 'MONGO_PORT');\n\tconst mongoUrlVar = envVar(mode, 'MONGO_URL');\n\tconst altMongoUrlVar = envVar(altMode, 'MONGO_URL');\n\tconst browserLoopVar = mode === 'support' ? 'RESOLVEIO_SUPPORT_QA_BROWSER' : 'RESOLVEIO_RUNNER_QA_BROWSER';\n\treturn [\n\t\t`export ${mode === 'support' ? 'RESOLVEIO_SUPPORT_QA_TMP' : 'RESOLVEIO_RUNNER_QA_TMP'}=\"${'${' + (mode === 'support' ? 'RESOLVEIO_SUPPORT_QA_TMP' : 'RESOLVEIO_RUNNER_QA_TMP') + ':-' + tmpRoot + '}'}\"`,\n\t\t`export ${mode === 'support' ? 'RESOLVEIO_SUPPORT_QA_HOME' : 'RESOLVEIO_RUNNER_QA_HOME'}=\"${'${' + (mode === 'support' ? 'RESOLVEIO_SUPPORT_QA_HOME' : 'RESOLVEIO_RUNNER_QA_HOME') + ':-' + homeRoot + '}'}\"`,\n\t\t`RESOLVEIO_QA_TMP=\"${'${' + (mode === 'support' ? 'RESOLVEIO_SUPPORT_QA_TMP' : 'RESOLVEIO_RUNNER_QA_TMP') + '}'}\"`,\n\t\t`RESOLVEIO_QA_HOME=\"${'${' + (mode === 'support' ? 'RESOLVEIO_SUPPORT_QA_HOME' : 'RESOLVEIO_RUNNER_QA_HOME') + '}'}\"`,\n\t\t'mkdir -p \"$RESOLVEIO_QA_HOME/.nvm\" \"$RESOLVEIO_QA_TMP/npm-cache\" \"$RESOLVEIO_QA_TMP/mongodb-binaries\" \"$RESOLVEIO_QA_TMP/mongodb-memory-server-core\"',\n\t\t'if [ ! -f \"$RESOLVEIO_QA_HOME/.nvm/nvm.sh\" ]; then',\n\t\t' cat > \"$RESOLVEIO_QA_HOME/.nvm/nvm.sh\" <<\\'RESOLVEIO_NVM_SHIM\\'',\n\t\t'nvm() {',\n\t\t' case \"$1\" in',\n\t\t' use|install|alias) return 0 ;;',\n\t\t' current) node -v 2>/dev/null || true; return 0 ;;',\n\t\t' *) return 0 ;;',\n\t\t' esac',\n\t\t'}',\n\t\t'RESOLVEIO_NVM_SHIM',\n\t\t'fi',\n\t\t'export HOME=\"$RESOLVEIO_QA_HOME\"',\n\t\t'export NVM_DIR=\"$RESOLVEIO_QA_HOME/.nvm\"',\n\t\ttoolsBinPath ? `export PATH=\"${toolsBinPath}:$PATH\"` : '',\n\t\t'export NODE_ENV=development',\n\t\t'# Local QA must start the application HTTP server even when launched from a dedicated support-manager worker.',\n\t\t'export IS_WORKERS_ENABLED=false',\n\t\t'export IS_WORKER_INSTANCE=false',\n\t\t'export DISABLE_WORKER_SERVER_CONNECTION=false',\n\t\t'export SUPPORT_CODEX_MANAGER_PROCESS_ONLY=false',\n\t\t'export SUPPORT_AUTO_MANAGER_PROCESS_ONLY=false',\n\t\t'export AI_ASSISTANT_CODEX_MANAGER_PROCESS_ONLY=false',\n\t\tbrowserslistPath ? `export BROWSERSLIST_CONFIG=\"${browserslistPath}\"` : '',\n\t\t`export ${clientPortVar}=\"${'${' + clientPortVar + ':-${' + altClientPortVar + ':-' + defaultPort + '}}'}\"`,\n\t\t`export ${altClientPortVar}=\"${'${' + altClientPortVar + ':-${' + clientPortVar + '}}'}\"`,\n\t\t`export ${clientUrlVar}=\"${'${' + clientUrlVar + ':-${' + altClientUrlVar + ':-http://localhost:${' + clientPortVar + '}}}'}\"`,\n\t\t`export ${altClientUrlVar}=\"${'${' + altClientUrlVar + ':-${' + clientUrlVar + '}}'}\"`,\n\t\t`export ${serverUrlVar}=\"${'${' + serverUrlVar + ':-${' + altServerUrlVar + ':-http://localhost:8080}}'}\"`,\n\t\t`export ${altServerUrlVar}=\"${'${' + altServerUrlVar + ':-${' + serverUrlVar + '}}'}\"`,\n\t\t'export ADDITIONAL_ALLOWED_ORIGINS=\"${ADDITIONAL_ALLOWED_ORIGINS:-$' + clientUrlVar + '}\"',\n\t\t`export ${viewportWidthVar}=\"${'${' + viewportWidthVar + ':-${' + altViewportWidthVar + ':-1920}}'}\"`,\n\t\t`export ${altViewportWidthVar}=\"${'${' + altViewportWidthVar + ':-${' + viewportWidthVar + '}}'}\"`,\n\t\t`export ${viewportHeightVar}=\"${'${' + viewportHeightVar + ':-${' + altViewportHeightVar + ':-1080}}'}\"`,\n\t\t`export ${altViewportHeightVar}=\"${'${' + altViewportHeightVar + ':-${' + viewportHeightVar + '}}'}\"`,\n\t\t`export ${timeoutVar}=\"${'${' + timeoutVar + ':-${' + altTimeoutVar + ':-900}}'}\"`,\n\t\t`export ${altTimeoutVar}=\"${'${' + altTimeoutVar + ':-${' + timeoutVar + '}}'}\"`,\n\t\t`export ${prebundleVar}=\"${'${' + prebundleVar + ':-${' + altPrebundleVar + ':-false}}'}\"`,\n\t\t`export ${altPrebundleVar}=\"${'${' + altPrebundleVar + ':-${' + prebundleVar + '}}'}\"`,\n\t\t`export ${reuseVar}=\"${'${' + reuseVar + ':-${' + altReuseVar + ':-true}}'}\"`,\n\t\t`export ${altReuseVar}=\"${'${' + altReuseVar + ':-${' + reuseVar + '}}'}\"`,\n\t\t`export ${keepaliveVar}=\"${'${' + keepaliveVar + ':-${' + altKeepaliveVar + ':-false}}'}\"`,\n\t\t`export ${altKeepaliveVar}=\"${'${' + altKeepaliveVar + ':-${' + keepaliveVar + '}}'}\"`,\n\t\t`export ${mongoPortVar}=\"${'${' + mongoPortVar + ':-${' + altMongoPortVar + ':-3001}}'}\"`,\n\t\t`export ${altMongoPortVar}=\"${'${' + altMongoPortVar + ':-${' + mongoPortVar + '}}'}\"`,\n\t\t`export ${mongoUrlVar}=\"${'${' + mongoUrlVar + ':-${' + altMongoUrlVar + ':-mongodb://127.0.0.1:${' + mongoPortVar + '}/resolveio?directConnection=true}}'}\"`,\n\t\t`export ${altMongoUrlVar}=\"${'${' + altMongoUrlVar + ':-${' + mongoUrlVar + '}}'}\"`,\n\t\t`export MONGO_URL=\"${'${MONGO_URL:-$' + mongoUrlVar + '}'}\"`,\n\t\t`export ${usernameVar}=\"${'${' + usernameVar + ':-${' + altUsernameVar + ':-' + username + '}}'}\"`,\n\t\t`export ${altUsernameVar}=\"${'${' + altUsernameVar + ':-${' + usernameVar + '}}'}\"`,\n\t\t`export ${passwordVar}=\"${'${' + passwordVar + ':-${' + altPasswordVar + ':-' + password + '}}'}\"`,\n\t\t`export ${altPasswordVar}=\"${'${' + altPasswordVar + ':-${' + passwordVar + '}}'}\"`,\n\t\t`export ${envVar(mode, 'JOB_ID')}=\"${'${' + envVar(mode, 'JOB_ID') + ':-${' + envVar(altMode, 'JOB_ID') + ':-' + jobId + '}}'}\"`,\n\t\t`export ${envVar(altMode, 'JOB_ID')}=\"${'${' + envVar(altMode, 'JOB_ID') + ':-${' + envVar(mode, 'JOB_ID') + '}}'}\"`,\n\t\t`export ${envVar(mode, 'OWNER_ID')}=\"${'${' + envVar(mode, 'OWNER_ID') + ':-${' + envVar(altMode, 'OWNER_ID') + ':-' + ownerId + '}}'}\"`,\n\t\t`export ${envVar(altMode, 'OWNER_ID')}=\"${'${' + envVar(altMode, 'OWNER_ID') + ':-${' + envVar(mode, 'OWNER_ID') + '}}'}\"`,\n\t\t`export ${envVar(mode, 'RUNNER_TOKEN')}=\"${'${' + envVar(mode, 'RUNNER_TOKEN') + ':-${' + envVar(altMode, 'RUNNER_TOKEN') + ':-' + runnerToken + '}}'}\"`,\n\t\t`export ${envVar(altMode, 'RUNNER_TOKEN')}=\"${'${' + envVar(altMode, 'RUNNER_TOKEN') + ':-${' + envVar(mode, 'RUNNER_TOKEN') + '}}'}\"`,\n\t\tmongoRuntimePath ? `export ${envVar(mode, 'MONGO_RUNTIME_PATH')}=\"${'${' + envVar(mode, 'MONGO_RUNTIME_PATH') + ':-${' + envVar(altMode, 'MONGO_RUNTIME_PATH') + ':-' + mongoRuntimePath + '}}'}\"` : '',\n\t\tmongoRuntimePath ? `export ${envVar(altMode, 'MONGO_RUNTIME_PATH')}=\"${'${' + envVar(altMode, 'MONGO_RUNTIME_PATH') + ':-${' + envVar(mode, 'MONGO_RUNTIME_PATH') + '}}'}\"` : '',\n\t\tmongoRuntimePath ? `export RESOLVEIO_QA_MONGO_RUNTIME_PATH=\"${'${RESOLVEIO_QA_MONGO_RUNTIME_PATH:-$' + envVar(mode, 'MONGO_RUNTIME_PATH') + '}'}\"` : '',\n\t\t`export ${envVar(mode, 'LIVE_DATA_REQUIRED')}=\"${'${' + envVar(mode, 'LIVE_DATA_REQUIRED') + ':-${' + envVar(altMode, 'LIVE_DATA_REQUIRED') + ':-' + qaLiveDataRequired + '}}'}\"`,\n\t\t`export ${envVar(altMode, 'LIVE_DATA_REQUIRED')}=\"${'${' + envVar(altMode, 'LIVE_DATA_REQUIRED') + ':-${' + envVar(mode, 'LIVE_DATA_REQUIRED') + '}}'}\"`,\n\t\t`export RESOLVEIO_QA_LIVE_DATA_REQUIRED=\"${'${RESOLVEIO_QA_LIVE_DATA_REQUIRED:-$' + envVar(mode, 'LIVE_DATA_REQUIRED') + '}'}\"`,\n\t\t'export PUPPETEER_CACHE_DIR=\"${PUPPETEER_CACHE_DIR:-/var/lib/resolveio/puppeteer}\"',\n\t\t'if [ ! -d \"$PUPPETEER_CACHE_DIR\" ] || [ ! -w \"$PUPPETEER_CACHE_DIR\" ]; then',\n\t\t' export PUPPETEER_CACHE_DIR=\"$RESOLVEIO_QA_TMP/puppeteer\"',\n\t\t' mkdir -p \"$PUPPETEER_CACHE_DIR\"',\n\t\t'fi',\n\t\t'export PLAYWRIGHT_BROWSERS_PATH=\"${PLAYWRIGHT_BROWSERS_PATH:-$RESOLVEIO_QA_HOME/.cache/ms-playwright}\"',\n\t\t'if [ -n \"${PUPPETEER_EXECUTABLE_PATH:-}\" ] && [ -x \"$PUPPETEER_EXECUTABLE_PATH\" ]; then',\n\t\t' export CHROME_BIN=\"${CHROME_BIN:-$PUPPETEER_EXECUTABLE_PATH}\"',\n\t\t'elif [ -n \"${CHROME_BIN:-}\" ] && [ -x \"$CHROME_BIN\" ]; then',\n\t\t' export PUPPETEER_EXECUTABLE_PATH=\"$CHROME_BIN\"',\n\t\t'else',\n\t\t` for ${browserLoopVar} in \"$PUPPETEER_CACHE_DIR\"/chrome-headless-shell/linux-*/chrome-headless-shell-linux64/chrome-headless-shell \"$PUPPETEER_CACHE_DIR\"/chrome/linux-*/chrome-linux64/chrome \"$PLAYWRIGHT_BROWSERS_PATH\"/chromium_headless_shell-*/chrome-headless-shell-linux64/chrome-headless-shell \"$PLAYWRIGHT_BROWSERS_PATH\"/chromium-*/chrome-linux64/chrome /usr/bin/google-chrome-stable /usr/bin/google-chrome /usr/bin/chromium /usr/bin/chromium-browser; do`,\n\t\t` if [ -x \"$${browserLoopVar}\" ]; then`,\n\t\t` export PUPPETEER_EXECUTABLE_PATH=\"$${browserLoopVar}\"`,\n\t\t` export CHROME_BIN=\"$${browserLoopVar}\"`,\n\t\t' break',\n\t\t' fi',\n\t\t' done',\n\t\t'fi',\n\t\t`unset ${browserLoopVar}`,\n\t\t'export NPM_CONFIG_CACHE=\"$RESOLVEIO_QA_TMP/npm-cache\"',\n\t\t'export NPM_CONFIG_PREFER_OFFLINE=true',\n\t\t'export NPM_CONFIG_PRODUCTION=false',\n\t\t'export npm_config_production=false',\n\t\t'export RESOLVEIO_SUPPORT_MONGOMS_PACKAGE_ROOT=\"$RESOLVEIO_QA_TMP/mongodb-memory-server-core\"',\n\t\t'export MONGOMS_DOWNLOAD_DIR=\"$RESOLVEIO_QA_TMP/mongodb-binaries\"',\n\t\tmongodbBinaryCachePath ? `export RESOLVEIO_SUPPORT_SHARED_MONGOMS_DOWNLOAD_DIR=\"${mongodbBinaryCachePath}\"` : '',\n\t\t'export MONGOMS_VERSION=\"${MONGOMS_VERSION:-7.0.14}\"',\n\t\t''\n\t].filter((line) => line !== '').join('\\n');\n}\n\nexport function buildResolveIORunnerLocalQaScript(): string {\n\treturn [\n\t\t'#!/usr/bin/env bash',\n\t\t'set -u',\n\t\t'TOOLS_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"',\n\t\t'source \"$TOOLS_DIR/env.sh\"',\n\t\t'PROJECT_ROOT=\"${1:-$(pwd)}\"',\n\t\t'PROJECT_ROOT=\"$(cd \"$PROJECT_ROOT\" && pwd)\"',\n\t\t'ARTIFACT_DIR=\"$PROJECT_ROOT/qa-artifacts\"',\n\t\t'mkdir -p \"$ARTIFACT_DIR\"',\n\t\t'CLIENT_URL=\"${RESOLVEIO_RUNNER_QA_CLIENT_URL:-${RESOLVEIO_SUPPORT_QA_CLIENT_URL:-http://localhost:${RESOLVEIO_RUNNER_QA_CLIENT_PORT:-${RESOLVEIO_SUPPORT_QA_CLIENT_PORT:-4200}}}}\"',\n\t\t'SERVER_URL=\"${RESOLVEIO_RUNNER_QA_SERVER_URL:-${RESOLVEIO_SUPPORT_QA_SERVER_URL:-http://localhost:8080}}\"',\n\t\t'CLIENT_PORT=\"${RESOLVEIO_RUNNER_QA_CLIENT_PORT:-${RESOLVEIO_SUPPORT_QA_CLIENT_PORT:-4200}}\"',\n\t\t'SERVER_PORT=\"${RESOLVEIO_RUNNER_QA_SERVER_PORT:-${RESOLVEIO_SUPPORT_QA_SERVER_PORT:-8080}}\"',\n\t\t'MONGO_PORT=\"${RESOLVEIO_RUNNER_QA_MONGO_PORT:-${RESOLVEIO_SUPPORT_QA_MONGO_PORT:-3001}}\"',\n\t\t'INSPECT_PORT=\"${RESOLVEIO_RUNNER_QA_INSPECT_PORT:-${RESOLVEIO_SUPPORT_QA_INSPECT_PORT:-9229}}\"',\n\t\t'STARTUP_TIMEOUT=\"${RESOLVEIO_RUNNER_QA_ANGULAR_STARTUP_TIMEOUT_SECONDS:-${RESOLVEIO_SUPPORT_QA_ANGULAR_STARTUP_TIMEOUT_SECONDS:-900}}\"',\n\t\t'ANGULAR_PREBUNDLE=\"${RESOLVEIO_RUNNER_QA_ANGULAR_PREBUNDLE:-${RESOLVEIO_SUPPORT_QA_ANGULAR_PREBUNDLE:-false}}\"',\n\t\t'REUSE_RUNNING=\"${RESOLVEIO_RUNNER_QA_REUSE_RUNNING:-${RESOLVEIO_SUPPORT_QA_REUSE_RUNNING:-true}}\"',\n\t\t'if [[ \"$(basename \"$TOOLS_DIR\")\" == *support* ]]; then',\n\t\t' KEEPALIVE=\"${RESOLVEIO_SUPPORT_QA_KEEPALIVE:-${RESOLVEIO_RUNNER_QA_KEEPALIVE:-false}}\"',\n\t\t'else',\n\t\t' KEEPALIVE=\"${RESOLVEIO_RUNNER_QA_KEEPALIVE:-${RESOLVEIO_SUPPORT_QA_KEEPALIVE:-false}}\"',\n\t\t'fi',\n\t\t'case \"${KEEPALIVE,,}\" in',\n\t\t' true|1|yes|on) exec >> \"$ARTIFACT_DIR/runner.log\" 2>&1 ;;',\n\t\t'esac',\n\t\t'SERVER_STABLE_SECONDS=\"${RESOLVEIO_RUNNER_QA_SERVER_STABLE_SECONDS:-${RESOLVEIO_SUPPORT_QA_SERVER_STABLE_SECONDS:-20}}\"',\n\t\t'LOCK_DIR=\"$ARTIFACT_DIR/.qa.lock\"',\n\t\t'SERVER_PID=\"\"',\n\t\t'CLIENT_PID=\"\"',\n\t\t'SERVER_REQUIRED=0',\n\t\t'RUNNER_REUSED_READY=0',\n\t\t'ANGULAR_PREBUNDLE_ARGS=()',\n\t\t'REPO_ROOT=\"$(git -C \"$PROJECT_ROOT\" rev-parse --show-toplevel 2>/dev/null || echo \"$PROJECT_ROOT\")\"',\n\t\tbuildRunnerProcessJanitorShellLibrary({ mode: 'support' }),\n\t\t'case \"${ANGULAR_PREBUNDLE,,}\" in',\n\t\t' false|0|no|off) ANGULAR_PREBUNDLE_ARGS=(--prebundle=false) ;;',\n\t\t'esac',\n\t\t'kill_tree() {',\n\t\t' local pid=\"$1\"',\n\t\t' [ -n \"$pid\" ] || return 0',\n\t\t' for child in $(pgrep -P \"$pid\" 2>/dev/null || true); do kill_tree \"$child\"; done',\n\t\t' kill \"$pid\" >/dev/null 2>&1 || true',\n\t\t' sleep 1',\n\t\t' kill -0 \"$pid\" >/dev/null 2>&1 && kill -9 \"$pid\" >/dev/null 2>&1 || true',\n\t\t'}',\n\t\t'kill_port_listeners() {',\n\t\t' local port=\"$1\"',\n\t\t' [ -n \"$port\" ] || return 0',\n\t\t' local pids=\"\"',\n\t\t' if command -v lsof >/dev/null 2>&1; then pids=\"$pids $(janitor_bounded 3 lsof -ti tcp:\"$port\")\"; fi',\n\t\t' if command -v fuser >/dev/null 2>&1; then pids=\"$pids $(janitor_bounded 3 fuser -n tcp \"$port\")\"; fi',\n\t\t' if command -v ss >/dev/null 2>&1; then pids=\"$pids $(janitor_bounded 3 ss -ltnp \"sport = :$port\" | sed -n \\'s/.*pid=\\\\([0-9][0-9]*\\\\).*/\\\\1/p\\' || true)\"; fi',\n\t\t' for pid in $pids; do',\n\t\t' [ \"$pid\" = \"$$\" ] && continue',\n\t\t' kill_tree \"$pid\"',\n\t\t' done',\n\t\t' sleep 1',\n\t\t'}',\n\t\t'kill_env_marked_processes() {',\n\t\t' [ -d /proc ] || return 0',\n\t\t' local job_id=\"${RESOLVEIO_RUNNER_QA_JOB_ID:-${RESOLVEIO_SUPPORT_QA_JOB_ID:-}}\"',\n\t\t' local owner_id=\"${RESOLVEIO_RUNNER_QA_OWNER_ID:-${RESOLVEIO_SUPPORT_QA_OWNER_ID:-}}\"',\n\t\t' local token=\"${RESOLVEIO_RUNNER_QA_RUNNER_TOKEN:-${RESOLVEIO_SUPPORT_QA_RUNNER_TOKEN:-}}\"',\n\t\t' [ -n \"$job_id$owner_id$token\" ] || return 0',\n\t\t' for env_file in /proc/[0-9]*/environ; do',\n\t\t' pid=\"${env_file#/proc/}\"',\n\t\t' pid=\"${pid%/environ}\"',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' [ \"$pid\" = \"$$\" ] && continue',\n\t\t' [ -r \"$env_file\" ] || continue',\n\t\t' env_text=\"$(tr \"\\\\0\" \"\\\\n\" < \"$env_file\" 2>/dev/null || true)\"',\n\t\t' [ -n \"$env_text\" ] || continue',\n\t\t' matched=0',\n\t\t' [ -n \"$job_id\" ] && echo \"$env_text\" | grep -Eq \"^(RESOLVEIO_RUNNER_QA_JOB_ID|RESOLVEIO_SUPPORT_QA_JOB_ID)=$job_id$\" && matched=1',\n\t\t' [ \"$matched\" = \"0\" ] && [ -n \"$owner_id\" ] && echo \"$env_text\" | grep -Eq \"^(RESOLVEIO_RUNNER_QA_OWNER_ID|RESOLVEIO_SUPPORT_QA_OWNER_ID)=$owner_id$\" && matched=1',\n\t\t' [ \"$matched\" = \"0\" ] && [ -n \"$token\" ] && echo \"$env_text\" | grep -Eq \"^(RESOLVEIO_RUNNER_QA_RUNNER_TOKEN|RESOLVEIO_SUPPORT_QA_RUNNER_TOKEN)=$token$\" && matched=1',\n\t\t' [ \"$matched\" = \"1\" ] && kill_tree \"$pid\"',\n\t\t' done',\n\t\t'}',\n\t\t'kill_artifact_log_writers() {',\n\t\t' local artifact=\"$ARTIFACT_DIR\"',\n\t\t' [ -n \"$artifact\" ] || return 0',\n\t\t' for pid in $(ps -eo pid=,args= | awk -v artifact=\"$artifact\" \\'index($0, artifact) && $0 !~ /awk -v artifact=/ && $0 !~ /ps -eo pid=,args=/ && $0 ~ /^ *[0-9]+ +(tee|tail -f) / {print $1}\\' 2>/dev/null || true); do',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' kill_tree \"$pid\"',\n\t\t' done',\n\t\t'}',\n\t\t'kill_local_mongo_processes() {',\n\t\t' local mongo_port=\"$MONGO_PORT\"',\n\t\t' [ -n \"$mongo_port\" ] || return 0',\n\t\t' for pid in $(ps -eo pid=,args= | awk -v port=\"$mongo_port\" \\'$0 !~ /awk -v port=/ && $0 !~ /ps -eo pid=,args=/ && $0 ~ /mongod/ && $0 ~ (\"--port \" port) && ($0 ~ /\\\\/tmp\\\\/resolveio-support-qa\\\\/.*mongod/ || $0 ~ /--dbpath mongo\\\\/data\\\\/db/) {print $1}\\' 2>/dev/null || true); do',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' [ \"$pid\" = \"$$\" ] && continue',\n\t\t' kill_tree \"$pid\"',\n\t\t' done',\n\t\t'}',\n\t\t'RUNNER_ANCESTOR_PIDS=\" $$ ${PPID:-} \"',\n\t\t'ancestor_pid=\"${PPID:-}\"',\n\t\t'while [ -n \"$ancestor_pid\" ] && [ \"$ancestor_pid\" != \"0\" ] && [ \"$ancestor_pid\" != \"1\" ]; do',\n\t\t' ancestor_pid=\"$(ps -o ppid= -p \"$ancestor_pid\" 2>/dev/null | tr -d \" \" || true)\"',\n\t\t' [ -n \"$ancestor_pid\" ] && RUNNER_ANCESTOR_PIDS=\"$RUNNER_ANCESTOR_PIDS $ancestor_pid \"',\n\t\t'done',\n\t\t'skip_cleanup_pid() {',\n\t\t' case \"$RUNNER_ANCESTOR_PIDS\" in *\" $1 \"*) return 0 ;; esac',\n\t\t' return 1',\n\t\t'}',\n\t\t'cleanup_project_processes() {',\n\t\t' for pid_file in \"$ARTIFACT_DIR/server.pid\" \"$ARTIFACT_DIR/client.pid\"; do',\n\t\t' [ -f \"$pid_file\" ] || continue',\n\t\t' pid=\"$(cat \"$pid_file\" 2>/dev/null || true)\"',\n\t\t' kill_tree \"$pid\"',\n\t\t' rm -f \"$pid_file\"',\n\t\t' done',\n\t\t' for pass in 1 2 3; do',\n\t\t' for port in \"$CLIENT_PORT\" \"$SERVER_PORT\" \"$MONGO_PORT\" \"$INSPECT_PORT\"; do kill_port_listeners \"$port\"; done',\n\t\t' kill_env_marked_processes',\n\t\t' kill_artifact_log_writers',\n\t\t' kill_local_mongo_processes',\n\t\t' for pid in $(ps -eo pid=,args= | awk -v root=\"$PROJECT_ROOT\" \\'index($0, root) && $0 !~ /awk -v root=/ && $0 !~ /ps -eo pid=,args=/ && $0 !~ /run-local-qa\\\\.sh|stop-local-qa\\\\.sh|bugfix-comparison-qa\\\\.sh/ && $0 ~ /(ng serve|node .*node_modules\\\\/\\\\.bin\\\\/ng|esbuild|npm run client|start_client\\\\.sh|npm run server|start_server\\\\.sh|nodemon|node .*tmp\\\\/index\\\\.js|mongod|mongodb-binaries\\\\/mongod)/ {print $1}\\' 2>/dev/null || true); do',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' kill_tree \"$pid\"',\n\t\t' done',\n\t\t' if [ -d /proc ]; then',\n\t\t' for proc_cwd in /proc/[0-9]*/cwd; do',\n\t\t' pid=\"${proc_cwd#/proc/}\"',\n\t\t' pid=\"${pid%/cwd}\"',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' cwd=\"$(readlink -f \"$proc_cwd\" 2>/dev/null || true)\"',\n\t\t' case \"$cwd\" in',\n\t\t' \"$PROJECT_ROOT\"|\"$PROJECT_ROOT\"/*) kill_tree \"$pid\" ;;',\n\t\t' esac',\n\t\t' done',\n\t\t' fi',\n\t\t' sleep 1',\n\t\t' done',\n\t\t' local wait_until=$((SECONDS + 60))',\n\t\t' while [ \"$SECONDS\" -lt \"$wait_until\" ]; do',\n\t\t' local found=0',\n\t\t' for port in \"$CLIENT_PORT\" \"$SERVER_PORT\" \"$MONGO_PORT\" \"$INSPECT_PORT\"; do',\n\t\t' port_pids=\"\"',\n\t\t' if command -v lsof >/dev/null 2>&1; then port_pids=\"$port_pids $(janitor_bounded 3 lsof -ti tcp:\"$port\")\"; fi',\n\t\t' for pid in $port_pids; do skip_cleanup_pid \"$pid\" && continue; found=1; kill_tree \"$pid\"; done',\n\t\t' done',\n\t\t' if [ -d /proc ]; then',\n\t\t' job_id=\"${RESOLVEIO_RUNNER_QA_JOB_ID:-${RESOLVEIO_SUPPORT_QA_JOB_ID:-}}\"',\n\t\t' owner_id=\"${RESOLVEIO_RUNNER_QA_OWNER_ID:-${RESOLVEIO_SUPPORT_QA_OWNER_ID:-}}\"',\n\t\t' token=\"${RESOLVEIO_RUNNER_QA_RUNNER_TOKEN:-${RESOLVEIO_SUPPORT_QA_RUNNER_TOKEN:-}}\"',\n\t\t' for env_file in /proc/[0-9]*/environ; do',\n\t\t' pid=\"${env_file#/proc/}\"',\n\t\t' pid=\"${pid%/environ}\"',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' [ -r \"$env_file\" ] || continue',\n\t\t' env_text=\"$(tr \"\\\\0\" \"\\\\n\" < \"$env_file\" 2>/dev/null || true)\"',\n\t\t' matched=0',\n\t\t' [ -n \"$job_id\" ] && echo \"$env_text\" | grep -Eq \"^(RESOLVEIO_RUNNER_QA_JOB_ID|RESOLVEIO_SUPPORT_QA_JOB_ID)=$job_id$\" && matched=1',\n\t\t' [ \"$matched\" = \"0\" ] && [ -n \"$owner_id\" ] && echo \"$env_text\" | grep -Eq \"^(RESOLVEIO_RUNNER_QA_OWNER_ID|RESOLVEIO_SUPPORT_QA_OWNER_ID)=$owner_id$\" && matched=1',\n\t\t' [ \"$matched\" = \"0\" ] && [ -n \"$token\" ] && echo \"$env_text\" | grep -Eq \"^(RESOLVEIO_RUNNER_QA_RUNNER_TOKEN|RESOLVEIO_SUPPORT_QA_RUNNER_TOKEN)=$token$\" && matched=1',\n\t\t' [ \"$matched\" = \"1\" ] && found=1 && kill_tree \"$pid\"',\n\t\t' done',\n\t\t' fi',\n\t\t' kill_artifact_log_writers',\n\t\t' kill_local_mongo_processes',\n\t\t' for pid in $(ps -eo pid=,args= | awk -v root=\"$PROJECT_ROOT\" \\'index($0, root) && $0 !~ /awk -v root=/ && $0 !~ /ps -eo pid=,args=/ && $0 !~ /run-local-qa\\\\.sh|stop-local-qa\\\\.sh|bugfix-comparison-qa\\\\.sh/ && $0 ~ /(ng serve|node .*node_modules\\\\/\\\\.bin\\\\/ng|esbuild|npm run client|start_client\\\\.sh|npm run server|start_server\\\\.sh|nodemon|node .*tmp\\\\/index\\\\.js|mongod|mongodb-binaries\\\\/mongod)/ {print $1}\\' 2>/dev/null || true); do',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' found=1',\n\t\t' done',\n\t\t' [ \"$found\" = \"0\" ] && break',\n\t\t' sleep 2',\n\t\t' done',\n\t\t'}',\n\t\t'cleanup() {',\n\t\t' [ \"${RUNNER_REUSED_READY:-0}\" = \"1\" ] && return 0',\n\t\t' janitor_stop_heartbeat',\n\t\t' rm -f \"$ARTIFACT_DIR/heartbeat.pid\"',\n\t\t' janitor_update_manifest_status cleanup_started',\n\t\t' kill_tree \"$SERVER_PID\"',\n\t\t' kill_tree \"$CLIENT_PID\"',\n\t\t' cleanup_project_processes',\n\t\t' janitor_update_manifest_status cleanup_complete',\n\t\t' janitor_write_cleanup_status complete 0',\n\t\t' rm -rf \"$LOCK_DIR\" >/dev/null 2>&1 || true',\n\t\t'}',\n\t\t'trap cleanup EXIT',\n\t\t'detach_keepalive() {',\n\t\t' janitor_update_manifest_status ready_keepalive',\n\t\t' janitor_write_cleanup_status ready_keepalive 0',\n\t\t' rm -rf \"$LOCK_DIR\" >/dev/null 2>&1 || true',\n\t\t' trap - EXIT',\n\t\t' disown >/dev/null 2>&1 || true',\n\t\t' echo \"ResolveIO AI runner QA keepalive detached at $CLIENT_URL; stop with $TOOLS_DIR/stop-local-qa.sh $PROJECT_ROOT.\"',\n\t\t' exit 0',\n\t\t'}',\n\t\t'probe_url() { node - \"$1\" <<\\'RESOLVEIO_PROBE\\'',\n\t\t'const http = require(\"http\");',\n\t\t'const https = require(\"https\");',\n\t\t'const url = process.argv[2];',\n\t\t'const mod = /^https:/i.test(url) ? https : http;',\n\t\t'const req = mod.get(url, { timeout: 2500 }, (res) => { res.resume(); process.exit(res.statusCode && res.statusCode < 500 ? 0 : 1); });',\n\t\t'req.on(\"timeout\", () => req.destroy(new Error(\"timeout\")));',\n\t\t'req.on(\"error\", () => process.exit(1));',\n\t\t'RESOLVEIO_PROBE',\n\t\t'}',\n\t\t'truthy() { case \"${1,,}\" in true|1|yes|on) return 0 ;; *) return 1 ;; esac; }',\n\t\t'reuse_running_app_if_ready() {',\n\t\t' truthy \"$REUSE_RUNNING\" || return 1',\n\t\t' [ -d \"$PROJECT_ROOT/server\" ] && SERVER_REQUIRED=1 || SERVER_REQUIRED=0',\n\t\t' probe_url \"$CLIENT_URL\" || return 1',\n\t\t' if [ \"$SERVER_REQUIRED\" = \"1\" ] && ! probe_url \"$SERVER_URL\"; then return 1; fi',\n\t\t' echo \"ResolveIO AI runner QA reusing already-ready local app at $CLIENT_URL\"',\n\t\t' return 0',\n\t\t'}',\n\t\t'log_has_fatal() {',\n\t\t' local file=\"$1\"',\n\t\t' [ -f \"$file\" ] || return 1',\n\t\t' grep -Eiq \"Unhandled Rejection|Cannot read properties of undefined|TypeError|ReferenceError|EADDRINUSE|app crashed|Failed to compile|Error: Cannot find module|NG[0-9]{4}|TS[0-9]{4}\" \"$file\"',\n\t\t'}',\n\t\t'prepare_angular_cache_dirs() {',\n\t\t' [ -d \"$PROJECT_ROOT\" ] || return 0',\n\t\t' mkdir -p \"$PROJECT_ROOT/.angular/cache\" 2>/dev/null || true',\n\t\t' local version=\"\"',\n\t\t' for pkg in \"$PROJECT_ROOT/node_modules/@angular/build/package.json\" \"$PROJECT_ROOT/node_modules/@angular/cli/package.json\" \"$PROJECT_ROOT/node_modules/@angular-devkit/build-angular/package.json\"; do',\n\t\t' if [ -f \"$pkg\" ]; then',\n\t\t' version=\"$(node -e \"try{console.log(require(process.argv[1]).version||\\\\\\\"\\\\\\\")}catch(e){}\" \"$pkg\" 2>/dev/null | head -1)\"',\n\t\t' [ -n \"$version\" ] && break',\n\t\t' fi',\n\t\t' done',\n\t\t' [ -n \"$version\" ] || return 0',\n\t\t' node - \"$PROJECT_ROOT/angular.json\" \"$(basename \"$PROJECT_ROOT\")\" <<\\'RESOLVEIO_ANGULAR_CACHE_PROJECTS\\' | while IFS= read -r project; do',\n\t\t'const fs = require(\"fs\");',\n\t\t'const path = process.argv[2];',\n\t\t'const fallback = process.argv[3];',\n\t\t'const names = new Set([fallback].filter(Boolean));',\n\t\t'try {',\n\t\t' const parsed = JSON.parse(fs.readFileSync(path, \"utf8\"));',\n\t\t' if (parsed.defaultProject) names.add(String(parsed.defaultProject));',\n\t\t' if (parsed.projects && typeof parsed.projects === \"object\") Object.keys(parsed.projects).forEach((name) => names.add(String(name)));',\n\t\t'} catch (error) {}',\n\t\t'for (const name of names) console.log(name.replace(/[^A-Za-z0-9._-]/g, \"_\"));',\n\t\t'RESOLVEIO_ANGULAR_CACHE_PROJECTS',\n\t\t' [ -n \"$project\" ] || continue',\n\t\t' mkdir -p \"$PROJECT_ROOT/.angular/cache/$version/$project/vite\" 2>/dev/null || true',\n\t\t' done',\n\t\t'}',\n\t\t'server_has_started() {',\n\t\t' local file=\"$ARTIFACT_DIR/server.log\"',\n\t\t' [ -f \"$file\" ] || return 1',\n\t\t' grep -Eiq \"nodemon.*starting|node .*tmp/index\\\\.js|Running as Worker|Standalone Node Reaper|listening on|server listening|Server listening|app listening|App listening|Finished .default.\" \"$file\"',\n\t\t'}',\n\t\t'wait_for_server_ready() {',\n\t\t' [ \"$SERVER_REQUIRED\" = \"1\" ] || return 0',\n\t\t' local end=$((SECONDS + STARTUP_TIMEOUT))',\n\t\t' while [ \"$SECONDS\" -lt \"$end\" ]; do',\n\t\t' if log_has_fatal \"$ARTIFACT_DIR/server.log\"; then return 4; fi',\n\t\t' if server_has_started; then',\n\t\t' local stable_until=$((SECONDS + SERVER_STABLE_SECONDS))',\n\t\t' while [ \"$SECONDS\" -lt \"$stable_until\" ]; do if log_has_fatal \"$ARTIFACT_DIR/server.log\"; then return 4; fi; sleep 2; done',\n\t\t' return 0',\n\t\t' fi',\n\t\t' if probe_url \"$SERVER_URL\"; then',\n\t\t' local stable_until=$((SECONDS + SERVER_STABLE_SECONDS))',\n\t\t' while [ \"$SECONDS\" -lt \"$stable_until\" ]; do if log_has_fatal \"$ARTIFACT_DIR/server.log\"; then return 4; fi; sleep 2; done',\n\t\t' return 0',\n\t\t' fi',\n\t\t' sleep 5',\n\t\t' done',\n\t\t' return 4',\n\t\t'}',\n\t\t'wait_for_client() {',\n\t\t' local end=$((SECONDS + STARTUP_TIMEOUT))',\n\t\t' while [ \"$SECONDS\" -lt \"$end\" ]; do',\n\t\t' if [ -n \"$CLIENT_PID\" ] && ! kill -0 \"$CLIENT_PID\" >/dev/null 2>&1; then return 2; fi',\n\t\t' if log_has_fatal \"$ARTIFACT_DIR/client.log\"; then return 3; fi',\n\t\t' if log_has_fatal \"$ARTIFACT_DIR/server.log\"; then return 4; fi',\n\t\t' if probe_url \"$CLIENT_URL\"; then wait_for_server_ready || return $?; return 0; fi',\n\t\t' sleep 5',\n\t\t' done',\n\t\t' return 1',\n\t\t'}',\n\t\t'if reuse_running_app_if_ready; then RUNNER_REUSED_READY=1; exit 0; fi',\n\t\t'if ! mkdir \"$LOCK_DIR\" 2>/dev/null; then',\n\t\t' if janitor_lock_is_live; then',\n\t\t' echo \"ResolveIO AI runner QA lock is already held by a live runner for $PROJECT_ROOT; refusing duplicate startup.\" | tee \"$ARTIFACT_DIR/runner.log\"',\n\t\t' exit 6',\n\t\t' fi',\n\t\t' echo \"ResolveIO AI runner QA lock is stale for $PROJECT_ROOT; cleaning scoped local QA processes before retrying.\" | tee \"$ARTIFACT_DIR/runner.log\"',\n\t\t' cleanup_project_processes',\n\t\t' rm -rf \"$LOCK_DIR\" >/dev/null 2>&1 || true',\n\t\t' if ! mkdir \"$LOCK_DIR\" 2>/dev/null; then',\n\t\t' echo \"ResolveIO AI runner QA lock is still held for $PROJECT_ROOT after cleanup. Stop the existing QA runner before starting another.\" | tee -a \"$ARTIFACT_DIR/runner.log\"',\n\t\t' exit 6',\n\t\t' fi',\n\t\t'fi',\n\t\t'janitor_write_lock',\n\t\t'cleanup_project_processes',\n\t\t': > \"$ARTIFACT_DIR/server.log\"',\n\t\t': > \"$ARTIFACT_DIR/client.log\"',\n\t\t': > \"$ARTIFACT_DIR/runner.log\"',\n\t\t'janitor_write_manifest',\n\t\t'janitor_start_heartbeat',\n\t\t'echo \"$RUNNER_JANITOR_HEARTBEAT_PID\" > \"$ARTIFACT_DIR/heartbeat.pid\"',\n\t\t'janitor_check_resources || exit $?',\n\t\t'if [ -d \"$PROJECT_ROOT/server\" ]; then',\n\t\t' SERVER_REQUIRED=1',\n\t\t' if node -e \"const p=require(process.argv[1]); process.exit(p.scripts&&p.scripts.server?0:1)\" \"$PROJECT_ROOT/server/package.json\" >/dev/null 2>&1; then',\n\t\t' (cd \"$PROJECT_ROOT/server\" && source \"$TOOLS_DIR/env.sh\" && npm run server 2>&1 | tee \"$ARTIFACT_DIR/server.log\") &',\n\t\t' elif [ -x \"$PROJECT_ROOT/server/start_server.sh\" ]; then',\n\t\t' (cd \"$PROJECT_ROOT/server\" && source \"$TOOLS_DIR/env.sh\" && ./start_server.sh 2>&1 | tee \"$ARTIFACT_DIR/server.log\") &',\n\t\t' else',\n\t\t' echo \"ResolveIO AI runner QA cannot find npm server script or start_server.sh for $PROJECT_ROOT/server\" | tee \"$ARTIFACT_DIR/server.log\"',\n\t\t' exit 5',\n\t\t' fi',\n\t\t' SERVER_PID=$!',\n\t\t' wait_for_server_ready',\n\t\t' SERVER_RESULT=$?',\n\t\t' if [ \"$SERVER_RESULT\" != \"0\" ]; then echo \"ResolveIO AI runner QA server startup fatal error. See $ARTIFACT_DIR/server.log\"; exit \"$SERVER_RESULT\"; fi',\n\t\t'fi',\n\t\t'CLIENT_HOST=\"${RESOLVEIO_RUNNER_QA_CLIENT_HOST:-${RESOLVEIO_SUPPORT_QA_CLIENT_HOST:-0.0.0.0}}\"',\n\t\t'if [ -x \"$PROJECT_ROOT/node_modules/.bin/ng\" ]; then',\n\t\t' prepare_angular_cache_dirs',\n\t\t' (cd \"$PROJECT_ROOT\" && source \"$TOOLS_DIR/env.sh\" && node --max_old_space_size=8048 ./node_modules/.bin/ng serve --watch --configuration local --host \"$CLIENT_HOST\" --port \"$CLIENT_PORT\" \"${ANGULAR_PREBUNDLE_ARGS[@]}\" 2>&1 | tee \"$ARTIFACT_DIR/client.log\") &',\n\t\t'elif [ -x \"$PROJECT_ROOT/start_client.sh\" ]; then',\n\t\t' (cd \"$PROJECT_ROOT\" && source \"$TOOLS_DIR/env.sh\" && ./start_client.sh 2>&1 | tee \"$ARTIFACT_DIR/client.log\") &',\n\t\t'elif node -e \"const p=require(process.argv[1]); process.exit(p.scripts&&p.scripts.client?0:1)\" \"$PROJECT_ROOT/package.json\" >/dev/null 2>&1; then',\n\t\t' (cd \"$PROJECT_ROOT\" && source \"$TOOLS_DIR/env.sh\" && npm run client 2>&1 | tee \"$ARTIFACT_DIR/client.log\") &',\n\t\t'else',\n\t\t' echo \"ResolveIO AI runner QA cannot find Angular CLI, start_client.sh, or npm client script for $PROJECT_ROOT\" | tee \"$ARTIFACT_DIR/client.log\"',\n\t\t' exit 5',\n\t\t'fi',\n\t\t'CLIENT_PID=$!',\n\t\t'wait_for_client',\n\t\t'RESULT=$?',\n\t\t'case \"$RESULT\" in',\n\t\t' 0)',\n\t\t' echo \"ResolveIO AI runner QA local app ready at $CLIENT_URL\";',\n\t\t' echo \"$SERVER_PID\" > \"$ARTIFACT_DIR/server.pid\";',\n\t\t' echo \"$CLIENT_PID\" > \"$ARTIFACT_DIR/client.pid\";',\n\t\t' if truthy \"$KEEPALIVE\"; then detach_keepalive; fi;',\n\t\t' exit 0',\n\t\t' ;;',\n\t\t' 2) echo \"ResolveIO AI runner QA client process exited before $CLIENT_URL became ready. See $ARTIFACT_DIR/client.log\"; exit 2 ;;',\n\t\t' 3) echo \"ResolveIO AI runner QA client startup fatal error. See $ARTIFACT_DIR/client.log\"; exit 3 ;;',\n\t\t' 4) echo \"ResolveIO AI runner QA server startup fatal error. See $ARTIFACT_DIR/server.log\"; exit 4 ;;',\n\t\t' *) echo \"ResolveIO AI runner QA local app did not become ready at $CLIENT_URL within ${STARTUP_TIMEOUT}s. See $ARTIFACT_DIR/client.log and $ARTIFACT_DIR/server.log\"; exit 1 ;;',\n\t\t'esac',\n\t\t''\n\t].join('\\n');\n}\n\nexport function buildResolveIORunnerLocalQaStopperScript(): string {\n\treturn [\n\t\t'#!/usr/bin/env bash',\n\t\t'set -u',\n\t\t'TOOLS_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"',\n\t\t'source \"$TOOLS_DIR/env.sh\"',\n\t\t'PROJECT_ROOT=\"${1:-$(pwd)}\"',\n\t\t'PROJECT_ROOT=\"$(cd \"$PROJECT_ROOT\" && pwd)\"',\n\t\t'ARTIFACT_DIR=\"$PROJECT_ROOT/qa-artifacts\"',\n\t\t'CLIENT_PORT=\"${RESOLVEIO_RUNNER_QA_CLIENT_PORT:-${RESOLVEIO_SUPPORT_QA_CLIENT_PORT:-4200}}\"',\n\t\t'SERVER_PORT=\"${RESOLVEIO_RUNNER_QA_SERVER_PORT:-${RESOLVEIO_SUPPORT_QA_SERVER_PORT:-8080}}\"',\n\t\t'MONGO_PORT=\"${RESOLVEIO_RUNNER_QA_MONGO_PORT:-${RESOLVEIO_SUPPORT_QA_MONGO_PORT:-3001}}\"',\n\t\t'INSPECT_PORT=\"${RESOLVEIO_RUNNER_QA_INSPECT_PORT:-${RESOLVEIO_SUPPORT_QA_INSPECT_PORT:-9229}}\"',\n\t\t'REPO_ROOT=\"$(git -C \"$PROJECT_ROOT\" rev-parse --show-toplevel 2>/dev/null || echo \"$PROJECT_ROOT\")\"',\n\t\tbuildRunnerProcessJanitorShellLibrary({ mode: 'support' }),\n\t\t'STOP_LOCK_DIR=\"$ARTIFACT_DIR/.qa.stop.lock\"',\n\t\t'if ! mkdir \"$STOP_LOCK_DIR\" 2>/dev/null; then',\n\t\t' lock_pid=\"$(cat \"$STOP_LOCK_DIR/pid\" 2>/dev/null || true)\"',\n\t\t' if [ -n \"$lock_pid\" ] && kill -0 \"$lock_pid\" >/dev/null 2>&1; then',\n\t\t' echo \"ResolveIO AI runner QA cleanup already active for $PROJECT_ROOT; refusing duplicate stopper.\"',\n\t\t' exit 0',\n\t\t' fi',\n\t\t' rm -rf \"$STOP_LOCK_DIR\" >/dev/null 2>&1 || true',\n\t\t' mkdir \"$STOP_LOCK_DIR\" 2>/dev/null || { echo \"ResolveIO AI runner QA cleanup lock still active for $PROJECT_ROOT.\"; exit 0; }',\n\t\t'fi',\n\t\t'echo \"$$\" > \"$STOP_LOCK_DIR/pid\" 2>/dev/null || true',\n\t\t'trap \\'rm -rf \"$STOP_LOCK_DIR\" >/dev/null 2>&1 || true\\' EXIT',\n\t\t'kill_tree() {',\n\t\t' local pid=\"$1\"',\n\t\t' [ -n \"$pid\" ] || return 0',\n\t\t' kill -0 \"$pid\" >/dev/null 2>&1 || return 0',\n\t\t' for child in $(pgrep -P \"$pid\" 2>/dev/null || true); do kill_tree \"$child\"; done',\n\t\t' kill \"$pid\" >/dev/null 2>&1 || true',\n\t\t' sleep 1',\n\t\t' kill -0 \"$pid\" >/dev/null 2>&1 && kill -9 \"$pid\" >/dev/null 2>&1 || true',\n\t\t'}',\n\t\t'kill_port_listeners() {',\n\t\t' local port=\"$1\"',\n\t\t' [ -n \"$port\" ] || return 0',\n\t\t' local pids=\"\"',\n\t\t' if command -v lsof >/dev/null 2>&1; then pids=\"$pids $(janitor_bounded 3 lsof -ti tcp:\"$port\")\"; fi',\n\t\t' if command -v fuser >/dev/null 2>&1; then pids=\"$pids $(janitor_bounded 3 fuser -n tcp \"$port\")\"; fi',\n\t\t' if command -v ss >/dev/null 2>&1; then pids=\"$pids $(janitor_bounded 3 ss -ltnp \"sport = :$port\" | sed -n \\'s/.*pid=\\\\([0-9][0-9]*\\\\).*/\\\\1/p\\' || true)\"; fi',\n\t\t' for pid in $pids; do',\n\t\t' [ \"$pid\" = \"$$\" ] && continue',\n\t\t' kill_tree \"$pid\"',\n\t\t' done',\n\t\t' sleep 1',\n\t\t'}',\n\t\t'kill_env_marked_processes() {',\n\t\t' [ -d /proc ] || return 0',\n\t\t' local job_id=\"${RESOLVEIO_RUNNER_QA_JOB_ID:-${RESOLVEIO_SUPPORT_QA_JOB_ID:-}}\"',\n\t\t' local owner_id=\"${RESOLVEIO_RUNNER_QA_OWNER_ID:-${RESOLVEIO_SUPPORT_QA_OWNER_ID:-}}\"',\n\t\t' local token=\"${RESOLVEIO_RUNNER_QA_RUNNER_TOKEN:-${RESOLVEIO_SUPPORT_QA_RUNNER_TOKEN:-}}\"',\n\t\t' [ -n \"$job_id$owner_id$token\" ] || return 0',\n\t\t' for env_file in /proc/[0-9]*/environ; do',\n\t\t' pid=\"${env_file#/proc/}\"',\n\t\t' pid=\"${pid%/environ}\"',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' [ \"$pid\" = \"$$\" ] && continue',\n\t\t' [ -r \"$env_file\" ] || continue',\n\t\t' env_text=\"$(tr \"\\\\0\" \"\\\\n\" < \"$env_file\" 2>/dev/null || true)\"',\n\t\t' [ -n \"$env_text\" ] || continue',\n\t\t' matched=0',\n\t\t' [ -n \"$job_id\" ] && echo \"$env_text\" | grep -Eq \"^(RESOLVEIO_RUNNER_QA_JOB_ID|RESOLVEIO_SUPPORT_QA_JOB_ID)=$job_id$\" && matched=1',\n\t\t' [ \"$matched\" = \"0\" ] && [ -n \"$owner_id\" ] && echo \"$env_text\" | grep -Eq \"^(RESOLVEIO_RUNNER_QA_OWNER_ID|RESOLVEIO_SUPPORT_QA_OWNER_ID)=$owner_id$\" && matched=1',\n\t\t' [ \"$matched\" = \"0\" ] && [ -n \"$token\" ] && echo \"$env_text\" | grep -Eq \"^(RESOLVEIO_RUNNER_QA_RUNNER_TOKEN|RESOLVEIO_SUPPORT_QA_RUNNER_TOKEN)=$token$\" && matched=1',\n\t\t' [ \"$matched\" = \"1\" ] && kill_tree \"$pid\" && killed_count=$((killed_count + 1))',\n\t\t' done',\n\t\t'}',\n\t\t'kill_artifact_log_writers() {',\n\t\t' local artifact=\"$ARTIFACT_DIR\"',\n\t\t' [ -n \"$artifact\" ] || return 0',\n\t\t' for pid in $(ps -eo pid=,args= | awk -v artifact=\"$artifact\" \\'index($0, artifact) && $0 !~ /awk -v artifact=/ && $0 !~ /ps -eo pid=,args=/ && $0 ~ /^ *[0-9]+ +(tee|tail -f) / {print $1}\\' 2>/dev/null || true); do',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' kill_tree \"$pid\"',\n\t\t' killed_count=$((killed_count + 1))',\n\t\t' done',\n\t\t'}',\n\t\t'kill_local_mongo_processes() {',\n\t\t' local mongo_port=\"$MONGO_PORT\"',\n\t\t' [ -n \"$mongo_port\" ] || return 0',\n\t\t' for pid in $(ps -eo pid=,args= | awk -v port=\"$mongo_port\" \\'$0 !~ /awk -v port=/ && $0 !~ /ps -eo pid=,args=/ && $0 ~ /mongod/ && $0 ~ (\"--port \" port) && ($0 ~ /\\\\/tmp\\\\/resolveio-support-qa\\\\/.*mongod/ || $0 ~ /--dbpath mongo\\\\/data\\\\/db/) {print $1}\\' 2>/dev/null || true); do',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' [ \"$pid\" = \"$$\" ] && continue',\n\t\t' kill_tree \"$pid\"',\n\t\t' killed_count=$((killed_count + 1))',\n\t\t' done',\n\t\t'}',\n\t\t'RUNNER_ANCESTOR_PIDS=\" $$ ${PPID:-} \"',\n\t\t'ancestor_pid=\"${PPID:-}\"',\n\t\t'while [ -n \"$ancestor_pid\" ] && [ \"$ancestor_pid\" != \"0\" ] && [ \"$ancestor_pid\" != \"1\" ]; do',\n\t\t' ancestor_pid=\"$(ps -o ppid= -p \"$ancestor_pid\" 2>/dev/null | tr -d \" \" || true)\"',\n\t\t' [ -n \"$ancestor_pid\" ] && RUNNER_ANCESTOR_PIDS=\"$RUNNER_ANCESTOR_PIDS $ancestor_pid \"',\n\t\t'done',\n\t\t'skip_cleanup_pid() {',\n\t\t' case \"$RUNNER_ANCESTOR_PIDS\" in *\" $1 \"*) return 0 ;; esac',\n\t\t' return 1',\n\t\t'}',\n\t\t'janitor_update_manifest_status cleanup_started',\n\t\t'killed_count=0',\n'for pid_file in \"$ARTIFACT_DIR/heartbeat.pid\" \"$ARTIFACT_DIR/server.pid\" \"$ARTIFACT_DIR/client.pid\"; do',\n\t\t' [ -f \"$pid_file\" ] || continue',\n\t\t' pid=\"$(cat \"$pid_file\" 2>/dev/null || true)\"',\n\t\t' kill_tree \"$pid\"',\n\t\t' killed_count=$((killed_count + 1))',\n\t\t' rm -f \"$pid_file\"',\n\t\t'done',\n\t\t'for pass in 1 2 3; do',\n\t\t' for port in \"$CLIENT_PORT\" \"$SERVER_PORT\" \"$MONGO_PORT\" \"$INSPECT_PORT\"; do kill_port_listeners \"$port\"; done',\n\t\t' kill_env_marked_processes',\n\t\t' kill_artifact_log_writers',\n\t\t' kill_local_mongo_processes',\n\t\t' for pid in $(ps -eo pid=,args= | awk -v root=\"$PROJECT_ROOT\" \\'index($0, root) && $0 !~ /awk -v root=/ && $0 !~ /ps -eo pid=,args=/ && $0 !~ /run-local-qa\\\\.sh|stop-local-qa\\\\.sh|bugfix-comparison-qa\\\\.sh/ && $0 ~ /(ng serve|node .*node_modules\\\\/\\\\.bin\\\\/ng|esbuild|npm run client|start_client\\\\.sh|npm run server|start_server\\\\.sh|nodemon|node .*tmp\\\\/index\\\\.js|mongod|mongodb-binaries\\\\/mongod)/ {print $1}\\' 2>/dev/null || true); do',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' kill_tree \"$pid\"',\n\t\t' killed_count=$((killed_count + 1))',\n\t\t' done',\n\t\t' if [ -d /proc ]; then',\n\t\t' for proc_cwd in /proc/[0-9]*/cwd; do',\n\t\t' pid=\"${proc_cwd#/proc/}\"',\n\t\t' pid=\"${pid%/cwd}\"',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' cwd=\"$(readlink -f \"$proc_cwd\" 2>/dev/null || true)\"',\n\t\t' case \"$cwd\" in',\n\t\t' \"$PROJECT_ROOT\"|\"$PROJECT_ROOT\"/*) kill_tree \"$pid\" ;;',\n\t\t' esac',\n\t\t' done',\n\t\t' fi',\n\t\t' sleep 1',\n\t\t'done',\n\t\t'wait_until=$((SECONDS + 60))',\n\t'while [ \"$SECONDS\" -lt \"$wait_until\" ]; do',\n\t\t' found=0',\n\t\t' for port in \"$CLIENT_PORT\" \"$SERVER_PORT\" \"$MONGO_PORT\" \"$INSPECT_PORT\"; do',\n\t\t' port_pids=\"\"',\n\t\t' if command -v lsof >/dev/null 2>&1; then port_pids=\"$port_pids $(janitor_bounded 3 lsof -ti tcp:\"$port\")\"; fi',\n\t\t' for pid in $port_pids; do skip_cleanup_pid \"$pid\" && continue; found=1; kill_tree \"$pid\"; done',\n\t\t' done',\n\t\t' if [ -d /proc ]; then',\n\t\t' job_id=\"${RESOLVEIO_RUNNER_QA_JOB_ID:-${RESOLVEIO_SUPPORT_QA_JOB_ID:-}}\"',\n\t\t' owner_id=\"${RESOLVEIO_RUNNER_QA_OWNER_ID:-${RESOLVEIO_SUPPORT_QA_OWNER_ID:-}}\"',\n\t\t' token=\"${RESOLVEIO_RUNNER_QA_RUNNER_TOKEN:-${RESOLVEIO_SUPPORT_QA_RUNNER_TOKEN:-}}\"',\n\t\t' for env_file in /proc/[0-9]*/environ; do',\n\t\t' pid=\"${env_file#/proc/}\"',\n\t\t' pid=\"${pid%/environ}\"',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' [ -r \"$env_file\" ] || continue',\n\t\t' env_text=\"$(tr \"\\\\0\" \"\\\\n\" < \"$env_file\" 2>/dev/null || true)\"',\n\t\t' matched=0',\n\t\t' [ -n \"$job_id\" ] && echo \"$env_text\" | grep -Eq \"^(RESOLVEIO_RUNNER_QA_JOB_ID|RESOLVEIO_SUPPORT_QA_JOB_ID)=$job_id$\" && matched=1',\n\t\t' [ \"$matched\" = \"0\" ] && [ -n \"$owner_id\" ] && echo \"$env_text\" | grep -Eq \"^(RESOLVEIO_RUNNER_QA_OWNER_ID|RESOLVEIO_SUPPORT_QA_OWNER_ID)=$owner_id$\" && matched=1',\n\t\t' [ \"$matched\" = \"0\" ] && [ -n \"$token\" ] && echo \"$env_text\" | grep -Eq \"^(RESOLVEIO_RUNNER_QA_RUNNER_TOKEN|RESOLVEIO_SUPPORT_QA_RUNNER_TOKEN)=$token$\" && matched=1',\n\t\t' [ \"$matched\" = \"1\" ] && found=1 && kill_tree \"$pid\"',\n\t\t' done',\n\t\t' fi',\n\t\t' kill_artifact_log_writers',\n\t\t' kill_local_mongo_processes',\n\t\t' for pid in $(ps -eo pid=,args= | awk -v root=\"$PROJECT_ROOT\" \\'index($0, root) && $0 !~ /awk -v root=/ && $0 !~ /ps -eo pid=,args=/ && $0 !~ /run-local-qa\\\\.sh|stop-local-qa\\\\.sh|bugfix-comparison-qa\\\\.sh/ && $0 ~ /(ng serve|node .*node_modules\\\\/\\\\.bin\\\\/ng|esbuild|npm run client|start_client\\\\.sh|npm run server|start_server\\\\.sh|nodemon|node .*tmp\\\\/index\\\\.js|mongod|mongodb-binaries\\\\/mongod)/ {print $1}\\' 2>/dev/null || true); do',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' found=1',\n\t\t' done',\n\t\t' [ \"$found\" = \"0\" ] && break',\n\t\t' sleep 2',\n\t\t'done',\n\t\t'rm -rf \"$ARTIFACT_DIR/.qa.lock\" >/dev/null 2>&1 || true',\n\t\t'janitor_update_manifest_status stopped',\n\t\t'janitor_write_cleanup_status stopped \"$killed_count\"',\n\t\t'echo \"ResolveIO AI runner QA local app stopped for $PROJECT_ROOT\"',\n\t\t''\n\t].join('\\n');\n}\n\nexport function buildResolveIORunnerQaLiveDataSeederScript(): string {\n\treturn [\n\t\t'#!/usr/bin/env node',\n\t\t'const fs = require(\"fs\");',\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 repoRoot = findRepoRoot(projectRoot);',\n\t\t'const artifactDir = path.join(projectRoot, \"qa-artifacts\");',\n\t\t'fs.mkdirSync(artifactDir, { recursive: true });',\n\t\t'const resultPath = path.join(artifactDir, \"qa-live-data-seed-result.json\");',\n\t\t'',\n\t\t'function writeResult(payload, exitCode = 0) {',\n\t\t' const full = { ...payload, created_at: new Date().toISOString() };',\n\t\t' fs.writeFileSync(resultPath, JSON.stringify(full, null, 2));',\n\t\t' console.log(JSON.stringify(full, null, 2));',\n\t\t' process.exit(exitCode);',\n\t\t'}',\n\t\t'',\n\t\t'function findRepoRoot(start) {',\n\t\t' let current = start;',\n\t\t' for (let i = 0; i < 8; i += 1) {',\n\t\t' if (fs.existsSync(path.join(current, \".git\"))) return current;',\n\t\t' const parent = path.dirname(current);',\n\t\t' if (parent === current) break;',\n\t\t' current = parent;',\n\t\t' }',\n\t\t' return start;',\n\t\t'}',\n\t\t'',\n\t\t'function readJsonIfExists(filePath) {',\n\t\t' try { return JSON.parse(fs.readFileSync(filePath, \"utf8\")); } catch (error) { return null; }',\n\t\t'}',\n\t\t'',\n\t'function resolveRuntimeSource() {',\n\t' const envUri = process.env.RESOLVEIO_QA_LIVE_MONGO_URL || process.env.RESOLVEIO_SUPPORT_QA_LIVE_MONGO_URL || process.env.RESOLVEIO_RUNNER_QA_LIVE_MONGO_URL || \"\";',\n\t' const envDb = process.env.RESOLVEIO_QA_LIVE_MONGO_DB || process.env.RESOLVEIO_SUPPORT_QA_LIVE_MONGO_DB || process.env.RESOLVEIO_RUNNER_QA_LIVE_MONGO_DB || \"\";',\n\t' if (envUri) return { uri: envUri, database: envDb };',\n\t' const explicitRuntimePaths = [',\n\t' process.env.RESOLVEIO_QA_MONGO_RUNTIME_PATH,',\n\t' process.env.RESOLVEIO_SUPPORT_QA_MONGO_RUNTIME_PATH,',\n\t' process.env.RESOLVEIO_RUNNER_QA_MONGO_RUNTIME_PATH',\n\t' ].map((value) => String(value || \"\").trim()).filter(Boolean);',\n\t' const candidates = [',\n\t' ...explicitRuntimePaths,',\n\t' path.join(repoRoot, \"mongo-context\", \".mongo-runtime.json\"),',\n\t' path.join(projectRoot, \"mongo-context\", \".mongo-runtime.json\"),',\n\t\t' path.join(repoRoot, \".resolveio-support-context\", \"mongo-context\", \".mongo-runtime.json\"),',\n\t\t' path.join(projectRoot, \".resolveio-support-context\", \"mongo-context\", \".mongo-runtime.json\"),',\n\t\t' path.join(repoRoot, \".resolveio-context\", \"mongo-context\", \".mongo-runtime.json\"),',\n\t\t' path.join(projectRoot, \".resolveio-context\", \"mongo-context\", \".mongo-runtime.json\")',\n\t\t' ];',\n\t\t' for (const candidate of candidates) {',\n\t\t' const parsed = readJsonIfExists(candidate);',\n\t\t' if (parsed && parsed.mongo_uri) return { uri: String(parsed.mongo_uri || \"\"), database: String(parsed.mongo_db || \"\") };',\n\t\t' }',\n\t\t' return { uri: \"\", database: \"\" };',\n\t\t'}',\n\t\t'',\n\t\t'function redactUri(uri) {',\n\t\t' return String(uri || \"\").replace(/(mongodb(?:\\\\+srv)?:\\\\/\\\\/)([^:@/?#]+):([^@/?#]+)@/i, \"$1$2:***@\");',\n\t\t'}',\n\t\t'',\n\t'function isLocalMongoUri(uri) {',\n\t' return /mongodb(?:\\\\+srv)?:\\\\/\\\\/(?:[^@/]+@)?(?:127\\\\.0\\\\.0\\\\.1|localhost)(?::\\\\d+)?\\\\//i.test(String(uri || \"\"));',\n\t'}',\n\t'',\n\t'function isLiveDataRequired() {',\n\t' return /^(true|1|yes|on)$/i.test(String(process.env.RESOLVEIO_QA_LIVE_DATA_REQUIRED || process.env.RESOLVEIO_SUPPORT_QA_LIVE_DATA_REQUIRED || process.env.RESOLVEIO_RUNNER_QA_LIVE_DATA_REQUIRED || \"\"));',\n\t'}',\n\t\t'',\n\t\t'function requireMongo() {',\n\t\t' const candidates = [',\n\t\t' path.join(projectRoot, \"server\", \"node_modules\", \"mongodb\"),',\n\t\t' path.join(projectRoot, \"node_modules\", \"mongodb\"),',\n\t\t' path.join(repoRoot, \"node_modules\", \"mongodb\"),',\n\t\t' \"mongodb\"',\n\t\t' ];',\n\t\t' const errors = [];',\n\t\t' for (const candidate of candidates) {',\n\t\t' try { return require(candidate); } catch (error) { errors.push(`${candidate}: ${error.message}`); }',\n\t\t' }',\n\t\t' throw new Error(`Unable to require mongodb package. ${errors.join(\" | \")}`);',\n\t\t'}',\n\t\t'',\n\t\t'function idValues(docs, keys) {',\n\t\t' const values = new Set();',\n\t\t' for (const doc of docs || []) {',\n\t\t' for (const key of keys) {',\n\t\t' const value = doc && doc[key];',\n\t\t' if (typeof value === \"string\" && value.trim()) values.add(value.trim());',\n\t\t' if (Array.isArray(value)) value.forEach((entry) => typeof entry === \"string\" && entry.trim() && values.add(entry.trim()));',\n\t\t' }',\n\t\t' }',\n\t\t' return Array.from(values);',\n\t\t'}',\n\t\t'',\n\t\t'function unique(values) { return Array.from(new Set((values || []).filter(Boolean).map(String))); }',\n\t\t'',\n\t\t'function readSeedHintText() {',\n\t\t' const chunks = [process.env.RESOLVEIO_QA_SEED_HINTS || \"\", process.env.RESOLVEIO_SUPPORT_QA_SEED_HINTS || \"\", process.env.RESOLVEIO_RUNNER_QA_SEED_HINTS || \"\"];',\n\t\t' const candidates = [',\n\t\t' path.join(repoRoot, \".resolveio-support-context\", \"manual-ticket.request.txt\"),',\n\t\t' path.join(projectRoot, \".resolveio-support-context\", \"manual-ticket.request.txt\"),',\n\t\t' path.join(repoRoot, \".resolveio-context\", \"manual-ticket.request.txt\"),',\n\t\t' path.join(projectRoot, \".resolveio-context\", \"manual-ticket.request.txt\")',\n\t\t' ];',\n\t\t' for (const candidate of candidates) {',\n\t\t' try { chunks.push(fs.readFileSync(candidate, \"utf8\")); } catch (error) {}',\n\t\t' }',\n\t\t' return chunks.filter(Boolean).join(\"\\\\n\");',\n\t\t'}',\n\t\t'',\n\t\t'function extractSeedIdentifiers() {',\n\t\t' const text = readSeedHintText();',\n\t\t' const identifiers = new Set();',\n\t\t' const explicit = /\\\\b(?:invoice|inv|bol|order|pso|ticket|wo|work\\\\s*order|delivery|treatment)\\\\s*(?:#|no\\\\.?|number|num)?\\\\s*[:#-]?\\\\s*([A-Z0-9][A-Z0-9._-]{2,})\\\\b/gi;',\n\t\t' let match;',\n\t\t' while ((match = explicit.exec(text)) !== null) identifiers.add(match[1]);',\n\t\t' const numeric = /\\\\b\\\\d{4,}\\\\b/g;',\n\t\t' while ((match = numeric.exec(text)) !== null) identifiers.add(match[0]);',\n\t\t' return Array.from(identifiers)',\n\t\t' .map((value) => String(value || \"\").trim())',\n\t\t' .filter((value) => value && !/^00?4\\\\d{3}$/.test(value))',\n\t\t' .slice(0, 20);',\n\t\t'}',\n\t\t'',\n\t\t'function identifierQuery(identifiers) {',\n\t\t' const ors = [];',\n\t\t' const fields = [\"_id\", \"invoice_number\", \"invoice_number_string\", \"order_number\", \"order_number_string\", \"activity_number\", \"bol_number\", \"bol_string\", \"ticket_number\", \"ticket\", \"wo_number\", \"work_order_number\", \"delivery_number\", \"treatment_number\"];',\n\t\t' for (const identifier of identifiers || []) {',\n\t\t' const escaped = String(identifier).replace(/[.*+?^${}()|[\\\\]\\\\\\\\]/g, \"\\\\\\\\$&\");',\n\t\t' const regex = new RegExp(escaped, \"i\");',\n\t\t' for (const field of fields) ors.push({ [field]: regex });',\n\t\t' ors.push({ \"bol.bol_string\": regex });',\n\t\t' ors.push({ \"bol.bol_number\": regex });',\n\t\t' }',\n\t\t' return ors.length ? { $or: ors } : null;',\n\t\t'}',\n\t\t'',\n\t\t'async function copyByIds(sourceDb, targetDb, collectionName, ids, summary, limit = 100) {',\n\t\t' const cleanIds = unique(ids).slice(0, limit);',\n\t\t' if (!cleanIds.length) return [];',\n\t\t' const docs = await sourceDb.collection(collectionName).find({ _id: { $in: cleanIds } }).limit(limit).toArray();',\n\t\t' for (const doc of docs) await targetDb.collection(collectionName).replaceOne({ _id: doc._id }, doc, { upsert: true });',\n\t\t' summary.collections[collectionName] = (summary.collections[collectionName] || 0) + docs.length;',\n\t\t' return docs;',\n\t\t'}',\n\t\t'',\n\t\t'async function copyQuery(sourceDb, targetDb, collectionName, query, summary, limit = 50, sort = null) {',\n\t\t' let cursor = sourceDb.collection(collectionName).find(query || {});',\n\t\t' if (sort) cursor = cursor.sort(sort);',\n\t\t' const docs = await cursor.limit(limit).toArray();',\n\t\t' for (const doc of docs) await targetDb.collection(collectionName).replaceOne({ _id: doc._id }, doc, { upsert: true });',\n\t\t' summary.collections[collectionName] = (summary.collections[collectionName] || 0) + docs.length;',\n\t\t' return docs;',\n\t\t'}',\n\t\t'',\n\t\t'async function selectDashboardReadyProductionDeliveryIds(sourceDb, query, limit = 5) {',\n\t\t' const rows = await sourceDb.collection(\"production-deliveries\").aggregate([',\n\t\t' { $match: query || {} },',\n\t\t' { $lookup: { from: \"production-sales-orders\", localField: \"id_activity\", foreignField: \"_id\", as: \"dbPSO\" } },',\n\t\t' { $unwind: { path: \"$dbPSO\", preserveNullAndEmptyArrays: false } },',\n\t\t' { $match: { \"dbPSO.type\": { $ne: \"Consignment Invoice\" } } },',\n\t\t' { $lookup: {',\n\t\t' from: \"bols\",',\n\t\t' let: { idBol: \"$id_bol\" },',\n\t\t' pipeline: [{ $match: { $expr: { $eq: [\"$_id\", \"$$idBol\"] }, status: \"Delivered\" } }],',\n\t\t' as: \"bol\"',\n\t\t' } },',\n\t\t' { $unwind: { path: \"$bol\", preserveNullAndEmptyArrays: false } },',\n\t\t' { $lookup: { from: \"production-locations\", localField: \"id_location\", foreignField: \"_id\", as: \"dbProductionLocation\" } },',\n\t\t' { $lookup: { from: \"well-groups\", localField: \"id_well_group\", foreignField: \"_id\", as: \"dbWellGroup\" } },',\n\t\t' { $match: { $or: [{ \"dbProductionLocation.0\": { $exists: true } }, { \"dbWellGroup.0\": { $exists: true } }] } },',\n\t\t' { $sort: { date: -1, date_ship: -1, date_treated: -1, updatedAt: -1 } },',\n\t\t' { $limit: limit },',\n\t\t' { $project: { _id: 1 } }',\n\t\t' ]).toArray();',\n\t\t' return rows.map((doc) => doc._id).filter(Boolean);',\n\t\t'}',\n\t\t'',\n\t\t'async function selectTruckTreatDashboardReadyProductionDeliveryIds(sourceDb, query, limit = 5) {',\n\t\t' const rows = await sourceDb.collection(\"production-deliveries\").aggregate([',\n\t\t' { $match: query || {} },',\n\t\t' { $lookup: {',\n\t\t' from: \"bols\",',\n\t\t' let: { idBol: \"$id_bol\" },',\n\t\t' pipeline: [{ $match: { $expr: { $eq: [\"$_id\", \"$$idBol\"] }, status: \"Delivered\" } }],',\n\t\t' as: \"bol\"',\n\t\t' } },',\n\t\t' { $unwind: { path: \"$bol\", preserveNullAndEmptyArrays: false } },',\n\t\t' { $lookup: { from: \"production-locations\", localField: \"id_location\", foreignField: \"_id\", as: \"dbProductionLocation\" } },',\n\t\t' { $lookup: { from: \"well-groups\", localField: \"id_well_group\", foreignField: \"_id\", as: \"dbWellGroup\" } },',\n\t\t' { $match: { $or: [{ \"dbProductionLocation.0\": { $exists: true } }, { \"dbWellGroup.0\": { $exists: true } }] } },',\n\t\t' { $sort: { date: -1, date_treated: -1, updatedAt: -1 } },',\n\t\t' { $limit: limit },',\n\t\t' { $project: { _id: 1 } }',\n\t\t' ]).toArray();',\n\t\t' return rows.map((doc) => doc._id).filter(Boolean);',\n\t\t'}',\n\t\t'',\n\t\t'async function selectLocalhostDeliveryFixtureIds(sourceDb, query, limit = 3) {',\n\t\t' const rows = await sourceDb.collection(\"production-deliveries\").aggregate([',\n\t\t' { $match: { $and: [query || {}, { id_bol: { $type: \"string\", $ne: \"\" } }] } },',\n\t\t' { $lookup: { from: \"production-sales-orders\", localField: \"id_activity\", foreignField: \"_id\", as: \"dbPSO\" } },',\n\t\t' { $unwind: { path: \"$dbPSO\", preserveNullAndEmptyArrays: false } },',\n\t\t' { $match: { \"dbPSO.type\": { $ne: \"Consignment Invoice\" } } },',\n\t\t' { $lookup: { from: \"production-locations\", localField: \"id_location\", foreignField: \"_id\", as: \"dbProductionLocation\" } },',\n\t\t' { $lookup: { from: \"well-groups\", localField: \"id_well_group\", foreignField: \"_id\", as: \"dbWellGroup\" } },',\n\t\t' { $match: { $or: [{ \"dbProductionLocation.0\": { $exists: true } }, { \"dbWellGroup.0\": { $exists: true } }] } },',\n\t\t' { $sort: { date: -1, date_ship: -1, updatedAt: -1 } },',\n\t\t' { $limit: limit },',\n\t\t' { $project: { _id: 1 } }',\n\t\t' ]).toArray();',\n\t\t' return rows.map((doc) => doc._id).filter(Boolean);',\n\t\t'}',\n\t\t'',\n\t\t'async function seedBillingInventory(sourceDb, targetDb) {',\n\t\t' const summary = { profile: \"billing_inventory\", collections: {}, selected: {}, notes: [] };',\n\t\t' const identifiers = extractSeedIdentifiers();',\n\t\t' summary.selected.seed_identifiers = identifiers;',\n\t\t' const billableDeliveryQuery = { $and: [',\n\t\t' { $or: [{ type: \"Delivery\" }, { type: \"Pickup\" }] },',\n\t\t' { $or: [{ consignment: { $exists: false } }, { consignment: false }] },',\n\t\t' { approved: true }, { invoiced: false }, { generic: false }, { \"invoices.0\": { $exists: false } },',\n\t\t' { $or: [{ billed: true }, { memo_bill: true }] }',\n\t\t' ] };',\n\t\t' const billableTruckTreatQuery = { $and: [',\n\t\t' { type: \"Truck Treat\" }, { invoiced: false }, { generic: false }, { approved: true }, { \"invoices.0\": { $exists: false } },',\n\t\t' { $or: [{ billed: true }, { memo_bill: true }] }',\n\t\t' ] };',\n\t\t' const hintedQuery = identifierQuery(identifiers);',\n\t\t' let hintedProductionDeliveries = [];',\n\t\t' let hintedTruckTreatingDeliveries = [];',\n\t\t' if (hintedQuery) {',\n\t\t' hintedProductionDeliveries = await copyByIds(sourceDb, targetDb, \"production-deliveries\", await selectDashboardReadyProductionDeliveryIds(sourceDb, { $and: [billableDeliveryQuery, hintedQuery] }, 3), summary, 3);',\n\t\t' hintedTruckTreatingDeliveries = await copyByIds(sourceDb, targetDb, \"production-deliveries\", await selectTruckTreatDashboardReadyProductionDeliveryIds(sourceDb, { $and: [billableTruckTreatQuery, hintedQuery] }, 3), summary, 3);',\n\t\t' if (hintedProductionDeliveries.length || hintedTruckTreatingDeliveries.length) summary.notes.push(`Preferred live records matching ticket identifiers: ${identifiers.join(\", \")}`);',\n\t\t' else summary.notes.push(`No billable live records matched ticket identifiers: ${identifiers.join(\", \")}`);',\n\t\t' }',\n\t\t'',\n\t\t' let fallbackProductionDeliveries = hintedProductionDeliveries.length ? [] : await copyByIds(sourceDb, targetDb, \"production-deliveries\", await selectDashboardReadyProductionDeliveryIds(sourceDb, billableDeliveryQuery, 3), summary, 3);',\n\t\t' if (!hintedProductionDeliveries.length && !fallbackProductionDeliveries.length) {',\n\t\t' fallbackProductionDeliveries = await copyByIds(sourceDb, targetDb, \"production-deliveries\", await selectLocalhostDeliveryFixtureIds(sourceDb, billableDeliveryQuery, 3), summary, 3);',\n\t\t' if (fallbackProductionDeliveries.length) summary.notes.push(\"Created localhost-only delivery invoice fixture from live delivery data by normalizing copied BOL status to Delivered in local Mongo.\");',\n\t\t' }',\n\t\t' const fallbackTruckTreatingDeliveries = hintedTruckTreatingDeliveries.length ? [] : await copyByIds(sourceDb, targetDb, \"production-deliveries\", await selectTruckTreatDashboardReadyProductionDeliveryIds(sourceDb, billableTruckTreatQuery, 3), summary, 3);',\n\t\t' const productionDeliveries = [...hintedProductionDeliveries, ...fallbackProductionDeliveries];',\n\t\t' const truckTreatingDeliveries = [...hintedTruckTreatingDeliveries, ...fallbackTruckTreatingDeliveries];',\n\t\t' summary.selected.production_deliveries = productionDeliveries.map((doc) => doc._id);',\n\t\t' summary.selected.truck_treating_deliveries = truckTreatingDeliveries.map((doc) => doc._id);',\n\t\t' if (!productionDeliveries.length && !truckTreatingDeliveries.length) {',\n\t\t' summary.notes.push(\"No live billable delivery or truck-treatment records matched the Billing Dashboard awaiting-invoice filters.\");',\n\t\t' }',\n\t\t'',\n\t\t' const sourceDocs = [...productionDeliveries, ...truckTreatingDeliveries];',\n\t\t' const customerIds = idValues(sourceDocs, [\"id_customer\"]);',\n\t\t' const locationIds = idValues(sourceDocs, [\"id_location\"]);',\n\t\t' const wellGroupIds = idValues(sourceDocs, [\"id_well_group\"]);',\n\t\t' const itemIds = idValues(sourceDocs, [\"id_item\", \"id_chemical\"]);',\n\t\t' const bolIds = idValues(sourceDocs, [\"id_bol\", \"id_bols\"]);',\n\t\t' const yardIds = idValues(sourceDocs, [\"id_yard\"]);',\n\t\t' const activityIds = idValues(sourceDocs, [\"id_activity\", \"id_production_sales_order\", \"id_sales_order\"]);',\n\t\t' const tankIds = idValues(sourceDocs, [\"id_tank\"]);',\n\t\t' const truckTreatingDetailIds = idValues(sourceDocs, [\"id_delivery\", \"id_truck_treating_delivery\"]);',\n\t\t' const userIds = idValues(sourceDocs, [\"id_user_approved\", \"id_driver\", \"id_driver_scheduled\", \"id_account_manager\", \"id_foreman\"]);',\n\t\t'',\n\t\t' const copiedLocations = await copyByIds(sourceDb, targetDb, \"production-locations\", locationIds, summary);',\n\t\t' const copiedWellGroups = await copyByIds(sourceDb, targetDb, \"well-groups\", wellGroupIds, summary);',\n\t\t' const copiedItems = await copyByIds(sourceDb, targetDb, \"items\", itemIds, summary);',\n\t\t' const copiedChemicals = await copyByIds(sourceDb, targetDb, \"chemicals\", itemIds, summary);',\n\t\t' await copyByIds(sourceDb, targetDb, \"production-sales-orders\", activityIds, summary);',\n\t\t' await copyByIds(sourceDb, targetDb, \"production-location-tanks\", tankIds, summary);',\n\t\t' await copyByIds(sourceDb, targetDb, \"truck-treating-deliveries\", truckTreatingDetailIds, summary);',\n\t\t' await copyByIds(sourceDb, targetDb, \"customers\", unique([...customerIds, ...idValues(copiedLocations, [\"id_customer\"]), ...idValues(copiedWellGroups, [\"id_customer\"])]), summary);',\n\t\t' await copyByIds(sourceDb, targetDb, \"bols\", bolIds, summary);',\n\t\t' if (productionDeliveries.length && bolIds.length) await targetDb.collection(\"bols\").updateMany({ _id: { $in: bolIds } }, { $set: { status: \"Delivered\" } });',\n\t\t' await copyByIds(sourceDb, targetDb, \"yards\", yardIds, summary);',\n\t\t' await copyByIds(sourceDb, targetDb, \"users\", userIds, summary);',\n\t\t'',\n\t\t' const copiedServiceItems = await copyQuery(sourceDb, targetDb, \"items\", { $or: [',\n\t\t' { type: /service/i }, { type: /misc/i }, { category: /service/i }, { category: /misc/i },',\n\t\t' { name: /surcharge|fuel|delivery|service|misc/i }',\n\t\t' ] }, summary, 40, { updatedAt: -1 });',\n\t\t' summary.selected.service_or_surcharge_items = copiedServiceItems.map((doc) => doc._id);',\n\t\t'',\n\t\t' await copyQuery(sourceDb, targetDb, \"sales-taxes\", {}, summary, 80, { updatedAt: -1 });',\n\t\t' await copyQuery(sourceDb, targetDb, \"state-counties\", {}, summary, 80, { updatedAt: -1 });',\n\t\t' await copyQuery(sourceDb, targetDb, \"accounting-codes\", {}, summary, 80, { updatedAt: -1 });',\n\t\t' await copyQuery(sourceDb, targetDb, \"pricing-items\", {}, summary, 40, { updatedAt: -1 });',\n\t\t' await copyQuery(sourceDb, targetDb, \"pricing-item-miscs\", {}, summary, 40, { updatedAt: -1 });',\n\t\t' await copyQuery(sourceDb, targetDb, \"pricing-item-services\", {}, summary, 40, { updatedAt: -1 });',\n\t\t'',\n\t\t' const inventoryLocationQuery = { $or: [',\n\t\t' { id_item: { $in: unique([...itemIds, ...copiedItems.map((doc) => doc._id), ...copiedServiceItems.map((doc) => doc._id)]) } },',\n\t\t' { id_chemical: { $in: unique([...itemIds, ...copiedChemicals.map((doc) => doc._id)]) } },',\n\t\t' { id_yard: { $in: yardIds } }',\n\t\t' ] };',\n\t\t' const inventoryLocations = await copyQuery(sourceDb, targetDb, \"inventory-locations\", inventoryLocationQuery, summary, 80, { updatedAt: -1 });',\n\t\t' await copyQuery(sourceDb, targetDb, \"inventory-transactions\", { $or: [',\n\t\t' { id_inventory_location: { $in: inventoryLocations.map((doc) => doc._id) } },',\n\t\t' { id_item: { $in: itemIds } },',\n\t\t' { id_chemical: { $in: itemIds } }',\n\t\t' ] }, summary, 120, { date: -1, updatedAt: -1 });',\n\t\t'',\n\t\t' summary.ready = productionDeliveries.length > 0 || truckTreatingDeliveries.length > 0;',\n\t\t' return summary;',\n\t\t'}',\n\t\t'',\n\t\t'(async () => {',\n\t\t' const { MongoClient } = requireMongo();',\n\t\t' const source = resolveRuntimeSource();',\n\t\t' const targetUri = process.env.RESOLVEIO_SUPPORT_QA_MONGO_URL || process.env.RESOLVEIO_RUNNER_QA_MONGO_URL || process.env.MONGO_URL || \"mongodb://127.0.0.1:3001/resolveio?directConnection=true\";',\n\t' if (!source.uri) writeResult({ status: isLiveDataRequired() ? \"failed\" : \"skipped\", reason: \"missing_live_mongo_uri\", required: isLiveDataRequired(), result_path: resultPath }, isLiveDataRequired() ? 5 : 0);',\n\t\t' if (!isLocalMongoUri(targetUri)) writeResult({ status: \"failed\", reason: \"target_mongo_must_be_localhost\", target_uri_redacted: redactUri(targetUri), result_path: resultPath }, 3);',\n\t\t' if (String(source.uri) === String(targetUri)) writeResult({ status: \"failed\", reason: \"source_and_target_mongo_match\", result_path: resultPath }, 3);',\n\t\t' const sourceClient = new MongoClient(source.uri, { readPreference: \"secondaryPreferred\", serverSelectionTimeoutMS: 15000 });',\n\t\t' const targetClient = new MongoClient(targetUri, { serverSelectionTimeoutMS: 15000 });',\n\t\t' try {',\n\t\t' await sourceClient.connect();',\n\t\t' await targetClient.connect();',\n\t\t' const sourceDb = source.database ? sourceClient.db(source.database) : sourceClient.db();',\n\t\t' const targetDb = targetClient.db();',\n\t\t' const summary = await seedBillingInventory(sourceDb, targetDb);',\n\t\t' writeResult({',\n\t\t' status: summary.ready ? \"pass\" : \"needs-data\",',\n\t\t' source_uri_redacted: redactUri(source.uri),',\n\t\t' target_uri_redacted: redactUri(targetUri),',\n\t\t' ...summary,',\n\t\t' result_path: resultPath',\n\t\t' }, summary.ready ? 0 : 4);',\n\t\t' } finally {',\n\t\t' await sourceClient.close().catch(() => undefined);',\n\t\t' await targetClient.close().catch(() => undefined);',\n\t\t' }',\n\t\t'})().catch((error) => writeResult({ status: \"failed\", reason: error && (error.stack || error.message) || String(error), result_path: resultPath }, 2));',\n\t\t''\n\t].join('\\n');\n}\n\nexport function buildResolveIORunnerBugfixComparisonQaScript(): string {\n\treturn [\n\t\t'#!/usr/bin/env bash',\n\t\t'set -u',\n\t\t'TOOLS_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"',\n\t\t'source \"$TOOLS_DIR/env.sh\"',\n\t\t'PROJECT_ROOT=\"${1:-}\"',\n\t\t'if [ -z \"$PROJECT_ROOT\" ]; then',\n\t\t' echo \"Usage: $0 <project-root> [baseline-ref] -- <qa-command...>\" >&2',\n\t\t' exit 2',\n\t\t'fi',\n\t\t'shift || true',\n\t\t'DEFAULT_BASELINE_REF=\"${RESOLVEIO_RUNNER_QA_BASELINE_REF:-${RESOLVEIO_SUPPORT_QA_BASELINE_REF:-origin/master}}\"',\n\t\t'if [ \"${1:-}\" = \"--\" ]; then',\n\t\t' BASELINE_REF=\"$DEFAULT_BASELINE_REF\"',\n\t\t'else',\n\t\t' BASELINE_REF=\"${1:-$DEFAULT_BASELINE_REF}\"',\n\t\t' if [ \"$#\" -gt 0 ]; then shift || true; fi',\n\t\t'fi',\n\t\t'if [ \"${1:-}\" = \"--\" ]; then shift; fi',\n\t\t'if [ \"$#\" -eq 0 ]; then',\n\t\t' echo \"Usage: $0 <project-root> [baseline-ref] -- <qa-command...>\" >&2',\n\t\t' exit 2',\n\t\t'fi',\n\t\t'PROJECT_ROOT=\"$(cd \"$PROJECT_ROOT\" && pwd)\"',\n\t\t'REPO_ROOT=\"$(git -C \"$PROJECT_ROOT\" rev-parse --show-toplevel)\"',\n\t\t'PROJECT_REL=\"$(git -C \"$REPO_ROOT\" ls-files --full-name \"$PROJECT_ROOT\" 2>/dev/null | head -1 | xargs dirname 2>/dev/null || true)\"',\n\t\t'if [ -z \"$PROJECT_REL\" ] || [ \"$PROJECT_REL\" = \".\" ]; then',\n\t\t' PROJECT_REL=\"$(node - \"$REPO_ROOT\" \"$PROJECT_ROOT\" <<\\'RESOLVEIO_REL\\'',\n\t\t'const path = require(\"path\");',\n\t\t'console.log(path.relative(process.argv[2], process.argv[3]) || \".\");',\n\t\t'RESOLVEIO_REL',\n\t\t' )\"',\n\t\t'fi',\n\t\t'ARTIFACT_DIR=\"$PROJECT_ROOT/qa-artifacts\"',\n\t\t'mkdir -p \"$ARTIFACT_DIR/baseline\" \"$ARTIFACT_DIR/candidate\"',\n\t\t'RESULT_JSON=\"$ARTIFACT_DIR/bugfix-comparison-result.json\"',\n\t\t'CANDIDATE_PATCH=\"$ARTIFACT_DIR/bugfix-candidate.patch\"',\n\t\t'ORIGINAL_HEAD=\"$(git -C \"$REPO_ROOT\" rev-parse HEAD)\"',\n\t\t'ORIGINAL_BRANCH=\"$(git -C \"$REPO_ROOT\" symbolic-ref --short HEAD 2>/dev/null || true)\"',\n\t\t'QA_COMMAND=(\"$@\")',\n\t\t'STOPPER=\"$TOOLS_DIR/stop-local-qa.sh\"',\n\t\t'CURRENT_PHASE=\"\"',\n\t\t'stop_local_qa() {',\n\t\t' \"$STOPPER\" \"$PROJECT_ROOT\" >/dev/null 2>&1 || true',\n\t\t'}',\n\t\t'write_json() {',\n\t\t' node - \"$RESULT_JSON\" \"$@\" <<\\'RESOLVEIO_WRITE_JSON\\'',\n\t\t'const fs = require(\"fs\");',\n\t\t'const [path, status, baselineExit, candidateExit, baselineRef, originalHead, projectRel, note] = process.argv.slice(2);',\n\t\t'const payload = {',\n\t\t' status,',\n\t\t' baseline_ref: baselineRef,',\n\t\t' original_head: originalHead,',\n\t\t' project: projectRel,',\n\t\t' baseline_exit_code: Number(baselineExit),',\n\t\t' candidate_exit_code: Number(candidateExit),',\n\t\t' code_switch_proven: Number(baselineExit) !== 0 && Number(candidateExit) === 0,',\n\t\t' candidate_passed: Number(candidateExit) === 0,',\n\t\t' baseline_failed: Number(baselineExit) !== 0,',\n\t\t' note,',\n\t\t' artifact_dirs: { baseline: \"qa-artifacts/baseline\", candidate: \"qa-artifacts/candidate\" },',\n\t\t' created_at: new Date().toISOString()',\n\t\t'};',\n\t\t'fs.writeFileSync(path, JSON.stringify(payload, null, 2));',\n\t\t'console.log(JSON.stringify(payload, null, 2));',\n\t\t'RESOLVEIO_WRITE_JSON',\n\t\t'}',\n\t\t'snapshot_phase_artifacts() {',\n\t\t' local phase=\"$1\"',\n\t\t' mkdir -p \"$ARTIFACT_DIR/$phase\"',\n\t\t' find \"$ARTIFACT_DIR\" -maxdepth 1 -type f \\\\( -name \"*.png\" -o -name \"*.jpg\" -o -name \"*.jpeg\" -o -name \"*.webp\" -o -name \"*.json\" -o -name \"*.txt\" -o -name \"*.log\" -o -name \"*.zip\" \\\\) -print0 2>/dev/null | while IFS= read -r -d \"\" file; do',\n\t\t' cp \"$file\" \"$ARTIFACT_DIR/$phase/$(basename \"$file\")\" 2>/dev/null || true',\n\t\t' done',\n\t\t'}',\n\t\t'capture_candidate_patch() {',\n\t\t' : > \"$CANDIDATE_PATCH\"',\n\t\t' git -C \"$REPO_ROOT\" diff --binary -- \"$PROJECT_REL\" >> \"$CANDIDATE_PATCH\" || true',\n\t\t' git -C \"$REPO_ROOT\" diff --binary --cached -- \"$PROJECT_REL\" >> \"$CANDIDATE_PATCH\" || true',\n\t\t'}',\n\t\t'checkout_project_from_ref() {',\n\t\t' local ref=\"$1\"',\n\t\t' git -C \"$REPO_ROOT\" checkout \"$ref\" -- \"$PROJECT_REL\"',\n\t\t'}',\n\t\t'restore_candidate() {',\n\t\t' stop_local_qa',\n\t\t' checkout_project_from_ref \"$ORIGINAL_HEAD\" || true',\n\t\t' if [ -s \"$CANDIDATE_PATCH\" ]; then',\n\t\t' git -C \"$REPO_ROOT\" apply --whitespace=nowarn \"$CANDIDATE_PATCH\" || true',\n\t\t' fi',\n\t\t'}',\n\t\t'trap restore_candidate EXIT',\n\t\t'run_phase() {',\n\t\t' local phase=\"$1\"',\n\t\t' CURRENT_PHASE=\"$phase\"',\n\t\t' stop_local_qa',\n\t\t' export RESOLVEIO_RUNNER_QA_PHASE=\"$phase\"',\n\t\t' export RESOLVEIO_SUPPORT_QA_PHASE=\"$phase\"',\n\t\t' export RESOLVEIO_RUNNER_QA_ARTIFACT_DIR=\"$ARTIFACT_DIR/$phase\"',\n\t\t' export RESOLVEIO_SUPPORT_QA_ARTIFACT_DIR=\"$ARTIFACT_DIR/$phase\"',\n\t\t' mkdir -p \"$ARTIFACT_DIR/$phase\"',\n\t\t' find \"$ARTIFACT_DIR/$phase\" -maxdepth 1 -type f \\\\( -name \"*.png\" -o -name \"*.jpg\" -o -name \"*.jpeg\" -o -name \"*.webp\" -o -name \"*.json\" -o -name \"*.txt\" -o -name \"*.log\" -o -name \"*.zip\" \\\\) -delete 2>/dev/null || true',\n\t\t' find \"$ARTIFACT_DIR\" -maxdepth 1 -type f \\\\( -name \"*.png\" -o -name \"*.jpg\" -o -name \"*.jpeg\" -o -name \"*.webp\" -o -name \"*proof.json\" -o -name \"auth-bootstrap-result.json\" \\\\) -delete 2>/dev/null || true',\n\t\t' set +e',\n\t\t' (cd \"$REPO_ROOT\" && \"${QA_COMMAND[@]}\") 2>&1 | tee \"$ARTIFACT_DIR/$phase/qa-command.log\"',\n\t\t' local rc=\"${PIPESTATUS[0]}\"',\n\t\t' set +e',\n\t\t' snapshot_phase_artifacts \"$phase\"',\n\t\t' stop_local_qa',\n\t\t' return \"$rc\"',\n\t\t'}',\n\t\t'echo \"ResolveIO bugfix comparison QA preserving candidate diff for $PROJECT_REL\"',\n\t\t'capture_candidate_patch',\n\t\t'stop_local_qa',\n\t\t'echo \"ResolveIO bugfix comparison QA baseline phase: $BASELINE_REF\"',\n\t\t'checkout_project_from_ref \"$BASELINE_REF\"',\n\t\t'run_phase baseline',\n\t\t'BASELINE_EXIT=\"$?\"',\n\t\t'echo \"ResolveIO bugfix comparison QA candidate phase: restored workspace candidate\"',\n\t\t'restore_candidate',\n\t\t'run_phase candidate',\n\t\t'CANDIDATE_EXIT=\"$?\"',\n\t\t'if [ \"$CANDIDATE_EXIT\" = \"0\" ] && [ \"$BASELINE_EXIT\" != \"0\" ]; then',\n\t\t' STATUS=\"pass\"',\n\t\t' NOTE=\"Baseline failed and candidate passed for the same QA command.\"',\n\t\t'elif [ \"$CANDIDATE_EXIT\" = \"0\" ]; then',\n\t\t' STATUS=\"inconclusive_baseline_also_passed\"',\n\t\t' NOTE=\"Candidate passed, but baseline also passed; the code switch did not prove the bug fix.\"',\n\t\t'else',\n\t\t' STATUS=\"fail_candidate_failed\"',\n\t\t' NOTE=\"Candidate failed the QA command.\"',\n\t\t'fi',\n\t\t'write_json \"$STATUS\" \"$BASELINE_EXIT\" \"$CANDIDATE_EXIT\" \"$BASELINE_REF\" \"$ORIGINAL_HEAD\" \"$PROJECT_REL\" \"$NOTE\"',\n\t\t'[ \"$CANDIDATE_EXIT\" = \"0\" ] || exit \"$CANDIDATE_EXIT\"',\n\t\t'[ \"$STATUS\" = \"pass\" ] || exit 8',\n\t\t''\n\t].join('\\n');\n}\n\nexport function buildResolveIORunnerQaWorkflowProbeScript(): string {\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 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 || \"/\";',\n\t\t'const targetRoute = routeArg.startsWith(\"/\") ? routeArg : `/${routeArg}`;',\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 matrixPath = path.join(artifactDir, \"qa-coverage-matrix.json\");',\n\t\t'const resultPath = path.join(artifactDir, \"qa-workflow-probe-result.json\");',\n\t\t'const passScreenshotPath = path.join(artifactDir, \"qa-workflow-route-ready.jpg\");',\n\t\t'const failScreenshotPath = path.join(artifactDir, \"qa-workflow-route-blocked.jpg\");',\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 || \"admin\";',\n\t\t'const password = process.env.RESOLVEIO_RUNNER_QA_PASSWORD || process.env.RESOLVEIO_SUPPORT_QA_PASSWORD || \"\";',\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'',\n\t\t'function stripTrailingSlash(value) { return String(value || \"\").replace(/\\\\/+$/, \"\"); }',\n\t\t'function delay(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); }',\n\t\t'function writeJson(filePath, payload) { fs.mkdirSync(path.dirname(filePath), { recursive: true }); fs.writeFileSync(filePath, JSON.stringify(payload, null, 2)); }',\n\t\t'function readJson(filePath) { try { return JSON.parse(fs.readFileSync(filePath, \"utf8\")); } catch (error) { return null; } }',\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: 3000 }, (res) => { res.resume(); resolve(Boolean(res.statusCode && res.statusCode < 500)); });',\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} catch (error) { resolve(false); }',\n\t\t'\t});',\n\t\t'}',\n\t\t'async function waitForHttpReady(url, label) {',\n\t\t'\tconst deadline = Date.now() + 45000;',\n\t\t'\twhile (Date.now() < deadline) {',\n\t\t'\t\tif (await requestReady(url)) return;',\n\t\t'\t\tawait delay(1500);',\n\t\t'\t}',\n\t\t'\tthrow new Error(`${label} did not become ready at ${url}`);',\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, { method: \"POST\", timeout: 20000, headers: { \"content-type\": \"application/json\", \"content-length\": Buffer.byteLength(body), \"origin\": clientUrl } }, (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) : {}; } catch (error) { reject(new Error(`${url} returned non-JSON HTTP ${res.statusCode}: ${raw.slice(0, 300)}`)); return; }',\n\t\t'\t\t\t\tif (!res.statusCode || res.statusCode >= 400) { reject(new Error(`${url} returned HTTP ${res.statusCode}: ${JSON.stringify(json).slice(0, 500)}`)); return; }',\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'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) { try { return require(candidate); } catch (error) {} }',\n\t\t'\tthrow new Error(\"Unable to require puppeteer from project/server node_modules or global resolution\");',\n\t\t'}',\n\t\t'async function launchBrowser(puppeteer) {',\n\t\t'\tconst launchOptions = { headless: true, defaultViewport: { width: viewportWidth, height: viewportHeight }, args: [\"--no-sandbox\", \"--disable-setuid-sandbox\", \"--disable-dev-shm-usage\", `--window-size=${viewportWidth},${viewportHeight}`] };',\n\t\t'\tif (process.env.PUPPETEER_EXECUTABLE_PATH || process.env.CHROME_BIN) launchOptions.executablePath = process.env.PUPPETEER_EXECUTABLE_PATH || process.env.CHROME_BIN;',\n\t\t'\treturn puppeteer.launch(launchOptions);',\n\t\t'}',\n\t\t'async function login() {',\n\t\t'\tif (!password) throw new Error(\"QA password is empty; source the generated env.sh before workflow probe\");',\n\t\t'\tconst loginJson = await requestJson(`${serverUrl}/login`, { username, password });',\n\t\t'\tconst refreshToken = loginJson && loginJson.result && loginJson.result.token;',\n\t\t'\tif (loginJson.error || !refreshToken) throw new Error(`Login failed: ${JSON.stringify(loginJson).slice(0, 800)}`);',\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) throw new Error(`Access token failed: ${JSON.stringify(accessJson).slice(0, 800)}`);',\n\t\t'\treturn { refreshToken, accessToken, user };',\n\t\t'}',\n\t\t'async function seedAuth(page, auth) {',\n\t\t'\tawait page.goto(clientUrl, { waitUntil: \"domcontentloaded\", timeout: 45000 });',\n\t\t'\tawait page.evaluate((payload) => {',\n\t\t'\t\tlocalStorage.clear();',\n\t\t'\t\tsessionStorage.clear();',\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 || {}), other: { ...((payload.user || {}).other || {}), tour_completed: true, took_tour: true }, settings: { ...((payload.user || {}).settings || {}), opening_route: payload.targetRoute } }));',\n\t\t'\t\tlocalStorage.setItem(\"lastURL\", payload.targetRoute);',\n\t\t'\t}, { ...auth, targetRoute });',\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 { url: location.href, title: document.title, bodyTextSnippet: bodyText.slice(0, 1200), hasLoginText: /Employee\\\\/Customer Login|Employee Sign In|Customer Access|Unable to sign in/i.test(bodyText), hasOfflineModeText: bodyText.includes(\"*** OFFLINE MODE ***\") };',\n\t\t'\t});',\n\t\t'}',\n\t\t'function updateMatrix(status, screenshotPath, caption, assertion) {',\n\t\t'\tconst matrix = readJson(matrixPath) || { status: \"started\", rows: [] };',\n\t\t'\tmatrix.workflow_probe = { status, route: targetRoute, screenshot: screenshotPath, caption, assertion, updated_at: new Date().toISOString() };',\n\t\t'\tmatrix.updated_at = new Date().toISOString();',\n\t\t'\tconst rows = Array.isArray(matrix.rows) ? matrix.rows : [];',\n\t\t'\tif (rows[0]) {',\n\t\t'\t\trows[0].route_probe = { status, route: targetRoute, screenshot: screenshotPath, caption, assertion, updated_at: matrix.updated_at };',\n\t\t'\t\tif (status !== \"pass\") { rows[0].status = \"blocked\"; rows[0].screenshot = screenshotPath; rows[0].caption = caption; }',\n\t\t'\t\telse if (!rows[0].status || rows[0].status === \"pending\") { rows[0].status = \"in_progress\"; }',\n\t\t'\t}',\n\t\t'\tmatrix.rows = rows;',\n\t\t'\twriteJson(matrixPath, matrix);',\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\tconst auth = await login();',\n\t\t'\t\tawait seedAuth(page, auth);',\n\t\t'\t\tawait page.goto(`${clientUrl}${targetRoute}`, { waitUntil: \"domcontentloaded\", timeout: 60000 });',\n\t\t'\t\tawait page.waitForSelector(\"body\", { timeout: 30000 });',\n\t\t'\t\tawait delay(2500);',\n\t\t'\t\tconst summary = await pageSummary(page);',\n\t\t'\t\tif (summary.hasLoginText || summary.hasOfflineModeText || !summary.bodyTextSnippet) throw new Error(`Workflow route did not reach authenticated app: ${JSON.stringify(summary).slice(0, 1000)}`);',\n\t\t'\t\tconst caption = `Workflow route ready: ${targetRoute} loaded in authenticated local QA with live seeded data available.`;',\n\t\t'\t\tawait page.screenshot({ path: passScreenshotPath, type: \"jpeg\", quality: 82, fullPage: false });',\n\t\t'\t\tupdateMatrix(\"pass\", passScreenshotPath, caption, \"Authenticated customer workflow route loaded; deeper row-specific UI/data proof still required.\");',\n\t\t'\t\tconst result = { status: \"pass\", clientUrl, serverUrl, targetRoute, screenshot: passScreenshotPath, caption, page: summary, matrix: matrixPath };',\n\t\t'\t\twriteJson(resultPath, result);',\n\t\t'\t\tconsole.log(JSON.stringify(result, null, 2));',\n\t\t'\t} catch (error) {',\n\t\t'\t\tlet summary = null;',\n\t\t'\t\ttry { if (page) { await page.screenshot({ path: failScreenshotPath, type: \"jpeg\", quality: 82, fullPage: false }); summary = await pageSummary(page); } } catch (screenshotError) {}',\n\t\t'\t\tconst caption = `Blocked before workflow QA: ${targetRoute} could not be reached in authenticated local QA.`;',\n\t\t'\t\tupdateMatrix(\"blocked\", failScreenshotPath, caption, error && (error.message || String(error)) || \"Workflow probe failed\");',\n\t\t'\t\tconst result = { status: \"blocked\", clientUrl, serverUrl, targetRoute, screenshot: failScreenshotPath, caption, error: error && (error.stack || error.message) || String(error), page: summary, matrix: matrixPath };',\n\t\t'\t\twriteJson(resultPath, result);',\n\t\t'\t\tconsole.error(JSON.stringify(result, null, 2));',\n\t\t'\t\tprocess.exitCode = 1;',\n\t\t'\t} finally {',\n\t\t'\t\tawait browser.close().catch(() => undefined);',\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\nexport function buildResolveIORunnerQaToolsReadme(options: ResolveIORunnerQaToolBundleOptions = {}): string {\n\tconst mode = options.mode || 'runner';\n\tconst toolsDir = mode === 'support' ? '.resolveio-support-tools' : '.resolveio-ai-runner-tools';\n\tconst port = normalizePort(options.qaClientPort, '4200');\n\tconst clientUrlVar = envVar(mode, 'CLIENT_URL');\n\tconst keepaliveVar = envVar(mode, 'KEEPALIVE');\n\tconst usernameVar = envVar(mode, 'USERNAME');\n\tconst passwordVar = envVar(mode, 'PASSWORD');\n\treturn [\n\t\t`# ResolveIO ${mode === 'support' ? 'Support' : 'AI Runner'} Local QA Tools`,\n\t\t'',\n\t\t'These scripts are generated by `@resolveio/server-lib` and are shared by support tickets, AICoder app-builder runs, and AI-terminal runs.',\n\t\t'',\n\t\t'```bash',\n\t\t`source ${toolsDir}/env.sh`,\n\t\t`${toolsDir}/run-local-qa.sh <project-root>`,\n\t\t`node ${toolsDir}/qa-live-data-seed.js <project-root>`,\n\t\t`node ${toolsDir}/qa-auth-bootstrap.js <project-root> /target-route`,\n\t\t`node ${toolsDir}/qa-workflow-probe.js <project-root> /target-route`,\n\t\t`${toolsDir}/bugfix-comparison-qa.sh <project-root> origin/master -- bash -lc '<same QA command>'`,\n\t\t'```',\n\t\t'',\n\t\t`This workspace reserves Angular QA client port ${port}; use \\`$${clientUrlVar}\\` instead of assuming 4200 is free.`,\n\t\t'The local QA runner starts server/client, polls the reserved client URL, writes `qa-artifacts/server.log` and `qa-artifacts/client.log`, and fails fast on fatal startup/runtime errors.',\n\t\t'The shared auth bootstrap first opens the exact localhost client origin, logs out any visible stale session, clears service workers/cache/IndexedDB/local/session storage, then calls `/login` and `/accessToken`, seeds `refreshToken`, `accessToken`, `user`, and `lastURL`, and writes `qa-artifacts/auth-bootstrap-result.json` plus a ready/failure screenshot.',\n\t\t'The shared workflow probe logs in with the same local QA account, opens the target customer route, captures an email-safe desktop JPEG, and updates `qa-artifacts/qa-coverage-matrix.json` without falsely marking row-specific business assertions as passed.',\n\t\t`For browser clickthrough work, start the runner once with \\`${keepaliveVar}=true ${toolsDir}/run-local-qa.sh <project-root>\\`; it detaches after the app is ready, and later calls should reuse \\`$${clientUrlVar}\\` for all login/upload/screenshot retries. Do not restart Angular for auth failures.`,\n\t\t'Do not wait for `networkidle0` or `networkidle2` in ResolveIO browser QA. Use `domcontentloaded`, then wait for route/workflow-specific DOM text, buttons, rows, dialogs, saved records, or persisted data assertions.',\n\t\t'Do not run `npm run build-dev`, `ng build`, or another Angular compile while keepalive `ng serve` is running. If a full Angular build is required after browser QA, first run the staged `stop-local-qa.sh`, then build, then restart `run-local-qa.sh` for final browser proof.',\n\t\t'Use desktop screenshots at 1920x1080 by default unless the task is explicitly mobile/responsive. Every screenshot must have a customer-facing caption.',\n\t\t'For import/export/form-submit/data workflows, prove before/action/after with representative data and a concrete row/count/value assertion.',\n\t\t'For data-backed workflows, run `qa-live-data-seed.js` after the local app is ready and before browser clickthrough. It reads a bounded live-data slice from `RESOLVEIO_QA_LIVE_MONGO_URL` or staged mongo context, writes only to localhost QA Mongo, prefers ticket-mentioned invoice/BOL/order/ticket identifiers, and records `qa-artifacts/qa-live-data-seed-result.json`.',\n\t\t'For bug fixes, use `bugfix-comparison-qa.sh` so baseline/master runs the exact same repro before the candidate/PR run. A passing candidate is not enough unless the comparison result shows baseline failed or the report explicitly explains why the baseline failure could not be reproduced.',\n\t\t`Use \\`$${usernameVar}\\` and \\`$${passwordVar}\\` for the local fixture admin account unless ticket/app-specific credentials are provided.`,\n\t\t'The env file reuses `/var/lib/resolveio/puppeteer`, npm cache, worker-safe Browserslist settings, and Angular cache prep so QA should not download a browser or rebuild cold caches unnecessarily.',\n\t\tmode === 'support'\n\t\t\t? 'Support workspaces also stage local `mongod` and `mongosh` wrappers so app server scripts can start MongoDB when the worker image does not have MongoDB installed.'\n\t\t\t: '',\n\t\t''\n\t].filter((line) => line !== '').join('\\n');\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/util/ai-runner-qa-tools.ts"],"names":[],"mappings":"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","file":"ai-runner-qa-tools.js","sourcesContent":["import { buildRunnerProcessJanitorShellLibrary } from './runner-process-janitor';\n\nexport interface ResolveIORunnerQaToolBundleOptions {\n\tmode?: 'support' | 'runner';\n\tqaClientPort?: number | string;\n\tdefaultUsername?: string;\n\tdefaultPassword?: string;\n\tjobId?: string;\n\townerId?: string;\n\trunnerToken?: string;\n\tmongoRuntimePath?: string;\n\tqaLiveDataRequired?: boolean;\n\ttoolsBinPath?: string;\n\tbrowserslistPath?: string;\n\tmongodbBinaryCachePath?: string;\n\ttmpRoot?: string;\n\thomeRoot?: string;\n}\n\nfunction shellDoubleQuote(value: string): string {\n\treturn String(value || '').replace(/[\"\\\\$`]/g, '\\\\$&');\n}\n\nfunction normalizePort(value: number | string | undefined, fallback: string): string {\n\tconst parsed = Number.parseInt(String(value || ''), 10);\n\treturn Number.isFinite(parsed) && parsed > 0 ? String(parsed) : fallback;\n}\n\nfunction envVar(mode: 'support' | 'runner', suffix: string): string {\n\treturn mode === 'support' ? `RESOLVEIO_SUPPORT_QA_${suffix}` : `RESOLVEIO_RUNNER_QA_${suffix}`;\n}\n\nexport function buildResolveIORunnerQaEnvScript(options: ResolveIORunnerQaToolBundleOptions = {}): string {\n\tconst mode = options.mode || 'runner';\n\tconst altMode = mode === 'support' ? 'runner' : 'support';\n\tconst tmpRoot = options.tmpRoot || (mode === 'support' ? '/tmp/resolveio-support-qa' : '/tmp/resolveio-ai-runner-qa');\n\tconst homeRoot = options.homeRoot || `${tmpRoot}/home`;\n\tconst defaultPort = normalizePort(options.qaClientPort, '4200');\n\tconst username = shellDoubleQuote(options.defaultUsername || 'admin');\n\tconst password = shellDoubleQuote(options.defaultPassword || '');\n\tconst jobId = shellDoubleQuote(options.jobId || '');\n\tconst ownerId = shellDoubleQuote(options.ownerId || '');\n\tconst runnerToken = shellDoubleQuote(options.runnerToken || '');\n\tconst mongoRuntimePath = shellDoubleQuote(options.mongoRuntimePath || '');\n\tconst qaLiveDataRequired = options.qaLiveDataRequired === true ? 'true' : 'false';\n\tconst toolsBinPath = options.toolsBinPath || '';\n\tconst browserslistPath = options.browserslistPath || '';\n\tconst mongodbBinaryCachePath = options.mongodbBinaryCachePath || '';\n\tconst clientPortVar = envVar(mode, 'CLIENT_PORT');\n\tconst altClientPortVar = envVar(altMode, 'CLIENT_PORT');\n\tconst clientUrlVar = envVar(mode, 'CLIENT_URL');\n\tconst altClientUrlVar = envVar(altMode, 'CLIENT_URL');\n\tconst serverUrlVar = envVar(mode, 'SERVER_URL');\n\tconst altServerUrlVar = envVar(altMode, 'SERVER_URL');\n\tconst usernameVar = envVar(mode, 'USERNAME');\n\tconst altUsernameVar = envVar(altMode, 'USERNAME');\n\tconst passwordVar = envVar(mode, 'PASSWORD');\n\tconst altPasswordVar = envVar(altMode, 'PASSWORD');\n\tconst viewportWidthVar = envVar(mode, 'VIEWPORT_WIDTH');\n\tconst altViewportWidthVar = envVar(altMode, 'VIEWPORT_WIDTH');\n\tconst viewportHeightVar = envVar(mode, 'VIEWPORT_HEIGHT');\n\tconst altViewportHeightVar = envVar(altMode, 'VIEWPORT_HEIGHT');\n\tconst timeoutVar = envVar(mode, 'ANGULAR_STARTUP_TIMEOUT_SECONDS');\n\tconst altTimeoutVar = envVar(altMode, 'ANGULAR_STARTUP_TIMEOUT_SECONDS');\n\tconst prebundleVar = envVar(mode, 'ANGULAR_PREBUNDLE');\n\tconst altPrebundleVar = envVar(altMode, 'ANGULAR_PREBUNDLE');\n\tconst reuseVar = envVar(mode, 'REUSE_RUNNING');\n\tconst altReuseVar = envVar(altMode, 'REUSE_RUNNING');\n\tconst keepaliveVar = envVar(mode, 'KEEPALIVE');\n\tconst altKeepaliveVar = envVar(altMode, 'KEEPALIVE');\n\tconst mongoPortVar = envVar(mode, 'MONGO_PORT');\n\tconst altMongoPortVar = envVar(altMode, 'MONGO_PORT');\n\tconst mongoUrlVar = envVar(mode, 'MONGO_URL');\n\tconst altMongoUrlVar = envVar(altMode, 'MONGO_URL');\n\tconst browserLoopVar = mode === 'support' ? 'RESOLVEIO_SUPPORT_QA_BROWSER' : 'RESOLVEIO_RUNNER_QA_BROWSER';\n\treturn [\n\t\t`export ${mode === 'support' ? 'RESOLVEIO_SUPPORT_QA_TMP' : 'RESOLVEIO_RUNNER_QA_TMP'}=\"${'${' + (mode === 'support' ? 'RESOLVEIO_SUPPORT_QA_TMP' : 'RESOLVEIO_RUNNER_QA_TMP') + ':-' + tmpRoot + '}'}\"`,\n\t\t`export ${mode === 'support' ? 'RESOLVEIO_SUPPORT_QA_HOME' : 'RESOLVEIO_RUNNER_QA_HOME'}=\"${'${' + (mode === 'support' ? 'RESOLVEIO_SUPPORT_QA_HOME' : 'RESOLVEIO_RUNNER_QA_HOME') + ':-' + homeRoot + '}'}\"`,\n\t\t`RESOLVEIO_QA_TMP=\"${'${' + (mode === 'support' ? 'RESOLVEIO_SUPPORT_QA_TMP' : 'RESOLVEIO_RUNNER_QA_TMP') + '}'}\"`,\n\t\t`RESOLVEIO_QA_HOME=\"${'${' + (mode === 'support' ? 'RESOLVEIO_SUPPORT_QA_HOME' : 'RESOLVEIO_RUNNER_QA_HOME') + '}'}\"`,\n\t\t'mkdir -p \"$RESOLVEIO_QA_HOME/.nvm\" \"$RESOLVEIO_QA_TMP/npm-cache\" \"$RESOLVEIO_QA_TMP/mongodb-binaries\" \"$RESOLVEIO_QA_TMP/mongodb-memory-server-core\"',\n\t\t'if [ ! -f \"$RESOLVEIO_QA_HOME/.nvm/nvm.sh\" ]; then',\n\t\t' cat > \"$RESOLVEIO_QA_HOME/.nvm/nvm.sh\" <<\\'RESOLVEIO_NVM_SHIM\\'',\n\t\t'nvm() {',\n\t\t' case \"$1\" in',\n\t\t' use|install|alias) return 0 ;;',\n\t\t' current) node -v 2>/dev/null || true; return 0 ;;',\n\t\t' *) return 0 ;;',\n\t\t' esac',\n\t\t'}',\n\t\t'RESOLVEIO_NVM_SHIM',\n\t\t'fi',\n\t\t'export HOME=\"$RESOLVEIO_QA_HOME\"',\n\t\t'export NVM_DIR=\"$RESOLVEIO_QA_HOME/.nvm\"',\n\t\ttoolsBinPath ? `export PATH=\"${toolsBinPath}:$PATH\"` : '',\n\t\t'export NODE_ENV=development',\n\t\t'# Local QA must start the application HTTP server even when launched from a dedicated support-manager worker.',\n\t\t'export IS_WORKERS_ENABLED=false',\n\t\t'export IS_WORKER_INSTANCE=false',\n\t\t'export DISABLE_WORKER_SERVER_CONNECTION=false',\n\t\t'export SUPPORT_CODEX_MANAGER_PROCESS_ONLY=false',\n\t\t'export SUPPORT_AUTO_MANAGER_PROCESS_ONLY=false',\n\t\t'export AI_ASSISTANT_CODEX_MANAGER_PROCESS_ONLY=false',\n\t\tbrowserslistPath ? `export BROWSERSLIST_CONFIG=\"${browserslistPath}\"` : '',\n\t\t`export ${clientPortVar}=\"${'${' + clientPortVar + ':-${' + altClientPortVar + ':-' + defaultPort + '}}'}\"`,\n\t\t`export ${altClientPortVar}=\"${'${' + altClientPortVar + ':-${' + clientPortVar + '}}'}\"`,\n\t\t`export ${clientUrlVar}=\"${'${' + clientUrlVar + ':-${' + altClientUrlVar + ':-http://localhost:${' + clientPortVar + '}}}'}\"`,\n\t\t`export ${altClientUrlVar}=\"${'${' + altClientUrlVar + ':-${' + clientUrlVar + '}}'}\"`,\n\t\t`export ${serverUrlVar}=\"${'${' + serverUrlVar + ':-${' + altServerUrlVar + ':-http://localhost:8080}}'}\"`,\n\t\t`export ${altServerUrlVar}=\"${'${' + altServerUrlVar + ':-${' + serverUrlVar + '}}'}\"`,\n\t\t'export ADDITIONAL_ALLOWED_ORIGINS=\"${ADDITIONAL_ALLOWED_ORIGINS:-$' + clientUrlVar + '}\"',\n\t\t`export ${viewportWidthVar}=\"${'${' + viewportWidthVar + ':-${' + altViewportWidthVar + ':-1920}}'}\"`,\n\t\t`export ${altViewportWidthVar}=\"${'${' + altViewportWidthVar + ':-${' + viewportWidthVar + '}}'}\"`,\n\t\t`export ${viewportHeightVar}=\"${'${' + viewportHeightVar + ':-${' + altViewportHeightVar + ':-1080}}'}\"`,\n\t\t`export ${altViewportHeightVar}=\"${'${' + altViewportHeightVar + ':-${' + viewportHeightVar + '}}'}\"`,\n\t\t`export ${timeoutVar}=\"${'${' + timeoutVar + ':-${' + altTimeoutVar + ':-900}}'}\"`,\n\t\t`export ${altTimeoutVar}=\"${'${' + altTimeoutVar + ':-${' + timeoutVar + '}}'}\"`,\n\t\t`export ${prebundleVar}=\"${'${' + prebundleVar + ':-${' + altPrebundleVar + ':-false}}'}\"`,\n\t\t`export ${altPrebundleVar}=\"${'${' + altPrebundleVar + ':-${' + prebundleVar + '}}'}\"`,\n\t\t`export ${reuseVar}=\"${'${' + reuseVar + ':-${' + altReuseVar + ':-true}}'}\"`,\n\t\t`export ${altReuseVar}=\"${'${' + altReuseVar + ':-${' + reuseVar + '}}'}\"`,\n\t\t`export ${keepaliveVar}=\"${'${' + keepaliveVar + ':-${' + altKeepaliveVar + ':-false}}'}\"`,\n\t\t`export ${altKeepaliveVar}=\"${'${' + altKeepaliveVar + ':-${' + keepaliveVar + '}}'}\"`,\n\t\t`export ${mongoPortVar}=\"${'${' + mongoPortVar + ':-${' + altMongoPortVar + ':-3001}}'}\"`,\n\t\t`export ${altMongoPortVar}=\"${'${' + altMongoPortVar + ':-${' + mongoPortVar + '}}'}\"`,\n\t\t`export ${mongoUrlVar}=\"${'${' + mongoUrlVar + ':-${' + altMongoUrlVar + ':-mongodb://127.0.0.1:${' + mongoPortVar + '}/resolveio?directConnection=true}}'}\"`,\n\t\t`export ${altMongoUrlVar}=\"${'${' + altMongoUrlVar + ':-${' + mongoUrlVar + '}}'}\"`,\n\t\t`export MONGO_URL=\"${'${MONGO_URL:-$' + mongoUrlVar + '}'}\"`,\n\t\t`export ${usernameVar}=\"${'${' + usernameVar + ':-${' + altUsernameVar + ':-' + username + '}}'}\"`,\n\t\t`export ${altUsernameVar}=\"${'${' + altUsernameVar + ':-${' + usernameVar + '}}'}\"`,\n\t\t`export ${passwordVar}=\"${'${' + passwordVar + ':-${' + altPasswordVar + ':-' + password + '}}'}\"`,\n\t\t`export ${altPasswordVar}=\"${'${' + altPasswordVar + ':-${' + passwordVar + '}}'}\"`,\n\t\t`export ${envVar(mode, 'JOB_ID')}=\"${'${' + envVar(mode, 'JOB_ID') + ':-${' + envVar(altMode, 'JOB_ID') + ':-' + jobId + '}}'}\"`,\n\t\t`export ${envVar(altMode, 'JOB_ID')}=\"${'${' + envVar(altMode, 'JOB_ID') + ':-${' + envVar(mode, 'JOB_ID') + '}}'}\"`,\n\t\t`export ${envVar(mode, 'OWNER_ID')}=\"${'${' + envVar(mode, 'OWNER_ID') + ':-${' + envVar(altMode, 'OWNER_ID') + ':-' + ownerId + '}}'}\"`,\n\t\t`export ${envVar(altMode, 'OWNER_ID')}=\"${'${' + envVar(altMode, 'OWNER_ID') + ':-${' + envVar(mode, 'OWNER_ID') + '}}'}\"`,\n\t\t`export ${envVar(mode, 'RUNNER_TOKEN')}=\"${'${' + envVar(mode, 'RUNNER_TOKEN') + ':-${' + envVar(altMode, 'RUNNER_TOKEN') + ':-' + runnerToken + '}}'}\"`,\n\t\t`export ${envVar(altMode, 'RUNNER_TOKEN')}=\"${'${' + envVar(altMode, 'RUNNER_TOKEN') + ':-${' + envVar(mode, 'RUNNER_TOKEN') + '}}'}\"`,\n\t\tmongoRuntimePath ? `export ${envVar(mode, 'MONGO_RUNTIME_PATH')}=\"${'${' + envVar(mode, 'MONGO_RUNTIME_PATH') + ':-${' + envVar(altMode, 'MONGO_RUNTIME_PATH') + ':-' + mongoRuntimePath + '}}'}\"` : '',\n\t\tmongoRuntimePath ? `export ${envVar(altMode, 'MONGO_RUNTIME_PATH')}=\"${'${' + envVar(altMode, 'MONGO_RUNTIME_PATH') + ':-${' + envVar(mode, 'MONGO_RUNTIME_PATH') + '}}'}\"` : '',\n\t\tmongoRuntimePath ? `export RESOLVEIO_QA_MONGO_RUNTIME_PATH=\"${'${RESOLVEIO_QA_MONGO_RUNTIME_PATH:-$' + envVar(mode, 'MONGO_RUNTIME_PATH') + '}'}\"` : '',\n\t\t`export ${envVar(mode, 'LIVE_DATA_REQUIRED')}=\"${'${' + envVar(mode, 'LIVE_DATA_REQUIRED') + ':-${' + envVar(altMode, 'LIVE_DATA_REQUIRED') + ':-' + qaLiveDataRequired + '}}'}\"`,\n\t\t`export ${envVar(altMode, 'LIVE_DATA_REQUIRED')}=\"${'${' + envVar(altMode, 'LIVE_DATA_REQUIRED') + ':-${' + envVar(mode, 'LIVE_DATA_REQUIRED') + '}}'}\"`,\n\t\t`export RESOLVEIO_QA_LIVE_DATA_REQUIRED=\"${'${RESOLVEIO_QA_LIVE_DATA_REQUIRED:-$' + envVar(mode, 'LIVE_DATA_REQUIRED') + '}'}\"`,\n\t\t'export PUPPETEER_CACHE_DIR=\"${PUPPETEER_CACHE_DIR:-/var/lib/resolveio/puppeteer}\"',\n\t\t'if [ ! -d \"$PUPPETEER_CACHE_DIR\" ] || [ ! -w \"$PUPPETEER_CACHE_DIR\" ]; then',\n\t\t' export PUPPETEER_CACHE_DIR=\"$RESOLVEIO_QA_TMP/puppeteer\"',\n\t\t' mkdir -p \"$PUPPETEER_CACHE_DIR\"',\n\t\t'fi',\n\t\t'export PLAYWRIGHT_BROWSERS_PATH=\"${PLAYWRIGHT_BROWSERS_PATH:-$RESOLVEIO_QA_HOME/.cache/ms-playwright}\"',\n\t\t'if [ -n \"${PUPPETEER_EXECUTABLE_PATH:-}\" ] && [ -x \"$PUPPETEER_EXECUTABLE_PATH\" ]; then',\n\t\t' export CHROME_BIN=\"${CHROME_BIN:-$PUPPETEER_EXECUTABLE_PATH}\"',\n\t\t'elif [ -n \"${CHROME_BIN:-}\" ] && [ -x \"$CHROME_BIN\" ]; then',\n\t\t' export PUPPETEER_EXECUTABLE_PATH=\"$CHROME_BIN\"',\n\t\t'else',\n\t\t` for ${browserLoopVar} in \"$PUPPETEER_CACHE_DIR\"/chrome-headless-shell/linux-*/chrome-headless-shell-linux64/chrome-headless-shell \"$PUPPETEER_CACHE_DIR\"/chrome/linux-*/chrome-linux64/chrome \"$PLAYWRIGHT_BROWSERS_PATH\"/chromium_headless_shell-*/chrome-headless-shell-linux64/chrome-headless-shell \"$PLAYWRIGHT_BROWSERS_PATH\"/chromium-*/chrome-linux64/chrome /usr/bin/google-chrome-stable /usr/bin/google-chrome /usr/bin/chromium /usr/bin/chromium-browser; do`,\n\t\t` if [ -x \"$${browserLoopVar}\" ]; then`,\n\t\t` export PUPPETEER_EXECUTABLE_PATH=\"$${browserLoopVar}\"`,\n\t\t` export CHROME_BIN=\"$${browserLoopVar}\"`,\n\t\t' break',\n\t\t' fi',\n\t\t' done',\n\t\t'fi',\n\t\t`unset ${browserLoopVar}`,\n\t\t'export NPM_CONFIG_CACHE=\"$RESOLVEIO_QA_TMP/npm-cache\"',\n\t\t'export NPM_CONFIG_PREFER_OFFLINE=true',\n\t\t'export NPM_CONFIG_PRODUCTION=false',\n\t\t'export npm_config_production=false',\n\t\t'export RESOLVEIO_SUPPORT_MONGOMS_PACKAGE_ROOT=\"$RESOLVEIO_QA_TMP/mongodb-memory-server-core\"',\n\t\t'export MONGOMS_DOWNLOAD_DIR=\"$RESOLVEIO_QA_TMP/mongodb-binaries\"',\n\t\tmongodbBinaryCachePath ? `export RESOLVEIO_SUPPORT_SHARED_MONGOMS_DOWNLOAD_DIR=\"${mongodbBinaryCachePath}\"` : '',\n\t\t'export MONGOMS_VERSION=\"${MONGOMS_VERSION:-7.0.14}\"',\n\t\t''\n\t].filter((line) => line !== '').join('\\n');\n}\n\nexport function buildResolveIORunnerLocalQaScript(): string {\n\treturn [\n\t\t'#!/usr/bin/env bash',\n\t\t'set -u',\n\t\t'TOOLS_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"',\n\t\t'source \"$TOOLS_DIR/env.sh\"',\n\t\t'PROJECT_ROOT=\"${1:-$(pwd)}\"',\n\t\t'PROJECT_ROOT=\"$(cd \"$PROJECT_ROOT\" && pwd)\"',\n\t\t'ARTIFACT_DIR=\"$PROJECT_ROOT/qa-artifacts\"',\n\t\t'mkdir -p \"$ARTIFACT_DIR\"',\n\t\t'CLIENT_URL=\"${RESOLVEIO_RUNNER_QA_CLIENT_URL:-${RESOLVEIO_SUPPORT_QA_CLIENT_URL:-http://localhost:${RESOLVEIO_RUNNER_QA_CLIENT_PORT:-${RESOLVEIO_SUPPORT_QA_CLIENT_PORT:-4200}}}}\"',\n\t\t'SERVER_URL=\"${RESOLVEIO_RUNNER_QA_SERVER_URL:-${RESOLVEIO_SUPPORT_QA_SERVER_URL:-http://localhost:8080}}\"',\n\t\t'CLIENT_PORT=\"${RESOLVEIO_RUNNER_QA_CLIENT_PORT:-${RESOLVEIO_SUPPORT_QA_CLIENT_PORT:-4200}}\"',\n\t\t'SERVER_PORT=\"${RESOLVEIO_RUNNER_QA_SERVER_PORT:-${RESOLVEIO_SUPPORT_QA_SERVER_PORT:-8080}}\"',\n\t\t'MONGO_PORT=\"${RESOLVEIO_RUNNER_QA_MONGO_PORT:-${RESOLVEIO_SUPPORT_QA_MONGO_PORT:-3001}}\"',\n\t\t'INSPECT_PORT=\"${RESOLVEIO_RUNNER_QA_INSPECT_PORT:-${RESOLVEIO_SUPPORT_QA_INSPECT_PORT:-9229}}\"',\n\t\t'STARTUP_TIMEOUT=\"${RESOLVEIO_RUNNER_QA_ANGULAR_STARTUP_TIMEOUT_SECONDS:-${RESOLVEIO_SUPPORT_QA_ANGULAR_STARTUP_TIMEOUT_SECONDS:-900}}\"',\n\t\t'ANGULAR_PREBUNDLE=\"${RESOLVEIO_RUNNER_QA_ANGULAR_PREBUNDLE:-${RESOLVEIO_SUPPORT_QA_ANGULAR_PREBUNDLE:-false}}\"',\n\t\t'REUSE_RUNNING=\"${RESOLVEIO_RUNNER_QA_REUSE_RUNNING:-${RESOLVEIO_SUPPORT_QA_REUSE_RUNNING:-true}}\"',\n\t\t'if [[ \"$(basename \"$TOOLS_DIR\")\" == *support* ]]; then',\n\t\t' KEEPALIVE=\"${RESOLVEIO_SUPPORT_QA_KEEPALIVE:-${RESOLVEIO_RUNNER_QA_KEEPALIVE:-false}}\"',\n\t\t'else',\n\t\t' KEEPALIVE=\"${RESOLVEIO_RUNNER_QA_KEEPALIVE:-${RESOLVEIO_SUPPORT_QA_KEEPALIVE:-false}}\"',\n\t\t'fi',\n\t\t'case \"${KEEPALIVE,,}\" in',\n\t\t' true|1|yes|on) exec >> \"$ARTIFACT_DIR/runner.log\" 2>&1 ;;',\n\t\t'esac',\n\t\t'SERVER_STABLE_SECONDS=\"${RESOLVEIO_RUNNER_QA_SERVER_STABLE_SECONDS:-${RESOLVEIO_SUPPORT_QA_SERVER_STABLE_SECONDS:-20}}\"',\n\t\t'LOCK_DIR=\"$ARTIFACT_DIR/.qa.lock\"',\n\t\t'SERVER_PID=\"\"',\n\t\t'CLIENT_PID=\"\"',\n\t\t'SERVER_REQUIRED=0',\n\t\t'RUNNER_REUSED_READY=0',\n\t\t'ANGULAR_PREBUNDLE_ARGS=()',\n\t\t'REPO_ROOT=\"$(git -C \"$PROJECT_ROOT\" rev-parse --show-toplevel 2>/dev/null || echo \"$PROJECT_ROOT\")\"',\n\t\tbuildRunnerProcessJanitorShellLibrary({ mode: 'support' }),\n\t\t'case \"${ANGULAR_PREBUNDLE,,}\" in',\n\t\t' false|0|no|off) ANGULAR_PREBUNDLE_ARGS=(--prebundle=false) ;;',\n\t\t'esac',\n\t\t'kill_tree() {',\n\t\t' local pid=\"$1\"',\n\t\t' [ -n \"$pid\" ] || return 0',\n\t\t' for child in $(pgrep -P \"$pid\" 2>/dev/null || true); do kill_tree \"$child\"; done',\n\t\t' kill \"$pid\" >/dev/null 2>&1 || true',\n\t\t' sleep 1',\n\t\t' kill -0 \"$pid\" >/dev/null 2>&1 && kill -9 \"$pid\" >/dev/null 2>&1 || true',\n\t\t'}',\n\t\t'kill_port_listeners() {',\n\t\t' local port=\"$1\"',\n\t\t' [ -n \"$port\" ] || return 0',\n\t\t' local pids=\"\"',\n\t\t' if command -v lsof >/dev/null 2>&1; then pids=\"$pids $(janitor_bounded 3 lsof -ti tcp:\"$port\")\"; fi',\n\t\t' if command -v fuser >/dev/null 2>&1; then pids=\"$pids $(janitor_bounded 3 fuser -n tcp \"$port\")\"; fi',\n\t\t' if command -v ss >/dev/null 2>&1; then pids=\"$pids $(janitor_bounded 3 ss -ltnp \"sport = :$port\" | sed -n \\'s/.*pid=\\\\([0-9][0-9]*\\\\).*/\\\\1/p\\' || true)\"; fi',\n\t\t' for pid in $pids; do',\n\t\t' [ \"$pid\" = \"$$\" ] && continue',\n\t\t' kill_tree \"$pid\"',\n\t\t' done',\n\t\t' sleep 1',\n\t\t'}',\n\t\t'kill_env_marked_processes() {',\n\t\t' [ -d /proc ] || return 0',\n\t\t' local job_id=\"${RESOLVEIO_RUNNER_QA_JOB_ID:-${RESOLVEIO_SUPPORT_QA_JOB_ID:-}}\"',\n\t\t' local owner_id=\"${RESOLVEIO_RUNNER_QA_OWNER_ID:-${RESOLVEIO_SUPPORT_QA_OWNER_ID:-}}\"',\n\t\t' local token=\"${RESOLVEIO_RUNNER_QA_RUNNER_TOKEN:-${RESOLVEIO_SUPPORT_QA_RUNNER_TOKEN:-}}\"',\n\t\t' [ -n \"$job_id$owner_id$token\" ] || return 0',\n\t\t' for env_file in /proc/[0-9]*/environ; do',\n\t\t' pid=\"${env_file#/proc/}\"',\n\t\t' pid=\"${pid%/environ}\"',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' [ \"$pid\" = \"$$\" ] && continue',\n\t\t' [ -r \"$env_file\" ] || continue',\n\t\t' env_text=\"$(tr \"\\\\0\" \"\\\\n\" < \"$env_file\" 2>/dev/null || true)\"',\n\t\t' [ -n \"$env_text\" ] || continue',\n\t\t' matched=0',\n\t\t' [ -n \"$job_id\" ] && echo \"$env_text\" | grep -Eq \"^(RESOLVEIO_RUNNER_QA_JOB_ID|RESOLVEIO_SUPPORT_QA_JOB_ID)=$job_id$\" && matched=1',\n\t\t' [ \"$matched\" = \"0\" ] && [ -n \"$owner_id\" ] && echo \"$env_text\" | grep -Eq \"^(RESOLVEIO_RUNNER_QA_OWNER_ID|RESOLVEIO_SUPPORT_QA_OWNER_ID)=$owner_id$\" && matched=1',\n\t\t' [ \"$matched\" = \"0\" ] && [ -n \"$token\" ] && echo \"$env_text\" | grep -Eq \"^(RESOLVEIO_RUNNER_QA_RUNNER_TOKEN|RESOLVEIO_SUPPORT_QA_RUNNER_TOKEN)=$token$\" && matched=1',\n\t\t' [ \"$matched\" = \"1\" ] && kill_tree \"$pid\"',\n\t\t' done',\n\t\t'}',\n\t\t'kill_artifact_log_writers() {',\n\t\t' local artifact=\"$ARTIFACT_DIR\"',\n\t\t' [ -n \"$artifact\" ] || return 0',\n\t\t' for pid in $(ps -eo pid=,args= | awk -v artifact=\"$artifact\" \\'index($0, artifact) && $0 !~ /awk -v artifact=/ && $0 !~ /ps -eo pid=,args=/ && $0 ~ /^ *[0-9]+ +(tee|tail -f) / {print $1}\\' 2>/dev/null || true); do',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' kill_tree \"$pid\"',\n\t\t' done',\n\t\t'}',\n\t\t'kill_local_mongo_processes() {',\n\t\t' local mongo_port=\"$MONGO_PORT\"',\n\t\t' [ -n \"$mongo_port\" ] || return 0',\n\t\t' for pid in $(ps -eo pid=,args= | awk -v port=\"$mongo_port\" \\'$0 !~ /awk -v port=/ && $0 !~ /ps -eo pid=,args=/ && $0 ~ /mongod/ && $0 ~ (\"--port \" port) && ($0 ~ /\\\\/tmp\\\\/resolveio-support-qa\\\\/.*mongod/ || $0 ~ /--dbpath mongo\\\\/data\\\\/db/) {print $1}\\' 2>/dev/null || true); do',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' [ \"$pid\" = \"$$\" ] && continue',\n\t\t' kill_tree \"$pid\"',\n\t\t' done',\n\t\t'}',\n\t\t'RUNNER_ANCESTOR_PIDS=\" $$ ${PPID:-} \"',\n\t\t'ancestor_pid=\"${PPID:-}\"',\n\t\t'while [ -n \"$ancestor_pid\" ] && [ \"$ancestor_pid\" != \"0\" ] && [ \"$ancestor_pid\" != \"1\" ]; do',\n\t\t' ancestor_pid=\"$(ps -o ppid= -p \"$ancestor_pid\" 2>/dev/null | tr -d \" \" || true)\"',\n\t\t' [ -n \"$ancestor_pid\" ] && RUNNER_ANCESTOR_PIDS=\"$RUNNER_ANCESTOR_PIDS $ancestor_pid \"',\n\t\t'done',\n\t\t'skip_cleanup_pid() {',\n\t\t' case \"$RUNNER_ANCESTOR_PIDS\" in *\" $1 \"*) return 0 ;; esac',\n\t\t' return 1',\n\t\t'}',\n\t\t'cleanup_project_processes() {',\n\t\t' for pid_file in \"$ARTIFACT_DIR/server.pid\" \"$ARTIFACT_DIR/client.pid\"; do',\n\t\t' [ -f \"$pid_file\" ] || continue',\n\t\t' pid=\"$(cat \"$pid_file\" 2>/dev/null || true)\"',\n\t\t' kill_tree \"$pid\"',\n\t\t' rm -f \"$pid_file\"',\n\t\t' done',\n\t\t' for pass in 1 2 3; do',\n\t\t' for port in \"$CLIENT_PORT\" \"$SERVER_PORT\" \"$MONGO_PORT\" \"$INSPECT_PORT\"; do kill_port_listeners \"$port\"; done',\n\t\t' kill_env_marked_processes',\n\t\t' kill_artifact_log_writers',\n\t\t' kill_local_mongo_processes',\n\t\t' for pid in $(ps -eo pid=,args= | awk -v root=\"$PROJECT_ROOT\" \\'index($0, root) && $0 !~ /awk -v root=/ && $0 !~ /ps -eo pid=,args=/ && $0 !~ /run-local-qa\\\\.sh|stop-local-qa\\\\.sh|bugfix-comparison-qa\\\\.sh/ && $0 ~ /(ng serve|node .*node_modules\\\\/\\\\.bin\\\\/ng|esbuild|npm run client|start_client\\\\.sh|npm run server|start_server\\\\.sh|nodemon|node .*tmp\\\\/index\\\\.js|mongod|mongodb-binaries\\\\/mongod)/ {print $1}\\' 2>/dev/null || true); do',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' kill_tree \"$pid\"',\n\t\t' done',\n\t\t' if [ -d /proc ]; then',\n\t\t' for proc_cwd in /proc/[0-9]*/cwd; do',\n\t\t' pid=\"${proc_cwd#/proc/}\"',\n\t\t' pid=\"${pid%/cwd}\"',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' cwd=\"$(readlink -f \"$proc_cwd\" 2>/dev/null || true)\"',\n\t\t' case \"$cwd\" in',\n\t\t' \"$PROJECT_ROOT\"|\"$PROJECT_ROOT\"/*) kill_tree \"$pid\" ;;',\n\t\t' esac',\n\t\t' done',\n\t\t' fi',\n\t\t' sleep 1',\n\t\t' done',\n\t\t' local wait_until=$((SECONDS + 60))',\n\t\t' while [ \"$SECONDS\" -lt \"$wait_until\" ]; do',\n\t\t' local found=0',\n\t\t' for port in \"$CLIENT_PORT\" \"$SERVER_PORT\" \"$MONGO_PORT\" \"$INSPECT_PORT\"; do',\n\t\t' port_pids=\"\"',\n\t\t' if command -v lsof >/dev/null 2>&1; then port_pids=\"$port_pids $(janitor_bounded 3 lsof -ti tcp:\"$port\")\"; fi',\n\t\t' for pid in $port_pids; do skip_cleanup_pid \"$pid\" && continue; found=1; kill_tree \"$pid\"; done',\n\t\t' done',\n\t\t' if [ -d /proc ]; then',\n\t\t' job_id=\"${RESOLVEIO_RUNNER_QA_JOB_ID:-${RESOLVEIO_SUPPORT_QA_JOB_ID:-}}\"',\n\t\t' owner_id=\"${RESOLVEIO_RUNNER_QA_OWNER_ID:-${RESOLVEIO_SUPPORT_QA_OWNER_ID:-}}\"',\n\t\t' token=\"${RESOLVEIO_RUNNER_QA_RUNNER_TOKEN:-${RESOLVEIO_SUPPORT_QA_RUNNER_TOKEN:-}}\"',\n\t\t' for env_file in /proc/[0-9]*/environ; do',\n\t\t' pid=\"${env_file#/proc/}\"',\n\t\t' pid=\"${pid%/environ}\"',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' [ -r \"$env_file\" ] || continue',\n\t\t' env_text=\"$(tr \"\\\\0\" \"\\\\n\" < \"$env_file\" 2>/dev/null || true)\"',\n\t\t' matched=0',\n\t\t' [ -n \"$job_id\" ] && echo \"$env_text\" | grep -Eq \"^(RESOLVEIO_RUNNER_QA_JOB_ID|RESOLVEIO_SUPPORT_QA_JOB_ID)=$job_id$\" && matched=1',\n\t\t' [ \"$matched\" = \"0\" ] && [ -n \"$owner_id\" ] && echo \"$env_text\" | grep -Eq \"^(RESOLVEIO_RUNNER_QA_OWNER_ID|RESOLVEIO_SUPPORT_QA_OWNER_ID)=$owner_id$\" && matched=1',\n\t\t' [ \"$matched\" = \"0\" ] && [ -n \"$token\" ] && echo \"$env_text\" | grep -Eq \"^(RESOLVEIO_RUNNER_QA_RUNNER_TOKEN|RESOLVEIO_SUPPORT_QA_RUNNER_TOKEN)=$token$\" && matched=1',\n\t\t' [ \"$matched\" = \"1\" ] && found=1 && kill_tree \"$pid\"',\n\t\t' done',\n\t\t' fi',\n\t\t' kill_artifact_log_writers',\n\t\t' kill_local_mongo_processes',\n\t\t' for pid in $(ps -eo pid=,args= | awk -v root=\"$PROJECT_ROOT\" \\'index($0, root) && $0 !~ /awk -v root=/ && $0 !~ /ps -eo pid=,args=/ && $0 !~ /run-local-qa\\\\.sh|stop-local-qa\\\\.sh|bugfix-comparison-qa\\\\.sh/ && $0 ~ /(ng serve|node .*node_modules\\\\/\\\\.bin\\\\/ng|esbuild|npm run client|start_client\\\\.sh|npm run server|start_server\\\\.sh|nodemon|node .*tmp\\\\/index\\\\.js|mongod|mongodb-binaries\\\\/mongod)/ {print $1}\\' 2>/dev/null || true); do',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' found=1',\n\t\t' done',\n\t\t' [ \"$found\" = \"0\" ] && break',\n\t\t' sleep 2',\n\t\t' done',\n\t\t'}',\n\t\t'cleanup() {',\n\t\t' [ \"${RUNNER_REUSED_READY:-0}\" = \"1\" ] && return 0',\n\t\t' janitor_stop_heartbeat',\n\t\t' rm -f \"$ARTIFACT_DIR/heartbeat.pid\"',\n\t\t' janitor_update_manifest_status cleanup_started',\n\t\t' kill_tree \"$SERVER_PID\"',\n\t\t' kill_tree \"$CLIENT_PID\"',\n\t\t' cleanup_project_processes',\n\t\t' janitor_update_manifest_status cleanup_complete',\n\t\t' janitor_write_cleanup_status complete 0',\n\t\t' rm -rf \"$LOCK_DIR\" >/dev/null 2>&1 || true',\n\t\t'}',\n\t\t'trap cleanup EXIT',\n\t\t'detach_keepalive() {',\n\t\t' if ! probe_url \"$CLIENT_URL\"; then',\n\t\t' echo \"ResolveIO AI runner QA keepalive refused: client URL is no longer reachable at $CLIENT_URL.\" | tee -a \"$ARTIFACT_DIR/runner.log\"',\n\t\t' return 1',\n\t\t' fi',\n\t\t' if [ \"${SERVER_REQUIRED:-0}\" = \"1\" ] && ! probe_url \"$SERVER_URL\"; then',\n\t\t' echo \"ResolveIO AI runner QA keepalive refused: server URL is no longer reachable at $SERVER_URL.\" | tee -a \"$ARTIFACT_DIR/runner.log\"',\n\t\t' return 4',\n\t\t' fi',\n\t\t' janitor_update_manifest_status ready_keepalive',\n\t\t' janitor_write_cleanup_status ready_keepalive 0',\n\t\t' rm -rf \"$LOCK_DIR\" >/dev/null 2>&1 || true',\n\t\t' trap - EXIT',\n\t\t' disown >/dev/null 2>&1 || true',\n\t\t' echo \"ResolveIO AI runner QA keepalive detached at $CLIENT_URL; stop with $TOOLS_DIR/stop-local-qa.sh $PROJECT_ROOT.\"',\n\t\t' exit 0',\n\t\t'}',\n\t\t'probe_url() { node - \"$1\" <<\\'RESOLVEIO_PROBE\\'',\n\t\t'const http = require(\"http\");',\n\t\t'const https = require(\"https\");',\n\t\t'const url = process.argv[2];',\n\t\t'const mod = /^https:/i.test(url) ? https : http;',\n\t\t'const req = mod.get(url, { timeout: 2500 }, (res) => { res.resume(); process.exit(res.statusCode && res.statusCode < 500 ? 0 : 1); });',\n\t\t'req.on(\"timeout\", () => req.destroy(new Error(\"timeout\")));',\n\t\t'req.on(\"error\", () => process.exit(1));',\n\t\t'RESOLVEIO_PROBE',\n\t\t'}',\n\t\t'truthy() { case \"${1,,}\" in true|1|yes|on) return 0 ;; *) return 1 ;; esac; }',\n\t\t'reuse_running_app_if_ready() {',\n\t\t' truthy \"$REUSE_RUNNING\" || return 1',\n\t\t' [ -d \"$PROJECT_ROOT/server\" ] && SERVER_REQUIRED=1 || SERVER_REQUIRED=0',\n\t\t' probe_url \"$CLIENT_URL\" || return 1',\n\t\t' if [ \"$SERVER_REQUIRED\" = \"1\" ] && ! probe_url \"$SERVER_URL\"; then return 1; fi',\n\t\t' echo \"ResolveIO AI runner QA reusing already-ready local app at $CLIENT_URL\"',\n\t\t' return 0',\n\t\t'}',\n\t\t'log_has_fatal() {',\n\t\t' local file=\"$1\"',\n\t\t' [ -f \"$file\" ] || return 1',\n\t\t' grep -Eiq \"Unhandled Rejection|Cannot read properties of undefined|TypeError|ReferenceError|EADDRINUSE|app crashed|Failed to compile|Error: Cannot find module|NG[0-9]{4}|TS[0-9]{4}\" \"$file\"',\n\t\t'}',\n\t\t'prepare_angular_cache_dirs() {',\n\t\t' [ -d \"$PROJECT_ROOT\" ] || return 0',\n\t\t' mkdir -p \"$PROJECT_ROOT/.angular/cache\" 2>/dev/null || true',\n\t\t' local version=\"\"',\n\t\t' for pkg in \"$PROJECT_ROOT/node_modules/@angular/build/package.json\" \"$PROJECT_ROOT/node_modules/@angular/cli/package.json\" \"$PROJECT_ROOT/node_modules/@angular-devkit/build-angular/package.json\"; do',\n\t\t' if [ -f \"$pkg\" ]; then',\n\t\t' version=\"$(node -e \"try{console.log(require(process.argv[1]).version||\\\\\\\"\\\\\\\")}catch(e){}\" \"$pkg\" 2>/dev/null | head -1)\"',\n\t\t' [ -n \"$version\" ] && break',\n\t\t' fi',\n\t\t' done',\n\t\t' [ -n \"$version\" ] || return 0',\n\t\t' node - \"$PROJECT_ROOT/angular.json\" \"$(basename \"$PROJECT_ROOT\")\" <<\\'RESOLVEIO_ANGULAR_CACHE_PROJECTS\\' | while IFS= read -r project; do',\n\t\t'const fs = require(\"fs\");',\n\t\t'const path = process.argv[2];',\n\t\t'const fallback = process.argv[3];',\n\t\t'const names = new Set([fallback].filter(Boolean));',\n\t\t'try {',\n\t\t' const parsed = JSON.parse(fs.readFileSync(path, \"utf8\"));',\n\t\t' if (parsed.defaultProject) names.add(String(parsed.defaultProject));',\n\t\t' if (parsed.projects && typeof parsed.projects === \"object\") Object.keys(parsed.projects).forEach((name) => names.add(String(name)));',\n\t\t'} catch (error) {}',\n\t\t'for (const name of names) console.log(name.replace(/[^A-Za-z0-9._-]/g, \"_\"));',\n\t\t'RESOLVEIO_ANGULAR_CACHE_PROJECTS',\n\t\t' [ -n \"$project\" ] || continue',\n\t\t' mkdir -p \"$PROJECT_ROOT/.angular/cache/$version/$project/vite\" 2>/dev/null || true',\n\t\t' done',\n\t\t'}',\n\t\t'server_has_started() {',\n\t\t' local file=\"$ARTIFACT_DIR/server.log\"',\n\t\t' [ -f \"$file\" ] || return 1',\n\t\t' grep -Eiq \"nodemon.*starting|node .*tmp/index\\\\.js|Running as Worker|Standalone Node Reaper|listening on|server listening|Server listening|app listening|App listening|Finished .default.\" \"$file\"',\n\t\t'}',\n\t\t'wait_for_server_ready() {',\n\t\t' [ \"$SERVER_REQUIRED\" = \"1\" ] || return 0',\n\t\t' local end=$((SECONDS + STARTUP_TIMEOUT))',\n\t\t' while [ \"$SECONDS\" -lt \"$end\" ]; do',\n\t\t' if log_has_fatal \"$ARTIFACT_DIR/server.log\"; then return 4; fi',\n\t\t' if server_has_started; then',\n\t\t' local stable_until=$((SECONDS + SERVER_STABLE_SECONDS))',\n\t\t' while [ \"$SECONDS\" -lt \"$stable_until\" ]; do if log_has_fatal \"$ARTIFACT_DIR/server.log\"; then return 4; fi; sleep 2; done',\n\t\t' return 0',\n\t\t' fi',\n\t\t' if probe_url \"$SERVER_URL\"; then',\n\t\t' local stable_until=$((SECONDS + SERVER_STABLE_SECONDS))',\n\t\t' while [ \"$SECONDS\" -lt \"$stable_until\" ]; do if log_has_fatal \"$ARTIFACT_DIR/server.log\"; then return 4; fi; sleep 2; done',\n\t\t' return 0',\n\t\t' fi',\n\t\t' sleep 5',\n\t\t' done',\n\t\t' return 4',\n\t\t'}',\n\t\t'wait_for_client() {',\n\t\t' local end=$((SECONDS + STARTUP_TIMEOUT))',\n\t\t' while [ \"$SECONDS\" -lt \"$end\" ]; do',\n\t\t' if [ -n \"$CLIENT_PID\" ] && ! kill -0 \"$CLIENT_PID\" >/dev/null 2>&1; then return 2; fi',\n\t\t' if log_has_fatal \"$ARTIFACT_DIR/client.log\"; then return 3; fi',\n\t\t' if log_has_fatal \"$ARTIFACT_DIR/server.log\"; then return 4; fi',\n\t\t' if probe_url \"$CLIENT_URL\"; then wait_for_server_ready || return $?; return 0; fi',\n\t\t' sleep 5',\n\t\t' done',\n\t\t' return 1',\n\t\t'}',\n\t\t'if reuse_running_app_if_ready; then RUNNER_REUSED_READY=1; exit 0; fi',\n\t\t'if ! mkdir \"$LOCK_DIR\" 2>/dev/null; then',\n\t\t' if janitor_lock_is_live; then',\n\t\t' echo \"ResolveIO AI runner QA lock is already held by a live runner for $PROJECT_ROOT; refusing duplicate startup.\" | tee \"$ARTIFACT_DIR/runner.log\"',\n\t\t' exit 6',\n\t\t' fi',\n\t\t' echo \"ResolveIO AI runner QA lock is stale for $PROJECT_ROOT; cleaning scoped local QA processes before retrying.\" | tee \"$ARTIFACT_DIR/runner.log\"',\n\t\t' cleanup_project_processes',\n\t\t' rm -rf \"$LOCK_DIR\" >/dev/null 2>&1 || true',\n\t\t' if ! mkdir \"$LOCK_DIR\" 2>/dev/null; then',\n\t\t' echo \"ResolveIO AI runner QA lock is still held for $PROJECT_ROOT after cleanup. Stop the existing QA runner before starting another.\" | tee -a \"$ARTIFACT_DIR/runner.log\"',\n\t\t' exit 6',\n\t\t' fi',\n\t\t'fi',\n\t\t'janitor_write_lock',\n\t\t'cleanup_project_processes',\n\t\t': > \"$ARTIFACT_DIR/server.log\"',\n\t\t': > \"$ARTIFACT_DIR/client.log\"',\n\t\t': > \"$ARTIFACT_DIR/runner.log\"',\n\t\t'janitor_write_manifest',\n\t\t'janitor_start_heartbeat',\n\t\t'echo \"$RUNNER_JANITOR_HEARTBEAT_PID\" > \"$ARTIFACT_DIR/heartbeat.pid\"',\n\t\t'janitor_check_resources || exit $?',\n\t\t'if [ -d \"$PROJECT_ROOT/server\" ]; then',\n\t\t' SERVER_REQUIRED=1',\n\t\t' if node -e \"const p=require(process.argv[1]); process.exit(p.scripts&&p.scripts.server?0:1)\" \"$PROJECT_ROOT/server/package.json\" >/dev/null 2>&1; then',\n\t\t' (cd \"$PROJECT_ROOT/server\" && source \"$TOOLS_DIR/env.sh\" && npm run server 2>&1 | tee \"$ARTIFACT_DIR/server.log\") &',\n\t\t' elif [ -x \"$PROJECT_ROOT/server/start_server.sh\" ]; then',\n\t\t' (cd \"$PROJECT_ROOT/server\" && source \"$TOOLS_DIR/env.sh\" && ./start_server.sh 2>&1 | tee \"$ARTIFACT_DIR/server.log\") &',\n\t\t' else',\n\t\t' echo \"ResolveIO AI runner QA cannot find npm server script or start_server.sh for $PROJECT_ROOT/server\" | tee \"$ARTIFACT_DIR/server.log\"',\n\t\t' exit 5',\n\t\t' fi',\n\t\t' SERVER_PID=$!',\n\t\t' wait_for_server_ready',\n\t\t' SERVER_RESULT=$?',\n\t\t' if [ \"$SERVER_RESULT\" != \"0\" ]; then echo \"ResolveIO AI runner QA server startup fatal error. See $ARTIFACT_DIR/server.log\"; exit \"$SERVER_RESULT\"; fi',\n\t\t'fi',\n\t\t'CLIENT_HOST=\"${RESOLVEIO_RUNNER_QA_CLIENT_HOST:-${RESOLVEIO_SUPPORT_QA_CLIENT_HOST:-0.0.0.0}}\"',\n\t\t'if [ -x \"$PROJECT_ROOT/node_modules/.bin/ng\" ]; then',\n\t\t' prepare_angular_cache_dirs',\n\t\t' (cd \"$PROJECT_ROOT\" && source \"$TOOLS_DIR/env.sh\" && node --max_old_space_size=8048 ./node_modules/.bin/ng serve --watch --configuration local --host \"$CLIENT_HOST\" --port \"$CLIENT_PORT\" \"${ANGULAR_PREBUNDLE_ARGS[@]}\" 2>&1 | tee \"$ARTIFACT_DIR/client.log\") &',\n\t\t'elif [ -x \"$PROJECT_ROOT/start_client.sh\" ]; then',\n\t\t' (cd \"$PROJECT_ROOT\" && source \"$TOOLS_DIR/env.sh\" && ./start_client.sh 2>&1 | tee \"$ARTIFACT_DIR/client.log\") &',\n\t\t'elif node -e \"const p=require(process.argv[1]); process.exit(p.scripts&&p.scripts.client?0:1)\" \"$PROJECT_ROOT/package.json\" >/dev/null 2>&1; then',\n\t\t' (cd \"$PROJECT_ROOT\" && source \"$TOOLS_DIR/env.sh\" && npm run client 2>&1 | tee \"$ARTIFACT_DIR/client.log\") &',\n\t\t'else',\n\t\t' echo \"ResolveIO AI runner QA cannot find Angular CLI, start_client.sh, or npm client script for $PROJECT_ROOT\" | tee \"$ARTIFACT_DIR/client.log\"',\n\t\t' exit 5',\n\t\t'fi',\n\t\t'CLIENT_PID=$!',\n\t\t'wait_for_client',\n\t\t'RESULT=$?',\n\t\t'case \"$RESULT\" in',\n\t\t' 0)',\n\t\t' echo \"ResolveIO AI runner QA local app ready at $CLIENT_URL\";',\n\t\t' echo \"$SERVER_PID\" > \"$ARTIFACT_DIR/server.pid\";',\n\t\t' echo \"$CLIENT_PID\" > \"$ARTIFACT_DIR/client.pid\";',\n\t\t' if truthy \"$KEEPALIVE\"; then detach_keepalive || exit $?; fi;',\n\t\t' exit 0',\n\t\t' ;;',\n\t\t' 2) echo \"ResolveIO AI runner QA client process exited before $CLIENT_URL became ready. See $ARTIFACT_DIR/client.log\"; exit 2 ;;',\n\t\t' 3) echo \"ResolveIO AI runner QA client startup fatal error. See $ARTIFACT_DIR/client.log\"; exit 3 ;;',\n\t\t' 4) echo \"ResolveIO AI runner QA server startup fatal error. See $ARTIFACT_DIR/server.log\"; exit 4 ;;',\n\t\t' *) echo \"ResolveIO AI runner QA local app did not become ready at $CLIENT_URL within ${STARTUP_TIMEOUT}s. See $ARTIFACT_DIR/client.log and $ARTIFACT_DIR/server.log\"; exit 1 ;;',\n\t\t'esac',\n\t\t''\n\t].join('\\n');\n}\n\nexport function buildResolveIORunnerLocalQaStopperScript(): string {\n\treturn [\n\t\t'#!/usr/bin/env bash',\n\t\t'set -u',\n\t\t'TOOLS_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"',\n\t\t'source \"$TOOLS_DIR/env.sh\"',\n\t\t'PROJECT_ROOT=\"${1:-$(pwd)}\"',\n\t\t'PROJECT_ROOT=\"$(cd \"$PROJECT_ROOT\" && pwd)\"',\n\t\t'ARTIFACT_DIR=\"$PROJECT_ROOT/qa-artifacts\"',\n\t\t'CLIENT_PORT=\"${RESOLVEIO_RUNNER_QA_CLIENT_PORT:-${RESOLVEIO_SUPPORT_QA_CLIENT_PORT:-4200}}\"',\n\t\t'SERVER_PORT=\"${RESOLVEIO_RUNNER_QA_SERVER_PORT:-${RESOLVEIO_SUPPORT_QA_SERVER_PORT:-8080}}\"',\n\t\t'MONGO_PORT=\"${RESOLVEIO_RUNNER_QA_MONGO_PORT:-${RESOLVEIO_SUPPORT_QA_MONGO_PORT:-3001}}\"',\n\t\t'INSPECT_PORT=\"${RESOLVEIO_RUNNER_QA_INSPECT_PORT:-${RESOLVEIO_SUPPORT_QA_INSPECT_PORT:-9229}}\"',\n\t\t'REPO_ROOT=\"$(git -C \"$PROJECT_ROOT\" rev-parse --show-toplevel 2>/dev/null || echo \"$PROJECT_ROOT\")\"',\n\t\tbuildRunnerProcessJanitorShellLibrary({ mode: 'support' }),\n\t\t'STOP_LOCK_DIR=\"$ARTIFACT_DIR/.qa.stop.lock\"',\n\t\t'if ! mkdir \"$STOP_LOCK_DIR\" 2>/dev/null; then',\n\t\t' lock_pid=\"$(cat \"$STOP_LOCK_DIR/pid\" 2>/dev/null || true)\"',\n\t\t' if [ -n \"$lock_pid\" ] && kill -0 \"$lock_pid\" >/dev/null 2>&1; then',\n\t\t' echo \"ResolveIO AI runner QA cleanup already active for $PROJECT_ROOT; refusing duplicate stopper.\"',\n\t\t' exit 0',\n\t\t' fi',\n\t\t' rm -rf \"$STOP_LOCK_DIR\" >/dev/null 2>&1 || true',\n\t\t' mkdir \"$STOP_LOCK_DIR\" 2>/dev/null || { echo \"ResolveIO AI runner QA cleanup lock still active for $PROJECT_ROOT.\"; exit 0; }',\n\t\t'fi',\n\t\t'echo \"$$\" > \"$STOP_LOCK_DIR/pid\" 2>/dev/null || true',\n\t\t'trap \\'rm -rf \"$STOP_LOCK_DIR\" >/dev/null 2>&1 || true\\' EXIT',\n\t\t'kill_tree() {',\n\t\t' local pid=\"$1\"',\n\t\t' [ -n \"$pid\" ] || return 0',\n\t\t' kill -0 \"$pid\" >/dev/null 2>&1 || return 0',\n\t\t' for child in $(pgrep -P \"$pid\" 2>/dev/null || true); do kill_tree \"$child\"; done',\n\t\t' kill \"$pid\" >/dev/null 2>&1 || true',\n\t\t' sleep 1',\n\t\t' kill -0 \"$pid\" >/dev/null 2>&1 && kill -9 \"$pid\" >/dev/null 2>&1 || true',\n\t\t'}',\n\t\t'kill_port_listeners() {',\n\t\t' local port=\"$1\"',\n\t\t' [ -n \"$port\" ] || return 0',\n\t\t' local pids=\"\"',\n\t\t' if command -v lsof >/dev/null 2>&1; then pids=\"$pids $(janitor_bounded 3 lsof -ti tcp:\"$port\")\"; fi',\n\t\t' if command -v fuser >/dev/null 2>&1; then pids=\"$pids $(janitor_bounded 3 fuser -n tcp \"$port\")\"; fi',\n\t\t' if command -v ss >/dev/null 2>&1; then pids=\"$pids $(janitor_bounded 3 ss -ltnp \"sport = :$port\" | sed -n \\'s/.*pid=\\\\([0-9][0-9]*\\\\).*/\\\\1/p\\' || true)\"; fi',\n\t\t' for pid in $pids; do',\n\t\t' [ \"$pid\" = \"$$\" ] && continue',\n\t\t' kill_tree \"$pid\"',\n\t\t' done',\n\t\t' sleep 1',\n\t\t'}',\n\t\t'kill_env_marked_processes() {',\n\t\t' [ -d /proc ] || return 0',\n\t\t' local job_id=\"${RESOLVEIO_RUNNER_QA_JOB_ID:-${RESOLVEIO_SUPPORT_QA_JOB_ID:-}}\"',\n\t\t' local owner_id=\"${RESOLVEIO_RUNNER_QA_OWNER_ID:-${RESOLVEIO_SUPPORT_QA_OWNER_ID:-}}\"',\n\t\t' local token=\"${RESOLVEIO_RUNNER_QA_RUNNER_TOKEN:-${RESOLVEIO_SUPPORT_QA_RUNNER_TOKEN:-}}\"',\n\t\t' [ -n \"$job_id$owner_id$token\" ] || return 0',\n\t\t' for env_file in /proc/[0-9]*/environ; do',\n\t\t' pid=\"${env_file#/proc/}\"',\n\t\t' pid=\"${pid%/environ}\"',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' [ \"$pid\" = \"$$\" ] && continue',\n\t\t' [ -r \"$env_file\" ] || continue',\n\t\t' env_text=\"$(tr \"\\\\0\" \"\\\\n\" < \"$env_file\" 2>/dev/null || true)\"',\n\t\t' [ -n \"$env_text\" ] || continue',\n\t\t' matched=0',\n\t\t' [ -n \"$job_id\" ] && echo \"$env_text\" | grep -Eq \"^(RESOLVEIO_RUNNER_QA_JOB_ID|RESOLVEIO_SUPPORT_QA_JOB_ID)=$job_id$\" && matched=1',\n\t\t' [ \"$matched\" = \"0\" ] && [ -n \"$owner_id\" ] && echo \"$env_text\" | grep -Eq \"^(RESOLVEIO_RUNNER_QA_OWNER_ID|RESOLVEIO_SUPPORT_QA_OWNER_ID)=$owner_id$\" && matched=1',\n\t\t' [ \"$matched\" = \"0\" ] && [ -n \"$token\" ] && echo \"$env_text\" | grep -Eq \"^(RESOLVEIO_RUNNER_QA_RUNNER_TOKEN|RESOLVEIO_SUPPORT_QA_RUNNER_TOKEN)=$token$\" && matched=1',\n\t\t' [ \"$matched\" = \"1\" ] && kill_tree \"$pid\" && killed_count=$((killed_count + 1))',\n\t\t' done',\n\t\t'}',\n\t\t'kill_artifact_log_writers() {',\n\t\t' local artifact=\"$ARTIFACT_DIR\"',\n\t\t' [ -n \"$artifact\" ] || return 0',\n\t\t' for pid in $(ps -eo pid=,args= | awk -v artifact=\"$artifact\" \\'index($0, artifact) && $0 !~ /awk -v artifact=/ && $0 !~ /ps -eo pid=,args=/ && $0 ~ /^ *[0-9]+ +(tee|tail -f) / {print $1}\\' 2>/dev/null || true); do',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' kill_tree \"$pid\"',\n\t\t' killed_count=$((killed_count + 1))',\n\t\t' done',\n\t\t'}',\n\t\t'kill_local_mongo_processes() {',\n\t\t' local mongo_port=\"$MONGO_PORT\"',\n\t\t' [ -n \"$mongo_port\" ] || return 0',\n\t\t' for pid in $(ps -eo pid=,args= | awk -v port=\"$mongo_port\" \\'$0 !~ /awk -v port=/ && $0 !~ /ps -eo pid=,args=/ && $0 ~ /mongod/ && $0 ~ (\"--port \" port) && ($0 ~ /\\\\/tmp\\\\/resolveio-support-qa\\\\/.*mongod/ || $0 ~ /--dbpath mongo\\\\/data\\\\/db/) {print $1}\\' 2>/dev/null || true); do',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' [ \"$pid\" = \"$$\" ] && continue',\n\t\t' kill_tree \"$pid\"',\n\t\t' killed_count=$((killed_count + 1))',\n\t\t' done',\n\t\t'}',\n\t\t'RUNNER_ANCESTOR_PIDS=\" $$ ${PPID:-} \"',\n\t\t'ancestor_pid=\"${PPID:-}\"',\n\t\t'while [ -n \"$ancestor_pid\" ] && [ \"$ancestor_pid\" != \"0\" ] && [ \"$ancestor_pid\" != \"1\" ]; do',\n\t\t' ancestor_pid=\"$(ps -o ppid= -p \"$ancestor_pid\" 2>/dev/null | tr -d \" \" || true)\"',\n\t\t' [ -n \"$ancestor_pid\" ] && RUNNER_ANCESTOR_PIDS=\"$RUNNER_ANCESTOR_PIDS $ancestor_pid \"',\n\t\t'done',\n\t\t'skip_cleanup_pid() {',\n\t\t' case \"$RUNNER_ANCESTOR_PIDS\" in *\" $1 \"*) return 0 ;; esac',\n\t\t' return 1',\n\t\t'}',\n\t\t'janitor_update_manifest_status cleanup_started',\n\t\t'killed_count=0',\n'for pid_file in \"$ARTIFACT_DIR/heartbeat.pid\" \"$ARTIFACT_DIR/server.pid\" \"$ARTIFACT_DIR/client.pid\"; do',\n\t\t' [ -f \"$pid_file\" ] || continue',\n\t\t' pid=\"$(cat \"$pid_file\" 2>/dev/null || true)\"',\n\t\t' kill_tree \"$pid\"',\n\t\t' killed_count=$((killed_count + 1))',\n\t\t' rm -f \"$pid_file\"',\n\t\t'done',\n\t\t'for pass in 1 2 3; do',\n\t\t' for port in \"$CLIENT_PORT\" \"$SERVER_PORT\" \"$MONGO_PORT\" \"$INSPECT_PORT\"; do kill_port_listeners \"$port\"; done',\n\t\t' kill_env_marked_processes',\n\t\t' kill_artifact_log_writers',\n\t\t' kill_local_mongo_processes',\n\t\t' for pid in $(ps -eo pid=,args= | awk -v root=\"$PROJECT_ROOT\" \\'index($0, root) && $0 !~ /awk -v root=/ && $0 !~ /ps -eo pid=,args=/ && $0 !~ /run-local-qa\\\\.sh|stop-local-qa\\\\.sh|bugfix-comparison-qa\\\\.sh/ && $0 ~ /(ng serve|node .*node_modules\\\\/\\\\.bin\\\\/ng|esbuild|npm run client|start_client\\\\.sh|npm run server|start_server\\\\.sh|nodemon|node .*tmp\\\\/index\\\\.js|mongod|mongodb-binaries\\\\/mongod)/ {print $1}\\' 2>/dev/null || true); do',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' kill_tree \"$pid\"',\n\t\t' killed_count=$((killed_count + 1))',\n\t\t' done',\n\t\t' if [ -d /proc ]; then',\n\t\t' for proc_cwd in /proc/[0-9]*/cwd; do',\n\t\t' pid=\"${proc_cwd#/proc/}\"',\n\t\t' pid=\"${pid%/cwd}\"',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' cwd=\"$(readlink -f \"$proc_cwd\" 2>/dev/null || true)\"',\n\t\t' case \"$cwd\" in',\n\t\t' \"$PROJECT_ROOT\"|\"$PROJECT_ROOT\"/*) kill_tree \"$pid\" ;;',\n\t\t' esac',\n\t\t' done',\n\t\t' fi',\n\t\t' sleep 1',\n\t\t'done',\n\t\t'wait_until=$((SECONDS + 60))',\n\t'while [ \"$SECONDS\" -lt \"$wait_until\" ]; do',\n\t\t' found=0',\n\t\t' for port in \"$CLIENT_PORT\" \"$SERVER_PORT\" \"$MONGO_PORT\" \"$INSPECT_PORT\"; do',\n\t\t' port_pids=\"\"',\n\t\t' if command -v lsof >/dev/null 2>&1; then port_pids=\"$port_pids $(janitor_bounded 3 lsof -ti tcp:\"$port\")\"; fi',\n\t\t' for pid in $port_pids; do skip_cleanup_pid \"$pid\" && continue; found=1; kill_tree \"$pid\"; done',\n\t\t' done',\n\t\t' if [ -d /proc ]; then',\n\t\t' job_id=\"${RESOLVEIO_RUNNER_QA_JOB_ID:-${RESOLVEIO_SUPPORT_QA_JOB_ID:-}}\"',\n\t\t' owner_id=\"${RESOLVEIO_RUNNER_QA_OWNER_ID:-${RESOLVEIO_SUPPORT_QA_OWNER_ID:-}}\"',\n\t\t' token=\"${RESOLVEIO_RUNNER_QA_RUNNER_TOKEN:-${RESOLVEIO_SUPPORT_QA_RUNNER_TOKEN:-}}\"',\n\t\t' for env_file in /proc/[0-9]*/environ; do',\n\t\t' pid=\"${env_file#/proc/}\"',\n\t\t' pid=\"${pid%/environ}\"',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' [ -r \"$env_file\" ] || continue',\n\t\t' env_text=\"$(tr \"\\\\0\" \"\\\\n\" < \"$env_file\" 2>/dev/null || true)\"',\n\t\t' matched=0',\n\t\t' [ -n \"$job_id\" ] && echo \"$env_text\" | grep -Eq \"^(RESOLVEIO_RUNNER_QA_JOB_ID|RESOLVEIO_SUPPORT_QA_JOB_ID)=$job_id$\" && matched=1',\n\t\t' [ \"$matched\" = \"0\" ] && [ -n \"$owner_id\" ] && echo \"$env_text\" | grep -Eq \"^(RESOLVEIO_RUNNER_QA_OWNER_ID|RESOLVEIO_SUPPORT_QA_OWNER_ID)=$owner_id$\" && matched=1',\n\t\t' [ \"$matched\" = \"0\" ] && [ -n \"$token\" ] && echo \"$env_text\" | grep -Eq \"^(RESOLVEIO_RUNNER_QA_RUNNER_TOKEN|RESOLVEIO_SUPPORT_QA_RUNNER_TOKEN)=$token$\" && matched=1',\n\t\t' [ \"$matched\" = \"1\" ] && found=1 && kill_tree \"$pid\"',\n\t\t' done',\n\t\t' fi',\n\t\t' kill_artifact_log_writers',\n\t\t' kill_local_mongo_processes',\n\t\t' for pid in $(ps -eo pid=,args= | awk -v root=\"$PROJECT_ROOT\" \\'index($0, root) && $0 !~ /awk -v root=/ && $0 !~ /ps -eo pid=,args=/ && $0 !~ /run-local-qa\\\\.sh|stop-local-qa\\\\.sh|bugfix-comparison-qa\\\\.sh/ && $0 ~ /(ng serve|node .*node_modules\\\\/\\\\.bin\\\\/ng|esbuild|npm run client|start_client\\\\.sh|npm run server|start_server\\\\.sh|nodemon|node .*tmp\\\\/index\\\\.js|mongod|mongodb-binaries\\\\/mongod)/ {print $1}\\' 2>/dev/null || true); do',\n\t\t' skip_cleanup_pid \"$pid\" && continue',\n\t\t' found=1',\n\t\t' done',\n\t\t' [ \"$found\" = \"0\" ] && break',\n\t\t' sleep 2',\n\t\t'done',\n\t\t'rm -rf \"$ARTIFACT_DIR/.qa.lock\" >/dev/null 2>&1 || true',\n\t\t'janitor_update_manifest_status stopped',\n\t\t'janitor_write_cleanup_status stopped \"$killed_count\"',\n\t\t'echo \"ResolveIO AI runner QA local app stopped for $PROJECT_ROOT\"',\n\t\t''\n\t].join('\\n');\n}\n\nexport function buildResolveIORunnerQaLiveDataSeederScript(): string {\n\treturn [\n\t\t'#!/usr/bin/env node',\n\t\t'const fs = require(\"fs\");',\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 repoRoot = findRepoRoot(projectRoot);',\n\t\t'const artifactDir = path.join(projectRoot, \"qa-artifacts\");',\n\t\t'fs.mkdirSync(artifactDir, { recursive: true });',\n\t\t'const resultPath = path.join(artifactDir, \"qa-live-data-seed-result.json\");',\n\t\t'',\n\t\t'function writeResult(payload, exitCode = 0) {',\n\t\t' const full = { ...payload, created_at: new Date().toISOString() };',\n\t\t' fs.writeFileSync(resultPath, JSON.stringify(full, null, 2));',\n\t\t' console.log(JSON.stringify(full, null, 2));',\n\t\t' process.exit(exitCode);',\n\t\t'}',\n\t\t'',\n\t\t'function findRepoRoot(start) {',\n\t\t' let current = start;',\n\t\t' for (let i = 0; i < 8; i += 1) {',\n\t\t' if (fs.existsSync(path.join(current, \".git\"))) return current;',\n\t\t' const parent = path.dirname(current);',\n\t\t' if (parent === current) break;',\n\t\t' current = parent;',\n\t\t' }',\n\t\t' return start;',\n\t\t'}',\n\t\t'',\n\t\t'function readJsonIfExists(filePath) {',\n\t\t' try { return JSON.parse(fs.readFileSync(filePath, \"utf8\")); } catch (error) { return null; }',\n\t\t'}',\n\t\t'',\n\t\t'function preserveExistingSeedResult(reason) {',\n\t\t' const existing = readJsonIfExists(resultPath);',\n\t\t' const status = String(existing && existing.status || \"\").toLowerCase();',\n\t\t' if ([\"pass\", \"needs-data\"].includes(status)) {',\n\t\t' const preserved = { ...existing, reused_existing: true, reuse_reason: reason, checked_at: new Date().toISOString() };',\n\t\t' console.log(JSON.stringify(preserved, null, 2));',\n\t\t' process.exit(0);',\n\t\t' }',\n\t\t'}',\n\t\t'',\n\t'function resolveRuntimeSource() {',\n\t' const envUri = process.env.RESOLVEIO_QA_LIVE_MONGO_URL || process.env.RESOLVEIO_SUPPORT_QA_LIVE_MONGO_URL || process.env.RESOLVEIO_RUNNER_QA_LIVE_MONGO_URL || \"\";',\n\t' const envDb = process.env.RESOLVEIO_QA_LIVE_MONGO_DB || process.env.RESOLVEIO_SUPPORT_QA_LIVE_MONGO_DB || process.env.RESOLVEIO_RUNNER_QA_LIVE_MONGO_DB || \"\";',\n\t' if (envUri) return { uri: envUri, database: envDb };',\n\t' const explicitRuntimePaths = [',\n\t' process.env.RESOLVEIO_QA_MONGO_RUNTIME_PATH,',\n\t' process.env.RESOLVEIO_SUPPORT_QA_MONGO_RUNTIME_PATH,',\n\t' process.env.RESOLVEIO_RUNNER_QA_MONGO_RUNTIME_PATH',\n\t' ].map((value) => String(value || \"\").trim()).filter(Boolean);',\n\t' const candidates = [',\n\t' ...explicitRuntimePaths,',\n\t' path.join(repoRoot, \"mongo-context\", \".mongo-runtime.json\"),',\n\t' path.join(projectRoot, \"mongo-context\", \".mongo-runtime.json\"),',\n\t\t' path.join(repoRoot, \".resolveio-support-context\", \"mongo-context\", \".mongo-runtime.json\"),',\n\t\t' path.join(projectRoot, \".resolveio-support-context\", \"mongo-context\", \".mongo-runtime.json\"),',\n\t\t' path.join(repoRoot, \".resolveio-context\", \"mongo-context\", \".mongo-runtime.json\"),',\n\t\t' path.join(projectRoot, \".resolveio-context\", \"mongo-context\", \".mongo-runtime.json\")',\n\t\t' ];',\n\t\t' for (const candidate of candidates) {',\n\t\t' const parsed = readJsonIfExists(candidate);',\n\t\t' if (parsed && parsed.mongo_uri) return { uri: String(parsed.mongo_uri || \"\"), database: String(parsed.mongo_db || \"\") };',\n\t\t' }',\n\t\t' return { uri: \"\", database: \"\" };',\n\t\t'}',\n\t\t'',\n\t\t'function redactUri(uri) {',\n\t\t' return String(uri || \"\").replace(/(mongodb(?:\\\\+srv)?:\\\\/\\\\/)([^:@/?#]+):([^@/?#]+)@/i, \"$1$2:***@\");',\n\t\t'}',\n\t\t'',\n\t'function isLocalMongoUri(uri) {',\n\t' return /mongodb(?:\\\\+srv)?:\\\\/\\\\/(?:[^@/]+@)?(?:127\\\\.0\\\\.0\\\\.1|localhost)(?::\\\\d+)?\\\\//i.test(String(uri || \"\"));',\n\t'}',\n\t'',\n\t'function isLiveDataRequired() {',\n\t' return /^(true|1|yes|on)$/i.test(String(process.env.RESOLVEIO_QA_LIVE_DATA_REQUIRED || process.env.RESOLVEIO_SUPPORT_QA_LIVE_DATA_REQUIRED || process.env.RESOLVEIO_RUNNER_QA_LIVE_DATA_REQUIRED || \"\"));',\n\t'}',\n\t\t'',\n\t\t'function requireMongo() {',\n\t\t' const candidates = [',\n\t\t' path.join(projectRoot, \"server\", \"node_modules\", \"mongodb\"),',\n\t\t' path.join(projectRoot, \"node_modules\", \"mongodb\"),',\n\t\t' path.join(repoRoot, \"node_modules\", \"mongodb\"),',\n\t\t' \"mongodb\"',\n\t\t' ];',\n\t\t' const errors = [];',\n\t\t' for (const candidate of candidates) {',\n\t\t' try { return require(candidate); } catch (error) { errors.push(`${candidate}: ${error.message}`); }',\n\t\t' }',\n\t\t' throw new Error(`Unable to require mongodb package. ${errors.join(\" | \")}`);',\n\t\t'}',\n\t\t'',\n\t\t'function idValues(docs, keys) {',\n\t\t' const values = new Set();',\n\t\t' for (const doc of docs || []) {',\n\t\t' for (const key of keys) {',\n\t\t' const value = doc && doc[key];',\n\t\t' if (typeof value === \"string\" && value.trim()) values.add(value.trim());',\n\t\t' if (Array.isArray(value)) value.forEach((entry) => typeof entry === \"string\" && entry.trim() && values.add(entry.trim()));',\n\t\t' }',\n\t\t' }',\n\t\t' return Array.from(values);',\n\t\t'}',\n\t\t'',\n\t\t'function unique(values) { return Array.from(new Set((values || []).filter(Boolean).map(String))); }',\n\t\t'',\n\t\t'function readSeedHintText() {',\n\t\t' const chunks = [process.env.RESOLVEIO_QA_SEED_HINTS || \"\", process.env.RESOLVEIO_SUPPORT_QA_SEED_HINTS || \"\", process.env.RESOLVEIO_RUNNER_QA_SEED_HINTS || \"\"];',\n\t\t' const candidates = [',\n\t\t' path.join(repoRoot, \".resolveio-support-context\", \"manual-ticket.request.txt\"),',\n\t\t' path.join(projectRoot, \".resolveio-support-context\", \"manual-ticket.request.txt\"),',\n\t\t' path.join(repoRoot, \".resolveio-context\", \"manual-ticket.request.txt\"),',\n\t\t' path.join(projectRoot, \".resolveio-context\", \"manual-ticket.request.txt\")',\n\t\t' ];',\n\t\t' for (const candidate of candidates) {',\n\t\t' try { chunks.push(fs.readFileSync(candidate, \"utf8\")); } catch (error) {}',\n\t\t' }',\n\t\t' return chunks.filter(Boolean).join(\"\\\\n\");',\n\t\t'}',\n\t\t'',\n\t\t'function extractSeedIdentifiers() {',\n\t\t' const text = readSeedHintText();',\n\t\t' const identifiers = new Set();',\n\t\t' const explicit = /\\\\b(?:invoice|inv|bol|order|pso|ticket|wo|work\\\\s*order|delivery|treatment)\\\\s*(?:#|no\\\\.?|number|num)?\\\\s*[:#-]?\\\\s*([A-Z0-9][A-Z0-9._-]{2,})\\\\b/gi;',\n\t\t' let match;',\n\t\t' while ((match = explicit.exec(text)) !== null) identifiers.add(match[1]);',\n\t\t' const numeric = /\\\\b\\\\d{4,}\\\\b/g;',\n\t\t' while ((match = numeric.exec(text)) !== null) identifiers.add(match[0]);',\n\t\t' return Array.from(identifiers)',\n\t\t' .map((value) => String(value || \"\").trim())',\n\t\t' .filter((value) => value && !/^00?4\\\\d{3}$/.test(value))',\n\t\t' .slice(0, 20);',\n\t\t'}',\n\t\t'',\n\t\t'function identifierQuery(identifiers) {',\n\t\t' const ors = [];',\n\t\t' const fields = [\"_id\", \"invoice_number\", \"invoice_number_string\", \"order_number\", \"order_number_string\", \"activity_number\", \"bol_number\", \"bol_string\", \"ticket_number\", \"ticket\", \"wo_number\", \"work_order_number\", \"delivery_number\", \"treatment_number\"];',\n\t\t' for (const identifier of identifiers || []) {',\n\t\t' const escaped = String(identifier).replace(/[.*+?^${}()|[\\\\]\\\\\\\\]/g, \"\\\\\\\\$&\");',\n\t\t' const regex = new RegExp(escaped, \"i\");',\n\t\t' for (const field of fields) ors.push({ [field]: regex });',\n\t\t' ors.push({ \"bol.bol_string\": regex });',\n\t\t' ors.push({ \"bol.bol_number\": regex });',\n\t\t' }',\n\t\t' return ors.length ? { $or: ors } : null;',\n\t\t'}',\n\t\t'',\n\t\t'async function copyByIds(sourceDb, targetDb, collectionName, ids, summary, limit = 100) {',\n\t\t' const cleanIds = unique(ids).slice(0, limit);',\n\t\t' if (!cleanIds.length) return [];',\n\t\t' const docs = await sourceDb.collection(collectionName).find({ _id: { $in: cleanIds } }).limit(limit).toArray();',\n\t\t' for (const doc of docs) await targetDb.collection(collectionName).replaceOne({ _id: doc._id }, doc, { upsert: true });',\n\t\t' summary.collections[collectionName] = (summary.collections[collectionName] || 0) + docs.length;',\n\t\t' return docs;',\n\t\t'}',\n\t\t'',\n\t\t'async function copyQuery(sourceDb, targetDb, collectionName, query, summary, limit = 50, sort = null) {',\n\t\t' let cursor = sourceDb.collection(collectionName).find(query || {});',\n\t\t' if (sort) cursor = cursor.sort(sort);',\n\t\t' const docs = await cursor.limit(limit).toArray();',\n\t\t' for (const doc of docs) await targetDb.collection(collectionName).replaceOne({ _id: doc._id }, doc, { upsert: true });',\n\t\t' summary.collections[collectionName] = (summary.collections[collectionName] || 0) + docs.length;',\n\t\t' return docs;',\n\t\t'}',\n\t\t'',\n\t\t'async function selectDashboardReadyProductionDeliveryIds(sourceDb, query, limit = 5) {',\n\t\t' const rows = await sourceDb.collection(\"production-deliveries\").aggregate([',\n\t\t' { $match: query || {} },',\n\t\t' { $lookup: { from: \"production-sales-orders\", localField: \"id_activity\", foreignField: \"_id\", as: \"dbPSO\" } },',\n\t\t' { $unwind: { path: \"$dbPSO\", preserveNullAndEmptyArrays: false } },',\n\t\t' { $match: { \"dbPSO.type\": { $ne: \"Consignment Invoice\" } } },',\n\t\t' { $lookup: {',\n\t\t' from: \"bols\",',\n\t\t' let: { idBol: \"$id_bol\" },',\n\t\t' pipeline: [{ $match: { $expr: { $eq: [\"$_id\", \"$$idBol\"] }, status: \"Delivered\" } }],',\n\t\t' as: \"bol\"',\n\t\t' } },',\n\t\t' { $unwind: { path: \"$bol\", preserveNullAndEmptyArrays: false } },',\n\t\t' { $lookup: { from: \"production-locations\", localField: \"id_location\", foreignField: \"_id\", as: \"dbProductionLocation\" } },',\n\t\t' { $lookup: { from: \"well-groups\", localField: \"id_well_group\", foreignField: \"_id\", as: \"dbWellGroup\" } },',\n\t\t' { $match: { $or: [{ \"dbProductionLocation.0\": { $exists: true } }, { \"dbWellGroup.0\": { $exists: true } }] } },',\n\t\t' { $sort: { date: -1, date_ship: -1, date_treated: -1, updatedAt: -1 } },',\n\t\t' { $limit: limit },',\n\t\t' { $project: { _id: 1 } }',\n\t\t' ]).toArray();',\n\t\t' return rows.map((doc) => doc._id).filter(Boolean);',\n\t\t'}',\n\t\t'',\n\t\t'async function selectTruckTreatDashboardReadyProductionDeliveryIds(sourceDb, query, limit = 5) {',\n\t\t' const rows = await sourceDb.collection(\"production-deliveries\").aggregate([',\n\t\t' { $match: query || {} },',\n\t\t' { $lookup: {',\n\t\t' from: \"bols\",',\n\t\t' let: { idBol: \"$id_bol\" },',\n\t\t' pipeline: [{ $match: { $expr: { $eq: [\"$_id\", \"$$idBol\"] }, status: \"Delivered\" } }],',\n\t\t' as: \"bol\"',\n\t\t' } },',\n\t\t' { $unwind: { path: \"$bol\", preserveNullAndEmptyArrays: false } },',\n\t\t' { $lookup: { from: \"production-locations\", localField: \"id_location\", foreignField: \"_id\", as: \"dbProductionLocation\" } },',\n\t\t' { $lookup: { from: \"well-groups\", localField: \"id_well_group\", foreignField: \"_id\", as: \"dbWellGroup\" } },',\n\t\t' { $match: { $or: [{ \"dbProductionLocation.0\": { $exists: true } }, { \"dbWellGroup.0\": { $exists: true } }] } },',\n\t\t' { $sort: { date: -1, date_treated: -1, updatedAt: -1 } },',\n\t\t' { $limit: limit },',\n\t\t' { $project: { _id: 1 } }',\n\t\t' ]).toArray();',\n\t\t' return rows.map((doc) => doc._id).filter(Boolean);',\n\t\t'}',\n\t\t'',\n\t\t'async function selectLocalhostDeliveryFixtureIds(sourceDb, query, limit = 3) {',\n\t\t' const rows = await sourceDb.collection(\"production-deliveries\").aggregate([',\n\t\t' { $match: { $and: [query || {}, { id_bol: { $type: \"string\", $ne: \"\" } }] } },',\n\t\t' { $lookup: { from: \"production-sales-orders\", localField: \"id_activity\", foreignField: \"_id\", as: \"dbPSO\" } },',\n\t\t' { $unwind: { path: \"$dbPSO\", preserveNullAndEmptyArrays: false } },',\n\t\t' { $match: { \"dbPSO.type\": { $ne: \"Consignment Invoice\" } } },',\n\t\t' { $lookup: { from: \"production-locations\", localField: \"id_location\", foreignField: \"_id\", as: \"dbProductionLocation\" } },',\n\t\t' { $lookup: { from: \"well-groups\", localField: \"id_well_group\", foreignField: \"_id\", as: \"dbWellGroup\" } },',\n\t\t' { $match: { $or: [{ \"dbProductionLocation.0\": { $exists: true } }, { \"dbWellGroup.0\": { $exists: true } }] } },',\n\t\t' { $sort: { date: -1, date_ship: -1, updatedAt: -1 } },',\n\t\t' { $limit: limit },',\n\t\t' { $project: { _id: 1 } }',\n\t\t' ]).toArray();',\n\t\t' return rows.map((doc) => doc._id).filter(Boolean);',\n\t\t'}',\n\t\t'',\n\t\t'async function seedBillingInventory(sourceDb, targetDb) {',\n\t\t' const summary = { profile: \"billing_inventory\", collections: {}, selected: {}, notes: [] };',\n\t\t' const identifiers = extractSeedIdentifiers();',\n\t\t' summary.selected.seed_identifiers = identifiers;',\n\t\t' const billableDeliveryQuery = { $and: [',\n\t\t' { $or: [{ type: \"Delivery\" }, { type: \"Pickup\" }] },',\n\t\t' { $or: [{ consignment: { $exists: false } }, { consignment: false }] },',\n\t\t' { approved: true }, { invoiced: false }, { generic: false }, { \"invoices.0\": { $exists: false } },',\n\t\t' { $or: [{ billed: true }, { memo_bill: true }] }',\n\t\t' ] };',\n\t\t' const billableTruckTreatQuery = { $and: [',\n\t\t' { type: \"Truck Treat\" }, { invoiced: false }, { generic: false }, { approved: true }, { \"invoices.0\": { $exists: false } },',\n\t\t' { $or: [{ billed: true }, { memo_bill: true }] }',\n\t\t' ] };',\n\t\t' const hintedQuery = identifierQuery(identifiers);',\n\t\t' let hintedProductionDeliveries = [];',\n\t\t' let hintedTruckTreatingDeliveries = [];',\n\t\t' if (hintedQuery) {',\n\t\t' hintedProductionDeliveries = await copyByIds(sourceDb, targetDb, \"production-deliveries\", await selectDashboardReadyProductionDeliveryIds(sourceDb, { $and: [billableDeliveryQuery, hintedQuery] }, 3), summary, 3);',\n\t\t' hintedTruckTreatingDeliveries = await copyByIds(sourceDb, targetDb, \"production-deliveries\", await selectTruckTreatDashboardReadyProductionDeliveryIds(sourceDb, { $and: [billableTruckTreatQuery, hintedQuery] }, 3), summary, 3);',\n\t\t' if (hintedProductionDeliveries.length || hintedTruckTreatingDeliveries.length) summary.notes.push(`Preferred live records matching ticket identifiers: ${identifiers.join(\", \")}`);',\n\t\t' else summary.notes.push(`No billable live records matched ticket identifiers: ${identifiers.join(\", \")}`);',\n\t\t' }',\n\t\t'',\n\t\t' let fallbackProductionDeliveries = hintedProductionDeliveries.length ? [] : await copyByIds(sourceDb, targetDb, \"production-deliveries\", await selectDashboardReadyProductionDeliveryIds(sourceDb, billableDeliveryQuery, 3), summary, 3);',\n\t\t' if (!hintedProductionDeliveries.length && !fallbackProductionDeliveries.length) {',\n\t\t' fallbackProductionDeliveries = await copyByIds(sourceDb, targetDb, \"production-deliveries\", await selectLocalhostDeliveryFixtureIds(sourceDb, billableDeliveryQuery, 3), summary, 3);',\n\t\t' if (fallbackProductionDeliveries.length) summary.notes.push(\"Created localhost-only delivery invoice fixture from live delivery data by normalizing copied BOL status to Delivered in local Mongo.\");',\n\t\t' }',\n\t\t' const fallbackTruckTreatingDeliveries = hintedTruckTreatingDeliveries.length ? [] : await copyByIds(sourceDb, targetDb, \"production-deliveries\", await selectTruckTreatDashboardReadyProductionDeliveryIds(sourceDb, billableTruckTreatQuery, 3), summary, 3);',\n\t\t' const productionDeliveries = [...hintedProductionDeliveries, ...fallbackProductionDeliveries];',\n\t\t' const truckTreatingDeliveries = [...hintedTruckTreatingDeliveries, ...fallbackTruckTreatingDeliveries];',\n\t\t' summary.selected.production_deliveries = productionDeliveries.map((doc) => doc._id);',\n\t\t' summary.selected.truck_treating_deliveries = truckTreatingDeliveries.map((doc) => doc._id);',\n\t\t' if (!productionDeliveries.length && !truckTreatingDeliveries.length) {',\n\t\t' summary.notes.push(\"No live billable delivery or truck-treatment records matched the Billing Dashboard awaiting-invoice filters.\");',\n\t\t' }',\n\t\t'',\n\t\t' const sourceDocs = [...productionDeliveries, ...truckTreatingDeliveries];',\n\t\t' const customerIds = idValues(sourceDocs, [\"id_customer\"]);',\n\t\t' const locationIds = idValues(sourceDocs, [\"id_location\"]);',\n\t\t' const wellGroupIds = idValues(sourceDocs, [\"id_well_group\"]);',\n\t\t' const itemIds = idValues(sourceDocs, [\"id_item\", \"id_chemical\"]);',\n\t\t' const bolIds = idValues(sourceDocs, [\"id_bol\", \"id_bols\"]);',\n\t\t' const yardIds = idValues(sourceDocs, [\"id_yard\"]);',\n\t\t' const activityIds = idValues(sourceDocs, [\"id_activity\", \"id_production_sales_order\", \"id_sales_order\"]);',\n\t\t' const tankIds = idValues(sourceDocs, [\"id_tank\"]);',\n\t\t' const truckTreatingDetailIds = idValues(sourceDocs, [\"id_delivery\", \"id_truck_treating_delivery\"]);',\n\t\t' const userIds = idValues(sourceDocs, [\"id_user_approved\", \"id_driver\", \"id_driver_scheduled\", \"id_account_manager\", \"id_foreman\"]);',\n\t\t'',\n\t\t' const copiedLocations = await copyByIds(sourceDb, targetDb, \"production-locations\", locationIds, summary);',\n\t\t' const copiedWellGroups = await copyByIds(sourceDb, targetDb, \"well-groups\", wellGroupIds, summary);',\n\t\t' const copiedItems = await copyByIds(sourceDb, targetDb, \"items\", itemIds, summary);',\n\t\t' const copiedChemicals = await copyByIds(sourceDb, targetDb, \"chemicals\", itemIds, summary);',\n\t\t' await copyByIds(sourceDb, targetDb, \"production-sales-orders\", activityIds, summary);',\n\t\t' await copyByIds(sourceDb, targetDb, \"production-location-tanks\", tankIds, summary);',\n\t\t' await copyByIds(sourceDb, targetDb, \"truck-treating-deliveries\", truckTreatingDetailIds, summary);',\n\t\t' await copyByIds(sourceDb, targetDb, \"customers\", unique([...customerIds, ...idValues(copiedLocations, [\"id_customer\"]), ...idValues(copiedWellGroups, [\"id_customer\"])]), summary);',\n\t\t' await copyByIds(sourceDb, targetDb, \"bols\", bolIds, summary);',\n\t\t' if (productionDeliveries.length && bolIds.length) await targetDb.collection(\"bols\").updateMany({ _id: { $in: bolIds } }, { $set: { status: \"Delivered\" } });',\n\t\t' await copyByIds(sourceDb, targetDb, \"yards\", yardIds, summary);',\n\t\t' await copyByIds(sourceDb, targetDb, \"users\", userIds, summary);',\n\t\t'',\n\t\t' const copiedServiceItems = await copyQuery(sourceDb, targetDb, \"items\", { $or: [',\n\t\t' { type: /service/i }, { type: /misc/i }, { category: /service/i }, { category: /misc/i },',\n\t\t' { name: /surcharge|fuel|delivery|service|misc/i }',\n\t\t' ] }, summary, 40, { updatedAt: -1 });',\n\t\t' summary.selected.service_or_surcharge_items = copiedServiceItems.map((doc) => doc._id);',\n\t\t'',\n\t\t' await copyQuery(sourceDb, targetDb, \"sales-taxes\", {}, summary, 80, { updatedAt: -1 });',\n\t\t' await copyQuery(sourceDb, targetDb, \"state-counties\", {}, summary, 80, { updatedAt: -1 });',\n\t\t' await copyQuery(sourceDb, targetDb, \"accounting-codes\", {}, summary, 80, { updatedAt: -1 });',\n\t\t' await copyQuery(sourceDb, targetDb, \"pricing-items\", {}, summary, 40, { updatedAt: -1 });',\n\t\t' await copyQuery(sourceDb, targetDb, \"pricing-item-miscs\", {}, summary, 40, { updatedAt: -1 });',\n\t\t' await copyQuery(sourceDb, targetDb, \"pricing-item-services\", {}, summary, 40, { updatedAt: -1 });',\n\t\t'',\n\t\t' const inventoryLocationQuery = { $or: [',\n\t\t' { id_item: { $in: unique([...itemIds, ...copiedItems.map((doc) => doc._id), ...copiedServiceItems.map((doc) => doc._id)]) } },',\n\t\t' { id_chemical: { $in: unique([...itemIds, ...copiedChemicals.map((doc) => doc._id)]) } },',\n\t\t' { id_yard: { $in: yardIds } }',\n\t\t' ] };',\n\t\t' const inventoryLocations = await copyQuery(sourceDb, targetDb, \"inventory-locations\", inventoryLocationQuery, summary, 80, { updatedAt: -1 });',\n\t\t' await copyQuery(sourceDb, targetDb, \"inventory-transactions\", { $or: [',\n\t\t' { id_inventory_location: { $in: inventoryLocations.map((doc) => doc._id) } },',\n\t\t' { id_item: { $in: itemIds } },',\n\t\t' { id_chemical: { $in: itemIds } }',\n\t\t' ] }, summary, 120, { date: -1, updatedAt: -1 });',\n\t\t'',\n\t\t' summary.ready = productionDeliveries.length > 0 || truckTreatingDeliveries.length > 0;',\n\t\t' return summary;',\n\t\t'}',\n\t\t'',\n\t\t'(async () => {',\n\t\t' const { MongoClient } = requireMongo();',\n\t\t' const source = resolveRuntimeSource();',\n\t\t' const targetUri = process.env.RESOLVEIO_SUPPORT_QA_MONGO_URL || process.env.RESOLVEIO_RUNNER_QA_MONGO_URL || process.env.MONGO_URL || \"mongodb://127.0.0.1:3001/resolveio?directConnection=true\";',\n\t' if (!source.uri) {',\n\t' preserveExistingSeedResult(\"missing_live_mongo_uri\");',\n\t' writeResult({ status: isLiveDataRequired() ? \"failed\" : \"skipped\", reason: \"missing_live_mongo_uri\", required: isLiveDataRequired(), result_path: resultPath }, isLiveDataRequired() ? 5 : 0);',\n\t' }',\n\t\t' if (!isLocalMongoUri(targetUri)) writeResult({ status: \"failed\", reason: \"target_mongo_must_be_localhost\", target_uri_redacted: redactUri(targetUri), result_path: resultPath }, 3);',\n\t\t' if (String(source.uri) === String(targetUri)) writeResult({ status: \"failed\", reason: \"source_and_target_mongo_match\", result_path: resultPath }, 3);',\n\t\t' const sourceClient = new MongoClient(source.uri, { readPreference: \"secondaryPreferred\", serverSelectionTimeoutMS: 15000 });',\n\t\t' const targetClient = new MongoClient(targetUri, { serverSelectionTimeoutMS: 15000 });',\n\t\t' try {',\n\t\t' await sourceClient.connect();',\n\t\t' await targetClient.connect();',\n\t\t' const sourceDb = source.database ? sourceClient.db(source.database) : sourceClient.db();',\n\t\t' const targetDb = targetClient.db();',\n\t\t' const summary = await seedBillingInventory(sourceDb, targetDb);',\n\t\t' writeResult({',\n\t\t' status: summary.ready ? \"pass\" : \"needs-data\",',\n\t\t' source_uri_redacted: redactUri(source.uri),',\n\t\t' target_uri_redacted: redactUri(targetUri),',\n\t\t' ...summary,',\n\t\t' result_path: resultPath',\n\t\t' }, summary.ready ? 0 : 4);',\n\t\t' } finally {',\n\t\t' await sourceClient.close().catch(() => undefined);',\n\t\t' await targetClient.close().catch(() => undefined);',\n\t\t' }',\n\t\t'})().catch((error) => writeResult({ status: \"failed\", reason: error && (error.stack || error.message) || String(error), result_path: resultPath }, 2));',\n\t\t''\n\t].join('\\n');\n}\n\nexport function buildResolveIORunnerBugfixComparisonQaScript(): string {\n\treturn [\n\t\t'#!/usr/bin/env bash',\n\t\t'set -u',\n\t\t'TOOLS_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"',\n\t\t'source \"$TOOLS_DIR/env.sh\"',\n\t\t'PROJECT_ROOT=\"${1:-}\"',\n\t\t'if [ -z \"$PROJECT_ROOT\" ]; then',\n\t\t' echo \"Usage: $0 <project-root> [baseline-ref] -- <qa-command...>\" >&2',\n\t\t' exit 2',\n\t\t'fi',\n\t\t'shift || true',\n\t\t'DEFAULT_BASELINE_REF=\"${RESOLVEIO_RUNNER_QA_BASELINE_REF:-${RESOLVEIO_SUPPORT_QA_BASELINE_REF:-origin/master}}\"',\n\t\t'if [ \"${1:-}\" = \"--\" ]; then',\n\t\t' BASELINE_REF=\"$DEFAULT_BASELINE_REF\"',\n\t\t'else',\n\t\t' BASELINE_REF=\"${1:-$DEFAULT_BASELINE_REF}\"',\n\t\t' if [ \"$#\" -gt 0 ]; then shift || true; fi',\n\t\t'fi',\n\t\t'if [ \"${1:-}\" = \"--\" ]; then shift; fi',\n\t\t'if [ \"$#\" -eq 0 ]; then',\n\t\t' echo \"Usage: $0 <project-root> [baseline-ref] -- <qa-command...>\" >&2',\n\t\t' exit 2',\n\t\t'fi',\n\t\t'PROJECT_ROOT=\"$(cd \"$PROJECT_ROOT\" && pwd)\"',\n\t\t'REPO_ROOT=\"$(git -C \"$PROJECT_ROOT\" rev-parse --show-toplevel)\"',\n\t\t'PROJECT_REL=\"$(git -C \"$REPO_ROOT\" ls-files --full-name \"$PROJECT_ROOT\" 2>/dev/null | head -1 | xargs dirname 2>/dev/null || true)\"',\n\t\t'if [ -z \"$PROJECT_REL\" ] || [ \"$PROJECT_REL\" = \".\" ]; then',\n\t\t' PROJECT_REL=\"$(node - \"$REPO_ROOT\" \"$PROJECT_ROOT\" <<\\'RESOLVEIO_REL\\'',\n\t\t'const path = require(\"path\");',\n\t\t'console.log(path.relative(process.argv[2], process.argv[3]) || \".\");',\n\t\t'RESOLVEIO_REL',\n\t\t' )\"',\n\t\t'fi',\n\t\t'ARTIFACT_DIR=\"$PROJECT_ROOT/qa-artifacts\"',\n\t\t'mkdir -p \"$ARTIFACT_DIR/baseline\" \"$ARTIFACT_DIR/candidate\"',\n\t\t'RESULT_JSON=\"$ARTIFACT_DIR/bugfix-comparison-result.json\"',\n\t\t'CANDIDATE_PATCH=\"$ARTIFACT_DIR/bugfix-candidate.patch\"',\n\t\t'ORIGINAL_HEAD=\"$(git -C \"$REPO_ROOT\" rev-parse HEAD)\"',\n\t\t'ORIGINAL_BRANCH=\"$(git -C \"$REPO_ROOT\" symbolic-ref --short HEAD 2>/dev/null || true)\"',\n\t\t'QA_COMMAND=(\"$@\")',\n\t\t'STOPPER=\"$TOOLS_DIR/stop-local-qa.sh\"',\n\t\t'CURRENT_PHASE=\"\"',\n\t\t'stop_local_qa() {',\n\t\t' \"$STOPPER\" \"$PROJECT_ROOT\" >/dev/null 2>&1 || true',\n\t\t'}',\n\t\t'write_json() {',\n\t\t' node - \"$RESULT_JSON\" \"$@\" <<\\'RESOLVEIO_WRITE_JSON\\'',\n\t\t'const fs = require(\"fs\");',\n\t\t'const [path, status, baselineExit, candidateExit, baselineRef, originalHead, projectRel, note] = process.argv.slice(2);',\n\t\t'const payload = {',\n\t\t' status,',\n\t\t' baseline_ref: baselineRef,',\n\t\t' original_head: originalHead,',\n\t\t' project: projectRel,',\n\t\t' baseline_exit_code: Number(baselineExit),',\n\t\t' candidate_exit_code: Number(candidateExit),',\n\t\t' code_switch_proven: Number(baselineExit) !== 0 && Number(candidateExit) === 0,',\n\t\t' candidate_passed: Number(candidateExit) === 0,',\n\t\t' baseline_failed: Number(baselineExit) !== 0,',\n\t\t' note,',\n\t\t' artifact_dirs: { baseline: \"qa-artifacts/baseline\", candidate: \"qa-artifacts/candidate\" },',\n\t\t' created_at: new Date().toISOString()',\n\t\t'};',\n\t\t'fs.writeFileSync(path, JSON.stringify(payload, null, 2));',\n\t\t'console.log(JSON.stringify(payload, null, 2));',\n\t\t'RESOLVEIO_WRITE_JSON',\n\t\t'}',\n\t\t'snapshot_phase_artifacts() {',\n\t\t' local phase=\"$1\"',\n\t\t' mkdir -p \"$ARTIFACT_DIR/$phase\"',\n\t\t' find \"$ARTIFACT_DIR\" -maxdepth 1 -type f \\\\( -name \"*.png\" -o -name \"*.jpg\" -o -name \"*.jpeg\" -o -name \"*.webp\" -o -name \"*.json\" -o -name \"*.txt\" -o -name \"*.log\" -o -name \"*.zip\" \\\\) -print0 2>/dev/null | while IFS= read -r -d \"\" file; do',\n\t\t' cp \"$file\" \"$ARTIFACT_DIR/$phase/$(basename \"$file\")\" 2>/dev/null || true',\n\t\t' done',\n\t\t'}',\n\t\t'capture_candidate_patch() {',\n\t\t' : > \"$CANDIDATE_PATCH\"',\n\t\t' git -C \"$REPO_ROOT\" diff --binary -- \"$PROJECT_REL\" >> \"$CANDIDATE_PATCH\" || true',\n\t\t' git -C \"$REPO_ROOT\" diff --binary --cached -- \"$PROJECT_REL\" >> \"$CANDIDATE_PATCH\" || true',\n\t\t'}',\n\t\t'checkout_project_from_ref() {',\n\t\t' local ref=\"$1\"',\n\t\t' git -C \"$REPO_ROOT\" checkout \"$ref\" -- \"$PROJECT_REL\"',\n\t\t'}',\n\t\t'restore_candidate() {',\n\t\t' stop_local_qa',\n\t\t' checkout_project_from_ref \"$ORIGINAL_HEAD\" || true',\n\t\t' if [ -s \"$CANDIDATE_PATCH\" ]; then',\n\t\t' git -C \"$REPO_ROOT\" apply --whitespace=nowarn \"$CANDIDATE_PATCH\" || true',\n\t\t' fi',\n\t\t'}',\n\t\t'trap restore_candidate EXIT',\n\t\t'run_phase() {',\n\t\t' local phase=\"$1\"',\n\t\t' CURRENT_PHASE=\"$phase\"',\n\t\t' stop_local_qa',\n\t\t' export RESOLVEIO_RUNNER_QA_PHASE=\"$phase\"',\n\t\t' export RESOLVEIO_SUPPORT_QA_PHASE=\"$phase\"',\n\t\t' export RESOLVEIO_RUNNER_QA_ARTIFACT_DIR=\"$ARTIFACT_DIR/$phase\"',\n\t\t' export RESOLVEIO_SUPPORT_QA_ARTIFACT_DIR=\"$ARTIFACT_DIR/$phase\"',\n\t\t' mkdir -p \"$ARTIFACT_DIR/$phase\"',\n\t\t' find \"$ARTIFACT_DIR/$phase\" -maxdepth 1 -type f \\\\( -name \"*.png\" -o -name \"*.jpg\" -o -name \"*.jpeg\" -o -name \"*.webp\" -o -name \"*.json\" -o -name \"*.txt\" -o -name \"*.log\" -o -name \"*.zip\" \\\\) -delete 2>/dev/null || true',\n\t\t' find \"$ARTIFACT_DIR\" -maxdepth 1 -type f \\\\( -name \"*.png\" -o -name \"*.jpg\" -o -name \"*.jpeg\" -o -name \"*.webp\" -o -name \"*proof.json\" -o -name \"auth-bootstrap-result.json\" \\\\) -delete 2>/dev/null || true',\n\t\t' set +e',\n\t\t' (cd \"$REPO_ROOT\" && \"${QA_COMMAND[@]}\") 2>&1 | tee \"$ARTIFACT_DIR/$phase/qa-command.log\"',\n\t\t' local rc=\"${PIPESTATUS[0]}\"',\n\t\t' set +e',\n\t\t' snapshot_phase_artifacts \"$phase\"',\n\t\t' stop_local_qa',\n\t\t' return \"$rc\"',\n\t\t'}',\n\t\t'echo \"ResolveIO bugfix comparison QA preserving candidate diff for $PROJECT_REL\"',\n\t\t'capture_candidate_patch',\n\t\t'stop_local_qa',\n\t\t'echo \"ResolveIO bugfix comparison QA baseline phase: $BASELINE_REF\"',\n\t\t'checkout_project_from_ref \"$BASELINE_REF\"',\n\t\t'run_phase baseline',\n\t\t'BASELINE_EXIT=\"$?\"',\n\t\t'echo \"ResolveIO bugfix comparison QA candidate phase: restored workspace candidate\"',\n\t\t'restore_candidate',\n\t\t'run_phase candidate',\n\t\t'CANDIDATE_EXIT=\"$?\"',\n\t\t'if [ \"$CANDIDATE_EXIT\" = \"0\" ] && [ \"$BASELINE_EXIT\" != \"0\" ]; then',\n\t\t' STATUS=\"pass\"',\n\t\t' NOTE=\"Baseline failed and candidate passed for the same QA command.\"',\n\t\t'elif [ \"$CANDIDATE_EXIT\" = \"0\" ]; then',\n\t\t' STATUS=\"inconclusive_baseline_also_passed\"',\n\t\t' NOTE=\"Candidate passed, but baseline also passed; the code switch did not prove the bug fix.\"',\n\t\t'else',\n\t\t' STATUS=\"fail_candidate_failed\"',\n\t\t' NOTE=\"Candidate failed the QA command.\"',\n\t\t'fi',\n\t\t'write_json \"$STATUS\" \"$BASELINE_EXIT\" \"$CANDIDATE_EXIT\" \"$BASELINE_REF\" \"$ORIGINAL_HEAD\" \"$PROJECT_REL\" \"$NOTE\"',\n\t\t'[ \"$CANDIDATE_EXIT\" = \"0\" ] || exit \"$CANDIDATE_EXIT\"',\n\t\t'[ \"$STATUS\" = \"pass\" ] || exit 8',\n\t\t''\n\t].join('\\n');\n}\n\nexport function buildResolveIORunnerQaWorkflowProbeScript(): string {\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 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 || \"/\";',\n\t\t'const targetRoute = routeArg.startsWith(\"/\") ? routeArg : `/${routeArg}`;',\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 matrixPath = path.join(artifactDir, \"qa-coverage-matrix.json\");',\n\t\t'const resultPath = path.join(artifactDir, \"qa-workflow-probe-result.json\");',\n\t\t'const passScreenshotPath = path.join(artifactDir, \"qa-workflow-route-ready.jpg\");',\n\t\t'const failScreenshotPath = path.join(artifactDir, \"qa-workflow-route-blocked.jpg\");',\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 || \"admin\";',\n\t\t'const password = process.env.RESOLVEIO_RUNNER_QA_PASSWORD || process.env.RESOLVEIO_SUPPORT_QA_PASSWORD || \"\";',\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'',\n\t\t'function stripTrailingSlash(value) { return String(value || \"\").replace(/\\\\/+$/, \"\"); }',\n\t\t'function delay(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); }',\n\t\t'function writeJson(filePath, payload) { fs.mkdirSync(path.dirname(filePath), { recursive: true }); fs.writeFileSync(filePath, JSON.stringify(payload, null, 2)); }',\n\t\t'function readJson(filePath) { try { return JSON.parse(fs.readFileSync(filePath, \"utf8\")); } catch (error) { return null; } }',\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: 3000 }, (res) => { res.resume(); resolve(Boolean(res.statusCode && res.statusCode < 500)); });',\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} catch (error) { resolve(false); }',\n\t\t'\t});',\n\t\t'}',\n\t\t'async function waitForHttpReady(url, label) {',\n\t\t'\tconst deadline = Date.now() + 45000;',\n\t\t'\twhile (Date.now() < deadline) {',\n\t\t'\t\tif (await requestReady(url)) return;',\n\t\t'\t\tawait delay(1500);',\n\t\t'\t}',\n\t\t'\tthrow new Error(`${label} did not become ready at ${url}`);',\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, { method: \"POST\", timeout: 20000, headers: { \"content-type\": \"application/json\", \"content-length\": Buffer.byteLength(body), \"origin\": clientUrl } }, (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) : {}; } catch (error) { reject(new Error(`${url} returned non-JSON HTTP ${res.statusCode}: ${raw.slice(0, 300)}`)); return; }',\n\t\t'\t\t\t\tif (!res.statusCode || res.statusCode >= 400) { reject(new Error(`${url} returned HTTP ${res.statusCode}: ${JSON.stringify(json).slice(0, 500)}`)); return; }',\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'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) { try { return require(candidate); } catch (error) {} }',\n\t\t'\tthrow new Error(\"Unable to require puppeteer from project/server node_modules or global resolution\");',\n\t\t'}',\n\t\t'async function launchBrowser(puppeteer) {',\n\t\t'\tconst launchOptions = { headless: true, defaultViewport: { width: viewportWidth, height: viewportHeight }, args: [\"--no-sandbox\", \"--disable-setuid-sandbox\", \"--disable-dev-shm-usage\", `--window-size=${viewportWidth},${viewportHeight}`] };',\n\t\t'\tif (process.env.PUPPETEER_EXECUTABLE_PATH || process.env.CHROME_BIN) launchOptions.executablePath = process.env.PUPPETEER_EXECUTABLE_PATH || process.env.CHROME_BIN;',\n\t\t'\treturn puppeteer.launch(launchOptions);',\n\t\t'}',\n\t\t'async function login() {',\n\t\t'\tif (!password) throw new Error(\"QA password is empty; source the generated env.sh before workflow probe\");',\n\t\t'\tconst loginJson = await requestJson(`${serverUrl}/login`, { username, password });',\n\t\t'\tconst refreshToken = loginJson && loginJson.result && loginJson.result.token;',\n\t\t'\tif (loginJson.error || !refreshToken) throw new Error(`Login failed: ${JSON.stringify(loginJson).slice(0, 800)}`);',\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) throw new Error(`Access token failed: ${JSON.stringify(accessJson).slice(0, 800)}`);',\n\t\t'\treturn { refreshToken, accessToken, user };',\n\t\t'}',\n\t\t'async function seedAuth(page, auth) {',\n\t\t'\tawait page.goto(clientUrl, { waitUntil: \"domcontentloaded\", timeout: 45000 });',\n\t\t'\tawait page.evaluate((payload) => {',\n\t\t'\t\tlocalStorage.clear();',\n\t\t'\t\tsessionStorage.clear();',\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 || {}), other: { ...((payload.user || {}).other || {}), tour_completed: true, took_tour: true }, settings: { ...((payload.user || {}).settings || {}), opening_route: payload.targetRoute } }));',\n\t\t'\t\tlocalStorage.setItem(\"lastURL\", payload.targetRoute);',\n\t\t'\t}, { ...auth, targetRoute });',\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 { url: location.href, title: document.title, bodyTextSnippet: bodyText.slice(0, 1200), hasLoginText: /Employee\\\\/Customer Login|Employee Sign In|Customer Access|Unable to sign in/i.test(bodyText), hasOfflineModeText: bodyText.includes(\"*** OFFLINE MODE ***\") };',\n\t\t'\t});',\n\t\t'}',\n\t\t'function updateMatrix(status, screenshotPath, caption, assertion) {',\n\t\t'\tconst matrix = readJson(matrixPath) || { status: \"started\", rows: [] };',\n\t\t'\tmatrix.workflow_probe = { status, route: targetRoute, screenshot: screenshotPath, caption, assertion, updated_at: new Date().toISOString() };',\n\t\t'\tmatrix.updated_at = new Date().toISOString();',\n\t\t'\tconst rows = Array.isArray(matrix.rows) ? matrix.rows : [];',\n\t\t'\tif (rows[0]) {',\n\t\t'\t\trows[0].route_probe = { status, route: targetRoute, screenshot: screenshotPath, caption, assertion, updated_at: matrix.updated_at };',\n\t\t'\t\tif (status !== \"pass\") { rows[0].status = \"blocked\"; rows[0].screenshot = screenshotPath; rows[0].caption = caption; }',\n\t\t'\t\telse if (!rows[0].status || rows[0].status === \"pending\") { rows[0].status = \"in_progress\"; }',\n\t\t'\t}',\n\t\t'\tmatrix.rows = rows;',\n\t\t'\twriteJson(matrixPath, matrix);',\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\tconst auth = await login();',\n\t\t'\t\tawait seedAuth(page, auth);',\n\t\t'\t\tawait page.goto(`${clientUrl}${targetRoute}`, { waitUntil: \"domcontentloaded\", timeout: 60000 });',\n\t\t'\t\tawait page.waitForSelector(\"body\", { timeout: 30000 });',\n\t\t'\t\tawait delay(2500);',\n\t\t'\t\tconst summary = await pageSummary(page);',\n\t\t'\t\tif (summary.hasLoginText || summary.hasOfflineModeText || !summary.bodyTextSnippet) throw new Error(`Workflow route did not reach authenticated app: ${JSON.stringify(summary).slice(0, 1000)}`);',\n\t\t'\t\tconst caption = `Workflow route ready: ${targetRoute} loaded in authenticated local QA with live seeded data available.`;',\n\t\t'\t\tawait page.screenshot({ path: passScreenshotPath, type: \"jpeg\", quality: 82, fullPage: false });',\n\t\t'\t\tupdateMatrix(\"pass\", passScreenshotPath, caption, \"Authenticated customer workflow route loaded; deeper row-specific UI/data proof still required.\");',\n\t\t'\t\tconst result = { status: \"pass\", clientUrl, serverUrl, targetRoute, screenshot: passScreenshotPath, caption, page: summary, matrix: matrixPath };',\n\t\t'\t\twriteJson(resultPath, result);',\n\t\t'\t\tconsole.log(JSON.stringify(result, null, 2));',\n\t\t'\t} catch (error) {',\n\t\t'\t\tlet summary = null;',\n\t\t'\t\ttry { if (page) { await page.screenshot({ path: failScreenshotPath, type: \"jpeg\", quality: 82, fullPage: false }); summary = await pageSummary(page); } } catch (screenshotError) {}',\n\t\t'\t\tconst caption = `Blocked before workflow QA: ${targetRoute} could not be reached in authenticated local QA.`;',\n\t\t'\t\tupdateMatrix(\"blocked\", failScreenshotPath, caption, error && (error.message || String(error)) || \"Workflow probe failed\");',\n\t\t'\t\tconst result = { status: \"blocked\", clientUrl, serverUrl, targetRoute, screenshot: failScreenshotPath, caption, error: error && (error.stack || error.message) || String(error), page: summary, matrix: matrixPath };',\n\t\t'\t\twriteJson(resultPath, result);',\n\t\t'\t\tconsole.error(JSON.stringify(result, null, 2));',\n\t\t'\t\tprocess.exitCode = 1;',\n\t\t'\t} finally {',\n\t\t'\t\tawait browser.close().catch(() => undefined);',\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\nexport function buildResolveIORunnerQaToolsReadme(options: ResolveIORunnerQaToolBundleOptions = {}): string {\n\tconst mode = options.mode || 'runner';\n\tconst toolsDir = mode === 'support' ? '.resolveio-support-tools' : '.resolveio-ai-runner-tools';\n\tconst port = normalizePort(options.qaClientPort, '4200');\n\tconst clientUrlVar = envVar(mode, 'CLIENT_URL');\n\tconst keepaliveVar = envVar(mode, 'KEEPALIVE');\n\tconst usernameVar = envVar(mode, 'USERNAME');\n\tconst passwordVar = envVar(mode, 'PASSWORD');\n\treturn [\n\t\t`# ResolveIO ${mode === 'support' ? 'Support' : 'AI Runner'} Local QA Tools`,\n\t\t'',\n\t\t'These scripts are generated by `@resolveio/server-lib` and are shared by support tickets, AICoder app-builder runs, and AI-terminal runs.',\n\t\t'',\n\t\t'```bash',\n\t\t`source ${toolsDir}/env.sh`,\n\t\t`${toolsDir}/run-local-qa.sh <project-root>`,\n\t\t`node ${toolsDir}/qa-live-data-seed.js <project-root>`,\n\t\t`node ${toolsDir}/qa-auth-bootstrap.js <project-root> /target-route`,\n\t\t`node ${toolsDir}/qa-workflow-probe.js <project-root> /target-route`,\n\t\t`${toolsDir}/bugfix-comparison-qa.sh <project-root> origin/master -- bash -lc '<same QA command>'`,\n\t\t'```',\n\t\t'',\n\t\t`This workspace reserves Angular QA client port ${port}; use \\`$${clientUrlVar}\\` instead of assuming 4200 is free.`,\n\t\t'The local QA runner starts server/client, polls the reserved client URL, writes `qa-artifacts/server.log` and `qa-artifacts/client.log`, and fails fast on fatal startup/runtime errors.',\n\t\t'The shared auth bootstrap first opens the exact localhost client origin, logs out any visible stale session, clears service workers/cache/IndexedDB/local/session storage, then calls `/login` and `/accessToken`, seeds `refreshToken`, `accessToken`, `user`, and `lastURL`, and writes `qa-artifacts/auth-bootstrap-result.json` plus a ready/failure screenshot.',\n\t\t'The shared workflow probe logs in with the same local QA account, opens the target customer route, captures an email-safe desktop JPEG, and updates `qa-artifacts/qa-coverage-matrix.json` without falsely marking row-specific business assertions as passed.',\n\t\t`For browser clickthrough work, start the runner once with \\`${keepaliveVar}=true ${toolsDir}/run-local-qa.sh <project-root>\\`; it detaches after the app is ready, and later calls should reuse \\`$${clientUrlVar}\\` for all login/upload/screenshot retries. Do not restart Angular for auth failures.`,\n\t\t'Do not wait for `networkidle0` or `networkidle2` in ResolveIO browser QA. Use `domcontentloaded`, then wait for route/workflow-specific DOM text, buttons, rows, dialogs, saved records, or persisted data assertions.',\n\t\t'Do not run `npm run build-dev`, `ng build`, or another Angular compile while keepalive `ng serve` is running. If a full Angular build is required after browser QA, first run the staged `stop-local-qa.sh`, then build, then restart `run-local-qa.sh` for final browser proof.',\n\t\t'Use desktop screenshots at 1920x1080 by default unless the task is explicitly mobile/responsive. Every screenshot must have a customer-facing caption.',\n\t\t'For import/export/form-submit/data workflows, prove before/action/after with representative data and a concrete row/count/value assertion.',\n\t\t'For data-backed workflows, run `qa-live-data-seed.js` after the local app is ready and before browser clickthrough. It reads a bounded live-data slice from `RESOLVEIO_QA_LIVE_MONGO_URL` or staged mongo context, writes only to localhost QA Mongo, prefers ticket-mentioned invoice/BOL/order/ticket identifiers, and records `qa-artifacts/qa-live-data-seed-result.json`.',\n\t\t'For bug fixes, use `bugfix-comparison-qa.sh` so baseline/master runs the exact same repro before the candidate/PR run. A passing candidate is not enough unless the comparison result shows baseline failed or the report explicitly explains why the baseline failure could not be reproduced.',\n\t\t`Use \\`$${usernameVar}\\` and \\`$${passwordVar}\\` for the local fixture admin account unless ticket/app-specific credentials are provided.`,\n\t\t'The env file reuses `/var/lib/resolveio/puppeteer`, npm cache, worker-safe Browserslist settings, and Angular cache prep so QA should not download a browser or rebuild cold caches unnecessarily.',\n\t\tmode === 'support'\n\t\t\t? 'Support workspaces also stage local `mongod` and `mongosh` wrappers so app server scripts can start MongoDB when the worker image does not have MongoDB installed.'\n\t\t\t: '',\n\t\t''\n\t].filter((line) => line !== '').join('\\n');\n}\n"]}
|