@resolveio/server-lib 22.3.83 → 22.3.84

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@resolveio/server-lib",
3
- "version": "22.3.83",
3
+ "version": "22.3.84",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "package": "./build_package.sh",
@@ -19,4 +19,5 @@ export declare function buildResolveIORunnerLocalQaScript(): string;
19
19
  export declare function buildResolveIORunnerLocalQaStopperScript(): string;
20
20
  export declare function buildResolveIORunnerQaLiveDataSeederScript(): string;
21
21
  export declare function buildResolveIORunnerBugfixComparisonQaScript(): string;
22
+ export declare function buildResolveIORunnerQaWorkflowProbeScript(): string;
22
23
  export declare function buildResolveIORunnerQaToolsReadme(options?: ResolveIORunnerQaToolBundleOptions): string;
@@ -5,6 +5,7 @@ exports.buildResolveIORunnerLocalQaScript = buildResolveIORunnerLocalQaScript;
5
5
  exports.buildResolveIORunnerLocalQaStopperScript = buildResolveIORunnerLocalQaStopperScript;
6
6
  exports.buildResolveIORunnerQaLiveDataSeederScript = buildResolveIORunnerQaLiveDataSeederScript;
7
7
  exports.buildResolveIORunnerBugfixComparisonQaScript = buildResolveIORunnerBugfixComparisonQaScript;
8
+ exports.buildResolveIORunnerQaWorkflowProbeScript = buildResolveIORunnerQaWorkflowProbeScript;
8
9
  exports.buildResolveIORunnerQaToolsReadme = buildResolveIORunnerQaToolsReadme;
9
10
  var runner_process_janitor_1 = require("./runner-process-janitor");
10
11
  function shellDoubleQuote(value) {
@@ -1159,6 +1160,174 @@ function buildResolveIORunnerBugfixComparisonQaScript() {
1159
1160
  ''
1160
1161
  ].join('\n');
1161
1162
  }
1163
+ function buildResolveIORunnerQaWorkflowProbeScript() {
1164
+ return [
1165
+ '#!/usr/bin/env node',
1166
+ "'use strict';",
1167
+ '',
1168
+ 'const fs = require("fs");',
1169
+ 'const http = require("http");',
1170
+ 'const https = require("https");',
1171
+ 'const path = require("path");',
1172
+ '',
1173
+ 'const projectRoot = path.resolve(process.argv[2] || process.cwd());',
1174
+ 'const routeArg = process.argv[3] || process.env.RESOLVEIO_RUNNER_QA_TARGET_ROUTE || process.env.RESOLVEIO_SUPPORT_QA_TARGET_ROUTE || "/";',
1175
+ 'const targetRoute = routeArg.startsWith("/") ? routeArg : `/${routeArg}`;',
1176
+ 'const artifactDir = path.resolve(process.env.RESOLVEIO_RUNNER_QA_ARTIFACT_DIR || process.env.RESOLVEIO_SUPPORT_QA_ARTIFACT_DIR || path.join(projectRoot, "qa-artifacts"));',
1177
+ 'const matrixPath = path.join(artifactDir, "qa-coverage-matrix.json");',
1178
+ 'const resultPath = path.join(artifactDir, "qa-workflow-probe-result.json");',
1179
+ 'const passScreenshotPath = path.join(artifactDir, "qa-workflow-route-ready.jpg");',
1180
+ 'const failScreenshotPath = path.join(artifactDir, "qa-workflow-route-blocked.jpg");',
1181
+ '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"}`);',
1182
+ 'const serverUrl = stripTrailingSlash(process.env.RESOLVEIO_RUNNER_QA_SERVER_URL || process.env.RESOLVEIO_SUPPORT_QA_SERVER_URL || "http://localhost:8080");',
1183
+ 'const username = process.env.RESOLVEIO_RUNNER_QA_USERNAME || process.env.RESOLVEIO_SUPPORT_QA_USERNAME || "admin";',
1184
+ 'const password = process.env.RESOLVEIO_RUNNER_QA_PASSWORD || process.env.RESOLVEIO_SUPPORT_QA_PASSWORD || "";',
1185
+ 'const viewportWidth = Number(process.env.RESOLVEIO_RUNNER_QA_VIEWPORT_WIDTH || process.env.RESOLVEIO_SUPPORT_QA_VIEWPORT_WIDTH || 1920);',
1186
+ 'const viewportHeight = Number(process.env.RESOLVEIO_RUNNER_QA_VIEWPORT_HEIGHT || process.env.RESOLVEIO_SUPPORT_QA_VIEWPORT_HEIGHT || 1080);',
1187
+ '',
1188
+ 'function stripTrailingSlash(value) { return String(value || "").replace(/\\/+$/, ""); }',
1189
+ 'function delay(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); }',
1190
+ 'function writeJson(filePath, payload) { fs.mkdirSync(path.dirname(filePath), { recursive: true }); fs.writeFileSync(filePath, JSON.stringify(payload, null, 2)); }',
1191
+ 'function readJson(filePath) { try { return JSON.parse(fs.readFileSync(filePath, "utf8")); } catch (error) { return null; } }',
1192
+ 'function requestReady(url) {',
1193
+ ' return new Promise((resolve) => {',
1194
+ ' try {',
1195
+ ' const parsed = new URL(url);',
1196
+ ' const mod = parsed.protocol === "https:" ? https : http;',
1197
+ ' const req = mod.get(parsed, { timeout: 3000 }, (res) => { res.resume(); resolve(Boolean(res.statusCode && res.statusCode < 500)); });',
1198
+ ' req.on("timeout", () => req.destroy(new Error("timeout")));',
1199
+ ' req.on("error", () => resolve(false));',
1200
+ ' } catch (error) { resolve(false); }',
1201
+ ' });',
1202
+ '}',
1203
+ 'async function waitForHttpReady(url, label) {',
1204
+ ' const deadline = Date.now() + 45000;',
1205
+ ' while (Date.now() < deadline) {',
1206
+ ' if (await requestReady(url)) return;',
1207
+ ' await delay(1500);',
1208
+ ' }',
1209
+ ' throw new Error(`${label} did not become ready at ${url}`);',
1210
+ '}',
1211
+ 'function requestJson(url, payload) {',
1212
+ ' return new Promise((resolve, reject) => {',
1213
+ ' const body = JSON.stringify(payload || {});',
1214
+ ' const parsed = new URL(url);',
1215
+ ' const mod = parsed.protocol === "https:" ? https : http;',
1216
+ ' const req = mod.request(parsed, { method: "POST", timeout: 20000, headers: { "content-type": "application/json", "content-length": Buffer.byteLength(body), "origin": clientUrl } }, (res) => {',
1217
+ ' let raw = "";',
1218
+ ' res.setEncoding("utf8");',
1219
+ ' res.on("data", (chunk) => { raw += chunk; });',
1220
+ ' res.on("end", () => {',
1221
+ ' let json = null;',
1222
+ ' try { json = raw ? JSON.parse(raw) : {}; } catch (error) { reject(new Error(`${url} returned non-JSON HTTP ${res.statusCode}: ${raw.slice(0, 300)}`)); return; }',
1223
+ ' if (!res.statusCode || res.statusCode >= 400) { reject(new Error(`${url} returned HTTP ${res.statusCode}: ${JSON.stringify(json).slice(0, 500)}`)); return; }',
1224
+ ' resolve(json);',
1225
+ ' });',
1226
+ ' });',
1227
+ ' req.on("timeout", () => req.destroy(new Error(`${url} timed out`)));',
1228
+ ' req.on("error", reject);',
1229
+ ' req.write(body);',
1230
+ ' req.end();',
1231
+ ' });',
1232
+ '}',
1233
+ 'function requirePuppeteer() {',
1234
+ ' const candidates = [',
1235
+ ' path.join(projectRoot, "server", "node_modules", "puppeteer", "lib", "cjs", "puppeteer", "puppeteer.js"),',
1236
+ ' path.join(projectRoot, "node_modules", "puppeteer", "lib", "cjs", "puppeteer", "puppeteer.js"),',
1237
+ ' path.join(process.cwd(), "server", "node_modules", "puppeteer", "lib", "cjs", "puppeteer", "puppeteer.js"),',
1238
+ ' path.join(process.cwd(), "node_modules", "puppeteer", "lib", "cjs", "puppeteer", "puppeteer.js"),',
1239
+ ' "puppeteer"',
1240
+ ' ];',
1241
+ ' for (const candidate of candidates) { try { return require(candidate); } catch (error) {} }',
1242
+ ' throw new Error("Unable to require puppeteer from project/server node_modules or global resolution");',
1243
+ '}',
1244
+ 'async function launchBrowser(puppeteer) {',
1245
+ ' const launchOptions = { headless: true, defaultViewport: { width: viewportWidth, height: viewportHeight }, args: ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage", `--window-size=${viewportWidth},${viewportHeight}`] };',
1246
+ ' if (process.env.PUPPETEER_EXECUTABLE_PATH || process.env.CHROME_BIN) launchOptions.executablePath = process.env.PUPPETEER_EXECUTABLE_PATH || process.env.CHROME_BIN;',
1247
+ ' return puppeteer.launch(launchOptions);',
1248
+ '}',
1249
+ 'async function login() {',
1250
+ ' if (!password) throw new Error("QA password is empty; source the generated env.sh before workflow probe");',
1251
+ ' const loginJson = await requestJson(`${serverUrl}/login`, { username, password });',
1252
+ ' const refreshToken = loginJson && loginJson.result && loginJson.result.token;',
1253
+ ' if (loginJson.error || !refreshToken) throw new Error(`Login failed: ${JSON.stringify(loginJson).slice(0, 800)}`);',
1254
+ ' const accessJson = await requestJson(`${serverUrl}/accessToken`, { refreshToken });',
1255
+ ' const accessToken = accessJson && accessJson.result && accessJson.result.token;',
1256
+ ' const user = accessJson && accessJson.result && accessJson.result.user;',
1257
+ ' if (accessJson.error || !accessToken || !user) throw new Error(`Access token failed: ${JSON.stringify(accessJson).slice(0, 800)}`);',
1258
+ ' return { refreshToken, accessToken, user };',
1259
+ '}',
1260
+ 'async function seedAuth(page, auth) {',
1261
+ ' await page.goto(clientUrl, { waitUntil: "domcontentloaded", timeout: 45000 });',
1262
+ ' await page.evaluate((payload) => {',
1263
+ ' localStorage.clear();',
1264
+ ' sessionStorage.clear();',
1265
+ ' localStorage.setItem("refreshToken", payload.refreshToken);',
1266
+ ' localStorage.setItem("accessToken", payload.accessToken);',
1267
+ ' localStorage.setItem("user", JSON.stringify({ ...(payload.user || {}), other: { ...((payload.user || {}).other || {}), tour_completed: true, took_tour: true }, settings: { ...((payload.user || {}).settings || {}), opening_route: payload.targetRoute } }));',
1268
+ ' localStorage.setItem("lastURL", payload.targetRoute);',
1269
+ ' }, { ...auth, targetRoute });',
1270
+ '}',
1271
+ 'async function pageSummary(page) {',
1272
+ ' return page.evaluate(() => {',
1273
+ ' const bodyText = (document.body && document.body.innerText || "").replace(/\\s+/g, " ").trim();',
1274
+ ' return { 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 ***") };',
1275
+ ' });',
1276
+ '}',
1277
+ 'function updateMatrix(status, screenshotPath, caption, assertion) {',
1278
+ ' const matrix = readJson(matrixPath) || { status: "started", rows: [] };',
1279
+ ' matrix.workflow_probe = { status, route: targetRoute, screenshot: screenshotPath, caption, assertion, updated_at: new Date().toISOString() };',
1280
+ ' matrix.updated_at = new Date().toISOString();',
1281
+ ' const rows = Array.isArray(matrix.rows) ? matrix.rows : [];',
1282
+ ' if (rows[0]) {',
1283
+ ' rows[0].route_probe = { status, route: targetRoute, screenshot: screenshotPath, caption, assertion, updated_at: matrix.updated_at };',
1284
+ ' if (status !== "pass") { rows[0].status = "blocked"; rows[0].screenshot = screenshotPath; rows[0].caption = caption; }',
1285
+ ' else if (!rows[0].status || rows[0].status === "pending") { rows[0].status = "in_progress"; }',
1286
+ ' }',
1287
+ ' matrix.rows = rows;',
1288
+ ' writeJson(matrixPath, matrix);',
1289
+ '}',
1290
+ '',
1291
+ '(async () => {',
1292
+ ' fs.mkdirSync(artifactDir, { recursive: true });',
1293
+ ' const puppeteer = requirePuppeteer();',
1294
+ ' const browser = await launchBrowser(puppeteer);',
1295
+ ' let page;',
1296
+ ' try {',
1297
+ ' await waitForHttpReady(clientUrl, "QA client");',
1298
+ ' await waitForHttpReady(serverUrl, "QA server");',
1299
+ ' page = await browser.newPage();',
1300
+ ' await page.setViewport({ width: viewportWidth, height: viewportHeight });',
1301
+ ' const auth = await login();',
1302
+ ' await seedAuth(page, auth);',
1303
+ ' await page.goto(`${clientUrl}${targetRoute}`, { waitUntil: "domcontentloaded", timeout: 60000 });',
1304
+ ' await page.waitForSelector("body", { timeout: 30000 });',
1305
+ ' await delay(2500);',
1306
+ ' const summary = await pageSummary(page);',
1307
+ ' if (summary.hasLoginText || summary.hasOfflineModeText || !summary.bodyTextSnippet) throw new Error(`Workflow route did not reach authenticated app: ${JSON.stringify(summary).slice(0, 1000)}`);',
1308
+ ' const caption = `Workflow route ready: ${targetRoute} loaded in authenticated local QA with live seeded data available.`;',
1309
+ ' await page.screenshot({ path: passScreenshotPath, type: "jpeg", quality: 82, fullPage: false });',
1310
+ ' updateMatrix("pass", passScreenshotPath, caption, "Authenticated customer workflow route loaded; deeper row-specific UI/data proof still required.");',
1311
+ ' const result = { status: "pass", clientUrl, serverUrl, targetRoute, screenshot: passScreenshotPath, caption, page: summary, matrix: matrixPath };',
1312
+ ' writeJson(resultPath, result);',
1313
+ ' console.log(JSON.stringify(result, null, 2));',
1314
+ ' } catch (error) {',
1315
+ ' let summary = null;',
1316
+ ' try { if (page) { await page.screenshot({ path: failScreenshotPath, type: "jpeg", quality: 82, fullPage: false }); summary = await pageSummary(page); } } catch (screenshotError) {}',
1317
+ ' const caption = `Blocked before workflow QA: ${targetRoute} could not be reached in authenticated local QA.`;',
1318
+ ' updateMatrix("blocked", failScreenshotPath, caption, error && (error.message || String(error)) || "Workflow probe failed");',
1319
+ ' const result = { status: "blocked", clientUrl, serverUrl, targetRoute, screenshot: failScreenshotPath, caption, error: error && (error.stack || error.message) || String(error), page: summary, matrix: matrixPath };',
1320
+ ' writeJson(resultPath, result);',
1321
+ ' console.error(JSON.stringify(result, null, 2));',
1322
+ ' process.exitCode = 1;',
1323
+ ' } finally {',
1324
+ ' await browser.close().catch(() => undefined);',
1325
+ ' process.exit(process.exitCode || 0);',
1326
+ ' }',
1327
+ '})();',
1328
+ ''
1329
+ ].join('\n');
1330
+ }
1162
1331
  function buildResolveIORunnerQaToolsReadme(options) {
1163
1332
  if (options === void 0) { options = {}; }
1164
1333
  var mode = options.mode || 'runner';
@@ -1178,12 +1347,14 @@ function buildResolveIORunnerQaToolsReadme(options) {
1178
1347
  "".concat(toolsDir, "/run-local-qa.sh <project-root>"),
1179
1348
  "node ".concat(toolsDir, "/qa-live-data-seed.js <project-root>"),
1180
1349
  "node ".concat(toolsDir, "/qa-auth-bootstrap.js <project-root> /target-route"),
1350
+ "node ".concat(toolsDir, "/qa-workflow-probe.js <project-root> /target-route"),
1181
1351
  "".concat(toolsDir, "/bugfix-comparison-qa.sh <project-root> origin/master -- bash -lc '<same QA command>'"),
1182
1352
  '```',
1183
1353
  '',
1184
1354
  "This workspace reserves Angular QA client port ".concat(port, "; use `$").concat(clientUrlVar, "` instead of assuming 4200 is free."),
1185
1355
  '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.',
1186
1356
  '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.',
1357
+ '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.',
1187
1358
  "For browser clickthrough work, start the runner once with `".concat(keepaliveVar, "=true ").concat(toolsDir, "/run-local-qa.sh <project-root>`; it detaches after the app is ready, and later calls should reuse `$").concat(clientUrlVar, "` for all login/upload/screenshot retries. Do not restart Angular for auth failures."),
1188
1359
  '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.',
1189
1360
  '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.',
@@ -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 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`${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`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' 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"]}