@validate.qa/runner 1.0.100 → 1.0.101
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-2OKZY3WN.mjs +291 -0
- package/dist/{chunk-5P4ANVDY.mjs → chunk-4SZC5ZX3.mjs} +5092 -760
- package/dist/{chunk-2XD63NU5.mjs → chunk-BBTK6OX6.mjs} +567 -84
- package/dist/{chunk-WDXLAZ2J.mjs → chunk-CO5KHIVX.mjs} +3 -2
- package/dist/{chunk-HMUC3DKS.mjs → chunk-CYWHPXNZ.mjs} +2281 -2
- package/dist/{chunk-MAO6ADAO.mjs → chunk-FQUH2PU2.mjs} +1 -1
- package/dist/{chunk-MBKGTELT.mjs → chunk-IGXGSDE3.mjs} +701 -283
- package/dist/{chunk-II7QW7MZ.mjs → chunk-M3VZGHZQ.mjs} +14 -11
- package/dist/{chunk-GIIIVO2W.mjs → chunk-SN3PN4G4.mjs} +151 -11
- package/dist/{chunk-6NIT2CCH.mjs → chunk-TPU42VH2.mjs} +10 -9
- package/dist/{chunk-MHFP3QXU.mjs → chunk-XZUUBMP3.mjs} +1 -1
- package/dist/cli.js +8994 -1015
- package/dist/cli.mjs +211 -170
- package/dist/credential-mcp-server.js +503 -79
- package/dist/credential-mcp-server.mjs +3 -3
- package/dist/discovery-mcp-server.js +654 -87
- package/dist/discovery-mcp-server.mjs +4 -4
- package/dist/dist-VTPBY2C4.mjs +17 -0
- package/dist/inbox-mcp-server.js +496 -73
- package/dist/inbox-mcp-server.mjs +3 -3
- package/dist/route-seed-worker.js +2780 -74
- package/dist/route-seed-worker.mjs +14 -7
- package/dist/run-write-guard-preload.js +1206 -160
- package/dist/run-write-guard-preload.mjs +5 -285
- package/dist/run-write-guard.js +1206 -160
- package/dist/run-write-guard.mjs +3 -3
- package/package.json +3 -3
- package/dist/dist-DFNLMH6L.mjs +0 -16
package/dist/cli.mjs
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
require_failure_triage,
|
|
13
13
|
require_fetch_cli_subscription,
|
|
14
14
|
require_framework_import_runner,
|
|
15
|
+
require_google_local_signin,
|
|
15
16
|
require_grok_repo_enrichment_runner,
|
|
16
17
|
require_inbox_stamp,
|
|
17
18
|
require_live_browser_registry,
|
|
@@ -30,21 +31,22 @@ import {
|
|
|
30
31
|
require_resource_governor,
|
|
31
32
|
require_result_api_call_bodies,
|
|
32
33
|
require_result_shed
|
|
33
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-4SZC5ZX3.mjs";
|
|
34
35
|
import {
|
|
35
36
|
require_tar_isolated
|
|
36
37
|
} from "./chunk-AAUHXGDS.mjs";
|
|
37
38
|
import "./chunk-EXRFHATN.mjs";
|
|
38
39
|
import {
|
|
39
40
|
require_browser_config
|
|
40
|
-
} from "./chunk-
|
|
41
|
-
import "./chunk-
|
|
42
|
-
import "./chunk-
|
|
43
|
-
import "./chunk-
|
|
44
|
-
import "./chunk-
|
|
45
|
-
import "./chunk-
|
|
46
|
-
import "./chunk-
|
|
47
|
-
import "./chunk-
|
|
41
|
+
} from "./chunk-TPU42VH2.mjs";
|
|
42
|
+
import "./chunk-M3VZGHZQ.mjs";
|
|
43
|
+
import "./chunk-XZUUBMP3.mjs";
|
|
44
|
+
import "./chunk-FQUH2PU2.mjs";
|
|
45
|
+
import "./chunk-CYWHPXNZ.mjs";
|
|
46
|
+
import "./chunk-CO5KHIVX.mjs";
|
|
47
|
+
import "./chunk-2OKZY3WN.mjs";
|
|
48
|
+
import "./chunk-BBTK6OX6.mjs";
|
|
49
|
+
import "./chunk-SN3PN4G4.mjs";
|
|
48
50
|
import {
|
|
49
51
|
LIVE_FRAME_IDLE_PUSH_INTERVAL_MS,
|
|
50
52
|
MOBILE_COLD_BOOT_SETTLE_MS,
|
|
@@ -57,17 +59,12 @@ import {
|
|
|
57
59
|
buildAppiumCode,
|
|
58
60
|
clampMobileSwipeDistance,
|
|
59
61
|
classifyErrorMessage,
|
|
60
|
-
coldSessionConfigMessage,
|
|
61
62
|
computeMobileSwipePointers,
|
|
62
63
|
decideHarPosted,
|
|
63
64
|
deriveAuthPlan,
|
|
64
|
-
detectApiColdSessionBlock,
|
|
65
|
-
detectAuthBlock,
|
|
66
|
-
detectSkippedApiCoverage,
|
|
67
65
|
hasPlatformLocator,
|
|
68
66
|
init_dist,
|
|
69
67
|
isAppOriginResolutionEnabled,
|
|
70
|
-
isAuthReadinessGuardEnabled,
|
|
71
68
|
isConfigLikeReason,
|
|
72
69
|
isEditableMobileClassName,
|
|
73
70
|
isRepositoryCli,
|
|
@@ -84,7 +81,7 @@ import {
|
|
|
84
81
|
stepAppliesToPlatform,
|
|
85
82
|
stepsMissingPlatformLocators,
|
|
86
83
|
stepsNeedingReviewForPlatform
|
|
87
|
-
} from "./chunk-
|
|
84
|
+
} from "./chunk-IGXGSDE3.mjs";
|
|
88
85
|
import {
|
|
89
86
|
__commonJS,
|
|
90
87
|
__toESM
|
|
@@ -95,7 +92,7 @@ var require_package = __commonJS({
|
|
|
95
92
|
"package.json"(exports, module) {
|
|
96
93
|
module.exports = {
|
|
97
94
|
name: "@validate.qa/runner",
|
|
98
|
-
version: "1.0.
|
|
95
|
+
version: "1.0.101",
|
|
99
96
|
description: "validate local test runner - execute Playwright tests on your machine, connected to validate.qa cloud.",
|
|
100
97
|
bin: {
|
|
101
98
|
"validate-runner": "dist/cli.js"
|
|
@@ -103,8 +100,8 @@ var require_package = __commonJS({
|
|
|
103
100
|
scripts: {
|
|
104
101
|
build: "tsup",
|
|
105
102
|
dev: "tsx src/cli.ts",
|
|
106
|
-
test: "node --import tsx --test src/supervisor-child-argv.test.ts src/audit-scrub.test.ts src/retry-on-429.test.ts src/version-check.test.ts src/poll-interval.test.ts src/node-version-notice.test.ts src/services/auth-state.test.ts src/run-no-longer-active.test.ts src/mode-abort-no-verdict.test.ts src/heartbeat-active-runs.test.ts src/result-post-structure-413.test.ts src/result-shed-routes-413.test.ts src/result-post-retry.test.ts src/result-media-stale-epoch.test.ts src/run-write-guard-bundle.test.ts src/spec-driver-bundle.test.ts src/mcp-servers-bundle.test.ts src/codex-files-mcp-server-bundle.test.ts src/claim-nonce-not-logged.test.ts src/sign-in-capture.test.ts src/run-status-verdicts.test.ts src/sign-in-failure.test.ts src/mobile-ui-path-succeeded.test.ts src/supervisor-policy.test.ts src/login-check.test.ts src/heal-post-lost-ack.test.ts src/heal-log-stream-chunks.test.ts src/shutdown-claim-window.test.ts",
|
|
107
|
-
"test:mobile": "node --import tsx --test --test-concurrency=2 src/runner.mobile-ai-env.test.ts src/mobile-device-queue.test.ts src/device-lease.test.ts src/runner-watchdog.test.ts src/result-payload-budget.test.ts src/result-media-upload.test.ts src/services/appium-*.test.ts src/services/*mobile*.test.ts src/services/mobile/*.test.ts",
|
|
103
|
+
test: "node --import tsx --test src/supervisor-child-argv.test.ts src/audit-scrub.test.ts src/retry-on-429.test.ts src/version-check.test.ts src/poll-interval.test.ts src/node-version-notice.test.ts src/services/auth-state.test.ts src/run-no-longer-active.test.ts src/mode-abort-no-verdict.test.ts src/discovery-render-type.test.ts src/batch-heal-no-start.test.ts src/heartbeat-active-runs.test.ts src/result-post-structure-413.test.ts src/result-shed-routes-413.test.ts src/result-post-retry.test.ts src/result-media-stale-epoch.test.ts src/run-write-guard-bundle.test.ts src/spec-driver-bundle.test.ts src/mcp-servers-bundle.test.ts src/codex-files-mcp-server-bundle.test.ts src/claim-nonce-not-logged.test.ts src/sign-in-capture.test.ts src/run-status-verdicts.test.ts src/sign-in-failure.test.ts src/mobile-ui-path-succeeded.test.ts src/supervisor-policy.test.ts src/login-check.test.ts src/heal-post-lost-ack.test.ts src/heal-log-stream-chunks.test.ts src/shutdown-claim-window.test.ts",
|
|
104
|
+
"test:mobile": "node --import tsx --test --test-concurrency=2 src/runner.mobile-ai-env.test.ts src/runner.mobile-heal-audit-wiring.test.ts src/mobile-device-queue.test.ts src/device-lease.test.ts src/runner-watchdog.test.ts src/result-payload-budget.test.ts src/result-media-upload.test.ts src/services/appium-*.test.ts src/services/*mobile*.test.ts src/services/mobile/*.test.ts",
|
|
108
105
|
"smoke:installed": "node scripts/installed-run-smoke.mjs",
|
|
109
106
|
"check:packed-mcp": "node scripts/packed-install-check.mjs",
|
|
110
107
|
prepublishOnly: "npm run build"
|
|
@@ -315,8 +312,9 @@ var import_playwright_native = __toESM(require_playwright_native());
|
|
|
315
312
|
var import_runner_core = __toESM(require_dist());
|
|
316
313
|
|
|
317
314
|
// src/runner.ts
|
|
318
|
-
var import_runner_core6 = __toESM(require_dist());
|
|
319
315
|
var import_runner_core7 = __toESM(require_dist());
|
|
316
|
+
var import_runner_core8 = __toESM(require_dist());
|
|
317
|
+
var import_runner_core9 = __toESM(require_dist());
|
|
320
318
|
var import_framework_import_runner = __toESM(require_framework_import_runner());
|
|
321
319
|
var import_grok_repo_enrichment_runner = __toESM(require_grok_repo_enrichment_runner());
|
|
322
320
|
var import_repo_brief_execution = __toESM(require_repo_brief_execution());
|
|
@@ -325,7 +323,7 @@ var import_cli_session_failure = __toESM(require_cli_session_failure());
|
|
|
325
323
|
var import_cli_limit = __toESM(require_cli_limit());
|
|
326
324
|
var import_fetch_cli_subscription = __toESM(require_fetch_cli_subscription());
|
|
327
325
|
var import_codex_cli = __toESM(require_codex_cli());
|
|
328
|
-
var
|
|
326
|
+
var import_runner_core10 = __toESM(require_dist());
|
|
329
327
|
|
|
330
328
|
// src/services/regression-runner.ts
|
|
331
329
|
var import_regression_runner = __toESM(require_regression_runner());
|
|
@@ -680,6 +678,9 @@ function computeMobileUiPathSucceeded(stepResults, authoredSteps) {
|
|
|
680
678
|
return passedBefore > 0;
|
|
681
679
|
}
|
|
682
680
|
|
|
681
|
+
// src/post-heal-outcome.ts
|
|
682
|
+
var import_runner_core5 = __toESM(require_dist());
|
|
683
|
+
|
|
683
684
|
// src/run-no-longer-active.ts
|
|
684
685
|
var RunNoLongerActiveError = class extends Error {
|
|
685
686
|
};
|
|
@@ -691,39 +692,19 @@ function withClaimEpoch(body, claimEpoch) {
|
|
|
691
692
|
}
|
|
692
693
|
|
|
693
694
|
// src/post-heal-outcome.ts
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
const tryPostPatch = async () => {
|
|
697
|
-
if (passed && healedCode && testCaseId) {
|
|
698
|
-
try {
|
|
699
|
-
await postHealResult2(healedCode, testCaseId);
|
|
700
|
-
log2?.ok?.(`\u{1FA7A} Healed code auto-patched for test ${testCaseId}`);
|
|
701
|
-
} catch (patchErr) {
|
|
702
|
-
const patchMsg = patchErr instanceof Error ? patchErr.message : String(patchErr);
|
|
703
|
-
log2?.warn?.(`Failed to auto-patch healed code: ${patchMsg}`);
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
};
|
|
707
|
-
try {
|
|
708
|
-
await postRunResult();
|
|
709
|
-
} catch (err) {
|
|
710
|
-
if (err instanceof RunNoLongerActiveError) {
|
|
711
|
-
await tryPostPatch();
|
|
712
|
-
}
|
|
713
|
-
throw err;
|
|
714
|
-
}
|
|
715
|
-
await tryPostPatch();
|
|
695
|
+
function postHealOutcome(options) {
|
|
696
|
+
return (0, import_runner_core5.postHealOutcome)({ ...options, isRunGone: (err) => err instanceof RunNoLongerActiveError });
|
|
716
697
|
}
|
|
717
698
|
|
|
718
699
|
// src/run-status-verdicts.ts
|
|
719
|
-
var
|
|
700
|
+
var import_runner_core6 = __toESM(require_dist());
|
|
720
701
|
function runStatusVerdicts(body, heldEpoch) {
|
|
721
702
|
const rows = body?.runs;
|
|
722
703
|
if (!Array.isArray(rows)) return [];
|
|
723
704
|
const verdicts = [];
|
|
724
705
|
for (const row of rows) {
|
|
725
706
|
if (!row || typeof row.id !== "string" || typeof row.status !== "string") continue;
|
|
726
|
-
const reason = (0,
|
|
707
|
+
const reason = (0, import_runner_core6.runStatusAbortReason)(row, heldEpoch(row.id));
|
|
727
708
|
if (!reason) continue;
|
|
728
709
|
verdicts.push({
|
|
729
710
|
runId: row.id,
|
|
@@ -9280,7 +9261,7 @@ async function postHealResult(serverUrl, headers, runId, healedCode, testCaseId,
|
|
|
9280
9261
|
}
|
|
9281
9262
|
throw new Error(`Failed to post heal result after ${MAX_ATTEMPTS} attempts: ${lastError?.message}`);
|
|
9282
9263
|
}
|
|
9283
|
-
async function postMobileHealResult(serverUrl, headers, runId, body) {
|
|
9264
|
+
async function postMobileHealResult(serverUrl, headers, runId, body, claimEpoch) {
|
|
9284
9265
|
const MAX_ATTEMPTS = 3;
|
|
9285
9266
|
let lastError;
|
|
9286
9267
|
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
@@ -9288,7 +9269,7 @@ async function postMobileHealResult(serverUrl, headers, runId, body) {
|
|
|
9288
9269
|
const res = await fetch(`${serverUrl}/api/runner/runs/${runId}/mobile-heal-result`, {
|
|
9289
9270
|
method: "POST",
|
|
9290
9271
|
headers,
|
|
9291
|
-
body: JSON.stringify(body),
|
|
9272
|
+
body: JSON.stringify(withClaimEpoch(body, claimEpoch)),
|
|
9292
9273
|
signal: AbortSignal.timeout(6e4)
|
|
9293
9274
|
});
|
|
9294
9275
|
if (res.ok) return;
|
|
@@ -9361,6 +9342,28 @@ function batchHealPartialHooks(serverUrl, headers, runId, projectId, warn = (msg
|
|
|
9361
9342
|
resumeLookup: () => fetchBatchHealAppliedIds(serverUrl, headers, runId, projectId)
|
|
9362
9343
|
};
|
|
9363
9344
|
}
|
|
9345
|
+
function batchHealNoStart(tests, baseUrl) {
|
|
9346
|
+
if (tests.length === 0) {
|
|
9347
|
+
return {
|
|
9348
|
+
body: { perTest: [], systemicDiagnosis: "batch-heal dispatched with empty tests list", logs: "no tests to heal", durationMs: 0 },
|
|
9349
|
+
level: "warn",
|
|
9350
|
+
logLine: "batch-heal: empty payload, posting no-op result"
|
|
9351
|
+
};
|
|
9352
|
+
}
|
|
9353
|
+
if (!baseUrl) {
|
|
9354
|
+
return {
|
|
9355
|
+
body: {
|
|
9356
|
+
perTest: [],
|
|
9357
|
+
systemicDiagnosis: "batch-heal aborted: BASE_URL missing for project (set BASE_URL in Test Data or FrameworkRepo.baseUrl)",
|
|
9358
|
+
logs: "batch-heal aborted: empty baseUrl",
|
|
9359
|
+
durationMs: 0
|
|
9360
|
+
},
|
|
9361
|
+
level: "fail",
|
|
9362
|
+
logLine: "batch-heal aborted \u2014 BASE_URL is empty in /pending payload"
|
|
9363
|
+
};
|
|
9364
|
+
}
|
|
9365
|
+
return null;
|
|
9366
|
+
}
|
|
9364
9367
|
var LOG_APPEND_MAX_LINES = 100;
|
|
9365
9368
|
var LOG_APPEND_MAX_LINE_LEN = 8 * 1024;
|
|
9366
9369
|
var LOG_APPEND_MAX_BODY_BYTES = 200 * 1024;
|
|
@@ -9688,6 +9691,12 @@ function pruneCollectedPageForPost(page) {
|
|
|
9688
9691
|
if (Array.isArray(out.observations)) out.observations = out.observations.slice(-200).map((obs) => compactForAudit(obs));
|
|
9689
9692
|
return out;
|
|
9690
9693
|
}
|
|
9694
|
+
function discoveryRenderTypeFields(input) {
|
|
9695
|
+
if (input.err instanceof import_runner_core7.UnsupportedRendererError) {
|
|
9696
|
+
return { detectedAppRenderType: input.err.renderType, appRenderDetectionSignals: input.err.signals };
|
|
9697
|
+
}
|
|
9698
|
+
return input.appRenderType ? { detectedAppRenderType: input.appRenderType } : {};
|
|
9699
|
+
}
|
|
9691
9700
|
function prepareDiscoveryResultForPost(result, logs2, options = {}) {
|
|
9692
9701
|
const safe = { ...result, logs: truncateTextTail(logs2, RESULT_LOG_MAX_CHARS, "discovery result log") };
|
|
9693
9702
|
if (Array.isArray(safe.collectedPages)) safe.collectedPages = safe.collectedPages.map(pruneCollectedPageForPost);
|
|
@@ -9864,8 +9873,8 @@ function toLiveBrowserMode(mode) {
|
|
|
9864
9873
|
async function executeRun(run2, opts, headers, requestLiveBrowserHeartbeat = () => {
|
|
9865
9874
|
}, onMobileHealPending = () => {
|
|
9866
9875
|
}, assignedDeviceId, mobileLeaseError, signal) {
|
|
9867
|
-
if (signal && (0,
|
|
9868
|
-
return (0,
|
|
9876
|
+
if (signal && (0, import_runner_core9.currentRunAbortSignal)() !== signal) {
|
|
9877
|
+
return (0, import_runner_core9.runWithAbortScope)({ signal, runId: run2.runId }, () => executeRun(run2, opts, headers, requestLiveBrowserHeartbeat, onMobileHealPending, assignedDeviceId, mobileLeaseError, signal));
|
|
9869
9878
|
}
|
|
9870
9879
|
const runMode = run2.mode ?? "run";
|
|
9871
9880
|
const RUNNER_MODE = opts.runnerMode ?? "playwright-native";
|
|
@@ -9923,7 +9932,7 @@ async function executeRun(run2, opts, headers, requestLiveBrowserHeartbeat = ()
|
|
|
9923
9932
|
...nativeOptions,
|
|
9924
9933
|
liveBrowserHandle,
|
|
9925
9934
|
onLiveBrowserSnapshot: requestLiveBrowserHeartbeat,
|
|
9926
|
-
signal: (0,
|
|
9935
|
+
signal: (0, import_runner_core9.linkRunAbort)(nativeOptions.signal) ?? signal
|
|
9927
9936
|
});
|
|
9928
9937
|
const callbackContext = {
|
|
9929
9938
|
runId: run2.runId,
|
|
@@ -9936,7 +9945,7 @@ async function executeRun(run2, opts, headers, requestLiveBrowserHeartbeat = ()
|
|
|
9936
9945
|
};
|
|
9937
9946
|
const startTime = Date.now();
|
|
9938
9947
|
try {
|
|
9939
|
-
(0,
|
|
9948
|
+
(0, import_runner_core9.throwIfRunAborted)();
|
|
9940
9949
|
if (mobileLeaseError) {
|
|
9941
9950
|
await postRunResult({
|
|
9942
9951
|
status: "ERROR",
|
|
@@ -10052,7 +10061,7 @@ async function executeRun(run2, opts, headers, requestLiveBrowserHeartbeat = ()
|
|
|
10052
10061
|
releaseAuthLock2
|
|
10053
10062
|
);
|
|
10054
10063
|
const duration2 = Date.now() - startTime;
|
|
10055
|
-
(0,
|
|
10064
|
+
(0, import_runner_core9.throwIfRunAborted)();
|
|
10056
10065
|
await postRunResult({
|
|
10057
10066
|
status: result2.passed ? "PASSED" : "FAILED",
|
|
10058
10067
|
apiCalls: result2.apiCalls,
|
|
@@ -10072,14 +10081,10 @@ async function executeRun(run2, opts, headers, requestLiveBrowserHeartbeat = ()
|
|
|
10072
10081
|
if (runMode === "batch-heal") {
|
|
10073
10082
|
const tests = run2.batchHealTests ?? [];
|
|
10074
10083
|
log.info(`Heal engine: GROK BATCH (${tests.length} tests, healerModel=${run2.healerModel ?? "null"})`);
|
|
10075
|
-
|
|
10076
|
-
|
|
10077
|
-
|
|
10078
|
-
|
|
10079
|
-
logs: "no tests to heal",
|
|
10080
|
-
durationMs: 0
|
|
10081
|
-
});
|
|
10082
|
-
log.warn("batch-heal: empty payload, posting no-op result");
|
|
10084
|
+
const noStart = batchHealNoStart(tests, run2.baseUrl);
|
|
10085
|
+
if (noStart) {
|
|
10086
|
+
await postBatchHealResult(opts.serverUrl, headers, run2.runId, noStart.body);
|
|
10087
|
+
log[noStart.level](noStart.logLine);
|
|
10083
10088
|
return;
|
|
10084
10089
|
}
|
|
10085
10090
|
const healLogStream = createHealLogStreamer(opts.serverUrl, headers, run2.runId);
|
|
@@ -10101,10 +10106,10 @@ async function executeRun(run2, opts, headers, requestLiveBrowserHeartbeat = ()
|
|
|
10101
10106
|
completionTokens: info.completionTokens,
|
|
10102
10107
|
totalTokens: info.totalTokens,
|
|
10103
10108
|
requestBody: { iteration: info.iteration, toolCalls: info.toolCalls, session: info.requestMessages?.[0] ?? null },
|
|
10104
|
-
metadata: { phase: "heal", groupId: info.groupId, iteration: info.iteration, healMode: "batch", ...(0,
|
|
10109
|
+
metadata: { phase: "heal", groupId: info.groupId, iteration: info.iteration, healMode: "batch", ...(0, import_runner_core9.cliAuditMetadata)(info) }
|
|
10105
10110
|
});
|
|
10106
10111
|
};
|
|
10107
|
-
const batchResult = await (0,
|
|
10112
|
+
const batchResult = await (0, import_runner_core9.executeGrokPerBatchHeal)(tests, run2.baseUrl, {
|
|
10108
10113
|
onAICall: batchHealAudit,
|
|
10109
10114
|
testVariables: run2.testVariables,
|
|
10110
10115
|
runNativeTest: runNativeWithLiveBrowser,
|
|
@@ -10113,7 +10118,7 @@ async function executeRun(run2, opts, headers, requestLiveBrowserHeartbeat = ()
|
|
|
10113
10118
|
saveRefreshedLogin: grokLogin?.saveRefreshed,
|
|
10114
10119
|
engineSpec: run2.healerModel,
|
|
10115
10120
|
// Returns HEAL_TIMEOUT: for the tests not reached instead of dying at the runner's batch-heal deadline.
|
|
10116
|
-
deadlineAt: (0,
|
|
10121
|
+
deadlineAt: (0, import_runner_core7.batchHealDeadlineAt)(startTime),
|
|
10117
10122
|
onLog: healLogStream.onLog,
|
|
10118
10123
|
...batchHealPartialHooks(opts.serverUrl, headers, run2.runId, run2.projectId || void 0)
|
|
10119
10124
|
});
|
|
@@ -10123,7 +10128,7 @@ async function executeRun(run2, opts, headers, requestLiveBrowserHeartbeat = ()
|
|
|
10123
10128
|
} catch (drainErr) {
|
|
10124
10129
|
console.warn(`[batch-heal] audit flush failed: ${drainErr instanceof Error ? drainErr.message : String(drainErr)}`);
|
|
10125
10130
|
}
|
|
10126
|
-
(0,
|
|
10131
|
+
(0, import_runner_core9.throwIfRunAborted)();
|
|
10127
10132
|
try {
|
|
10128
10133
|
await postBatchHealResult(opts.serverUrl, headers, run2.runId, {
|
|
10129
10134
|
perTest: batchResult.perTest,
|
|
@@ -10131,7 +10136,7 @@ async function executeRun(run2, opts, headers, requestLiveBrowserHeartbeat = ()
|
|
|
10131
10136
|
logs: batchResult.logs,
|
|
10132
10137
|
durationMs: batchResult.totalDurationMs
|
|
10133
10138
|
});
|
|
10134
|
-
log.ok((0,
|
|
10139
|
+
log.ok((0, import_runner_core9.batchHealCompleteLine)(batchResult.perTest, tests.length, batchResult.totalDurationMs));
|
|
10135
10140
|
} catch (err) {
|
|
10136
10141
|
log.fail(`Failed to post batch heal result: ${err instanceof Error ? err.message : String(err)}`);
|
|
10137
10142
|
}
|
|
@@ -10164,6 +10169,8 @@ async function executeRun(run2, opts, headers, requestLiveBrowserHeartbeat = ()
|
|
|
10164
10169
|
}, run2.runId) : void 0;
|
|
10165
10170
|
const apiHealStorageStatePath = apiHealAuthPaths ? (0, import_local_auth_state.getValidLocalAuthState)(apiHealAuthPaths.cachePath) : void 0;
|
|
10166
10171
|
if (apiHealStorageStatePath) log.info("API heal verification is using saved auth state");
|
|
10172
|
+
const apiHealAuthSession = (0, import_runner_core8.sharedAuthSessionStamp)(apiHealStorageStatePath, false, sharedAuthInjectEnabled());
|
|
10173
|
+
const apiHealAuthPlan = authPlanFor(run2.tags, run2.playwrightCode);
|
|
10167
10174
|
const stickyAppBugs = /* @__PURE__ */ new Map();
|
|
10168
10175
|
const lastChangeByTitle = /* @__PURE__ */ new Map();
|
|
10169
10176
|
let currentError = run2.lastFailureError ?? "Test failed";
|
|
@@ -10278,12 +10285,28 @@ ${finalVerifyResult.logs ?? ""}`;
|
|
|
10278
10285
|
continue;
|
|
10279
10286
|
}
|
|
10280
10287
|
if (result3.verdict === "app_bug") {
|
|
10281
|
-
log.warn(` APP BUG detected (legacy schema): ${result3.bugDescription}`);
|
|
10282
10288
|
const duration4 = Date.now() - startTime;
|
|
10289
|
+
const legacyCold = (0, import_runner_core8.apiHealColdSessionRelabel)("legacy", {
|
|
10290
|
+
tags: run2.tags ?? [],
|
|
10291
|
+
authStateExpected: apiHealAuthPlan.needsCookieState,
|
|
10292
|
+
authStateInjected: !!apiHealStorageStatePath,
|
|
10293
|
+
needsStandingHeaders: apiHealAuthPlan.needsStandingHeaders,
|
|
10294
|
+
standingHeadersInjected: !!finalVerifyResult?.authDelivery?.standingHeadersInjected,
|
|
10295
|
+
error: result3.bugDescription ?? null,
|
|
10296
|
+
logs: finalVerifyResult?.logs ?? null,
|
|
10297
|
+
apiCalls: null
|
|
10298
|
+
});
|
|
10299
|
+
if (legacyCold) {
|
|
10300
|
+
log.warn(legacyCold.logLine);
|
|
10301
|
+
await postRunResult({ status: legacyCold.status, error: legacyCold.error, duration: duration4, authSession: apiHealAuthSession });
|
|
10302
|
+
return;
|
|
10303
|
+
}
|
|
10304
|
+
log.warn(` APP BUG detected (legacy schema): ${result3.bugDescription}`);
|
|
10283
10305
|
await postRunResult({
|
|
10284
10306
|
status: "FAILED",
|
|
10285
10307
|
error: `APP_BUG: ${result3.bugDescription}`,
|
|
10286
|
-
duration: duration4
|
|
10308
|
+
duration: duration4,
|
|
10309
|
+
authSession: apiHealAuthSession
|
|
10287
10310
|
});
|
|
10288
10311
|
return;
|
|
10289
10312
|
}
|
|
@@ -10353,7 +10376,7 @@ ${finalVerifyResult.logs ?? ""}`;
|
|
|
10353
10376
|
let apiHealStatus = passed ? "PASSED" : "FAILED";
|
|
10354
10377
|
let apiHealError = composedError;
|
|
10355
10378
|
if (!passed) {
|
|
10356
|
-
if (finalUnfixableReason?.startsWith(
|
|
10379
|
+
if (finalUnfixableReason?.startsWith(import_runner_core7.HEAL_AI_NO_ANSWER_ERROR_PREFIX)) {
|
|
10357
10380
|
apiHealStatus = "ERROR";
|
|
10358
10381
|
apiHealError = finalUnfixableReason;
|
|
10359
10382
|
} else if (isConfigLikeReason(classifyErrorMessage(composedError ?? null))) {
|
|
@@ -10390,6 +10413,23 @@ ${finalVerifyResult.logs ?? ""}`;
|
|
|
10390
10413
|
}
|
|
10391
10414
|
}
|
|
10392
10415
|
}
|
|
10416
|
+
if (apiHealStatus === "FAILED") {
|
|
10417
|
+
const cold = (0, import_runner_core8.apiHealColdSessionRelabel)("verified", {
|
|
10418
|
+
tags: run2.tags ?? [],
|
|
10419
|
+
authStateExpected: apiHealAuthPlan.needsCookieState,
|
|
10420
|
+
authStateInjected: !!apiHealStorageStatePath,
|
|
10421
|
+
needsStandingHeaders: apiHealAuthPlan.needsStandingHeaders,
|
|
10422
|
+
standingHeadersInjected: !!finalVerifyResult?.authDelivery?.standingHeadersInjected,
|
|
10423
|
+
error: apiHealError ?? finalVerifyResult?.error,
|
|
10424
|
+
logs: finalVerifyResult?.logs,
|
|
10425
|
+
apiCalls: rawSteps.flatMap((s) => s.apiCalls ?? [])
|
|
10426
|
+
});
|
|
10427
|
+
if (cold) {
|
|
10428
|
+
apiHealStatus = cold.status;
|
|
10429
|
+
apiHealError = cold.error;
|
|
10430
|
+
log.warn(`${cold.logLine} (was "${composedError?.slice(0, 60)}\u2026")`);
|
|
10431
|
+
}
|
|
10432
|
+
}
|
|
10393
10433
|
await postHealOutcome({
|
|
10394
10434
|
postRunResult: () => postRunResult({
|
|
10395
10435
|
status: apiHealStatus,
|
|
@@ -10401,9 +10441,10 @@ ${finalVerifyResult.logs ?? ""}`;
|
|
|
10401
10441
|
// Preserve healedCode on ALL heal outcomes - including app-bug-only -
|
|
10402
10442
|
// so the file with test.fixme wraps gets committed and audited.
|
|
10403
10443
|
healedCode: lastHealedCode,
|
|
10404
|
-
duration: duration3
|
|
10444
|
+
duration: duration3,
|
|
10445
|
+
authSession: apiHealAuthSession
|
|
10405
10446
|
}),
|
|
10406
|
-
postHealResult: (healedCode, testCaseId) => postHealResult(opts.serverUrl, headers, run2.runId, healedCode, testCaseId),
|
|
10447
|
+
postHealResult: (healedCode, testCaseId) => postHealResult(opts.serverUrl, headers, run2.runId, healedCode, testCaseId, void 0, void 0, run2.claimEpoch),
|
|
10407
10448
|
passed,
|
|
10408
10449
|
healedCode: lastHealedCode,
|
|
10409
10450
|
testCaseId: run2.testCaseId,
|
|
@@ -10439,11 +10480,11 @@ ${finalVerifyResult.logs ?? ""}`;
|
|
|
10439
10480
|
completionTokens: info.completionTokens,
|
|
10440
10481
|
totalTokens: info.totalTokens,
|
|
10441
10482
|
...hasTurnDetails ? buildAuditTurnPayload(info, run2.testVariables) : { requestBody: { iteration: info.iteration, toolCalls: info.toolCalls } },
|
|
10442
|
-
metadata: { groupId: info.groupId, iteration: info.iteration, testName: run2.testCaseId, ...(0,
|
|
10483
|
+
metadata: { groupId: info.groupId, iteration: info.iteration, testName: run2.testCaseId, ...(0, import_runner_core9.cliAuditMetadata)(info) }
|
|
10443
10484
|
});
|
|
10444
10485
|
};
|
|
10445
10486
|
const envOverride = (process.env.USE_GROK_PER_TEST_HEAL ?? "").trim().toLowerCase();
|
|
10446
|
-
const useGrokHeal = envOverride === "true" ? true : envOverride === "false" ? false : (0,
|
|
10487
|
+
const useGrokHeal = envOverride === "true" ? true : envOverride === "false" ? false : (0, import_runner_core9.getAgenticEngine)(run2.healerModel) === "grok-build";
|
|
10447
10488
|
const healOpts = {
|
|
10448
10489
|
testName: run2.testName ?? run2.testCaseId ?? void 0,
|
|
10449
10490
|
tags: run2.tags ?? [],
|
|
@@ -10466,19 +10507,19 @@ ${finalVerifyResult.logs ?? ""}`;
|
|
|
10466
10507
|
// Whole-heal deadline: the runner's heal deadline minus a margin for the result
|
|
10467
10508
|
// post (heal-deadline.ts). The healer stops both phases inside it and returns a
|
|
10468
10509
|
// HEAL_TIMEOUT: result instead of dying as a reclaimed task.
|
|
10469
|
-
deadlineAt: (0,
|
|
10510
|
+
deadlineAt: (0, import_runner_core7.healDeadlineAt)(startTime)
|
|
10470
10511
|
};
|
|
10471
10512
|
if (useGrokHeal) {
|
|
10472
10513
|
const source = envOverride === "true" ? "env USE_GROK_PER_TEST_HEAL=true" : `healerModel=${run2.healerModel ?? "null"}`;
|
|
10473
10514
|
log.info(`Heal engine: GROK (${source})`);
|
|
10474
10515
|
}
|
|
10475
10516
|
const grokHealLogStream = useGrokHeal ? createHealLogStreamer(opts.serverUrl, headers, run2.runId) : null;
|
|
10476
|
-
const httpFallback = run2.healerFallbackModel && (0,
|
|
10517
|
+
const httpFallback = run2.healerFallbackModel && (0, import_runner_core9.getAgenticEngine)(run2.healerFallbackModel) === "native" ? run2.healerFallbackModel : null;
|
|
10477
10518
|
let result2;
|
|
10478
10519
|
if (useGrokHeal) {
|
|
10479
10520
|
try {
|
|
10480
10521
|
const grokLogin = await (0, import_fetch_cli_subscription.fetchCliSubscription)({ serverUrl: opts.serverUrl, runId: run2.runId, headers, cli: "grok-build", onLog: grokHealLogStream?.onLog });
|
|
10481
|
-
result2 = await (0,
|
|
10522
|
+
result2 = await (0, import_runner_core9.executeGrokPerTestHeal)(run2.playwrightCode, run2.steps, run2.baseUrl, {
|
|
10482
10523
|
...healOpts,
|
|
10483
10524
|
subscriptionSecret: grokLogin?.value,
|
|
10484
10525
|
saveRefreshedLogin: grokLogin?.saveRefreshed,
|
|
@@ -10523,9 +10564,9 @@ ${finalVerifyResult.logs ?? ""}`;
|
|
|
10523
10564
|
console.warn(`[runner]${auditDrainError}`);
|
|
10524
10565
|
}
|
|
10525
10566
|
const isConfigIssue = !result2.passed && isConfigLikeReason(classifyErrorMessage(result2.error ?? null));
|
|
10526
|
-
let healStatus = (0,
|
|
10567
|
+
let healStatus = (0, import_runner_core7.healRunStatus)({ passed: result2.passed, error: result2.error, isConfigIssue });
|
|
10527
10568
|
let healError = result2.error;
|
|
10528
|
-
if (!result2.passed && !isConfigIssue && !(0,
|
|
10569
|
+
if (!result2.passed && !isConfigIssue && !(0, import_runner_core7.isHealAiQuotaError)(result2.error)) {
|
|
10529
10570
|
const rlDetection = (0, import_rate_limit_detector.detectRateLimitBlock)({
|
|
10530
10571
|
testName: run2.testName ?? run2.testCaseId ?? void 0,
|
|
10531
10572
|
tags: run2.tags ?? [],
|
|
@@ -10540,6 +10581,19 @@ ${finalVerifyResult.logs ?? ""}`;
|
|
|
10540
10581
|
log.warn(`\u26A0 Heal rate-limit override \u2014 was ${result2.error?.slice(0, 60)}\u2026, now ERROR/RATE_LIMIT_BLOCKED`);
|
|
10541
10582
|
}
|
|
10542
10583
|
}
|
|
10584
|
+
const loginRelabel = (0, import_runner_core8.uiHealLoginRelabel)({
|
|
10585
|
+
status: healStatus,
|
|
10586
|
+
testName: run2.testName ?? run2.testCaseId ?? void 0,
|
|
10587
|
+
tags: run2.tags ?? [],
|
|
10588
|
+
error: healError,
|
|
10589
|
+
logs: result2.logs,
|
|
10590
|
+
stepResults: result2.stepResults
|
|
10591
|
+
});
|
|
10592
|
+
if (loginRelabel) {
|
|
10593
|
+
healStatus = loginRelabel.status;
|
|
10594
|
+
healError = loginRelabel.error;
|
|
10595
|
+
log.warn(loginRelabel.logLine);
|
|
10596
|
+
}
|
|
10543
10597
|
await postHealOutcome({
|
|
10544
10598
|
postRunResult: () => postRunResult({
|
|
10545
10599
|
status: healStatus,
|
|
@@ -10565,7 +10619,7 @@ ${finalVerifyResult.logs ?? ""}`;
|
|
|
10565
10619
|
});
|
|
10566
10620
|
if (result2.logs) {
|
|
10567
10621
|
const phaseLines = result2.logs.split("\n").filter(
|
|
10568
|
-
(l) => l.includes("Phase 1: Script-driven") || l.includes("Phase 2: Escalating") || l.includes("Phase 2: Script + MCP") || l.includes("Phase 1 succeeded") || l.includes("Phase 2 iteration") || l.includes("\u{1F916} Verdict:") || l.includes("\u{1F916} finish_fix:") || l.includes("native verification PASSED") || l.includes("native verification FAILED") || l.includes("Native run PASSED") || l.includes("Native run FAILED") || l.includes("APP_BUG") || l.includes("CONFIG_ISSUE") || l.includes("needs_browser") || l.includes("--- Attempt") || l.includes("Proven commands") || l.includes("Proven fix:") || l.includes("Same error repeated") || l.includes("aborting heal")
|
|
10622
|
+
(l) => l.includes("Phase 1: Script-driven") || l.includes("Phase 2: Escalating") || l.includes("Phase 2: Script + MCP") || l.includes("Phase 1 succeeded") || l.includes("Phase 2 iteration") || l.includes("\u{1F916} Verdict:") || l.includes("\u{1F916} finish_fix:") || l.includes("Preflight:") || l.includes("native verification PASSED") || l.includes("native verification FAILED") || l.includes("Native run PASSED") || l.includes("Native run FAILED") || l.includes("APP_BUG") || l.includes("CONFIG_ISSUE") || l.includes("needs_browser") || l.includes("--- Attempt") || l.includes("Proven commands") || l.includes("Proven fix:") || l.includes("Same error repeated") || l.includes("aborting heal")
|
|
10569
10623
|
);
|
|
10570
10624
|
if (phaseLines.length > 0) {
|
|
10571
10625
|
console.log(`${C2.gray}\u2500\u2500 Heal summary \u2500\u2500${C2.reset}`);
|
|
@@ -10578,7 +10632,7 @@ ${finalVerifyResult.logs ?? ""}`;
|
|
|
10578
10632
|
);
|
|
10579
10633
|
return;
|
|
10580
10634
|
}
|
|
10581
|
-
if (runMode === "regression-suite") {
|
|
10635
|
+
if (runMode === "regression-suite" || runMode === "system-regression-suite") {
|
|
10582
10636
|
if (!run2.repoOwner || !run2.repoName) {
|
|
10583
10637
|
await postRunResult({ status: "ERROR", error: "No framework repo configured" });
|
|
10584
10638
|
log.fail("ERROR - No framework repo configured");
|
|
@@ -10600,8 +10654,8 @@ ${finalVerifyResult.logs ?? ""}`;
|
|
|
10600
10654
|
// A cancel, a newer claim or a shutdown kills the suite instead of letting it run on for up to 85 min.
|
|
10601
10655
|
signal
|
|
10602
10656
|
});
|
|
10603
|
-
const overall =
|
|
10604
|
-
(0,
|
|
10657
|
+
const overall = (0, import_regression_runner.regressionSuiteOverall)(result2);
|
|
10658
|
+
(0, import_runner_core9.throwIfRunAborted)();
|
|
10605
10659
|
await postRegressionResult(opts.serverUrl, headers, run2.runId, {
|
|
10606
10660
|
overall,
|
|
10607
10661
|
logs: result2.logs,
|
|
@@ -10631,9 +10685,9 @@ ${finalVerifyResult.logs ?? ""}`;
|
|
|
10631
10685
|
`${opts.serverUrl}/api/runner/runs/${run2.runId}/ai-audit-batch`,
|
|
10632
10686
|
headers
|
|
10633
10687
|
);
|
|
10634
|
-
const suiteQuotaFallback = (0,
|
|
10688
|
+
const suiteQuotaFallback = (0, import_runner_core7.createQuotaFallbackState)(run2.quotaFallbackModel);
|
|
10635
10689
|
for (const failedTest of failedTests) {
|
|
10636
|
-
(0,
|
|
10690
|
+
(0, import_runner_core9.throwIfRunAborted)();
|
|
10637
10691
|
const testStart = Date.now();
|
|
10638
10692
|
log.info(` Healing: ${failedTest.name ?? failedTest.testCaseId}`);
|
|
10639
10693
|
const isApiTest = Array.isArray(failedTest.tags) && failedTest.tags.includes("@api");
|
|
@@ -10701,12 +10755,12 @@ ${finalVerifyResult.logs ?? ""}`;
|
|
|
10701
10755
|
completionTokens: info.completionTokens,
|
|
10702
10756
|
totalTokens: info.totalTokens,
|
|
10703
10757
|
...hasTurnDetails ? buildAuditTurnPayload(info, run2.testVariables) : { requestBody: { iteration: info.iteration, toolCalls: info.toolCalls } },
|
|
10704
|
-
metadata: { groupId: info.groupId, testCaseId: failedTest.testCaseId, ...(0,
|
|
10758
|
+
metadata: { groupId: info.groupId, testCaseId: failedTest.testCaseId, ...(0, import_runner_core9.cliAuditMetadata)(info) }
|
|
10705
10759
|
});
|
|
10706
10760
|
};
|
|
10707
10761
|
try {
|
|
10708
10762
|
const envOverrideSuite = (process.env.USE_GROK_PER_TEST_HEAL ?? "").trim().toLowerCase();
|
|
10709
|
-
const useGrokHealSuite = envOverrideSuite === "true" ? true : envOverrideSuite === "false" ? false : (0,
|
|
10763
|
+
const useGrokHealSuite = envOverrideSuite === "true" ? true : envOverrideSuite === "false" ? false : (0, import_runner_core9.getAgenticEngine)(run2.healerModel) === "grok-build";
|
|
10710
10764
|
const suiteHealOpts = {
|
|
10711
10765
|
testName: failedTest.name ?? failedTest.testCaseId,
|
|
10712
10766
|
tags: failedTest.tags ?? [],
|
|
@@ -10714,6 +10768,10 @@ ${finalVerifyResult.logs ?? ""}`;
|
|
|
10714
10768
|
healingContext: failedTest.healingContext ?? null,
|
|
10715
10769
|
selectorRegistryContext: failedTest.selectorRegistryContext ?? null,
|
|
10716
10770
|
lastFailureError: failedTest.error,
|
|
10771
|
+
// A suite heal reports fixed / app-bug / config-issue / unfixable only (/live-suite-result has no
|
|
10772
|
+
// entry for a test that passed unchanged), so the original re-run preflight stays off here: a
|
|
10773
|
+
// TRANSIENT_PASS would otherwise be posted as "unfixable". Heal mode keeps the preflight.
|
|
10774
|
+
originalRerun: false,
|
|
10717
10775
|
testVariables: run2.testVariables,
|
|
10718
10776
|
onAICall: healAudit,
|
|
10719
10777
|
modelOverride: run2.healerModel,
|
|
@@ -10724,12 +10782,12 @@ ${finalVerifyResult.logs ?? ""}`;
|
|
|
10724
10782
|
liveBrowserHandle,
|
|
10725
10783
|
runNativeTest: runNativeWithLiveBrowser
|
|
10726
10784
|
};
|
|
10727
|
-
const suiteHttpFallback = run2.healerFallbackModel && (0,
|
|
10785
|
+
const suiteHttpFallback = run2.healerFallbackModel && (0, import_runner_core9.getAgenticEngine)(run2.healerFallbackModel) === "native" ? run2.healerFallbackModel : null;
|
|
10728
10786
|
let healResult;
|
|
10729
10787
|
if (useGrokHealSuite) {
|
|
10730
10788
|
try {
|
|
10731
10789
|
const grokLogin = await (0, import_fetch_cli_subscription.fetchCliSubscription)({ serverUrl: opts.serverUrl, runId: run2.runId, headers, cli: "grok-build", onLog: log.warn });
|
|
10732
|
-
healResult = await (0,
|
|
10790
|
+
healResult = await (0, import_runner_core9.executeGrokPerTestHeal)(
|
|
10733
10791
|
failedTest.playwrightCode,
|
|
10734
10792
|
failedTest.steps ?? [],
|
|
10735
10793
|
run2.baseUrl,
|
|
@@ -10769,8 +10827,8 @@ ${finalVerifyResult.logs ?? ""}`;
|
|
|
10769
10827
|
});
|
|
10770
10828
|
log.ok(` Fixed: ${failedTest.name}`);
|
|
10771
10829
|
} else {
|
|
10772
|
-
const quotaStopped = (0,
|
|
10773
|
-
const isAppBug = !quotaStopped && (0,
|
|
10830
|
+
const quotaStopped = (0, import_runner_core7.isHealAiQuotaError)(healResult.error);
|
|
10831
|
+
const isAppBug = !quotaStopped && (0, import_runner_core7.isAppBugHealError)(healResult.error);
|
|
10774
10832
|
const isConfigIssue = isConfigLikeReason(classifyErrorMessage(healResult.error ?? null));
|
|
10775
10833
|
const status = isAppBug ? "app-bug" : isConfigIssue ? "config-issue" : "unfixable";
|
|
10776
10834
|
const diagnosisMsg = isAppBug ? "Application error detected" : isConfigIssue ? "Configuration issue detected" : quotaStopped && healResult.error ? healResult.error : "Could not heal after AI attempts";
|
|
@@ -10794,7 +10852,7 @@ ${finalVerifyResult.logs ?? ""}`;
|
|
|
10794
10852
|
log.fail(` Heal crashed: ${failedTest.name}`);
|
|
10795
10853
|
}
|
|
10796
10854
|
}
|
|
10797
|
-
(0,
|
|
10855
|
+
(0, import_runner_core9.throwIfRunAborted)();
|
|
10798
10856
|
const lastResult = results[results.length - 1];
|
|
10799
10857
|
if (lastResult) {
|
|
10800
10858
|
fetch(`${opts.serverUrl}/api/runner/runs/${run2.runId}/live-suite-progress`, {
|
|
@@ -10823,7 +10881,7 @@ ${finalVerifyResult.logs ?? ""}`;
|
|
|
10823
10881
|
auditDrainWarning = drainErr instanceof Error ? drainErr.message : String(drainErr);
|
|
10824
10882
|
console.warn(`[runner] ${auditDrainWarning}`);
|
|
10825
10883
|
}
|
|
10826
|
-
(0,
|
|
10884
|
+
(0, import_runner_core9.throwIfRunAborted)();
|
|
10827
10885
|
const liveRes = await postLiveSuiteResult(opts.serverUrl, headers, run2.runId, {
|
|
10828
10886
|
overall,
|
|
10829
10887
|
duration: totalDuration,
|
|
@@ -10914,7 +10972,8 @@ ${finalVerifyResult.logs ?? ""}`;
|
|
|
10914
10972
|
const res = await fetchRetrying429(`${opts.serverUrl}/api/runner/runs/${run2.runId}/locator-discovery-result`, () => ({
|
|
10915
10973
|
method: "POST",
|
|
10916
10974
|
headers: { ...headers, "Content-Type": "application/json" },
|
|
10917
|
-
|
|
10975
|
+
// The epoch /claim returned: the server refuses a harvest whose run was re-claimed since (stale worker).
|
|
10976
|
+
body: JSON.stringify(withClaimEpoch({
|
|
10918
10977
|
bundles: harvest.bundles,
|
|
10919
10978
|
// A divergence mark is durable (it blocks auto re-harvest and can
|
|
10920
10979
|
// flip the test to needs_manual_authoring), so an environmentally
|
|
@@ -10932,7 +10991,7 @@ ${finalVerifyResult.logs ?? ""}`;
|
|
|
10932
10991
|
reachedOrder: harvest.reachedOrder,
|
|
10933
10992
|
logs: harvest.logs,
|
|
10934
10993
|
...harvest.environmentalError ? { environmentalError: harvest.environmentalError } : {}
|
|
10935
|
-
}),
|
|
10994
|
+
}, run2.claimEpoch)),
|
|
10936
10995
|
signal: AbortSignal.timeout(3e4)
|
|
10937
10996
|
}));
|
|
10938
10997
|
if (res.ok) {
|
|
@@ -10997,7 +11056,7 @@ ${finalVerifyResult.logs ?? ""}`;
|
|
|
10997
11056
|
}
|
|
10998
11057
|
return await res.json();
|
|
10999
11058
|
};
|
|
11000
|
-
ctx.smsReserve = (0,
|
|
11059
|
+
ctx.smsReserve = (0, import_runner_core9.createHttpSmsReserveTransport)({ serverUrl: opts.serverUrl, headers, runId: run2.runId });
|
|
11001
11060
|
}
|
|
11002
11061
|
const canonicalBase = await (0, import_base_url_check.canonicalDiscoveryBaseUrl)(
|
|
11003
11062
|
{ runMode, baseUrl: ctx.baseUrl, native: ctx.medium === "IOS_NATIVE" || ctx.medium === "ANDROID_NATIVE" || run2.framework === "APPIUM" }
|
|
@@ -11059,7 +11118,7 @@ ${finalVerifyResult.logs ?? ""}`;
|
|
|
11059
11118
|
...info.exitReason ? { exitReason: info.exitReason } : {},
|
|
11060
11119
|
// Engine cost, engine id, step, cache + reasoning tokens: same helper as the server's
|
|
11061
11120
|
// Fargate path (execute-run.ts), so the two rows agree.
|
|
11062
|
-
...(0,
|
|
11121
|
+
...(0, import_runner_core9.cliAuditMetadata)(info),
|
|
11063
11122
|
...ledgerAudit ? {
|
|
11064
11123
|
ledger: ledgerAudit.entries,
|
|
11065
11124
|
ledgerEntryCount: ledgerAudit.originalLength,
|
|
@@ -11143,7 +11202,8 @@ ${finalVerifyResult.logs ?? ""}`;
|
|
|
11143
11202
|
headers: { ...headers, "Content-Type": "application/json" },
|
|
11144
11203
|
// Framework-import runs span MANY features in one Session, so each test
|
|
11145
11204
|
// carries its own suiteName; per-feature deep runs fall back to ctx.featureName.
|
|
11146
|
-
|
|
11205
|
+
// claimEpoch: the server fences each save to this claim (a re-claimed run refuses it with 409).
|
|
11206
|
+
body: JSON.stringify(withClaimEpoch({ ...test, suiteName: test.suiteName ?? ctx.featureName, phase }, run2.claimEpoch)),
|
|
11147
11207
|
signal: AbortSignal.timeout(3e4)
|
|
11148
11208
|
});
|
|
11149
11209
|
if (res.status === 402) throw new import_discovery.PlanLimitReached();
|
|
@@ -11300,7 +11360,8 @@ ${finalVerifyResult.logs ?? ""}`;
|
|
|
11300
11360
|
healthObservations: rest.healthObservations
|
|
11301
11361
|
},
|
|
11302
11362
|
// Only send tests that were NOT confirmed streamed to avoid duplicates
|
|
11303
|
-
tests: rest.tests.filter((t) => !streamedTestKeys.has(fingerprintTest(t)))
|
|
11363
|
+
tests: rest.tests.filter((t) => !streamedTestKeys.has(fingerprintTest(t))),
|
|
11364
|
+
...discoveryRenderTypeFields({ appRenderType: ctx.appRenderType })
|
|
11304
11365
|
}, logs2, { omitScreenshots: isNativeDiscovery });
|
|
11305
11366
|
};
|
|
11306
11367
|
const acquireLoginBrowser = async () => {
|
|
@@ -11602,7 +11663,7 @@ ${line}`)
|
|
|
11602
11663
|
};
|
|
11603
11664
|
} else if (ctx.frameworkImport?.kind === "app-map") {
|
|
11604
11665
|
log.info("Building the app map.");
|
|
11605
|
-
discoveryResult = await (0,
|
|
11666
|
+
discoveryResult = await (0, import_runner_core9.runAppMapOnRunner)(ctx, { onLog: (line) => log.info(line) });
|
|
11606
11667
|
} else if (ctx.frameworkImport?.kind === "repo-enrichment") {
|
|
11607
11668
|
const frameworkImport = ctx.frameworkImport;
|
|
11608
11669
|
const primaryCli = isRepositoryCli(frameworkImport.repoHuntCli) ? frameworkImport.repoHuntCli : "claude-code";
|
|
@@ -11627,7 +11688,7 @@ ${line}`)
|
|
|
11627
11688
|
})
|
|
11628
11689
|
});
|
|
11629
11690
|
} else {
|
|
11630
|
-
const runRepoImport = ctx.frameworkImport?.kind === "repo-analysis" ?
|
|
11691
|
+
const runRepoImport = ctx.frameworkImport?.kind === "repo-analysis" ? import_runner_core9.runRepoAnalysisOnRunner : import_framework_import_runner.runFrameworkImportOnRunner;
|
|
11631
11692
|
discoveryResult = await runRepoImport(ctx, { onTestGenerated, acquireLoginBrowser, onAudit: discoveryAudit }, {
|
|
11632
11693
|
archiveUrl: ctx.frameworkImport?.source === "upload" ? `${opts.serverUrl}/api/runner/runs/${run2.runId}/import-archive` : void 0,
|
|
11633
11694
|
archiveAuthHeader: headers.Authorization,
|
|
@@ -11689,7 +11750,7 @@ ${line}`)
|
|
|
11689
11750
|
}
|
|
11690
11751
|
const resolveCliSubscription = (engine, logLine) => (0, import_fetch_cli_subscription.fetchCliSubscription)({ serverUrl: opts.serverUrl, runId: run2.runId, headers, cli: engine, onLog: logLine });
|
|
11691
11752
|
const claimNonce = claimNonceFor(run2.runId);
|
|
11692
|
-
savedSignIn = ["survey", "deep", "full"].includes(ctx.mode) && claimNonce && typeof run2.claimEpoch === "number" ? await (0,
|
|
11753
|
+
savedSignIn = ["survey", "deep", "full"].includes(ctx.mode) && claimNonce && typeof run2.claimEpoch === "number" ? await (0, import_runner_core9.openSavedSignInSession)((0, import_runner_core9.createSavedSignInClient)({
|
|
11693
11754
|
serverUrl: opts.serverUrl,
|
|
11694
11755
|
runId: run2.runId,
|
|
11695
11756
|
claimEpoch: run2.claimEpoch,
|
|
@@ -11709,7 +11770,7 @@ ${line}`)
|
|
|
11709
11770
|
if (ctx.repoBrief) discoveryResult.repoBrief = ctx.repoBrief;
|
|
11710
11771
|
if (ctx.repoRouteManifest) discoveryResult.repoRouteManifest = ctx.repoRouteManifest;
|
|
11711
11772
|
}
|
|
11712
|
-
discoveryResult = (0,
|
|
11773
|
+
discoveryResult = (0, import_runner_core9.redactDiscoveryCaptureNeedles)(discoveryResult, {
|
|
11713
11774
|
...run2.testVariables,
|
|
11714
11775
|
// The saved sign-in's values, with every one it rotated to during the run.
|
|
11715
11776
|
...savedSignIn?.loaded ? savedSignIn.secrets() : {}
|
|
@@ -11825,7 +11886,8 @@ ${line}`)
|
|
|
11825
11886
|
cliLimit: (0, import_cli_limit.cliLimitFromError)(err),
|
|
11826
11887
|
streamedTestCount: streamedTestKeys.size,
|
|
11827
11888
|
streamedTestCaseIds: Array.from(streamedTestCaseIds),
|
|
11828
|
-
exploreAlreadySaved: explorePosted
|
|
11889
|
+
exploreAlreadySaved: explorePosted,
|
|
11890
|
+
...discoveryRenderTypeFields({ err })
|
|
11829
11891
|
}, `Fatal error: ${msg}${drainMsg}`);
|
|
11830
11892
|
await postShedOn413((body) => fetchRetrying429(`${opts.serverUrl}/api/runner/runs/${run2.runId}/discovery-result`, () => ({
|
|
11831
11893
|
method: "POST",
|
|
@@ -11866,7 +11928,7 @@ ${line}`)
|
|
|
11866
11928
|
completionTokens: info.completionTokens,
|
|
11867
11929
|
totalTokens: info.totalTokens,
|
|
11868
11930
|
...hasTurnDetails ? buildAuditTurnPayload(info, run2.testVariables) : { requestBody: { iteration: info.iteration, toolCalls: info.toolCalls } },
|
|
11869
|
-
metadata: { groupId: info.groupId, iteration: info.iteration, phase: "capture", featureName: ctx.featureName, ...(0,
|
|
11931
|
+
metadata: { groupId: info.groupId, iteration: info.iteration, phase: "capture", featureName: ctx.featureName, ...(0, import_runner_core9.cliAuditMetadata)(info) }
|
|
11870
11932
|
});
|
|
11871
11933
|
};
|
|
11872
11934
|
const logBuffer = [];
|
|
@@ -12041,7 +12103,7 @@ ${line}`)
|
|
|
12041
12103
|
verdict: "fix",
|
|
12042
12104
|
recovered: true,
|
|
12043
12105
|
priorStepsHash: run2.priorStepsHash
|
|
12044
|
-
});
|
|
12106
|
+
}, run2.claimEpoch);
|
|
12045
12107
|
log.ok("\u{1FA7A} Reproduce PASSED \u2014 test recovered (flaky); promotion posted");
|
|
12046
12108
|
} catch (patchErr) {
|
|
12047
12109
|
log.warn(`Failed to post recovered heal result: ${patchErr instanceof Error ? patchErr.message : String(patchErr)}`);
|
|
@@ -12083,7 +12145,7 @@ ${line}`)
|
|
|
12083
12145
|
completionTokens: info.completionTokens,
|
|
12084
12146
|
totalTokens: info.totalTokens,
|
|
12085
12147
|
...hasTurnDetails ? buildAuditTurnPayload(info, run2.testVariables) : { requestBody: { iteration: info.iteration, toolCalls: info.toolCalls } },
|
|
12086
|
-
metadata: { groupId: info.groupId, iteration: info.iteration, testName: run2.testCaseId, platform: "mobile", ...(0,
|
|
12148
|
+
metadata: { groupId: info.groupId, iteration: info.iteration, testName: run2.testCaseId, platform: "mobile", ...(0, import_runner_core9.cliAuditMetadata)(info) }
|
|
12087
12149
|
});
|
|
12088
12150
|
},
|
|
12089
12151
|
runLiveHeal: async (a) => {
|
|
@@ -12278,7 +12340,7 @@ ${line}`)
|
|
|
12278
12340
|
extraTags: heal.extraTags,
|
|
12279
12341
|
removeTags: heal.removeTags,
|
|
12280
12342
|
priorStepsHash: run2.priorStepsHash
|
|
12281
|
-
});
|
|
12343
|
+
}, run2.claimEpoch);
|
|
12282
12344
|
log.ok(`\u{1FA7A} Mobile heal patch posted for test ${run2.testCaseId ?? "(unknown)"} (verdict: ${heal.verdict})`);
|
|
12283
12345
|
} catch (patchErr) {
|
|
12284
12346
|
log.warn(`Failed to post mobile heal patch: ${patchErr instanceof Error ? patchErr.message : String(patchErr)}`);
|
|
@@ -12425,6 +12487,7 @@ ${result2.logs}`);
|
|
|
12425
12487
|
if (storageStatePath) {
|
|
12426
12488
|
log.info(`Using auth state for project ${projectId}`);
|
|
12427
12489
|
}
|
|
12490
|
+
const authSession = (0, import_runner_core8.sharedAuthSessionStamp)(storageStatePath, captureAuth, sharedAuthInjectEnabled());
|
|
12428
12491
|
const runAuthPath = captureAuth ? authPaths?.capturePath : void 0;
|
|
12429
12492
|
const result = await (async () => {
|
|
12430
12493
|
try {
|
|
@@ -12483,49 +12546,25 @@ ${result2.logs}`);
|
|
|
12483
12546
|
runStatus = "ERROR";
|
|
12484
12547
|
log.warn(`\u26A0 CONFIG_ISSUE from native execution - posting ERROR (not FAILED) to skip heal/quarantine`);
|
|
12485
12548
|
}
|
|
12486
|
-
|
|
12487
|
-
|
|
12488
|
-
|
|
12489
|
-
|
|
12490
|
-
|
|
12491
|
-
|
|
12492
|
-
|
|
12493
|
-
|
|
12494
|
-
|
|
12495
|
-
|
|
12496
|
-
|
|
12497
|
-
|
|
12498
|
-
|
|
12499
|
-
|
|
12500
|
-
|
|
12501
|
-
|
|
12502
|
-
|
|
12503
|
-
tags: run2.tags ?? [],
|
|
12504
|
-
authStateExpected,
|
|
12505
|
-
authStateInjected: !!storageStatePath,
|
|
12506
|
-
needsStandingHeaders: normalRunAuthPlan.needsStandingHeaders,
|
|
12507
|
-
standingHeadersInjected: !!result.authDelivery?.standingHeadersInjected,
|
|
12508
|
-
error: runError,
|
|
12509
|
-
logs: result.logs,
|
|
12510
|
-
stepResults: result.stepResults,
|
|
12511
|
-
apiCalls: result.apiCalls
|
|
12512
|
-
});
|
|
12513
|
-
if (cold.detected) {
|
|
12514
|
-
runStatus = "ERROR";
|
|
12515
|
-
runError = coldSessionConfigMessage("Authenticated @api test ran", cold.reason, cold.evidence);
|
|
12516
|
-
log.warn(`\u26A0 Cold @api session detected - posting ERROR/CONFIG_ISSUE (not FAILED) to skip heal + avoid a false app_bug`);
|
|
12517
|
-
}
|
|
12518
|
-
}
|
|
12519
|
-
if (runStatus === "PASSED" && Array.isArray(run2.tags) && run2.tags.includes("@api")) {
|
|
12520
|
-
const skipCoverage = detectSkippedApiCoverage(result.stepResults ?? []);
|
|
12521
|
-
if (skipCoverage.detected) {
|
|
12522
|
-
runStatus = "ERROR";
|
|
12523
|
-
runError = skipCoverage.error;
|
|
12524
|
-
log.warn(`\u26A0 ${skipCoverage.logLine}`);
|
|
12525
|
-
}
|
|
12526
|
-
}
|
|
12549
|
+
const authRelabel = (0, import_runner_core8.authReadinessRelabel)({
|
|
12550
|
+
status: runStatus,
|
|
12551
|
+
error: runError,
|
|
12552
|
+
testName: run2.testName ?? run2.testCaseId ?? void 0,
|
|
12553
|
+
tags: run2.tags ?? [],
|
|
12554
|
+
logs: result.logs,
|
|
12555
|
+
stepResults: result.stepResults,
|
|
12556
|
+
apiCalls: result.apiCalls,
|
|
12557
|
+
authStateExpected,
|
|
12558
|
+
authStateInjected: !!storageStatePath,
|
|
12559
|
+
needsStandingHeaders: normalRunAuthPlan.needsStandingHeaders,
|
|
12560
|
+
standingHeadersInjected: !!result.authDelivery?.standingHeadersInjected
|
|
12561
|
+
});
|
|
12562
|
+
if (authRelabel) {
|
|
12563
|
+
runStatus = authRelabel.status;
|
|
12564
|
+
runError = authRelabel.error;
|
|
12565
|
+
log.warn(authRelabel.logLine);
|
|
12527
12566
|
}
|
|
12528
|
-
(0,
|
|
12567
|
+
(0, import_runner_core9.throwIfRunAborted)();
|
|
12529
12568
|
await postRunResult({
|
|
12530
12569
|
status: runStatus,
|
|
12531
12570
|
stepResults: result.stepResults,
|
|
@@ -12542,7 +12581,8 @@ ${result2.logs}`);
|
|
|
12542
12581
|
expectedFailure: result.passed ? result.expectedFailure === true : void 0,
|
|
12543
12582
|
expectedFailureReason: result.passed && result.expectedFailure ? result.expectedFailureReason : void 0,
|
|
12544
12583
|
unexpectedPass: result.passed ? void 0 : result.unexpectedPass === true,
|
|
12545
|
-
bugTestBroken: result.passed ? void 0 : result.bugTestBroken === true
|
|
12584
|
+
bugTestBroken: result.passed ? void 0 : result.bugTestBroken === true,
|
|
12585
|
+
authSession
|
|
12546
12586
|
});
|
|
12547
12587
|
if (result.passed) {
|
|
12548
12588
|
log.ok(`PASSED (${duration}ms)${result.suspiciousPass ? ` \u26A0 suspicious-pass (viewport ${Math.round(result.suspiciousPass.emptyRatio * 100)}% blank)` : ""}`);
|
|
@@ -12558,14 +12598,14 @@ ${result2.logs}`);
|
|
|
12558
12598
|
}
|
|
12559
12599
|
} catch (err) {
|
|
12560
12600
|
if (err instanceof RunNoLongerActiveError) throw err;
|
|
12561
|
-
const abortOutcome = (0,
|
|
12601
|
+
const abortOutcome = (0, import_runner_core9.runAbortOutcome)(err, signal);
|
|
12562
12602
|
if (abortOutcome === "discard") {
|
|
12563
|
-
throw new RunNoLongerActiveError((0,
|
|
12603
|
+
throw new RunNoLongerActiveError((0, import_runner_core9.isSupersededRunAbort)(signal) ? `Run ${run2.runId} was superseded by a newer claim` : `Run ${run2.runId} was cancelled on the server`);
|
|
12564
12604
|
}
|
|
12565
12605
|
if (abortOutcome === "interrupt") {
|
|
12566
12606
|
await postRunResult({
|
|
12567
12607
|
status: "ERROR",
|
|
12568
|
-
error:
|
|
12608
|
+
error: import_runner_core9.RUNNER_SHUTDOWN_INTERRUPT_ERROR,
|
|
12569
12609
|
duration: Date.now() - startTime
|
|
12570
12610
|
});
|
|
12571
12611
|
throw err;
|
|
@@ -13042,7 +13082,7 @@ async function startRunner(opts) {
|
|
|
13042
13082
|
}
|
|
13043
13083
|
});
|
|
13044
13084
|
};
|
|
13045
|
-
const removeSupersededRunFetchFence = (0,
|
|
13085
|
+
const removeSupersededRunFetchFence = (0, import_runner_core9.installSupersededRunFetchFence)(serverUrl);
|
|
13046
13086
|
const heartbeat = setInterval(sendHeartbeat, 3e4);
|
|
13047
13087
|
sendHeartbeat();
|
|
13048
13088
|
let shuttingDown = false;
|
|
@@ -13054,7 +13094,7 @@ async function startRunner(opts) {
|
|
|
13054
13094
|
headers: { ...headers, "Content-Type": "application/json" },
|
|
13055
13095
|
body: JSON.stringify(withClaimEpoch({
|
|
13056
13096
|
status: "ERROR",
|
|
13057
|
-
error:
|
|
13097
|
+
error: import_runner_core9.RUNNER_SHUTDOWN_INTERRUPT_ERROR
|
|
13058
13098
|
}, claimEpoch)),
|
|
13059
13099
|
signal: AbortSignal.timeout(5e3)
|
|
13060
13100
|
}));
|
|
@@ -13070,7 +13110,7 @@ async function startRunner(opts) {
|
|
|
13070
13110
|
setTimeout(() => {
|
|
13071
13111
|
console.error("Shutdown grace elapsed \u2014 forcing exit");
|
|
13072
13112
|
process.exit(0);
|
|
13073
|
-
}, (0,
|
|
13113
|
+
}, (0, import_runner_core9.shutdownForceExitMs)(SHUTDOWN_FINISH_MS, SHUTDOWN_GRACE_MS)).unref();
|
|
13074
13114
|
clearInterval(heartbeat);
|
|
13075
13115
|
heartbeatKeepalive.stop();
|
|
13076
13116
|
if (liveBrowserHeartbeatTimer) {
|
|
@@ -13088,7 +13128,7 @@ async function startRunner(opts) {
|
|
|
13088
13128
|
if (claimPending) {
|
|
13089
13129
|
await Promise.race([claimPending, new Promise((resolve3) => setTimeout(resolve3, SHUTDOWN_FINISH_MS))]);
|
|
13090
13130
|
}
|
|
13091
|
-
await (0,
|
|
13131
|
+
await (0, import_runner_core9.drainAbortedRuns)({
|
|
13092
13132
|
finishMs: SHUTDOWN_FINISH_MS,
|
|
13093
13133
|
graceMs: SHUTDOWN_GRACE_MS,
|
|
13094
13134
|
inFlight: activeWebRuns,
|
|
@@ -13105,7 +13145,7 @@ async function startRunner(opts) {
|
|
|
13105
13145
|
await stopAppiumServer();
|
|
13106
13146
|
}
|
|
13107
13147
|
await browserPool.shutdown();
|
|
13108
|
-
await (0,
|
|
13148
|
+
await (0, import_runner_core10.closeLocalGoogleBroker)().catch(() => {
|
|
13109
13149
|
});
|
|
13110
13150
|
removeSupersededRunFetchFence();
|
|
13111
13151
|
log.info("Runner stopped.");
|
|
@@ -13167,7 +13207,7 @@ async function startRunner(opts) {
|
|
|
13167
13207
|
const abortSupersededRun = (runId, status, serverEpoch) => {
|
|
13168
13208
|
const controller = activeWebRuns.get(runId);
|
|
13169
13209
|
const already = !controller || controller.signal.aborted;
|
|
13170
|
-
if (controller) (0,
|
|
13210
|
+
if (controller) (0, import_runner_core9.abortRun)(controller, "superseded", runId);
|
|
13171
13211
|
activeMobileRuns.get(runId)?.triggerAbort(`was superseded by a newer claim (epoch ${serverEpoch ?? "?"}, status ${status})`);
|
|
13172
13212
|
if (!already) {
|
|
13173
13213
|
log.warn(`Run ${runId} was superseded by a newer claim (server epoch ${serverEpoch ?? "?"}, ours ${claimedRunEpochs.get(runId) ?? "?"}) \u2014 stopping it; this runner posts nothing more for it.`);
|
|
@@ -13183,7 +13223,7 @@ async function startRunner(opts) {
|
|
|
13183
13223
|
(runId, status) => {
|
|
13184
13224
|
const controller = activeWebRuns.get(runId);
|
|
13185
13225
|
const already = !controller || controller.signal.aborted;
|
|
13186
|
-
if (controller) (0,
|
|
13226
|
+
if (controller) (0, import_runner_core9.abortRun)(controller, "cancelled", runId);
|
|
13187
13227
|
activeMobileRuns.get(runId)?.triggerAbort(`was cancelled server-side (status ${status})`);
|
|
13188
13228
|
if (!already) log.warn(`Run ${runId} was cancelled server-side (status ${status}) \u2014 stopping it.`);
|
|
13189
13229
|
}
|
|
@@ -13437,9 +13477,9 @@ async function startRunner(opts) {
|
|
|
13437
13477
|
runWatchdog.start();
|
|
13438
13478
|
}
|
|
13439
13479
|
activeWebRuns.set(run2.runId, runAbort);
|
|
13440
|
-
const runWritePolicy = (0,
|
|
13441
|
-
const runSignInSecrets = runWritePolicy ? (0,
|
|
13442
|
-
(0,
|
|
13480
|
+
const runWritePolicy = (0, import_runner_core9.parseRunWritePolicy)(run2.runWritePolicy);
|
|
13481
|
+
const runSignInSecrets = runWritePolicy ? (0, import_runner_core9.signInSecretsOf)({ ...run2.discoveryContext?.variableContext?.allTestVariables ?? {}, ...run2.testVariables ?? {} }) : [];
|
|
13482
|
+
(0, import_runner_core9.withRunWritePolicyScope)(runWritePolicy, () => (0, import_runner_core10.withLocalGoogleScope)({ baseUrl: run2.baseUrl, runId: run2.runId }, () => executeRun(run2, opts, headers, requestLiveBrowserHeartbeat, (testCaseId) => {
|
|
13443
13483
|
if (mobilePlatform) {
|
|
13444
13484
|
mobileHealHold.set(testCaseId, { expiry: Date.now() + MOBILE_HEAL_HOLD_MS, platform: mobilePlatform });
|
|
13445
13485
|
}
|
|
@@ -13447,11 +13487,11 @@ async function startRunner(opts) {
|
|
|
13447
13487
|
runsCompleted++;
|
|
13448
13488
|
}).catch((err) => {
|
|
13449
13489
|
const abortReason = runAbort.signal.reason;
|
|
13450
|
-
if ((0,
|
|
13490
|
+
if ((0, import_runner_core9.isRunAbortError)(abortReason) && abortReason.reason === "shutdown") {
|
|
13451
13491
|
log.info(`Run ${run2.runId} was interrupted by runner shutdown.`);
|
|
13452
13492
|
return;
|
|
13453
13493
|
}
|
|
13454
|
-
if ((0,
|
|
13494
|
+
if ((0, import_runner_core9.isSupersededRunAbort)(runAbort.signal)) {
|
|
13455
13495
|
log.info(`Run ${run2.runId} was superseded by a newer claim; this runner posted nothing more for it.`);
|
|
13456
13496
|
return;
|
|
13457
13497
|
}
|
|
@@ -13489,7 +13529,7 @@ async function startRunner(opts) {
|
|
|
13489
13529
|
const action = authRefusalAction({
|
|
13490
13530
|
consecutive: consecutiveAuthRefusals,
|
|
13491
13531
|
activeRuns,
|
|
13492
|
-
cloud:
|
|
13532
|
+
cloud: IS_CLOUD
|
|
13493
13533
|
});
|
|
13494
13534
|
if (action === "exit") {
|
|
13495
13535
|
log.fail(`${message} (the server refused it ${consecutiveAuthRefusals} times in a row)`);
|
|
@@ -13545,14 +13585,15 @@ async function checkRunnerLogin(serverUrl, token, fetchImpl = fetch) {
|
|
|
13545
13585
|
|
|
13546
13586
|
// src/services/sign-in-capture.ts
|
|
13547
13587
|
var import_browser_config3 = __toESM(require_browser_config());
|
|
13588
|
+
var import_google_local_signin = __toESM(require_google_local_signin());
|
|
13548
13589
|
import { chromium } from "playwright";
|
|
13549
13590
|
async function captureSignInInteractively(url, ready, opts = {}) {
|
|
13550
13591
|
const target = new URL(url);
|
|
13551
13592
|
let browser;
|
|
13552
13593
|
try {
|
|
13553
|
-
browser = await chromium.launch({ headless: false, ignoreDefaultArgs: ["--enable-automation"], channel: "chrome", env: (0, import_browser_config3.browserLaunchEnv)() });
|
|
13594
|
+
browser = await chromium.launch({ headless: false, ignoreDefaultArgs: ["--enable-automation"], args: [...import_google_local_signin.SIGN_IN_WINDOW_ARGS], channel: "chrome", env: (0, import_browser_config3.browserLaunchEnv)() });
|
|
13554
13595
|
} catch {
|
|
13555
|
-
browser = await chromium.launch({ headless: false, ignoreDefaultArgs: ["--enable-automation"], env: (0, import_browser_config3.browserLaunchEnv)() });
|
|
13596
|
+
browser = await chromium.launch({ headless: false, ignoreDefaultArgs: ["--enable-automation"], args: [...import_google_local_signin.SIGN_IN_WINDOW_ARGS], env: (0, import_browser_config3.browserLaunchEnv)() });
|
|
13556
13597
|
}
|
|
13557
13598
|
return captureFromBrowser(browser, target, ready, opts.timeoutMs ?? SIGN_IN_TIMEOUT_MS);
|
|
13558
13599
|
}
|
|
@@ -13918,7 +13959,7 @@ program.command("start").description("Start the local test runner").option("--to
|
|
|
13918
13959
|
console.warn("\u26A0 --tunnel has no effect without --mobile (the mobile bridge it exposes only starts with --mobile). Ignoring --tunnel.");
|
|
13919
13960
|
}
|
|
13920
13961
|
{
|
|
13921
|
-
const { hasLocalGoogleLogin, GOOGLE_LOGIN_COMMAND } = await import("./dist-
|
|
13962
|
+
const { hasLocalGoogleLogin, GOOGLE_LOGIN_COMMAND } = await import("./dist-VTPBY2C4.mjs");
|
|
13922
13963
|
if (process.stdout.isTTY && !await hasLocalGoogleLogin()) {
|
|
13923
13964
|
console.log(`Tip: for apps with "Continue with Google", sign in to Google once on this machine: ${GOOGLE_LOGIN_COMMAND}`);
|
|
13924
13965
|
}
|
|
@@ -13948,7 +13989,7 @@ async function askLine(question) {
|
|
|
13948
13989
|
}
|
|
13949
13990
|
}
|
|
13950
13991
|
program.command("google-login").description('Sign in to a TEST Google account once on this machine, for tests of apps with "Continue with Google"').option("--site <url...>", "The app address(es) this sign-in may be used for, e.g. https://app.example.com").option("--allow-file-storage", "Keep the sign-in in a plain file (only you can read it) when the system keychain is not available").option("--yes", "Confirm without asking that this is a dedicated test Google account").action(async (opts) => {
|
|
13951
|
-
const { signInLocalGoogle, LOCAL_GOOGLE_WARNING, grantedSite, defaultStoreKind } = await import("./dist-
|
|
13992
|
+
const { signInLocalGoogle, LOCAL_GOOGLE_WARNING, grantedSite, defaultStoreKind } = await import("./dist-VTPBY2C4.mjs");
|
|
13952
13993
|
console.log(LOCAL_GOOGLE_WARNING);
|
|
13953
13994
|
if (!opts.yes) {
|
|
13954
13995
|
const answer = await askLine('Is this a dedicated test Google account? Type "yes" to continue: ');
|
|
@@ -13983,7 +14024,7 @@ program.command("google-login").description('Sign in to a TEST Google account on
|
|
|
13983
14024
|
}
|
|
13984
14025
|
});
|
|
13985
14026
|
program.command("google-allow-site <url>").description("Let the Google sign-in on this machine be used for one more app address").action(async (url) => {
|
|
13986
|
-
const { allowLocalGoogleSites, grantedSite } = await import("./dist-
|
|
14027
|
+
const { allowLocalGoogleSites, grantedSite } = await import("./dist-VTPBY2C4.mjs");
|
|
13987
14028
|
const site = grantedSite(url);
|
|
13988
14029
|
if (!site) {
|
|
13989
14030
|
console.error("\u2717 Not a web address: give it like https://app.example.com");
|
|
@@ -13997,7 +14038,7 @@ program.command("google-allow-site <url>").description("Let the Google sign-in o
|
|
|
13997
14038
|
console.log(`\u2713 Tests on this machine may now use the Google sign-in for: ${sites.join(", ")}`);
|
|
13998
14039
|
});
|
|
13999
14040
|
program.command("google-logout").description("Forget the Google sign-in saved on this machine").action(async () => {
|
|
14000
|
-
const { deleteLocalGoogleLogin } = await import("./dist-
|
|
14041
|
+
const { deleteLocalGoogleLogin } = await import("./dist-VTPBY2C4.mjs");
|
|
14001
14042
|
await deleteLocalGoogleLogin();
|
|
14002
14043
|
console.log("\u2713 Google sign-in removed from this machine");
|
|
14003
14044
|
});
|