@quikit/quikpilot-runner 8.2.6 → 8.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +108 -19
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -73,10 +73,11 @@ async function describeQuikTrackFailure(res) {
|
|
|
73
73
|
|
|
74
74
|
// src/callback.ts
|
|
75
75
|
async function reportBack(params) {
|
|
76
|
-
const { callbackUrl, runToken, status, branch, warnings, targeting, baselineFindings } = params;
|
|
76
|
+
const { callbackUrl, runToken, status, branch, warnings, targeting, baselineFindings, unresolved } = params;
|
|
77
77
|
const body = { status };
|
|
78
78
|
if (branch) body.branch = branch;
|
|
79
79
|
if (warnings && warnings.length > 0) body.warnings = warnings;
|
|
80
|
+
if (unresolved && unresolved.length > 0) body.unresolved = unresolved;
|
|
80
81
|
if (targeting) body.targeting = targeting;
|
|
81
82
|
if (baselineFindings) body.baselineFindings = baselineFindings;
|
|
82
83
|
const res = await fetch(callbackUrl, {
|
|
@@ -250,6 +251,16 @@ function findDeniedContextPath(contextPaths, patterns) {
|
|
|
250
251
|
return null;
|
|
251
252
|
}
|
|
252
253
|
|
|
254
|
+
// src/runtimeReplyError.ts
|
|
255
|
+
var RuntimeReplyError = class extends Error {
|
|
256
|
+
summary;
|
|
257
|
+
constructor(message, summary) {
|
|
258
|
+
super(message);
|
|
259
|
+
this.name = "RuntimeReplyError";
|
|
260
|
+
this.summary = summary;
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
|
|
253
264
|
// src/envelope.ts
|
|
254
265
|
var HARD_FAILURE_CODES = /* @__PURE__ */ new Set(["SEMANTIC_MISMATCH", "CONTRACT_REJECTED"]);
|
|
255
266
|
function checkHardFailure(envelope, useCase, target) {
|
|
@@ -257,17 +268,25 @@ function checkHardFailure(envelope, useCase, target) {
|
|
|
257
268
|
if (typeof code === "string" && HARD_FAILURE_CODES.has(code)) {
|
|
258
269
|
const detail = envelope.error ?? envelope.normalizedText ?? "no detail";
|
|
259
270
|
const where = target ? ` for ${target}` : "";
|
|
260
|
-
throw new
|
|
271
|
+
throw new RuntimeReplyError(
|
|
272
|
+
`${useCase} failed${where} (${code}, not retried): ${detail}`,
|
|
273
|
+
`${useCase} failed${where} (${code}, not retried)`
|
|
274
|
+
);
|
|
261
275
|
}
|
|
262
276
|
}
|
|
263
277
|
function unwrapExecute(envelope, useCase) {
|
|
264
278
|
if (envelope?.success === false) {
|
|
265
|
-
|
|
266
|
-
|
|
279
|
+
const code = envelope.errorCode ?? "UNKNOWN";
|
|
280
|
+
throw new RuntimeReplyError(
|
|
281
|
+
`${useCase} failed: ${code} \u2014 ${envelope.error ?? envelope.normalizedText ?? "no message"}`,
|
|
282
|
+
`${useCase} failed: ${code}`
|
|
267
283
|
);
|
|
268
284
|
}
|
|
269
285
|
if (!envelope?.structuredJson || typeof envelope.structuredJson !== "object") {
|
|
270
|
-
throw new
|
|
286
|
+
throw new RuntimeReplyError(
|
|
287
|
+
`${useCase} returned no structuredJson: ${JSON.stringify(envelope)}`,
|
|
288
|
+
`${useCase} returned no structuredJson`
|
|
289
|
+
);
|
|
271
290
|
}
|
|
272
291
|
return envelope.structuredJson;
|
|
273
292
|
}
|
|
@@ -419,7 +438,7 @@ function narrowJudgedByForTarget(pool, targets, codeTargetPath) {
|
|
|
419
438
|
function toWireJudgedBy(pool) {
|
|
420
439
|
return pool.map(({ automationId: _automationId, ...rest }) => rest);
|
|
421
440
|
}
|
|
422
|
-
async function runCodegen(gateway, targets, workspaceListing, requiredTests, deniedPaths, judgedByPool, warnings, contextLog) {
|
|
441
|
+
async function runCodegen(gateway, targets, workspaceListing, requiredTests, deniedPaths, judgedByPool, warnings, contextLog, outOfScope = []) {
|
|
423
442
|
const results = [];
|
|
424
443
|
const listing = new Set(workspaceListing);
|
|
425
444
|
for (const target of targets) {
|
|
@@ -468,6 +487,9 @@ async function runCodegen(gateway, targets, workspaceListing, requiredTests, den
|
|
|
468
487
|
if (deniedPaths.length > 0) {
|
|
469
488
|
contextData.deniedPaths = deniedPaths;
|
|
470
489
|
}
|
|
490
|
+
if (outOfScope.length > 0) {
|
|
491
|
+
contextData.outOfScope = outOfScope;
|
|
492
|
+
}
|
|
471
493
|
if (target.automationIds && target.automationIds.length > 0) {
|
|
472
494
|
const wanted = new Set(target.automationIds);
|
|
473
495
|
contextData.requiredTests = requiredTests.filter((tc) => wanted.has(tc.automationId));
|
|
@@ -521,16 +543,23 @@ async function runCodegen(gateway, targets, workspaceListing, requiredTests, den
|
|
|
521
543
|
}
|
|
522
544
|
if (typeof envelope?.errorCode === "string" && NOT_RETRIED_CODES.has(envelope.errorCode)) {
|
|
523
545
|
const detail = envelope.error ? `: ${envelope.error}` : "";
|
|
524
|
-
throw new
|
|
546
|
+
throw new RuntimeReplyError(
|
|
547
|
+
`Codegen refused ${target.path}: ${envelope.errorCode} (not retried)${detail}`,
|
|
548
|
+
`Codegen refused ${target.path}: ${envelope.errorCode} (not retried)`
|
|
549
|
+
);
|
|
525
550
|
}
|
|
526
551
|
if (envelope?.errorCode === JUDGED_BY_TOO_LARGE_CODE) {
|
|
527
552
|
const detail = envelope.error ? `: ${envelope.error}` : "";
|
|
528
|
-
throw new
|
|
553
|
+
throw new RuntimeReplyError(
|
|
554
|
+
`Codegen refused ${target.path}: ${envelope.errorCode} (after narrowing)${detail}`,
|
|
555
|
+
`Codegen refused ${target.path}: ${envelope.errorCode} (after narrowing)`
|
|
556
|
+
);
|
|
529
557
|
}
|
|
530
558
|
const body = unwrapExecute(envelope, "pilot.codegen");
|
|
531
559
|
if (body.outcome !== "GENERATED" || typeof body.newContent !== "string") {
|
|
532
|
-
throw new
|
|
533
|
-
`Codegen refused ${target.path}${body.refusalReason ? `: ${body.refusalReason}` : ""}
|
|
560
|
+
throw new RuntimeReplyError(
|
|
561
|
+
`Codegen refused ${target.path}${body.refusalReason ? `: ${body.refusalReason}` : ""}`,
|
|
562
|
+
`Codegen refused ${target.path}`
|
|
534
563
|
);
|
|
535
564
|
}
|
|
536
565
|
if (!isNewFile) {
|
|
@@ -1038,14 +1067,27 @@ var MAX_FAILURE_TEXT = 500;
|
|
|
1038
1067
|
function unescapeXmlEntities(s) {
|
|
1039
1068
|
return s.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/&/g, "&");
|
|
1040
1069
|
}
|
|
1070
|
+
var FAILURE_FILE_LINE_RE = /^[ \t]*File:[ \t]*(\S[^\r\n]*?:\d+(?::\d+)?)[ \t]*$/m;
|
|
1071
|
+
var MAX_FAILURE_LOCATION = 300;
|
|
1072
|
+
function parseFailureLocation(body) {
|
|
1073
|
+
const found = FAILURE_FILE_LINE_RE.exec(unescapeXmlEntities(body));
|
|
1074
|
+
if (!found) return null;
|
|
1075
|
+
const path = normalizeSlashes(found[1]);
|
|
1076
|
+
const root = `${normalizeSlashes(process.cwd()).replace(/\/+$/, "")}/`;
|
|
1077
|
+
const shown = path.startsWith(root) ? path.slice(root.length) : path;
|
|
1078
|
+
return shown.length > MAX_FAILURE_LOCATION ? `${shown.slice(0, MAX_FAILURE_LOCATION)}\u2026` : shown;
|
|
1079
|
+
}
|
|
1041
1080
|
function extractFailureText(testcaseBody) {
|
|
1042
1081
|
const tag = FAILURE_TAG_RE.exec(testcaseBody);
|
|
1043
1082
|
if (!tag) return null;
|
|
1044
1083
|
const [, attrs = "", inner = ""] = tag;
|
|
1045
|
-
const
|
|
1084
|
+
const messageAttr = FAILURE_MESSAGE_ATTR_RE.exec(attrs)?.[1];
|
|
1085
|
+
const raw = (messageAttr ?? inner).trim();
|
|
1046
1086
|
if (!raw) return null;
|
|
1047
1087
|
const text = unescapeXmlEntities(raw);
|
|
1048
|
-
|
|
1088
|
+
const shortened = text.length > MAX_FAILURE_TEXT ? `${text.slice(0, MAX_FAILURE_TEXT)}\u2026` : text;
|
|
1089
|
+
const location = messageAttr === void 0 ? null : parseFailureLocation(inner);
|
|
1090
|
+
return location ? `${shortened} [at ${location}]` : shortened;
|
|
1049
1091
|
}
|
|
1050
1092
|
var RESULT_RANK = { pass: 0, skipped: 1, fail: 2 };
|
|
1051
1093
|
function parseJUnitOutcomes(xml) {
|
|
@@ -1347,6 +1389,36 @@ async function runBaselineCheck(params) {
|
|
|
1347
1389
|
}
|
|
1348
1390
|
}
|
|
1349
1391
|
|
|
1392
|
+
// src/failureReason.ts
|
|
1393
|
+
var MAX_FAILURE_REASON = 1e3;
|
|
1394
|
+
var MIN_SECRET_LENGTH = 8;
|
|
1395
|
+
var CREDENTIAL_PATTERNS = [
|
|
1396
|
+
/\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{20,}/g,
|
|
1397
|
+
/\bgithub_pat_[A-Za-z0-9_]{20,}/g,
|
|
1398
|
+
/x-access-token:[^@\s'"]+/gi,
|
|
1399
|
+
// Only the header form ("Authorization: basic <token>"), or a bearer value that contains a digit: a bare
|
|
1400
|
+
// "basic authentication" or "bearer of the news" is prose, not a credential.
|
|
1401
|
+
/\bauthorization:\s*(?:basic|bearer|token)\s+[^\s'"]{8,}/gi,
|
|
1402
|
+
/\bbearer\s+(?=[A-Za-z0-9._~+/=-]*\d)[A-Za-z0-9._~+/=-]{12,}/gi,
|
|
1403
|
+
// A signed JWT is three dot-separated segments; QuikPilot's own run token is an encrypted JWT (alg dir) of five, the
|
|
1404
|
+
// second one empty. The run token is also matched by exact value, but one that has expired or rotated is not the
|
|
1405
|
+
// current value, so its shape has to catch it.
|
|
1406
|
+
/\beyJ[A-Za-z0-9_-]{8,}(?:\.[A-Za-z0-9_-]*){2,4}/g
|
|
1407
|
+
];
|
|
1408
|
+
function failureSummary(err) {
|
|
1409
|
+
if (err instanceof RuntimeReplyError) return err.summary;
|
|
1410
|
+
return err instanceof Error ? err.message : String(err);
|
|
1411
|
+
}
|
|
1412
|
+
function describeFailureReason(message, secrets = []) {
|
|
1413
|
+
let text = message;
|
|
1414
|
+
for (const secret of secrets) {
|
|
1415
|
+
if (secret.length >= MIN_SECRET_LENGTH) text = text.split(secret).join("***");
|
|
1416
|
+
}
|
|
1417
|
+
for (const pattern of CREDENTIAL_PATTERNS) text = text.replace(pattern, "***");
|
|
1418
|
+
text = text.replace(/\s*\n\s*/g, " | ").trim();
|
|
1419
|
+
return text.length > MAX_FAILURE_REASON ? `${text.slice(0, MAX_FAILURE_REASON)}\u2026` : text;
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1350
1422
|
// src/scopeCheck.ts
|
|
1351
1423
|
var import_node_fs7 = require("node:fs");
|
|
1352
1424
|
var ROUTE_METHOD_RE = /\b(GET|POST|PUT|PATCH|DELETE)\b/i;
|
|
@@ -1461,7 +1533,7 @@ function validateTarget(target, workspaceRoot, workspaceListing, deniedPaths) {
|
|
|
1461
1533
|
);
|
|
1462
1534
|
}
|
|
1463
1535
|
}
|
|
1464
|
-
async function runTargeting(gateway, design, workspaceRoot, workspaceListing, requiredTests, deniedPaths, testDiscovery) {
|
|
1536
|
+
async function runTargeting(gateway, design, workspaceRoot, workspaceListing, requiredTests, deniedPaths, testDiscovery, outOfScope = []) {
|
|
1465
1537
|
const contextData = {
|
|
1466
1538
|
// NESTED, NOT FLATTENED — a second vocabulary for the design shape is exactly what
|
|
1467
1539
|
// QUIKTR-1034 cost.
|
|
@@ -1476,6 +1548,9 @@ async function runTargeting(gateway, design, workspaceRoot, workspaceListing, re
|
|
|
1476
1548
|
if (deniedPaths.length > 0) {
|
|
1477
1549
|
contextData.deniedPaths = deniedPaths;
|
|
1478
1550
|
}
|
|
1551
|
+
if (outOfScope.length > 0) {
|
|
1552
|
+
contextData.outOfScope = outOfScope;
|
|
1553
|
+
}
|
|
1479
1554
|
contextData.testDiscovery = testDiscovery;
|
|
1480
1555
|
const res = await fetchWithRetry(`${gateway.baseUrl}/ai/execute`, {
|
|
1481
1556
|
method: "POST",
|
|
@@ -1498,7 +1573,10 @@ async function runTargeting(gateway, design, workspaceRoot, workspaceListing, re
|
|
|
1498
1573
|
return { outcome: "NO_TARGET", reason: body.noTargetReason ?? null };
|
|
1499
1574
|
}
|
|
1500
1575
|
if (body.outcome !== "TARGETED" || !Array.isArray(body.targets)) {
|
|
1501
|
-
throw new
|
|
1576
|
+
throw new RuntimeReplyError(
|
|
1577
|
+
`pilot.targeting returned an unexpected shape: ${JSON.stringify(body)}`,
|
|
1578
|
+
"pilot.targeting returned an unexpected shape"
|
|
1579
|
+
);
|
|
1502
1580
|
}
|
|
1503
1581
|
const listing = new Set(workspaceListing);
|
|
1504
1582
|
for (const target of body.targets) {
|
|
@@ -1677,6 +1755,13 @@ async function run(env2) {
|
|
|
1677
1755
|
let branch;
|
|
1678
1756
|
let runToken;
|
|
1679
1757
|
const warnings = [];
|
|
1758
|
+
const failureReasons = [];
|
|
1759
|
+
const recordFailure = (err) => {
|
|
1760
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1761
|
+
console.error(message);
|
|
1762
|
+
const reason = describeFailureReason(failureSummary(err), runToken ? [runToken] : []);
|
|
1763
|
+
if (reason) failureReasons.push(reason);
|
|
1764
|
+
};
|
|
1680
1765
|
let targeted = false;
|
|
1681
1766
|
let generatedFilePaths = [];
|
|
1682
1767
|
let targetingResult;
|
|
@@ -1687,6 +1772,7 @@ async function run(env2) {
|
|
|
1687
1772
|
packet = await fetchPacket(env2.packetUrl, runToken, env2.githubRunId);
|
|
1688
1773
|
checkoutTargetBranch(packet.targetBranch);
|
|
1689
1774
|
const workspaceListing = listWorkspaceFiles(packet.workspaceRoot);
|
|
1775
|
+
const outOfScope = (packet.outOfScope ?? []).map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
1690
1776
|
const targeting = await runTargeting(
|
|
1691
1777
|
packet.gateway,
|
|
1692
1778
|
packet.design,
|
|
@@ -1694,7 +1780,8 @@ async function run(env2) {
|
|
|
1694
1780
|
workspaceListing,
|
|
1695
1781
|
packet.requiredTests,
|
|
1696
1782
|
packet.guardrails.deniedPaths,
|
|
1697
|
-
packet.testDiscovery
|
|
1783
|
+
packet.testDiscovery,
|
|
1784
|
+
outOfScope
|
|
1698
1785
|
);
|
|
1699
1786
|
targetingResult = targeting;
|
|
1700
1787
|
if (targeting.outcome === "NO_TARGET") {
|
|
@@ -1742,7 +1829,8 @@ async function run(env2) {
|
|
|
1742
1829
|
packet.guardrails.deniedPaths,
|
|
1743
1830
|
judgedByPool,
|
|
1744
1831
|
warnings,
|
|
1745
|
-
contextLog
|
|
1832
|
+
contextLog,
|
|
1833
|
+
outOfScope
|
|
1746
1834
|
)
|
|
1747
1835
|
]);
|
|
1748
1836
|
const depCheck = checkImportDependencies(
|
|
@@ -1805,7 +1893,7 @@ async function run(env2) {
|
|
|
1805
1893
|
}
|
|
1806
1894
|
} catch (err) {
|
|
1807
1895
|
hardFailure = true;
|
|
1808
|
-
|
|
1896
|
+
recordFailure(err);
|
|
1809
1897
|
}
|
|
1810
1898
|
if (!runToken) {
|
|
1811
1899
|
console.error("No run token \u2014 the OIDC exchange failed before QuikTrack could be reached.");
|
|
@@ -1842,7 +1930,7 @@ async function run(env2) {
|
|
|
1842
1930
|
branch = result.branch;
|
|
1843
1931
|
} catch (err) {
|
|
1844
1932
|
hardFailure = true;
|
|
1845
|
-
|
|
1933
|
+
recordFailure(err);
|
|
1846
1934
|
}
|
|
1847
1935
|
}
|
|
1848
1936
|
await reportBack({
|
|
@@ -1853,7 +1941,8 @@ async function run(env2) {
|
|
|
1853
1941
|
warnings,
|
|
1854
1942
|
// Omitted when targeting itself never produced a result (nothing exists to record).
|
|
1855
1943
|
targeting: targetingResult ? buildTargetingRecord(targetingResult, contextLog) : void 0,
|
|
1856
|
-
baselineFindings
|
|
1944
|
+
baselineFindings,
|
|
1945
|
+
unresolved: failureReasons.length > 0 ? failureReasons : void 0
|
|
1857
1946
|
});
|
|
1858
1947
|
return { hardFailure };
|
|
1859
1948
|
}
|