@probelabs/visor 0.1.98 → 0.1.100
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/README.md +2 -1
- package/action.yml +7 -2
- package/defaults/.visor.yaml +4 -4
- package/dist/action-cli-bridge.d.ts +1 -0
- package/dist/action-cli-bridge.d.ts.map +1 -1
- package/dist/ai-review-service.d.ts.map +1 -1
- package/dist/check-execution-engine.d.ts +8 -2
- package/dist/check-execution-engine.d.ts.map +1 -1
- package/dist/cli-main.d.ts.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/debug-visualizer/debug-span-exporter.d.ts +47 -0
- package/dist/debug-visualizer/debug-span-exporter.d.ts.map +1 -0
- package/dist/debug-visualizer/trace-reader.d.ts +117 -0
- package/dist/debug-visualizer/trace-reader.d.ts.map +1 -0
- package/dist/debug-visualizer/ui/index.html +2568 -0
- package/dist/debug-visualizer/ws-server.d.ts +99 -0
- package/dist/debug-visualizer/ws-server.d.ts.map +1 -0
- package/dist/defaults/.visor.yaml +4 -4
- package/dist/failure-condition-evaluator.d.ts.map +1 -1
- package/dist/generated/config-schema.d.ts +1 -1
- package/dist/generated/config-schema.d.ts.map +1 -1
- package/dist/generated/config-schema.json +2 -1
- package/dist/git-repository-analyzer.d.ts +1 -7
- package/dist/git-repository-analyzer.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17601 -1749
- package/dist/liquid-extensions.d.ts +1 -1
- package/dist/liquid-extensions.d.ts.map +1 -1
- package/dist/output/code-review/schema.json +2 -2
- package/dist/output/traces/run-2025-10-22T10-40-34-055Z.ndjson +218 -0
- package/dist/pr-analyzer.d.ts +2 -1
- package/dist/pr-analyzer.d.ts.map +1 -1
- package/dist/providers/ai-check-provider.d.ts.map +1 -1
- package/dist/providers/check-provider-registry.d.ts.map +1 -1
- package/dist/providers/check-provider.interface.d.ts +17 -6
- package/dist/providers/check-provider.interface.d.ts.map +1 -1
- package/dist/providers/command-check-provider.d.ts.map +1 -1
- package/dist/providers/github-ops-provider.d.ts.map +1 -1
- package/dist/providers/http-check-provider.d.ts.map +1 -1
- package/dist/providers/human-input-check-provider.d.ts +78 -0
- package/dist/providers/human-input-check-provider.d.ts.map +1 -0
- package/dist/providers/index.d.ts +2 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/mcp-check-provider.d.ts.map +1 -1
- package/dist/providers/memory-check-provider.d.ts.map +1 -1
- package/dist/sdk/check-execution-engine-F3662LY7.mjs +11 -0
- package/dist/sdk/{chunk-I3GQJIR7.mjs → chunk-B5QBV2QJ.mjs} +2 -2
- package/dist/sdk/chunk-B5QBV2QJ.mjs.map +1 -0
- package/dist/sdk/{chunk-IG3BFIIN.mjs → chunk-FVS5CJ5S.mjs} +30 -1
- package/dist/sdk/chunk-FVS5CJ5S.mjs.map +1 -0
- package/dist/sdk/{chunk-YXOWIDEF.mjs → chunk-TUTOLSFV.mjs} +15 -3
- package/dist/sdk/chunk-TUTOLSFV.mjs.map +1 -0
- package/dist/sdk/{chunk-POYXI3MQ.mjs → chunk-X2JKUOE5.mjs} +1375 -570
- package/dist/sdk/chunk-X2JKUOE5.mjs.map +1 -0
- package/dist/sdk/{liquid-extensions-GMEGEGC3.mjs → liquid-extensions-KVL4MKRH.mjs} +2 -2
- package/dist/sdk/{mermaid-telemetry-4DUEYCLE.mjs → mermaid-telemetry-FBF6D35S.mjs} +2 -2
- package/dist/sdk/sdk.d.mts +58 -2
- package/dist/sdk/sdk.d.ts +58 -2
- package/dist/sdk/sdk.js +1629 -733
- package/dist/sdk/sdk.js.map +1 -1
- package/dist/sdk/sdk.mjs +12 -6
- package/dist/sdk/sdk.mjs.map +1 -1
- package/dist/sdk/{tracer-init-RJGAIOBP.mjs → tracer-init-WC75N5NW.mjs} +2 -2
- package/dist/sdk.d.ts +5 -2
- package/dist/sdk.d.ts.map +1 -1
- package/dist/telemetry/file-span-exporter.d.ts.map +1 -1
- package/dist/telemetry/opentelemetry.d.ts +2 -0
- package/dist/telemetry/opentelemetry.d.ts.map +1 -1
- package/dist/telemetry/state-capture.d.ts +53 -0
- package/dist/telemetry/state-capture.d.ts.map +1 -0
- package/dist/telemetry/trace-helpers.d.ts.map +1 -1
- package/dist/traces/run-2025-10-22T10-40-34-055Z.ndjson +218 -0
- package/dist/types/cli.d.ts +6 -0
- package/dist/types/cli.d.ts.map +1 -1
- package/dist/types/config.d.ts +40 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/utils/file-exclusion.d.ts +50 -0
- package/dist/utils/file-exclusion.d.ts.map +1 -0
- package/dist/utils/interactive-prompt.d.ts +26 -0
- package/dist/utils/interactive-prompt.d.ts.map +1 -0
- package/dist/utils/sandbox.d.ts +26 -0
- package/dist/utils/sandbox.d.ts.map +1 -0
- package/dist/utils/stdin-reader.d.ts +22 -0
- package/dist/utils/stdin-reader.d.ts.map +1 -0
- package/dist/utils/tracer-init.d.ts +0 -5
- package/dist/utils/tracer-init.d.ts.map +1 -1
- package/package.json +8 -4
- package/dist/output/traces/run-2025-10-19T19-05-29-328Z.ndjson +0 -40
- package/dist/output/traces/run-2025-10-19T19-05-42-253Z.ndjson +0 -40
- package/dist/output/traces/run-2025-10-19T19-05-42-805Z.ndjson +0 -40
- package/dist/output/traces/run-2025-10-19T19-05-43-323Z.ndjson +0 -40
- package/dist/output/traces/run-2025-10-19T19-05-43-841Z.ndjson +0 -12
- package/dist/sdk/check-execution-engine-W5FLIWZL.mjs +0 -11
- package/dist/sdk/chunk-I3GQJIR7.mjs.map +0 -1
- package/dist/sdk/chunk-IG3BFIIN.mjs.map +0 -1
- package/dist/sdk/chunk-POYXI3MQ.mjs.map +0 -1
- package/dist/sdk/chunk-YXOWIDEF.mjs.map +0 -1
- package/dist/traces/run-2025-10-19T19-05-29-328Z.ndjson +0 -40
- package/dist/traces/run-2025-10-19T19-05-42-253Z.ndjson +0 -40
- package/dist/traces/run-2025-10-19T19-05-42-805Z.ndjson +0 -40
- package/dist/traces/run-2025-10-19T19-05-43-323Z.ndjson +0 -40
- package/dist/traces/run-2025-10-19T19-05-43-841Z.ndjson +0 -12
- /package/dist/sdk/{check-execution-engine-W5FLIWZL.mjs.map → check-execution-engine-F3662LY7.mjs.map} +0 -0
- /package/dist/sdk/{liquid-extensions-GMEGEGC3.mjs.map → liquid-extensions-KVL4MKRH.mjs.map} +0 -0
- /package/dist/sdk/{mermaid-telemetry-4DUEYCLE.mjs.map → mermaid-telemetry-FBF6D35S.mjs.map} +0 -0
- /package/dist/sdk/{tracer-init-RJGAIOBP.mjs.map → tracer-init-WC75N5NW.mjs.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
init_tracer_init,
|
|
3
3
|
initializeTracer
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-TUTOLSFV.mjs";
|
|
5
5
|
import {
|
|
6
6
|
MemoryStore,
|
|
7
7
|
createExtendedLiquid,
|
|
@@ -11,15 +11,16 @@ import {
|
|
|
11
11
|
logger,
|
|
12
12
|
logger_exports,
|
|
13
13
|
resolveAssociationFromEvent
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-B5QBV2QJ.mjs";
|
|
15
15
|
import {
|
|
16
16
|
addEvent,
|
|
17
17
|
addFailIfTriggered,
|
|
18
18
|
emitNdjsonFallback,
|
|
19
19
|
emitNdjsonSpanWithEvents,
|
|
20
20
|
fallback_ndjson_exports,
|
|
21
|
-
init_fallback_ndjson
|
|
22
|
-
|
|
21
|
+
init_fallback_ndjson,
|
|
22
|
+
withActiveSpan
|
|
23
|
+
} from "./chunk-FVS5CJ5S.mjs";
|
|
23
24
|
import {
|
|
24
25
|
__esm,
|
|
25
26
|
__export,
|
|
@@ -139,7 +140,7 @@ var init_session_registry = __esm({
|
|
|
139
140
|
});
|
|
140
141
|
if (sourceAgent.debug && checkName) {
|
|
141
142
|
try {
|
|
142
|
-
const { initializeTracer: initializeTracer2 } = await import("./tracer-init-
|
|
143
|
+
const { initializeTracer: initializeTracer2 } = await import("./tracer-init-WC75N5NW.mjs");
|
|
143
144
|
const tracerResult = await initializeTracer2(newSessionId, checkName);
|
|
144
145
|
if (tracerResult) {
|
|
145
146
|
clonedAgent.tracer = tracerResult.tracer;
|
|
@@ -476,7 +477,7 @@ ${content}
|
|
|
476
477
|
* Sleep utility
|
|
477
478
|
*/
|
|
478
479
|
sleep(ms) {
|
|
479
|
-
return new Promise((
|
|
480
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
480
481
|
}
|
|
481
482
|
/**
|
|
482
483
|
* Group results by specified criteria
|
|
@@ -544,7 +545,7 @@ async function processDiffWithOutline(diffContent) {
|
|
|
544
545
|
}
|
|
545
546
|
try {
|
|
546
547
|
const originalProbePath = process.env.PROBE_PATH;
|
|
547
|
-
const
|
|
548
|
+
const fs7 = __require("fs");
|
|
548
549
|
const possiblePaths = [
|
|
549
550
|
// Relative to current working directory (most common in production)
|
|
550
551
|
path.join(process.cwd(), "node_modules/@probelabs/probe/bin/probe-binary"),
|
|
@@ -555,7 +556,7 @@ async function processDiffWithOutline(diffContent) {
|
|
|
555
556
|
];
|
|
556
557
|
let probeBinaryPath;
|
|
557
558
|
for (const candidatePath of possiblePaths) {
|
|
558
|
-
if (
|
|
559
|
+
if (fs7.existsSync(candidatePath)) {
|
|
559
560
|
probeBinaryPath = candidatePath;
|
|
560
561
|
break;
|
|
561
562
|
}
|
|
@@ -630,6 +631,7 @@ var AIReviewService = class {
|
|
|
630
631
|
this.config.model = process.env.MODEL_NAME;
|
|
631
632
|
}
|
|
632
633
|
}
|
|
634
|
+
// NOTE: per request, no additional redaction/encryption helpers are used.
|
|
633
635
|
/**
|
|
634
636
|
* Execute AI review using probe agent
|
|
635
637
|
*/
|
|
@@ -946,14 +948,12 @@ ${prContext}
|
|
|
946
948
|
const isIssue = prContextInfo.isIssue === true;
|
|
947
949
|
const isPRContext = prContextInfo.isPRContext === true;
|
|
948
950
|
const includeCodeContext = isPRContext || prContextInfo.includeCodeContext !== false;
|
|
949
|
-
const log2 = this.config.debug ? console.error : () => {
|
|
950
|
-
};
|
|
951
951
|
if (isPRContext) {
|
|
952
|
-
|
|
952
|
+
log("\u{1F50D} Including full code diffs in AI context (PR mode)");
|
|
953
953
|
} else if (!includeCodeContext) {
|
|
954
|
-
|
|
954
|
+
log("\u{1F4CA} Including only file summary in AI context (no diffs)");
|
|
955
955
|
} else {
|
|
956
|
-
|
|
956
|
+
log("\u{1F50D} Including code diffs in AI context");
|
|
957
957
|
}
|
|
958
958
|
if (isIssue) {
|
|
959
959
|
let context2 = `<issue>
|
|
@@ -1201,8 +1201,8 @@ ${schemaString}`);
|
|
|
1201
1201
|
}
|
|
1202
1202
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
1203
1203
|
try {
|
|
1204
|
-
const
|
|
1205
|
-
const
|
|
1204
|
+
const fs7 = __require("fs");
|
|
1205
|
+
const path9 = __require("path");
|
|
1206
1206
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1207
1207
|
const provider = this.config.provider || "auto";
|
|
1208
1208
|
const model = this.config.model || "default";
|
|
@@ -1316,20 +1316,20 @@ ${"=".repeat(60)}
|
|
|
1316
1316
|
`;
|
|
1317
1317
|
readableVersion += `${"=".repeat(60)}
|
|
1318
1318
|
`;
|
|
1319
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
1320
|
-
if (!
|
|
1321
|
-
|
|
1319
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path9.join(process.cwd(), "debug-artifacts");
|
|
1320
|
+
if (!fs7.existsSync(debugArtifactsDir)) {
|
|
1321
|
+
fs7.mkdirSync(debugArtifactsDir, { recursive: true });
|
|
1322
1322
|
}
|
|
1323
|
-
const debugFile =
|
|
1323
|
+
const debugFile = path9.join(
|
|
1324
1324
|
debugArtifactsDir,
|
|
1325
1325
|
`prompt-${_checkName || "unknown"}-${timestamp}.json`
|
|
1326
1326
|
);
|
|
1327
|
-
|
|
1328
|
-
const readableFile =
|
|
1327
|
+
fs7.writeFileSync(debugFile, debugJson, "utf-8");
|
|
1328
|
+
const readableFile = path9.join(
|
|
1329
1329
|
debugArtifactsDir,
|
|
1330
1330
|
`prompt-${_checkName || "unknown"}-${timestamp}.txt`
|
|
1331
1331
|
);
|
|
1332
|
-
|
|
1332
|
+
fs7.writeFileSync(readableFile, readableVersion, "utf-8");
|
|
1333
1333
|
log(`
|
|
1334
1334
|
\u{1F4BE} Full debug info saved to:`);
|
|
1335
1335
|
log(` JSON: ${debugFile}`);
|
|
@@ -1361,8 +1361,8 @@ ${"=".repeat(60)}
|
|
|
1361
1361
|
log(`\u{1F4E4} Response length: ${response.length} characters`);
|
|
1362
1362
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
1363
1363
|
try {
|
|
1364
|
-
const
|
|
1365
|
-
const
|
|
1364
|
+
const fs7 = __require("fs");
|
|
1365
|
+
const path9 = __require("path");
|
|
1366
1366
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1367
1367
|
const agentAny2 = agent;
|
|
1368
1368
|
let fullHistory = [];
|
|
@@ -1373,10 +1373,10 @@ ${"=".repeat(60)}
|
|
|
1373
1373
|
} else if (agentAny2._messages) {
|
|
1374
1374
|
fullHistory = agentAny2._messages;
|
|
1375
1375
|
}
|
|
1376
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
1377
|
-
const
|
|
1376
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path9.join(process.cwd(), "debug-artifacts");
|
|
1377
|
+
const sessionBase = path9.join(
|
|
1378
1378
|
debugArtifactsDir,
|
|
1379
|
-
`session-${_checkName || "unknown"}-${timestamp}
|
|
1379
|
+
`session-${_checkName || "unknown"}-${timestamp}`
|
|
1380
1380
|
);
|
|
1381
1381
|
const sessionData = {
|
|
1382
1382
|
timestamp,
|
|
@@ -1384,15 +1384,9 @@ ${"=".repeat(60)}
|
|
|
1384
1384
|
provider: this.config.provider || "auto",
|
|
1385
1385
|
model: this.config.model || "default",
|
|
1386
1386
|
schema: effectiveSchema,
|
|
1387
|
-
|
|
1388
|
-
totalMessages: fullHistory.length,
|
|
1389
|
-
latestResponse: response
|
|
1387
|
+
totalMessages: fullHistory.length
|
|
1390
1388
|
};
|
|
1391
|
-
|
|
1392
|
-
const sessionTxtFile = path6.join(
|
|
1393
|
-
debugArtifactsDir,
|
|
1394
|
-
`session-${_checkName || "unknown"}-${timestamp}.txt`
|
|
1395
|
-
);
|
|
1389
|
+
fs7.writeFileSync(sessionBase + ".json", JSON.stringify(sessionData, null, 2), "utf-8");
|
|
1396
1390
|
let readable = `=============================================================
|
|
1397
1391
|
`;
|
|
1398
1392
|
readable += `COMPLETE AI SESSION HISTORY (AFTER RESPONSE)
|
|
@@ -1409,22 +1403,18 @@ ${"=".repeat(60)}
|
|
|
1409
1403
|
|
|
1410
1404
|
`;
|
|
1411
1405
|
fullHistory.forEach((msg, idx) => {
|
|
1406
|
+
const role = msg.role || "unknown";
|
|
1407
|
+
const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content, null, 2);
|
|
1412
1408
|
readable += `
|
|
1413
1409
|
${"=".repeat(60)}
|
|
1410
|
+
MESSAGE ${idx + 1}/${fullHistory.length}
|
|
1411
|
+
Role: ${role}
|
|
1412
|
+
${"=".repeat(60)}
|
|
1414
1413
|
`;
|
|
1415
|
-
readable += `MESSAGE ${idx + 1}/${fullHistory.length}
|
|
1416
|
-
`;
|
|
1417
|
-
readable += `Role: ${msg.role || "unknown"}
|
|
1418
|
-
`;
|
|
1419
|
-
readable += `${"=".repeat(60)}
|
|
1420
|
-
`;
|
|
1421
|
-
const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content, null, 2);
|
|
1422
1414
|
readable += content + "\n";
|
|
1423
1415
|
});
|
|
1424
|
-
|
|
1416
|
+
fs7.writeFileSync(sessionBase + ".summary.txt", readable, "utf-8");
|
|
1425
1417
|
log(`\u{1F4BE} Complete session history saved:`);
|
|
1426
|
-
log(` JSON: ${sessionFile}`);
|
|
1427
|
-
log(` TXT: ${sessionTxtFile}`);
|
|
1428
1418
|
log(` - Contains ALL ${fullHistory.length} messages (prompts + responses)`);
|
|
1429
1419
|
} catch (error) {
|
|
1430
1420
|
log(`\u26A0\uFE0F Could not save complete session history: ${error}`);
|
|
@@ -1432,11 +1422,11 @@ ${"=".repeat(60)}
|
|
|
1432
1422
|
}
|
|
1433
1423
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
1434
1424
|
try {
|
|
1435
|
-
const
|
|
1436
|
-
const
|
|
1425
|
+
const fs7 = __require("fs");
|
|
1426
|
+
const path9 = __require("path");
|
|
1437
1427
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1438
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
1439
|
-
const responseFile =
|
|
1428
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path9.join(process.cwd(), "debug-artifacts");
|
|
1429
|
+
const responseFile = path9.join(
|
|
1440
1430
|
debugArtifactsDir,
|
|
1441
1431
|
`response-${_checkName || "unknown"}-${timestamp}.txt`
|
|
1442
1432
|
);
|
|
@@ -1469,7 +1459,7 @@ ${"=".repeat(60)}
|
|
|
1469
1459
|
`;
|
|
1470
1460
|
responseContent += `${"=".repeat(60)}
|
|
1471
1461
|
`;
|
|
1472
|
-
|
|
1462
|
+
fs7.writeFileSync(responseFile, responseContent, "utf-8");
|
|
1473
1463
|
log(`\u{1F4BE} Response saved to: ${responseFile}`);
|
|
1474
1464
|
} catch (error) {
|
|
1475
1465
|
log(`\u26A0\uFE0F Could not save response file: ${error}`);
|
|
@@ -1485,9 +1475,9 @@ ${"=".repeat(60)}
|
|
|
1485
1475
|
await agentAny._telemetryConfig.shutdown();
|
|
1486
1476
|
log(`\u{1F4CA} OpenTelemetry trace saved to: ${agentAny._traceFilePath}`);
|
|
1487
1477
|
if (process.env.GITHUB_ACTIONS) {
|
|
1488
|
-
const
|
|
1489
|
-
if (
|
|
1490
|
-
const stats =
|
|
1478
|
+
const fs7 = __require("fs");
|
|
1479
|
+
if (fs7.existsSync(agentAny._traceFilePath)) {
|
|
1480
|
+
const stats = fs7.statSync(agentAny._traceFilePath);
|
|
1491
1481
|
console.log(
|
|
1492
1482
|
`::notice title=AI Trace Saved::${agentAny._traceFilePath} (${stats.size} bytes)`
|
|
1493
1483
|
);
|
|
@@ -1498,12 +1488,14 @@ ${"=".repeat(60)}
|
|
|
1498
1488
|
log(`\u{1F4CA} Trace saved to: ${agentAny._traceFilePath}`);
|
|
1499
1489
|
}
|
|
1500
1490
|
} catch (exportError) {
|
|
1501
|
-
|
|
1491
|
+
logger.warn(`\u26A0\uFE0F Warning: Failed to export trace for cloned session: ${exportError}`);
|
|
1502
1492
|
}
|
|
1503
1493
|
}
|
|
1504
1494
|
return { response, effectiveSchema };
|
|
1505
1495
|
} catch (error) {
|
|
1506
|
-
|
|
1496
|
+
logger.error(
|
|
1497
|
+
`\u274C ProbeAgent session reuse failed: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1498
|
+
);
|
|
1507
1499
|
throw new Error(
|
|
1508
1500
|
`ProbeAgent session reuse failed: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1509
1501
|
);
|
|
@@ -1607,8 +1599,8 @@ ${schemaString}`);
|
|
|
1607
1599
|
const model = this.config.model || "default";
|
|
1608
1600
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
1609
1601
|
try {
|
|
1610
|
-
const
|
|
1611
|
-
const
|
|
1602
|
+
const fs7 = __require("fs");
|
|
1603
|
+
const path9 = __require("path");
|
|
1612
1604
|
const os = __require("os");
|
|
1613
1605
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1614
1606
|
const debugData = {
|
|
@@ -1682,30 +1674,20 @@ ${"=".repeat(60)}
|
|
|
1682
1674
|
readableVersion += `${"=".repeat(60)}
|
|
1683
1675
|
`;
|
|
1684
1676
|
const tempDir = os.tmpdir();
|
|
1685
|
-
const promptFile =
|
|
1686
|
-
|
|
1677
|
+
const promptFile = path9.join(tempDir, `visor-prompt-${timestamp}.txt`);
|
|
1678
|
+
fs7.writeFileSync(promptFile, prompt, "utf-8");
|
|
1687
1679
|
log(`
|
|
1688
1680
|
\u{1F4BE} Prompt saved to: ${promptFile}`);
|
|
1689
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
1681
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path9.join(process.cwd(), "debug-artifacts");
|
|
1690
1682
|
try {
|
|
1691
|
-
|
|
1692
|
-
fs5.mkdirSync(debugArtifactsDir, { recursive: true });
|
|
1693
|
-
}
|
|
1694
|
-
const debugFile = path6.join(
|
|
1695
|
-
debugArtifactsDir,
|
|
1696
|
-
`prompt-${_checkName || "unknown"}-${timestamp}.json`
|
|
1697
|
-
);
|
|
1698
|
-
fs5.writeFileSync(debugFile, debugJson, "utf-8");
|
|
1699
|
-
const readableFile = path6.join(
|
|
1683
|
+
const base = path9.join(
|
|
1700
1684
|
debugArtifactsDir,
|
|
1701
|
-
`prompt-${_checkName || "unknown"}-${timestamp}
|
|
1685
|
+
`prompt-${_checkName || "unknown"}-${timestamp}`
|
|
1702
1686
|
);
|
|
1703
|
-
|
|
1687
|
+
fs7.writeFileSync(base + ".json", debugJson, "utf-8");
|
|
1688
|
+
fs7.writeFileSync(base + ".summary.txt", readableVersion, "utf-8");
|
|
1704
1689
|
log(`
|
|
1705
|
-
\u{1F4BE} Full debug info saved to
|
|
1706
|
-
log(` JSON: ${debugFile}`);
|
|
1707
|
-
log(` TXT: ${readableFile}`);
|
|
1708
|
-
log(` - Includes: prompt, schema, provider, model, and schema options`);
|
|
1690
|
+
\u{1F4BE} Full debug info saved to directory: ${debugArtifactsDir}`);
|
|
1709
1691
|
} catch {
|
|
1710
1692
|
}
|
|
1711
1693
|
log(`
|
|
@@ -1748,8 +1730,8 @@ $ ${cliCommand}
|
|
|
1748
1730
|
log(`\u{1F4E4} Response length: ${response.length} characters`);
|
|
1749
1731
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
1750
1732
|
try {
|
|
1751
|
-
const
|
|
1752
|
-
const
|
|
1733
|
+
const fs7 = __require("fs");
|
|
1734
|
+
const path9 = __require("path");
|
|
1753
1735
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1754
1736
|
const agentAny = agent;
|
|
1755
1737
|
let fullHistory = [];
|
|
@@ -1760,10 +1742,10 @@ $ ${cliCommand}
|
|
|
1760
1742
|
} else if (agentAny._messages) {
|
|
1761
1743
|
fullHistory = agentAny._messages;
|
|
1762
1744
|
}
|
|
1763
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
1764
|
-
const
|
|
1745
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path9.join(process.cwd(), "debug-artifacts");
|
|
1746
|
+
const sessionBase = path9.join(
|
|
1765
1747
|
debugArtifactsDir,
|
|
1766
|
-
`session-${_checkName || "unknown"}-${timestamp}
|
|
1748
|
+
`session-${_checkName || "unknown"}-${timestamp}`
|
|
1767
1749
|
);
|
|
1768
1750
|
const sessionData = {
|
|
1769
1751
|
timestamp,
|
|
@@ -1771,15 +1753,9 @@ $ ${cliCommand}
|
|
|
1771
1753
|
provider: this.config.provider || "auto",
|
|
1772
1754
|
model: this.config.model || "default",
|
|
1773
1755
|
schema: effectiveSchema,
|
|
1774
|
-
|
|
1775
|
-
totalMessages: fullHistory.length,
|
|
1776
|
-
latestResponse: response
|
|
1756
|
+
totalMessages: fullHistory.length
|
|
1777
1757
|
};
|
|
1778
|
-
|
|
1779
|
-
const sessionTxtFile = path6.join(
|
|
1780
|
-
debugArtifactsDir,
|
|
1781
|
-
`session-${_checkName || "unknown"}-${timestamp}.txt`
|
|
1782
|
-
);
|
|
1758
|
+
fs7.writeFileSync(sessionBase + ".json", JSON.stringify(sessionData, null, 2), "utf-8");
|
|
1783
1759
|
let readable = `=============================================================
|
|
1784
1760
|
`;
|
|
1785
1761
|
readable += `COMPLETE AI SESSION HISTORY (AFTER RESPONSE)
|
|
@@ -1796,22 +1772,18 @@ $ ${cliCommand}
|
|
|
1796
1772
|
|
|
1797
1773
|
`;
|
|
1798
1774
|
fullHistory.forEach((msg, idx) => {
|
|
1775
|
+
const role = msg.role || "unknown";
|
|
1776
|
+
const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content, null, 2);
|
|
1799
1777
|
readable += `
|
|
1800
1778
|
${"=".repeat(60)}
|
|
1779
|
+
MESSAGE ${idx + 1}/${fullHistory.length}
|
|
1780
|
+
Role: ${role}
|
|
1781
|
+
${"=".repeat(60)}
|
|
1801
1782
|
`;
|
|
1802
|
-
readable += `MESSAGE ${idx + 1}/${fullHistory.length}
|
|
1803
|
-
`;
|
|
1804
|
-
readable += `Role: ${msg.role || "unknown"}
|
|
1805
|
-
`;
|
|
1806
|
-
readable += `${"=".repeat(60)}
|
|
1807
|
-
`;
|
|
1808
|
-
const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content, null, 2);
|
|
1809
1783
|
readable += content + "\n";
|
|
1810
1784
|
});
|
|
1811
|
-
|
|
1785
|
+
fs7.writeFileSync(sessionBase + ".summary.txt", readable, "utf-8");
|
|
1812
1786
|
log(`\u{1F4BE} Complete session history saved:`);
|
|
1813
|
-
log(` JSON: ${sessionFile}`);
|
|
1814
|
-
log(` TXT: ${sessionTxtFile}`);
|
|
1815
1787
|
log(` - Contains ALL ${fullHistory.length} messages (prompts + responses)`);
|
|
1816
1788
|
} catch (error) {
|
|
1817
1789
|
log(`\u26A0\uFE0F Could not save complete session history: ${error}`);
|
|
@@ -1819,11 +1791,11 @@ ${"=".repeat(60)}
|
|
|
1819
1791
|
}
|
|
1820
1792
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
1821
1793
|
try {
|
|
1822
|
-
const
|
|
1823
|
-
const
|
|
1794
|
+
const fs7 = __require("fs");
|
|
1795
|
+
const path9 = __require("path");
|
|
1824
1796
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1825
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
1826
|
-
const responseFile =
|
|
1797
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path9.join(process.cwd(), "debug-artifacts");
|
|
1798
|
+
const responseFile = path9.join(
|
|
1827
1799
|
debugArtifactsDir,
|
|
1828
1800
|
`response-${_checkName || "unknown"}-${timestamp}.txt`
|
|
1829
1801
|
);
|
|
@@ -1856,7 +1828,7 @@ ${"=".repeat(60)}
|
|
|
1856
1828
|
`;
|
|
1857
1829
|
responseContent += `${"=".repeat(60)}
|
|
1858
1830
|
`;
|
|
1859
|
-
|
|
1831
|
+
fs7.writeFileSync(responseFile, responseContent, "utf-8");
|
|
1860
1832
|
log(`\u{1F4BE} Response saved to: ${responseFile}`);
|
|
1861
1833
|
} catch (error) {
|
|
1862
1834
|
log(`\u26A0\uFE0F Could not save response file: ${error}`);
|
|
@@ -1874,9 +1846,9 @@ ${"=".repeat(60)}
|
|
|
1874
1846
|
await telemetry.shutdown();
|
|
1875
1847
|
log(`\u{1F4CA} OpenTelemetry trace saved to: ${traceFilePath}`);
|
|
1876
1848
|
if (process.env.GITHUB_ACTIONS) {
|
|
1877
|
-
const
|
|
1878
|
-
if (
|
|
1879
|
-
const stats =
|
|
1849
|
+
const fs7 = __require("fs");
|
|
1850
|
+
if (fs7.existsSync(traceFilePath)) {
|
|
1851
|
+
const stats = fs7.statSync(traceFilePath);
|
|
1880
1852
|
console.log(
|
|
1881
1853
|
`::notice title=AI Trace Saved::OpenTelemetry trace file size: ${stats.size} bytes`
|
|
1882
1854
|
);
|
|
@@ -1887,7 +1859,7 @@ ${"=".repeat(60)}
|
|
|
1887
1859
|
log(`\u{1F4CA} Trace saved to: ${traceFilePath}`);
|
|
1888
1860
|
}
|
|
1889
1861
|
} catch (exportError) {
|
|
1890
|
-
|
|
1862
|
+
logger.warn(`\u26A0\uFE0F Warning: Failed to export trace: ${exportError}`);
|
|
1891
1863
|
}
|
|
1892
1864
|
}
|
|
1893
1865
|
if (_checkName) {
|
|
@@ -1914,8 +1886,8 @@ ${"=".repeat(60)}
|
|
|
1914
1886
|
* Load schema content from schema files or inline definitions
|
|
1915
1887
|
*/
|
|
1916
1888
|
async loadSchemaContent(schema) {
|
|
1917
|
-
const
|
|
1918
|
-
const
|
|
1889
|
+
const fs7 = __require("fs").promises;
|
|
1890
|
+
const path9 = __require("path");
|
|
1919
1891
|
if (typeof schema === "object" && schema !== null) {
|
|
1920
1892
|
log("\u{1F4CB} Using inline schema object from configuration");
|
|
1921
1893
|
return JSON.stringify(schema);
|
|
@@ -1928,14 +1900,14 @@ ${"=".repeat(60)}
|
|
|
1928
1900
|
}
|
|
1929
1901
|
} catch {
|
|
1930
1902
|
}
|
|
1931
|
-
if ((schema.startsWith("./") || schema.includes(".json")) && !
|
|
1903
|
+
if ((schema.startsWith("./") || schema.includes(".json")) && !path9.isAbsolute(schema)) {
|
|
1932
1904
|
if (schema.includes("..") || schema.includes("\0")) {
|
|
1933
1905
|
throw new Error("Invalid schema path: path traversal not allowed");
|
|
1934
1906
|
}
|
|
1935
1907
|
try {
|
|
1936
|
-
const schemaPath2 =
|
|
1908
|
+
const schemaPath2 = path9.resolve(process.cwd(), schema);
|
|
1937
1909
|
log(`\u{1F4CB} Loading custom schema from file: ${schemaPath2}`);
|
|
1938
|
-
const schemaContent = await
|
|
1910
|
+
const schemaContent = await fs7.readFile(schemaPath2, "utf-8");
|
|
1939
1911
|
return schemaContent.trim();
|
|
1940
1912
|
} catch (error) {
|
|
1941
1913
|
throw new Error(
|
|
@@ -1947,9 +1919,9 @@ ${"=".repeat(60)}
|
|
|
1947
1919
|
if (!sanitizedSchemaName || sanitizedSchemaName !== schema) {
|
|
1948
1920
|
throw new Error("Invalid schema name");
|
|
1949
1921
|
}
|
|
1950
|
-
const schemaPath =
|
|
1922
|
+
const schemaPath = path9.join(process.cwd(), "output", sanitizedSchemaName, "schema.json");
|
|
1951
1923
|
try {
|
|
1952
|
-
const schemaContent = await
|
|
1924
|
+
const schemaContent = await fs7.readFile(schemaPath, "utf-8");
|
|
1953
1925
|
return schemaContent.trim();
|
|
1954
1926
|
} catch (error) {
|
|
1955
1927
|
throw new Error(
|
|
@@ -2059,6 +2031,25 @@ ${"=".repeat(60)}
|
|
|
2059
2031
|
}
|
|
2060
2032
|
}
|
|
2061
2033
|
const isCustomSchema = _schema === "custom" || _schema && (_schema.startsWith("./") || _schema.endsWith(".json")) || _schema && _schema !== "code-review" && !_schema.includes("output/");
|
|
2034
|
+
const _debugSchemaLogging = this.config.debug === true || process.env.VISOR_DEBUG_AI_SESSIONS === "true";
|
|
2035
|
+
if (_debugSchemaLogging) {
|
|
2036
|
+
const details = {
|
|
2037
|
+
schema: _schema,
|
|
2038
|
+
isCustomSchema,
|
|
2039
|
+
isCustomLiteral: _schema === "custom",
|
|
2040
|
+
startsWithDotSlash: typeof _schema === "string" ? _schema.startsWith("./") : false,
|
|
2041
|
+
endsWithJson: typeof _schema === "string" ? _schema.endsWith(".json") : false,
|
|
2042
|
+
notCodeReview: _schema !== "code-review",
|
|
2043
|
+
noOutputPrefix: typeof _schema === "string" ? !_schema.includes("output/") : false
|
|
2044
|
+
};
|
|
2045
|
+
try {
|
|
2046
|
+
log(`\u{1F50D} Schema detection: ${JSON.stringify(details)}`);
|
|
2047
|
+
} catch {
|
|
2048
|
+
log(
|
|
2049
|
+
`\u{1F50D} Schema detection: _schema="${String(_schema)}", isCustomSchema=${isCustomSchema}`
|
|
2050
|
+
);
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2062
2053
|
if (isCustomSchema) {
|
|
2063
2054
|
log("\u{1F4CB} Custom schema detected - preserving all fields from parsed JSON");
|
|
2064
2055
|
log(`\u{1F4CA} Schema: ${_schema}`);
|
|
@@ -2104,33 +2095,39 @@ ${"=".repeat(60)}
|
|
|
2104
2095
|
log("\u2705 Successfully created ReviewSummary");
|
|
2105
2096
|
return result;
|
|
2106
2097
|
} catch (error) {
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2098
|
+
const detailed = this.config.debug === true || process.env.VISOR_DEBUG_AI_SESSIONS === "true";
|
|
2099
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2100
|
+
if (detailed) {
|
|
2101
|
+
logger.debug(`\u274C Failed to parse AI response: ${message}`);
|
|
2102
|
+
logger.debug("\u{1F4C4} FULL RAW RESPONSE:");
|
|
2103
|
+
logger.debug("=".repeat(80));
|
|
2104
|
+
logger.debug(response);
|
|
2105
|
+
logger.debug("=".repeat(80));
|
|
2106
|
+
logger.debug(`\u{1F4CF} Response length: ${response.length} characters`);
|
|
2107
|
+
if (error instanceof SyntaxError) {
|
|
2108
|
+
logger.debug("\u{1F50D} JSON parsing error - the response may not be valid JSON");
|
|
2109
|
+
logger.debug(`\u{1F50D} Error details: ${error.message}`);
|
|
2110
|
+
const errorMatch = error.message.match(/position (\d+)/);
|
|
2111
|
+
if (errorMatch) {
|
|
2112
|
+
const position = parseInt(errorMatch[1]);
|
|
2113
|
+
logger.debug(`\u{1F50D} Error at position ${position}:`);
|
|
2114
|
+
const start = Math.max(0, position - 50);
|
|
2115
|
+
const end = Math.min(response.length, position + 50);
|
|
2116
|
+
logger.debug(`\u{1F50D} Context: "${response.substring(start, end)}"`);
|
|
2117
|
+
logger.debug(`\u{1F50D} Response beginning: "${response.substring(0, 100)}"`);
|
|
2118
|
+
}
|
|
2119
|
+
if (response.includes("I cannot")) {
|
|
2120
|
+
logger.debug("\u{1F50D} Response appears to be a refusal/explanation rather than JSON");
|
|
2121
|
+
}
|
|
2122
|
+
if (response.includes("```")) {
|
|
2123
|
+
logger.debug("\u{1F50D} Response appears to contain markdown code blocks");
|
|
2124
|
+
}
|
|
2125
|
+
if (response.startsWith("<")) {
|
|
2126
|
+
logger.debug("\u{1F50D} Response appears to start with XML/HTML");
|
|
2127
|
+
}
|
|
2133
2128
|
}
|
|
2129
|
+
} else {
|
|
2130
|
+
logger.error(`\u274C Failed to parse AI response: ${message}`);
|
|
2134
2131
|
}
|
|
2135
2132
|
throw new Error(
|
|
2136
2133
|
`Invalid AI response format: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
@@ -2195,7 +2192,7 @@ ${"=".repeat(60)}
|
|
|
2195
2192
|
* Generate mock response for testing
|
|
2196
2193
|
*/
|
|
2197
2194
|
async generateMockResponse(_prompt) {
|
|
2198
|
-
await new Promise((
|
|
2195
|
+
await new Promise((resolve4) => setTimeout(resolve4, 500));
|
|
2199
2196
|
const mockResponse = {
|
|
2200
2197
|
content: JSON.stringify({
|
|
2201
2198
|
issues: [
|
|
@@ -2277,7 +2274,7 @@ var PRReviewer = class {
|
|
|
2277
2274
|
async reviewPR(owner, repo, prNumber, prInfo, options = {}) {
|
|
2278
2275
|
const { debug = false, config, checks } = options;
|
|
2279
2276
|
if (config && checks && checks.length > 0) {
|
|
2280
|
-
const { CheckExecutionEngine: CheckExecutionEngine2 } = await import("./check-execution-engine-
|
|
2277
|
+
const { CheckExecutionEngine: CheckExecutionEngine2 } = await import("./check-execution-engine-F3662LY7.mjs");
|
|
2281
2278
|
const engine = new CheckExecutionEngine2();
|
|
2282
2279
|
const { results } = await engine.executeGroupedChecks(
|
|
2283
2280
|
prInfo,
|
|
@@ -2308,15 +2305,15 @@ var PRReviewer = class {
|
|
|
2308
2305
|
if (["code-review", "overview", "plain", "text"].includes(schema)) {
|
|
2309
2306
|
return true;
|
|
2310
2307
|
}
|
|
2311
|
-
const
|
|
2312
|
-
const
|
|
2308
|
+
const fs7 = __require("fs").promises;
|
|
2309
|
+
const path9 = __require("path");
|
|
2313
2310
|
const sanitizedSchemaName = schema.replace(/[^a-zA-Z0-9-]/g, "");
|
|
2314
2311
|
if (!sanitizedSchemaName || sanitizedSchemaName !== schema) {
|
|
2315
2312
|
return false;
|
|
2316
2313
|
}
|
|
2317
|
-
const schemaPath =
|
|
2314
|
+
const schemaPath = path9.join(process.cwd(), "output", sanitizedSchemaName, "schema.json");
|
|
2318
2315
|
try {
|
|
2319
|
-
const schemaContent = await
|
|
2316
|
+
const schemaContent = await fs7.readFile(schemaPath, "utf-8");
|
|
2320
2317
|
const schemaObj = JSON.parse(schemaContent);
|
|
2321
2318
|
const properties = schemaObj.properties;
|
|
2322
2319
|
return !!(properties && "text" in properties);
|
|
@@ -2382,16 +2379,16 @@ var PRReviewer = class {
|
|
|
2382
2379
|
}
|
|
2383
2380
|
}
|
|
2384
2381
|
async formatGroupComment(checkResults, _options, _githubContext) {
|
|
2385
|
-
const
|
|
2382
|
+
const normalize2 = (s) => s.replace(/\\n/g, "\n");
|
|
2386
2383
|
const checkContents = checkResults.map((result) => {
|
|
2387
2384
|
const trimmed = result.content?.trim();
|
|
2388
|
-
if (trimmed) return
|
|
2385
|
+
if (trimmed) return normalize2(trimmed);
|
|
2389
2386
|
const out = result.output;
|
|
2390
2387
|
if (out) {
|
|
2391
|
-
if (typeof out === "string" && out.trim()) return
|
|
2388
|
+
if (typeof out === "string" && out.trim()) return normalize2(out.trim());
|
|
2392
2389
|
if (typeof out === "object") {
|
|
2393
2390
|
const txt = out.text || out.response || out.message;
|
|
2394
|
-
if (typeof txt === "string" && txt.trim()) return
|
|
2391
|
+
if (typeof txt === "string" && txt.trim()) return normalize2(txt.trim());
|
|
2395
2392
|
}
|
|
2396
2393
|
}
|
|
2397
2394
|
return "";
|
|
@@ -2487,15 +2484,15 @@ var PRReviewer = class {
|
|
|
2487
2484
|
}
|
|
2488
2485
|
saveDebugArtifact(debug) {
|
|
2489
2486
|
try {
|
|
2490
|
-
const
|
|
2491
|
-
const
|
|
2492
|
-
const debugDir =
|
|
2493
|
-
if (!
|
|
2494
|
-
|
|
2487
|
+
const fs7 = __require("fs");
|
|
2488
|
+
const path9 = __require("path");
|
|
2489
|
+
const debugDir = path9.join(process.cwd(), "debug-artifacts");
|
|
2490
|
+
if (!fs7.existsSync(debugDir)) {
|
|
2491
|
+
fs7.mkdirSync(debugDir, { recursive: true });
|
|
2495
2492
|
}
|
|
2496
2493
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
2497
2494
|
const filename = `visor-debug-${timestamp}.md`;
|
|
2498
|
-
const filepath =
|
|
2495
|
+
const filepath = path9.join(debugDir, filename);
|
|
2499
2496
|
const content = [
|
|
2500
2497
|
`# Visor Debug Information`,
|
|
2501
2498
|
``,
|
|
@@ -2516,7 +2513,7 @@ var PRReviewer = class {
|
|
|
2516
2513
|
debug.rawResponse,
|
|
2517
2514
|
"```"
|
|
2518
2515
|
].join("\n");
|
|
2519
|
-
|
|
2516
|
+
fs7.writeFileSync(filepath, content, "utf8");
|
|
2520
2517
|
return filename;
|
|
2521
2518
|
} catch (error) {
|
|
2522
2519
|
console.error("Failed to save debug artifact:", error);
|
|
@@ -2527,15 +2524,90 @@ var PRReviewer = class {
|
|
|
2527
2524
|
|
|
2528
2525
|
// src/git-repository-analyzer.ts
|
|
2529
2526
|
import { simpleGit } from "simple-git";
|
|
2530
|
-
import * as
|
|
2527
|
+
import * as path3 from "path";
|
|
2528
|
+
import * as fs2 from "fs";
|
|
2529
|
+
|
|
2530
|
+
// src/utils/file-exclusion.ts
|
|
2531
|
+
import ignore from "ignore";
|
|
2531
2532
|
import * as fs from "fs";
|
|
2533
|
+
import * as path2 from "path";
|
|
2534
|
+
var DEFAULT_EXCLUSION_PATTERNS = [
|
|
2535
|
+
"dist/",
|
|
2536
|
+
"build/",
|
|
2537
|
+
".next/",
|
|
2538
|
+
"out/",
|
|
2539
|
+
"node_modules/",
|
|
2540
|
+
"coverage/",
|
|
2541
|
+
".turbo/",
|
|
2542
|
+
"bundled/"
|
|
2543
|
+
];
|
|
2544
|
+
var FileExclusionHelper = class {
|
|
2545
|
+
gitignore = null;
|
|
2546
|
+
workingDirectory;
|
|
2547
|
+
/**
|
|
2548
|
+
* @param workingDirectory - Directory to search for .gitignore
|
|
2549
|
+
* @param additionalPatterns - Additional patterns to include (optional, defaults to common build artifacts)
|
|
2550
|
+
*/
|
|
2551
|
+
constructor(workingDirectory = process.cwd(), additionalPatterns = DEFAULT_EXCLUSION_PATTERNS) {
|
|
2552
|
+
const normalizedPath = path2.resolve(workingDirectory);
|
|
2553
|
+
if (normalizedPath.includes("\0")) {
|
|
2554
|
+
throw new Error("Invalid workingDirectory: contains null bytes");
|
|
2555
|
+
}
|
|
2556
|
+
this.workingDirectory = normalizedPath;
|
|
2557
|
+
this.loadGitignore(additionalPatterns);
|
|
2558
|
+
}
|
|
2559
|
+
/**
|
|
2560
|
+
* Load .gitignore patterns from the working directory (called once in constructor)
|
|
2561
|
+
* @param additionalPatterns - Additional patterns to add to gitignore rules
|
|
2562
|
+
*/
|
|
2563
|
+
loadGitignore(additionalPatterns) {
|
|
2564
|
+
const gitignorePath = path2.resolve(this.workingDirectory, ".gitignore");
|
|
2565
|
+
const resolvedWorkingDir = path2.resolve(this.workingDirectory);
|
|
2566
|
+
try {
|
|
2567
|
+
const relativePath = path2.relative(resolvedWorkingDir, gitignorePath);
|
|
2568
|
+
if (relativePath.startsWith("..") || path2.isAbsolute(relativePath)) {
|
|
2569
|
+
throw new Error("Invalid gitignore path: path traversal detected");
|
|
2570
|
+
}
|
|
2571
|
+
if (relativePath !== ".gitignore") {
|
|
2572
|
+
throw new Error("Invalid gitignore path: must be .gitignore in working directory");
|
|
2573
|
+
}
|
|
2574
|
+
this.gitignore = ignore();
|
|
2575
|
+
if (additionalPatterns && additionalPatterns.length > 0) {
|
|
2576
|
+
this.gitignore.add(additionalPatterns);
|
|
2577
|
+
}
|
|
2578
|
+
if (fs.existsSync(gitignorePath)) {
|
|
2579
|
+
const rawContent = fs.readFileSync(gitignorePath, "utf8");
|
|
2580
|
+
const gitignoreContent = rawContent.replace(/[\r\n]+/g, "\n").replace(/[\x00-\x09\x0B-\x1F\x7F]/g, "").split("\n").filter((line) => line.length < 1e3).join("\n").trim();
|
|
2581
|
+
this.gitignore.add(gitignoreContent);
|
|
2582
|
+
console.error("\u2705 Loaded .gitignore patterns for file filtering");
|
|
2583
|
+
} else if (additionalPatterns && additionalPatterns.length > 0) {
|
|
2584
|
+
console.error("\u26A0\uFE0F No .gitignore found, using default exclusion patterns");
|
|
2585
|
+
}
|
|
2586
|
+
} catch (error) {
|
|
2587
|
+
console.warn("\u26A0\uFE0F Failed to load .gitignore:", error instanceof Error ? error.message : error);
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
/**
|
|
2591
|
+
* Check if a file should be excluded based on .gitignore patterns
|
|
2592
|
+
*/
|
|
2593
|
+
shouldExcludeFile(filename) {
|
|
2594
|
+
if (this.gitignore) {
|
|
2595
|
+
return this.gitignore.ignores(filename);
|
|
2596
|
+
}
|
|
2597
|
+
return false;
|
|
2598
|
+
}
|
|
2599
|
+
};
|
|
2600
|
+
|
|
2601
|
+
// src/git-repository-analyzer.ts
|
|
2532
2602
|
var MAX_PATCH_SIZE = 50 * 1024;
|
|
2533
2603
|
var GitRepositoryAnalyzer = class {
|
|
2534
2604
|
git;
|
|
2535
2605
|
cwd;
|
|
2606
|
+
fileExclusionHelper;
|
|
2536
2607
|
constructor(workingDirectory = process.cwd()) {
|
|
2537
2608
|
this.cwd = workingDirectory;
|
|
2538
2609
|
this.git = simpleGit(workingDirectory);
|
|
2610
|
+
this.fileExclusionHelper = new FileExclusionHelper(workingDirectory);
|
|
2539
2611
|
}
|
|
2540
2612
|
/**
|
|
2541
2613
|
* Analyze the current git repository state and return data compatible with PRInfo interface
|
|
@@ -2670,34 +2742,6 @@ ${file.patch}`).join("\n\n");
|
|
|
2670
2742
|
return "main";
|
|
2671
2743
|
}
|
|
2672
2744
|
}
|
|
2673
|
-
/**
|
|
2674
|
-
* Check if a file should be excluded from analysis
|
|
2675
|
-
* This includes:
|
|
2676
|
-
* - Files in .gitignore (even if force-added)
|
|
2677
|
-
* - Built/generated files (dist/, build/, .next/, etc.)
|
|
2678
|
-
*/
|
|
2679
|
-
async shouldExcludeFile(filename) {
|
|
2680
|
-
const excludePatterns = [
|
|
2681
|
-
/^dist\//,
|
|
2682
|
-
/^build\//,
|
|
2683
|
-
/^\.next\//,
|
|
2684
|
-
/^out\//,
|
|
2685
|
-
/^node_modules\//,
|
|
2686
|
-
/^coverage\//,
|
|
2687
|
-
/^\.turbo\//
|
|
2688
|
-
];
|
|
2689
|
-
for (const pattern of excludePatterns) {
|
|
2690
|
-
if (pattern.test(filename)) {
|
|
2691
|
-
return true;
|
|
2692
|
-
}
|
|
2693
|
-
}
|
|
2694
|
-
try {
|
|
2695
|
-
const result = await this.git.raw(["check-ignore", filename]);
|
|
2696
|
-
return result.trim().length > 0;
|
|
2697
|
-
} catch {
|
|
2698
|
-
return false;
|
|
2699
|
-
}
|
|
2700
|
-
}
|
|
2701
2745
|
/**
|
|
2702
2746
|
* Truncate a patch if it exceeds MAX_PATCH_SIZE
|
|
2703
2747
|
*/
|
|
@@ -2738,11 +2782,11 @@ ${file.patch}`).join("\n\n");
|
|
|
2738
2782
|
}))
|
|
2739
2783
|
];
|
|
2740
2784
|
for (const { file, status: status2 } of fileChanges) {
|
|
2741
|
-
if (
|
|
2785
|
+
if (this.fileExclusionHelper.shouldExcludeFile(file)) {
|
|
2742
2786
|
console.error(`\u23ED\uFE0F Skipping excluded file: ${file}`);
|
|
2743
2787
|
continue;
|
|
2744
2788
|
}
|
|
2745
|
-
const filePath =
|
|
2789
|
+
const filePath = path3.join(this.cwd, file);
|
|
2746
2790
|
const fileChange = await this.analyzeFileChange(file, status2, filePath, includeContext);
|
|
2747
2791
|
changes.push(fileChange);
|
|
2748
2792
|
}
|
|
@@ -2763,7 +2807,7 @@ ${file.patch}`).join("\n\n");
|
|
|
2763
2807
|
return [];
|
|
2764
2808
|
}
|
|
2765
2809
|
for (const file of diffSummary.files) {
|
|
2766
|
-
if (
|
|
2810
|
+
if (this.fileExclusionHelper.shouldExcludeFile(file.file)) {
|
|
2767
2811
|
console.error(`\u23ED\uFE0F Skipping excluded file: ${file.file}`);
|
|
2768
2812
|
continue;
|
|
2769
2813
|
}
|
|
@@ -2818,7 +2862,7 @@ ${file.patch}`).join("\n\n");
|
|
|
2818
2862
|
let content;
|
|
2819
2863
|
let truncated = false;
|
|
2820
2864
|
try {
|
|
2821
|
-
if (includeContext && status !== "added" &&
|
|
2865
|
+
if (includeContext && status !== "added" && fs2.existsSync(filePath)) {
|
|
2822
2866
|
const diff = await this.git.diff(["--", filename]).catch(() => "");
|
|
2823
2867
|
if (diff) {
|
|
2824
2868
|
const result = this.truncatePatch(diff, filename);
|
|
@@ -2828,7 +2872,7 @@ ${file.patch}`).join("\n\n");
|
|
|
2828
2872
|
additions = lines.filter((line) => line.startsWith("+")).length;
|
|
2829
2873
|
deletions = lines.filter((line) => line.startsWith("-")).length;
|
|
2830
2874
|
}
|
|
2831
|
-
} else if (status !== "added" &&
|
|
2875
|
+
} else if (status !== "added" && fs2.existsSync(filePath)) {
|
|
2832
2876
|
const diff = await this.git.diff(["--", filename]).catch(() => "");
|
|
2833
2877
|
if (diff) {
|
|
2834
2878
|
const lines = diff.split("\n");
|
|
@@ -2836,17 +2880,17 @@ ${file.patch}`).join("\n\n");
|
|
|
2836
2880
|
deletions = lines.filter((line) => line.startsWith("-")).length;
|
|
2837
2881
|
}
|
|
2838
2882
|
}
|
|
2839
|
-
if (status === "added" &&
|
|
2883
|
+
if (status === "added" && fs2.existsSync(filePath)) {
|
|
2840
2884
|
try {
|
|
2841
|
-
const stats =
|
|
2885
|
+
const stats = fs2.statSync(filePath);
|
|
2842
2886
|
if (stats.isFile() && stats.size < 1024 * 1024) {
|
|
2843
2887
|
if (includeContext) {
|
|
2844
|
-
content =
|
|
2888
|
+
content = fs2.readFileSync(filePath, "utf8");
|
|
2845
2889
|
const result = this.truncatePatch(content, filename);
|
|
2846
2890
|
patch = result.patch;
|
|
2847
2891
|
truncated = result.truncated;
|
|
2848
2892
|
}
|
|
2849
|
-
const fileContent = includeContext ? content :
|
|
2893
|
+
const fileContent = includeContext ? content : fs2.readFileSync(filePath, "utf8");
|
|
2850
2894
|
additions = fileContent.split("\n").length;
|
|
2851
2895
|
}
|
|
2852
2896
|
} catch {
|
|
@@ -2929,11 +2973,14 @@ ${file.patch}`).join("\n\n");
|
|
|
2929
2973
|
};
|
|
2930
2974
|
|
|
2931
2975
|
// src/pr-analyzer.ts
|
|
2976
|
+
import * as path4 from "path";
|
|
2932
2977
|
var PRAnalyzer = class {
|
|
2933
|
-
constructor(octokit, maxRetries = 3) {
|
|
2978
|
+
constructor(octokit, maxRetries = 3, workingDirectory = path4.resolve(process.cwd())) {
|
|
2934
2979
|
this.octokit = octokit;
|
|
2935
2980
|
this.maxRetries = maxRetries;
|
|
2981
|
+
this.fileExclusionHelper = new FileExclusionHelper(workingDirectory);
|
|
2936
2982
|
}
|
|
2983
|
+
fileExclusionHelper;
|
|
2937
2984
|
/**
|
|
2938
2985
|
* Fetch commit diff for incremental analysis
|
|
2939
2986
|
*/
|
|
@@ -2989,14 +3036,25 @@ ${file.patch}`).join("\n\n");
|
|
|
2989
3036
|
const authorAssociation = pr.author_association && typeof pr.author_association === "string" ? pr.author_association : void 0;
|
|
2990
3037
|
const base = pr.base && typeof pr.base === "object" && pr.base.ref ? typeof pr.base.ref === "string" ? pr.base.ref : String(pr.base.ref) : "main";
|
|
2991
3038
|
const head = pr.head && typeof pr.head === "object" && pr.head.ref ? typeof pr.head.ref === "string" ? pr.head.ref : String(pr.head.ref) : "feature";
|
|
2992
|
-
|
|
3039
|
+
let skippedCount = 0;
|
|
3040
|
+
const validFiles = files ? files.filter((file) => file && typeof file === "object" && file.filename).filter((file) => {
|
|
3041
|
+
const filename = typeof file.filename === "string" ? file.filename : String(file.filename || "unknown");
|
|
3042
|
+
if (!filename || this.fileExclusionHelper.shouldExcludeFile(filename)) {
|
|
3043
|
+
skippedCount++;
|
|
3044
|
+
return false;
|
|
3045
|
+
}
|
|
3046
|
+
return true;
|
|
3047
|
+
}).map((file) => ({
|
|
2993
3048
|
filename: typeof file.filename === "string" ? file.filename : String(file.filename || "unknown"),
|
|
2994
3049
|
additions: typeof file.additions === "number" ? Math.max(0, file.additions) : 0,
|
|
2995
3050
|
deletions: typeof file.deletions === "number" ? Math.max(0, file.deletions) : 0,
|
|
2996
3051
|
changes: typeof file.changes === "number" ? Math.max(0, file.changes) : 0,
|
|
2997
3052
|
patch: typeof file.patch === "string" ? file.patch : void 0,
|
|
2998
3053
|
status: ["added", "removed", "modified", "renamed"].includes(file.status) ? file.status : "modified"
|
|
2999
|
-
}))
|
|
3054
|
+
})) : [];
|
|
3055
|
+
if (skippedCount > 0) {
|
|
3056
|
+
console.log(`\u23ED\uFE0F Skipped ${skippedCount} excluded file(s)`);
|
|
3057
|
+
}
|
|
3000
3058
|
const prInfo = {
|
|
3001
3059
|
number: typeof pr.number === "number" ? pr.number : parseInt(String(pr.number || 1), 10),
|
|
3002
3060
|
title,
|
|
@@ -3075,7 +3133,7 @@ ${file.patch}`).join("\n\n");
|
|
|
3075
3133
|
}
|
|
3076
3134
|
if (this.isRetryableError(error)) {
|
|
3077
3135
|
const delay = Math.min(1e3 * Math.pow(2, attempt), 5e3);
|
|
3078
|
-
await new Promise((
|
|
3136
|
+
await new Promise((resolve4) => setTimeout(resolve4, delay));
|
|
3079
3137
|
} else {
|
|
3080
3138
|
throw error;
|
|
3081
3139
|
}
|
|
@@ -3202,8 +3260,8 @@ var EnvironmentResolver = class {
|
|
|
3202
3260
|
};
|
|
3203
3261
|
|
|
3204
3262
|
// src/issue-filter.ts
|
|
3205
|
-
import * as
|
|
3206
|
-
import * as
|
|
3263
|
+
import * as fs3 from "fs";
|
|
3264
|
+
import * as path5 from "path";
|
|
3207
3265
|
var IssueFilter = class {
|
|
3208
3266
|
fileCache = /* @__PURE__ */ new Map();
|
|
3209
3267
|
suppressionEnabled;
|
|
@@ -3271,17 +3329,17 @@ var IssueFilter = class {
|
|
|
3271
3329
|
return this.fileCache.get(filePath);
|
|
3272
3330
|
}
|
|
3273
3331
|
try {
|
|
3274
|
-
const resolvedPath =
|
|
3275
|
-
if (!
|
|
3276
|
-
if (
|
|
3277
|
-
const content2 =
|
|
3332
|
+
const resolvedPath = path5.isAbsolute(filePath) ? filePath : path5.join(workingDir, filePath);
|
|
3333
|
+
if (!fs3.existsSync(resolvedPath)) {
|
|
3334
|
+
if (fs3.existsSync(filePath)) {
|
|
3335
|
+
const content2 = fs3.readFileSync(filePath, "utf8");
|
|
3278
3336
|
const lines2 = content2.split("\n");
|
|
3279
3337
|
this.fileCache.set(filePath, lines2);
|
|
3280
3338
|
return lines2;
|
|
3281
3339
|
}
|
|
3282
3340
|
return null;
|
|
3283
3341
|
}
|
|
3284
|
-
const content =
|
|
3342
|
+
const content = fs3.readFileSync(resolvedPath, "utf8");
|
|
3285
3343
|
const lines = content.split("\n");
|
|
3286
3344
|
this.fileCache.set(filePath, lines);
|
|
3287
3345
|
return lines;
|
|
@@ -3298,8 +3356,135 @@ var IssueFilter = class {
|
|
|
3298
3356
|
};
|
|
3299
3357
|
|
|
3300
3358
|
// src/providers/ai-check-provider.ts
|
|
3301
|
-
import
|
|
3302
|
-
import
|
|
3359
|
+
import fs4 from "fs/promises";
|
|
3360
|
+
import path6 from "path";
|
|
3361
|
+
import { trace, context as otContext } from "@opentelemetry/api";
|
|
3362
|
+
|
|
3363
|
+
// src/telemetry/state-capture.ts
|
|
3364
|
+
var MAX_ATTRIBUTE_LENGTH = 1e4;
|
|
3365
|
+
var MAX_ARRAY_ITEMS = 100;
|
|
3366
|
+
function safeSerialize(value, maxLength = MAX_ATTRIBUTE_LENGTH) {
|
|
3367
|
+
try {
|
|
3368
|
+
if (value === void 0 || value === null) return String(value);
|
|
3369
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
3370
|
+
const json = JSON.stringify(value, (key, val) => {
|
|
3371
|
+
if (typeof val === "object" && val !== null) {
|
|
3372
|
+
if (seen.has(val)) return "[Circular]";
|
|
3373
|
+
seen.add(val);
|
|
3374
|
+
}
|
|
3375
|
+
if (typeof val === "string" && val.length > maxLength) {
|
|
3376
|
+
return val.substring(0, maxLength) + "...[truncated]";
|
|
3377
|
+
}
|
|
3378
|
+
return val;
|
|
3379
|
+
});
|
|
3380
|
+
if (json.length > maxLength) {
|
|
3381
|
+
return json.substring(0, maxLength) + "...[truncated]";
|
|
3382
|
+
}
|
|
3383
|
+
return json;
|
|
3384
|
+
} catch (err) {
|
|
3385
|
+
return `[Error serializing: ${err instanceof Error ? err.message : String(err)}]`;
|
|
3386
|
+
}
|
|
3387
|
+
}
|
|
3388
|
+
function captureCheckInputContext(span, context) {
|
|
3389
|
+
try {
|
|
3390
|
+
const keys = Object.keys(context);
|
|
3391
|
+
span.setAttribute("visor.check.input.keys", keys.join(","));
|
|
3392
|
+
span.setAttribute("visor.check.input.count", keys.length);
|
|
3393
|
+
span.setAttribute("visor.check.input.context", safeSerialize(context));
|
|
3394
|
+
if (context.pr) {
|
|
3395
|
+
span.setAttribute("visor.check.input.pr", safeSerialize(context.pr, 1e3));
|
|
3396
|
+
}
|
|
3397
|
+
if (context.outputs) {
|
|
3398
|
+
span.setAttribute("visor.check.input.outputs", safeSerialize(context.outputs, 5e3));
|
|
3399
|
+
}
|
|
3400
|
+
if (context.env) {
|
|
3401
|
+
span.setAttribute("visor.check.input.env_keys", Object.keys(context.env).join(","));
|
|
3402
|
+
}
|
|
3403
|
+
} catch (err) {
|
|
3404
|
+
try {
|
|
3405
|
+
span.setAttribute("visor.check.input.error", String(err));
|
|
3406
|
+
} catch {
|
|
3407
|
+
}
|
|
3408
|
+
}
|
|
3409
|
+
}
|
|
3410
|
+
function captureCheckOutput(span, output) {
|
|
3411
|
+
try {
|
|
3412
|
+
span.setAttribute("visor.check.output.type", typeof output);
|
|
3413
|
+
if (Array.isArray(output)) {
|
|
3414
|
+
span.setAttribute("visor.check.output.length", output.length);
|
|
3415
|
+
const preview = output.slice(0, 10);
|
|
3416
|
+
span.setAttribute("visor.check.output.preview", safeSerialize(preview, 2e3));
|
|
3417
|
+
}
|
|
3418
|
+
span.setAttribute("visor.check.output", safeSerialize(output));
|
|
3419
|
+
} catch (err) {
|
|
3420
|
+
try {
|
|
3421
|
+
span.setAttribute("visor.check.output.error", String(err));
|
|
3422
|
+
} catch {
|
|
3423
|
+
}
|
|
3424
|
+
}
|
|
3425
|
+
}
|
|
3426
|
+
function captureForEachState(span, items, index, currentItem) {
|
|
3427
|
+
try {
|
|
3428
|
+
span.setAttribute("visor.foreach.total", items.length);
|
|
3429
|
+
span.setAttribute("visor.foreach.index", index);
|
|
3430
|
+
span.setAttribute("visor.foreach.current_item", safeSerialize(currentItem, 500));
|
|
3431
|
+
if (items.length <= MAX_ARRAY_ITEMS) {
|
|
3432
|
+
span.setAttribute("visor.foreach.items", safeSerialize(items));
|
|
3433
|
+
} else {
|
|
3434
|
+
span.setAttribute(
|
|
3435
|
+
"visor.foreach.items.preview",
|
|
3436
|
+
safeSerialize(items.slice(0, MAX_ARRAY_ITEMS))
|
|
3437
|
+
);
|
|
3438
|
+
span.setAttribute("visor.foreach.items.truncated", true);
|
|
3439
|
+
}
|
|
3440
|
+
} catch (err) {
|
|
3441
|
+
span.setAttribute("visor.foreach.error", String(err));
|
|
3442
|
+
}
|
|
3443
|
+
}
|
|
3444
|
+
function captureTransformJS(span, code, input, output) {
|
|
3445
|
+
try {
|
|
3446
|
+
const codePreview = code.length > 2e3 ? code.substring(0, 2e3) + "...[truncated]" : code;
|
|
3447
|
+
span.setAttribute("visor.transform.code", codePreview);
|
|
3448
|
+
span.setAttribute("visor.transform.code.length", code.length);
|
|
3449
|
+
span.setAttribute("visor.transform.input", safeSerialize(input, 2e3));
|
|
3450
|
+
span.setAttribute("visor.transform.output", safeSerialize(output, 2e3));
|
|
3451
|
+
} catch (err) {
|
|
3452
|
+
span.setAttribute("visor.transform.error", String(err));
|
|
3453
|
+
}
|
|
3454
|
+
}
|
|
3455
|
+
function captureProviderCall(span, providerType, request, response) {
|
|
3456
|
+
try {
|
|
3457
|
+
span.setAttribute("visor.provider.type", providerType);
|
|
3458
|
+
if (request.model) span.setAttribute("visor.provider.request.model", String(request.model));
|
|
3459
|
+
if (request.prompt) {
|
|
3460
|
+
span.setAttribute("visor.provider.request.prompt.length", request.prompt.length);
|
|
3461
|
+
span.setAttribute("visor.provider.request.prompt.preview", request.prompt.substring(0, 500));
|
|
3462
|
+
}
|
|
3463
|
+
if (response.content) {
|
|
3464
|
+
span.setAttribute("visor.provider.response.length", response.content.length);
|
|
3465
|
+
span.setAttribute("visor.provider.response.preview", response.content.substring(0, 500));
|
|
3466
|
+
}
|
|
3467
|
+
if (response.tokens) {
|
|
3468
|
+
span.setAttribute("visor.provider.response.tokens", response.tokens);
|
|
3469
|
+
}
|
|
3470
|
+
} catch (err) {
|
|
3471
|
+
span.setAttribute("visor.provider.error", String(err));
|
|
3472
|
+
}
|
|
3473
|
+
}
|
|
3474
|
+
function captureStateSnapshot(span, checkId, outputs, memory) {
|
|
3475
|
+
try {
|
|
3476
|
+
span.addEvent("state.snapshot", {
|
|
3477
|
+
"visor.snapshot.check_id": checkId,
|
|
3478
|
+
"visor.snapshot.outputs": safeSerialize(outputs, 5e3),
|
|
3479
|
+
"visor.snapshot.memory": safeSerialize(memory, 5e3),
|
|
3480
|
+
"visor.snapshot.timestamp": (/* @__PURE__ */ new Date()).toISOString()
|
|
3481
|
+
});
|
|
3482
|
+
} catch (err) {
|
|
3483
|
+
span.setAttribute("visor.snapshot.error", String(err));
|
|
3484
|
+
}
|
|
3485
|
+
}
|
|
3486
|
+
|
|
3487
|
+
// src/providers/ai-check-provider.ts
|
|
3303
3488
|
var AICheckProvider = class extends CheckProvider {
|
|
3304
3489
|
aiReviewService;
|
|
3305
3490
|
liquidEngine;
|
|
@@ -3416,7 +3601,7 @@ var AICheckProvider = class extends CheckProvider {
|
|
|
3416
3601
|
const hasFileExtension = /\.[a-zA-Z0-9]{1,10}$/i.test(str);
|
|
3417
3602
|
const hasPathSeparators = /[\/\\]/.test(str);
|
|
3418
3603
|
const isRelativePath = /^\.{1,2}\//.test(str);
|
|
3419
|
-
const isAbsolutePath =
|
|
3604
|
+
const isAbsolutePath = path6.isAbsolute(str);
|
|
3420
3605
|
const hasTypicalFileChars = /^[a-zA-Z0-9._\-\/\\:~]+$/.test(str);
|
|
3421
3606
|
if (!(hasFileExtension || isRelativePath || isAbsolutePath || hasPathSeparators)) {
|
|
3422
3607
|
return false;
|
|
@@ -3426,14 +3611,14 @@ var AICheckProvider = class extends CheckProvider {
|
|
|
3426
3611
|
}
|
|
3427
3612
|
try {
|
|
3428
3613
|
let resolvedPath;
|
|
3429
|
-
if (
|
|
3430
|
-
resolvedPath =
|
|
3614
|
+
if (path6.isAbsolute(str)) {
|
|
3615
|
+
resolvedPath = path6.normalize(str);
|
|
3431
3616
|
} else {
|
|
3432
|
-
resolvedPath =
|
|
3617
|
+
resolvedPath = path6.resolve(process.cwd(), str);
|
|
3433
3618
|
}
|
|
3434
|
-
const
|
|
3619
|
+
const fs7 = __require("fs").promises;
|
|
3435
3620
|
try {
|
|
3436
|
-
const stat = await
|
|
3621
|
+
const stat = await fs7.stat(resolvedPath);
|
|
3437
3622
|
return stat.isFile();
|
|
3438
3623
|
} catch {
|
|
3439
3624
|
return hasFileExtension && (isRelativePath || isAbsolutePath || hasPathSeparators);
|
|
@@ -3450,14 +3635,14 @@ var AICheckProvider = class extends CheckProvider {
|
|
|
3450
3635
|
throw new Error("Prompt file must have .liquid extension");
|
|
3451
3636
|
}
|
|
3452
3637
|
let resolvedPath;
|
|
3453
|
-
if (
|
|
3638
|
+
if (path6.isAbsolute(promptPath)) {
|
|
3454
3639
|
resolvedPath = promptPath;
|
|
3455
3640
|
} else {
|
|
3456
|
-
resolvedPath =
|
|
3641
|
+
resolvedPath = path6.resolve(process.cwd(), promptPath);
|
|
3457
3642
|
}
|
|
3458
|
-
if (!
|
|
3459
|
-
const normalizedPath =
|
|
3460
|
-
const currentDir =
|
|
3643
|
+
if (!path6.isAbsolute(promptPath)) {
|
|
3644
|
+
const normalizedPath = path6.normalize(resolvedPath);
|
|
3645
|
+
const currentDir = path6.resolve(process.cwd());
|
|
3461
3646
|
if (!normalizedPath.startsWith(currentDir)) {
|
|
3462
3647
|
throw new Error("Invalid prompt file path: path traversal detected");
|
|
3463
3648
|
}
|
|
@@ -3466,7 +3651,7 @@ var AICheckProvider = class extends CheckProvider {
|
|
|
3466
3651
|
throw new Error("Invalid prompt file path: path traversal detected");
|
|
3467
3652
|
}
|
|
3468
3653
|
try {
|
|
3469
|
-
const promptContent = await
|
|
3654
|
+
const promptContent = await fs4.readFile(resolvedPath, "utf-8");
|
|
3470
3655
|
return promptContent;
|
|
3471
3656
|
} catch (error) {
|
|
3472
3657
|
throw new Error(
|
|
@@ -3639,6 +3824,40 @@ var AICheckProvider = class extends CheckProvider {
|
|
|
3639
3824
|
);
|
|
3640
3825
|
}
|
|
3641
3826
|
}
|
|
3827
|
+
const templateContext = {
|
|
3828
|
+
pr: {
|
|
3829
|
+
number: prInfo.number,
|
|
3830
|
+
title: prInfo.title,
|
|
3831
|
+
author: prInfo.author,
|
|
3832
|
+
branch: prInfo.head,
|
|
3833
|
+
base: prInfo.base
|
|
3834
|
+
},
|
|
3835
|
+
files: prInfo.files,
|
|
3836
|
+
outputs: _dependencyResults ? Object.fromEntries(
|
|
3837
|
+
Array.from(_dependencyResults.entries()).map(([checkName, result]) => [
|
|
3838
|
+
checkName,
|
|
3839
|
+
result.output !== void 0 ? result.output : result
|
|
3840
|
+
])
|
|
3841
|
+
) : {}
|
|
3842
|
+
};
|
|
3843
|
+
try {
|
|
3844
|
+
const span = trace.getSpan(otContext.active());
|
|
3845
|
+
if (span) {
|
|
3846
|
+
captureCheckInputContext(span, templateContext);
|
|
3847
|
+
}
|
|
3848
|
+
} catch {
|
|
3849
|
+
}
|
|
3850
|
+
try {
|
|
3851
|
+
const checkId = config.checkName || config.id || "unknown";
|
|
3852
|
+
const ctxJson = JSON.stringify(templateContext);
|
|
3853
|
+
const { emitNdjsonSpanWithEvents: emitNdjsonSpanWithEvents2 } = (init_fallback_ndjson(), __toCommonJS(fallback_ndjson_exports));
|
|
3854
|
+
emitNdjsonSpanWithEvents2(
|
|
3855
|
+
"visor.check",
|
|
3856
|
+
{ "visor.check.id": checkId, "visor.check.input.context": ctxJson },
|
|
3857
|
+
[]
|
|
3858
|
+
);
|
|
3859
|
+
} catch {
|
|
3860
|
+
}
|
|
3642
3861
|
const processedPrompt = await this.processPrompt(
|
|
3643
3862
|
customPrompt,
|
|
3644
3863
|
prInfo,
|
|
@@ -3691,10 +3910,43 @@ var AICheckProvider = class extends CheckProvider {
|
|
|
3691
3910
|
const suppressionEnabled = config.suppressionEnabled !== false;
|
|
3692
3911
|
const issueFilter = new IssueFilter(suppressionEnabled);
|
|
3693
3912
|
const filteredIssues = issueFilter.filterIssues(result.issues || [], process.cwd());
|
|
3694
|
-
|
|
3913
|
+
const finalResult = {
|
|
3695
3914
|
...result,
|
|
3696
3915
|
issues: filteredIssues
|
|
3697
3916
|
};
|
|
3917
|
+
try {
|
|
3918
|
+
const span = trace.getSpan(otContext.active());
|
|
3919
|
+
if (span) {
|
|
3920
|
+
captureProviderCall(
|
|
3921
|
+
span,
|
|
3922
|
+
"ai",
|
|
3923
|
+
{
|
|
3924
|
+
prompt: processedPrompt.substring(0, 500),
|
|
3925
|
+
// Preview only
|
|
3926
|
+
model: aiConfig.model
|
|
3927
|
+
},
|
|
3928
|
+
{
|
|
3929
|
+
content: JSON.stringify(finalResult).substring(0, 500),
|
|
3930
|
+
tokens: result.usage?.totalTokens
|
|
3931
|
+
}
|
|
3932
|
+
);
|
|
3933
|
+
const outputForSpan = finalResult.output ?? finalResult;
|
|
3934
|
+
captureCheckOutput(span, outputForSpan);
|
|
3935
|
+
}
|
|
3936
|
+
} catch {
|
|
3937
|
+
}
|
|
3938
|
+
try {
|
|
3939
|
+
const checkId = config.checkName || config.id || "unknown";
|
|
3940
|
+
const outJson = JSON.stringify(finalResult.output ?? finalResult);
|
|
3941
|
+
const { emitNdjsonSpanWithEvents: emitNdjsonSpanWithEvents2 } = (init_fallback_ndjson(), __toCommonJS(fallback_ndjson_exports));
|
|
3942
|
+
emitNdjsonSpanWithEvents2(
|
|
3943
|
+
"visor.check",
|
|
3944
|
+
{ "visor.check.id": checkId, "visor.check.output": outJson },
|
|
3945
|
+
[]
|
|
3946
|
+
);
|
|
3947
|
+
} catch {
|
|
3948
|
+
}
|
|
3949
|
+
return finalResult;
|
|
3698
3950
|
} catch (error) {
|
|
3699
3951
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3700
3952
|
console.error(`\u274C AI Check Provider Error for check: ${errorMessage}`);
|
|
@@ -3739,6 +3991,7 @@ var AICheckProvider = class extends CheckProvider {
|
|
|
3739
3991
|
};
|
|
3740
3992
|
|
|
3741
3993
|
// src/providers/http-check-provider.ts
|
|
3994
|
+
import { trace as trace2, context as otContext2 } from "@opentelemetry/api";
|
|
3742
3995
|
var HttpCheckProvider = class extends CheckProvider {
|
|
3743
3996
|
liquid;
|
|
3744
3997
|
constructor() {
|
|
@@ -3800,6 +4053,13 @@ var HttpCheckProvider = class extends CheckProvider {
|
|
|
3800
4053
|
outputs: dependencyResults ? Object.fromEntries(dependencyResults) : {},
|
|
3801
4054
|
metadata: config.metadata || {}
|
|
3802
4055
|
};
|
|
4056
|
+
try {
|
|
4057
|
+
const span = trace2.getSpan(otContext2.active());
|
|
4058
|
+
if (span) {
|
|
4059
|
+
captureCheckInputContext(span, templateContext);
|
|
4060
|
+
}
|
|
4061
|
+
} catch {
|
|
4062
|
+
}
|
|
3803
4063
|
let payload;
|
|
3804
4064
|
try {
|
|
3805
4065
|
const renderedBody = await this.liquid.parseAndRender(bodyTemplate, templateContext);
|
|
@@ -3822,10 +4082,31 @@ var HttpCheckProvider = class extends CheckProvider {
|
|
|
3822
4082
|
const suppressionEnabled = config.suppressionEnabled !== false;
|
|
3823
4083
|
const issueFilter = new IssueFilter(suppressionEnabled);
|
|
3824
4084
|
const filteredIssues = issueFilter.filterIssues(result.issues || [], process.cwd());
|
|
3825
|
-
|
|
4085
|
+
const finalResult = {
|
|
3826
4086
|
...result,
|
|
3827
4087
|
issues: filteredIssues
|
|
3828
4088
|
};
|
|
4089
|
+
try {
|
|
4090
|
+
const span = trace2.getSpan(otContext2.active());
|
|
4091
|
+
if (span) {
|
|
4092
|
+
captureProviderCall(
|
|
4093
|
+
span,
|
|
4094
|
+
"http",
|
|
4095
|
+
{
|
|
4096
|
+
url,
|
|
4097
|
+
method,
|
|
4098
|
+
body: JSON.stringify(payload).substring(0, 500)
|
|
4099
|
+
},
|
|
4100
|
+
{
|
|
4101
|
+
content: JSON.stringify(response).substring(0, 500)
|
|
4102
|
+
}
|
|
4103
|
+
);
|
|
4104
|
+
const outputForSpan = finalResult.output ?? finalResult;
|
|
4105
|
+
captureCheckOutput(span, outputForSpan);
|
|
4106
|
+
}
|
|
4107
|
+
} catch {
|
|
4108
|
+
}
|
|
4109
|
+
return finalResult;
|
|
3829
4110
|
} catch (error) {
|
|
3830
4111
|
return this.createErrorResult(url, error);
|
|
3831
4112
|
}
|
|
@@ -4474,8 +4755,111 @@ var LogCheckProvider = class extends CheckProvider {
|
|
|
4474
4755
|
}
|
|
4475
4756
|
};
|
|
4476
4757
|
|
|
4477
|
-
// src/
|
|
4758
|
+
// src/utils/sandbox.ts
|
|
4478
4759
|
import Sandbox from "@nyariv/sandboxjs";
|
|
4760
|
+
function createSecureSandbox() {
|
|
4761
|
+
const globals = {
|
|
4762
|
+
...Sandbox.SAFE_GLOBALS,
|
|
4763
|
+
Math,
|
|
4764
|
+
JSON,
|
|
4765
|
+
// Provide console with limited surface. Calls are harmless in CI logs and
|
|
4766
|
+
// help with debugging value_js / transform_js expressions.
|
|
4767
|
+
console: {
|
|
4768
|
+
log: console.log,
|
|
4769
|
+
warn: console.warn,
|
|
4770
|
+
error: console.error
|
|
4771
|
+
}
|
|
4772
|
+
};
|
|
4773
|
+
const prototypeWhitelist = new Map(Sandbox.SAFE_PROTOTYPES);
|
|
4774
|
+
const arrayMethods = /* @__PURE__ */ new Set([
|
|
4775
|
+
"some",
|
|
4776
|
+
"every",
|
|
4777
|
+
"filter",
|
|
4778
|
+
"map",
|
|
4779
|
+
"reduce",
|
|
4780
|
+
"find",
|
|
4781
|
+
"includes",
|
|
4782
|
+
"indexOf",
|
|
4783
|
+
"length",
|
|
4784
|
+
"slice",
|
|
4785
|
+
"concat",
|
|
4786
|
+
"join",
|
|
4787
|
+
"push",
|
|
4788
|
+
"pop",
|
|
4789
|
+
"shift",
|
|
4790
|
+
"unshift",
|
|
4791
|
+
"sort",
|
|
4792
|
+
"reverse",
|
|
4793
|
+
"flat",
|
|
4794
|
+
"flatMap"
|
|
4795
|
+
]);
|
|
4796
|
+
prototypeWhitelist.set(Array.prototype, arrayMethods);
|
|
4797
|
+
const stringMethods = /* @__PURE__ */ new Set([
|
|
4798
|
+
"toLowerCase",
|
|
4799
|
+
"toUpperCase",
|
|
4800
|
+
"includes",
|
|
4801
|
+
"indexOf",
|
|
4802
|
+
"startsWith",
|
|
4803
|
+
"endsWith",
|
|
4804
|
+
"slice",
|
|
4805
|
+
"substring",
|
|
4806
|
+
"length",
|
|
4807
|
+
"trim",
|
|
4808
|
+
"split",
|
|
4809
|
+
"replace",
|
|
4810
|
+
"match",
|
|
4811
|
+
"padStart",
|
|
4812
|
+
"padEnd"
|
|
4813
|
+
]);
|
|
4814
|
+
prototypeWhitelist.set(String.prototype, stringMethods);
|
|
4815
|
+
const objectMethods = /* @__PURE__ */ new Set([
|
|
4816
|
+
"hasOwnProperty",
|
|
4817
|
+
"toString",
|
|
4818
|
+
"valueOf",
|
|
4819
|
+
"keys",
|
|
4820
|
+
"values"
|
|
4821
|
+
]);
|
|
4822
|
+
prototypeWhitelist.set(Object.prototype, objectMethods);
|
|
4823
|
+
return new Sandbox({ globals, prototypeWhitelist });
|
|
4824
|
+
}
|
|
4825
|
+
function compileAndRun(sandbox, userCode, scope, opts = { injectLog: true, wrapFunction: true, logPrefix: "[sandbox]" }) {
|
|
4826
|
+
const inject = opts?.injectLog === true;
|
|
4827
|
+
let safePrefix = String(opts?.logPrefix ?? "[sandbox]");
|
|
4828
|
+
safePrefix = safePrefix.replace(/[\r\n\t\0]/g, "").replace(/[`$\\]/g, "").replace(/\$\{/g, "").slice(0, 64);
|
|
4829
|
+
const header = inject ? `const __lp = ${JSON.stringify(safePrefix)}; const log = (...a) => { try { console.log(__lp, ...a); } catch {} };
|
|
4830
|
+
` : "";
|
|
4831
|
+
const body = opts.wrapFunction ? `const __fn = () => {
|
|
4832
|
+
${userCode}
|
|
4833
|
+
};
|
|
4834
|
+
return __fn();
|
|
4835
|
+
` : `${userCode}`;
|
|
4836
|
+
const code = `${header}${body}`;
|
|
4837
|
+
let exec;
|
|
4838
|
+
try {
|
|
4839
|
+
exec = sandbox.compile(code);
|
|
4840
|
+
} catch (e) {
|
|
4841
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
4842
|
+
throw new Error(`sandbox_compile_error: ${msg}`);
|
|
4843
|
+
}
|
|
4844
|
+
let out;
|
|
4845
|
+
try {
|
|
4846
|
+
out = exec(scope);
|
|
4847
|
+
} catch (e) {
|
|
4848
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
4849
|
+
throw new Error(`sandbox_execution_error: ${msg}`);
|
|
4850
|
+
}
|
|
4851
|
+
if (out && typeof out.run === "function") {
|
|
4852
|
+
try {
|
|
4853
|
+
return out.run();
|
|
4854
|
+
} catch (e) {
|
|
4855
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
4856
|
+
throw new Error(`sandbox_runner_error: ${msg}`);
|
|
4857
|
+
}
|
|
4858
|
+
}
|
|
4859
|
+
return out;
|
|
4860
|
+
}
|
|
4861
|
+
|
|
4862
|
+
// src/providers/github-ops-provider.ts
|
|
4479
4863
|
var GitHubOpsProvider = class extends CheckProvider {
|
|
4480
4864
|
sandbox;
|
|
4481
4865
|
getName() {
|
|
@@ -4502,25 +4886,20 @@ var GitHubOpsProvider = class extends CheckProvider {
|
|
|
4502
4886
|
}
|
|
4503
4887
|
async execute(prInfo, config, dependencyResults) {
|
|
4504
4888
|
const cfg = config;
|
|
4505
|
-
|
|
4889
|
+
const octokit = config.eventContext?.octokit;
|
|
4506
4890
|
if (!octokit) {
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
]
|
|
4520
|
-
};
|
|
4521
|
-
}
|
|
4522
|
-
const { Octokit } = await import("@octokit/rest");
|
|
4523
|
-
octokit = new Octokit({ auth: token });
|
|
4891
|
+
return {
|
|
4892
|
+
issues: [
|
|
4893
|
+
{
|
|
4894
|
+
file: "system",
|
|
4895
|
+
line: 0,
|
|
4896
|
+
ruleId: "github/missing_octokit",
|
|
4897
|
+
message: "No authenticated Octokit instance available in event context. GitHub operations require proper authentication context.",
|
|
4898
|
+
severity: "error",
|
|
4899
|
+
category: "logic"
|
|
4900
|
+
}
|
|
4901
|
+
]
|
|
4902
|
+
};
|
|
4524
4903
|
}
|
|
4525
4904
|
const repoEnv = process.env.GITHUB_REPOSITORY || "";
|
|
4526
4905
|
const [owner, repo] = repoEnv.split("/");
|
|
@@ -4598,14 +4977,19 @@ var GitHubOpsProvider = class extends CheckProvider {
|
|
|
4598
4977
|
if (cfg.value_js && cfg.value_js.trim()) {
|
|
4599
4978
|
try {
|
|
4600
4979
|
const sandbox = this.getSecureSandbox();
|
|
4601
|
-
const
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
const res =
|
|
4980
|
+
const depOutputs = {};
|
|
4981
|
+
if (dependencyResults) {
|
|
4982
|
+
for (const [name, result] of dependencyResults.entries()) {
|
|
4983
|
+
const summary = result;
|
|
4984
|
+
depOutputs[name] = summary.output !== void 0 ? summary.output : summary;
|
|
4985
|
+
}
|
|
4986
|
+
}
|
|
4987
|
+
const res = compileAndRun(
|
|
4988
|
+
sandbox,
|
|
4989
|
+
cfg.value_js,
|
|
4990
|
+
{ pr: prInfo, values, outputs: depOutputs },
|
|
4991
|
+
{ injectLog: true, wrapFunction: true, logPrefix: "[github:value_js]" }
|
|
4992
|
+
);
|
|
4609
4993
|
if (typeof res === "string") values = [res];
|
|
4610
4994
|
else if (Array.isArray(res)) values = res.map((v) => String(v));
|
|
4611
4995
|
} catch (e) {
|
|
@@ -4696,51 +5080,14 @@ ${cfg.value_js}
|
|
|
4696
5080
|
*/
|
|
4697
5081
|
getSecureSandbox() {
|
|
4698
5082
|
if (this.sandbox) return this.sandbox;
|
|
4699
|
-
|
|
4700
|
-
...Sandbox.SAFE_GLOBALS,
|
|
4701
|
-
Math
|
|
4702
|
-
};
|
|
4703
|
-
const prototypeWhitelist = new Map(Sandbox.SAFE_PROTOTYPES);
|
|
4704
|
-
const arrayMethods = /* @__PURE__ */ new Set([
|
|
4705
|
-
"some",
|
|
4706
|
-
"every",
|
|
4707
|
-
"filter",
|
|
4708
|
-
"map",
|
|
4709
|
-
"reduce",
|
|
4710
|
-
"find",
|
|
4711
|
-
"includes",
|
|
4712
|
-
"indexOf",
|
|
4713
|
-
"length",
|
|
4714
|
-
"slice",
|
|
4715
|
-
"concat",
|
|
4716
|
-
"join"
|
|
4717
|
-
]);
|
|
4718
|
-
prototypeWhitelist.set(Array.prototype, arrayMethods);
|
|
4719
|
-
const stringMethods = /* @__PURE__ */ new Set([
|
|
4720
|
-
"toLowerCase",
|
|
4721
|
-
"toUpperCase",
|
|
4722
|
-
"includes",
|
|
4723
|
-
"indexOf",
|
|
4724
|
-
"startsWith",
|
|
4725
|
-
"endsWith",
|
|
4726
|
-
"slice",
|
|
4727
|
-
"substring",
|
|
4728
|
-
"length",
|
|
4729
|
-
"trim",
|
|
4730
|
-
"split",
|
|
4731
|
-
"replace"
|
|
4732
|
-
]);
|
|
4733
|
-
prototypeWhitelist.set(String.prototype, stringMethods);
|
|
4734
|
-
const objectMethods = /* @__PURE__ */ new Set(["hasOwnProperty", "toString", "valueOf"]);
|
|
4735
|
-
prototypeWhitelist.set(Object.prototype, objectMethods);
|
|
4736
|
-
this.sandbox = new Sandbox({ globals, prototypeWhitelist });
|
|
5083
|
+
this.sandbox = createSecureSandbox();
|
|
4737
5084
|
return this.sandbox;
|
|
4738
5085
|
}
|
|
4739
5086
|
};
|
|
4740
5087
|
|
|
4741
5088
|
// src/providers/claude-code-check-provider.ts
|
|
4742
|
-
import
|
|
4743
|
-
import
|
|
5089
|
+
import fs5 from "fs/promises";
|
|
5090
|
+
import path7 from "path";
|
|
4744
5091
|
|
|
4745
5092
|
// src/providers/claude-code-types.ts
|
|
4746
5093
|
async function safeImport(moduleName) {
|
|
@@ -4901,7 +5248,7 @@ var ClaudeCodeCheckProvider = class extends CheckProvider {
|
|
|
4901
5248
|
const hasFileExtension = /\.[a-zA-Z0-9]{1,10}$/i.test(str);
|
|
4902
5249
|
const hasPathSeparators = /[\/\\]/.test(str);
|
|
4903
5250
|
const isRelativePath = /^\.{1,2}\//.test(str);
|
|
4904
|
-
const isAbsolutePath =
|
|
5251
|
+
const isAbsolutePath = path7.isAbsolute(str);
|
|
4905
5252
|
const hasTypicalFileChars = /^[a-zA-Z0-9._\-\/\\:~]+$/.test(str);
|
|
4906
5253
|
if (!(hasFileExtension || isRelativePath || isAbsolutePath || hasPathSeparators)) {
|
|
4907
5254
|
return false;
|
|
@@ -4911,13 +5258,13 @@ var ClaudeCodeCheckProvider = class extends CheckProvider {
|
|
|
4911
5258
|
}
|
|
4912
5259
|
try {
|
|
4913
5260
|
let resolvedPath;
|
|
4914
|
-
if (
|
|
4915
|
-
resolvedPath =
|
|
5261
|
+
if (path7.isAbsolute(str)) {
|
|
5262
|
+
resolvedPath = path7.normalize(str);
|
|
4916
5263
|
} else {
|
|
4917
|
-
resolvedPath =
|
|
5264
|
+
resolvedPath = path7.resolve(process.cwd(), str);
|
|
4918
5265
|
}
|
|
4919
5266
|
try {
|
|
4920
|
-
const stat = await
|
|
5267
|
+
const stat = await fs5.stat(resolvedPath);
|
|
4921
5268
|
return stat.isFile();
|
|
4922
5269
|
} catch {
|
|
4923
5270
|
return hasFileExtension && (isRelativePath || isAbsolutePath || hasPathSeparators);
|
|
@@ -4934,14 +5281,14 @@ var ClaudeCodeCheckProvider = class extends CheckProvider {
|
|
|
4934
5281
|
throw new Error("Prompt file must have .liquid extension");
|
|
4935
5282
|
}
|
|
4936
5283
|
let resolvedPath;
|
|
4937
|
-
if (
|
|
5284
|
+
if (path7.isAbsolute(promptPath)) {
|
|
4938
5285
|
resolvedPath = promptPath;
|
|
4939
5286
|
} else {
|
|
4940
|
-
resolvedPath =
|
|
5287
|
+
resolvedPath = path7.resolve(process.cwd(), promptPath);
|
|
4941
5288
|
}
|
|
4942
|
-
if (!
|
|
4943
|
-
const normalizedPath =
|
|
4944
|
-
const currentDir =
|
|
5289
|
+
if (!path7.isAbsolute(promptPath)) {
|
|
5290
|
+
const normalizedPath = path7.normalize(resolvedPath);
|
|
5291
|
+
const currentDir = path7.resolve(process.cwd());
|
|
4945
5292
|
if (!normalizedPath.startsWith(currentDir)) {
|
|
4946
5293
|
throw new Error("Invalid prompt file path: path traversal detected");
|
|
4947
5294
|
}
|
|
@@ -4950,7 +5297,7 @@ var ClaudeCodeCheckProvider = class extends CheckProvider {
|
|
|
4950
5297
|
throw new Error("Invalid prompt file path: path traversal detected");
|
|
4951
5298
|
}
|
|
4952
5299
|
try {
|
|
4953
|
-
const promptContent = await
|
|
5300
|
+
const promptContent = await fs5.readFile(resolvedPath, "utf-8");
|
|
4954
5301
|
return promptContent;
|
|
4955
5302
|
} catch (error) {
|
|
4956
5303
|
throw new Error(
|
|
@@ -5192,8 +5539,8 @@ var ClaudeCodeCheckProvider = class extends CheckProvider {
|
|
|
5192
5539
|
};
|
|
5193
5540
|
|
|
5194
5541
|
// src/providers/command-check-provider.ts
|
|
5195
|
-
import Sandbox2 from "@nyariv/sandboxjs";
|
|
5196
5542
|
init_logger();
|
|
5543
|
+
import { trace as trace3, context as otContext3 } from "@opentelemetry/api";
|
|
5197
5544
|
var CommandCheckProvider = class extends CheckProvider {
|
|
5198
5545
|
liquid;
|
|
5199
5546
|
sandbox;
|
|
@@ -5206,13 +5553,7 @@ var CommandCheckProvider = class extends CheckProvider {
|
|
|
5206
5553
|
});
|
|
5207
5554
|
}
|
|
5208
5555
|
createSecureSandbox() {
|
|
5209
|
-
|
|
5210
|
-
...Sandbox2.SAFE_GLOBALS,
|
|
5211
|
-
console,
|
|
5212
|
-
JSON
|
|
5213
|
-
};
|
|
5214
|
-
const prototypeWhitelist = new Map(Sandbox2.SAFE_PROTOTYPES);
|
|
5215
|
-
return new Sandbox2({ globals, prototypeWhitelist });
|
|
5556
|
+
return createSecureSandbox();
|
|
5216
5557
|
}
|
|
5217
5558
|
getName() {
|
|
5218
5559
|
return "command";
|
|
@@ -5261,6 +5602,24 @@ var CommandCheckProvider = class extends CheckProvider {
|
|
|
5261
5602
|
logger.debug(
|
|
5262
5603
|
`\u{1F527} Debug: Template outputs keys: ${Object.keys(templateContext.outputs || {}).join(", ")}`
|
|
5263
5604
|
);
|
|
5605
|
+
try {
|
|
5606
|
+
const span = trace3.getSpan(otContext3.active());
|
|
5607
|
+
if (span) {
|
|
5608
|
+
captureCheckInputContext(span, templateContext);
|
|
5609
|
+
}
|
|
5610
|
+
} catch {
|
|
5611
|
+
}
|
|
5612
|
+
try {
|
|
5613
|
+
const checkId = config.checkName || config.id || "unknown";
|
|
5614
|
+
const ctxJson = JSON.stringify(templateContext);
|
|
5615
|
+
const { emitNdjsonSpanWithEvents: emitNdjsonSpanWithEvents2 } = (init_fallback_ndjson(), __toCommonJS(fallback_ndjson_exports));
|
|
5616
|
+
emitNdjsonSpanWithEvents2(
|
|
5617
|
+
"visor.check",
|
|
5618
|
+
{ "visor.check.id": checkId, "visor.check.input.context": ctxJson },
|
|
5619
|
+
[{ name: "check.started" }, { name: "check.completed" }]
|
|
5620
|
+
);
|
|
5621
|
+
} catch {
|
|
5622
|
+
}
|
|
5264
5623
|
try {
|
|
5265
5624
|
let renderedCommand = command;
|
|
5266
5625
|
if (command.includes("{{") || command.includes("{%")) {
|
|
@@ -5455,8 +5814,12 @@ ${bodyWithReturn}
|
|
|
5455
5814
|
if (parsedFromSandboxJson !== void 0) {
|
|
5456
5815
|
finalOutput = parsedFromSandboxJson;
|
|
5457
5816
|
} else {
|
|
5458
|
-
|
|
5459
|
-
|
|
5817
|
+
finalOutput = compileAndRun(
|
|
5818
|
+
this.sandbox,
|
|
5819
|
+
code,
|
|
5820
|
+
{ scope: jsContext },
|
|
5821
|
+
{ injectLog: false, wrapFunction: false }
|
|
5822
|
+
);
|
|
5460
5823
|
}
|
|
5461
5824
|
try {
|
|
5462
5825
|
if (finalOutput && typeof finalOutput === "object" && !Array.isArray(finalOutput) && (finalOutput.error === void 0 || finalOutput.issues === void 0)) {
|
|
@@ -5867,6 +6230,27 @@ ${bodyWithReturn}
|
|
|
5867
6230
|
...content ? { content } : {},
|
|
5868
6231
|
...promoted
|
|
5869
6232
|
};
|
|
6233
|
+
try {
|
|
6234
|
+
const span = trace3.getSpan(otContext3.active());
|
|
6235
|
+
if (span) {
|
|
6236
|
+
captureCheckOutput(span, outputForDependents);
|
|
6237
|
+
if (transformJs && output !== finalOutput) {
|
|
6238
|
+
captureTransformJS(span, transformJs, output, finalOutput);
|
|
6239
|
+
}
|
|
6240
|
+
}
|
|
6241
|
+
} catch {
|
|
6242
|
+
}
|
|
6243
|
+
try {
|
|
6244
|
+
const checkId = config.checkName || config.id || "unknown";
|
|
6245
|
+
const outJson = JSON.stringify(result.output ?? result);
|
|
6246
|
+
const { emitNdjsonSpanWithEvents: emitNdjsonSpanWithEvents2 } = (init_fallback_ndjson(), __toCommonJS(fallback_ndjson_exports));
|
|
6247
|
+
emitNdjsonSpanWithEvents2(
|
|
6248
|
+
"visor.check",
|
|
6249
|
+
{ "visor.check.id": checkId, "visor.check.output": outJson },
|
|
6250
|
+
[{ name: "check.started" }, { name: "check.completed" }]
|
|
6251
|
+
);
|
|
6252
|
+
} catch {
|
|
6253
|
+
}
|
|
5870
6254
|
try {
|
|
5871
6255
|
if (transformJs) {
|
|
5872
6256
|
const rawObj = snapshotForExtraction || finalOutput;
|
|
@@ -6431,7 +6815,6 @@ ${stderrOutput}` : `Command execution failed: ${errorMessage}`;
|
|
|
6431
6815
|
|
|
6432
6816
|
// src/providers/memory-check-provider.ts
|
|
6433
6817
|
init_logger();
|
|
6434
|
-
import Sandbox3 from "@nyariv/sandboxjs";
|
|
6435
6818
|
var MemoryCheckProvider = class extends CheckProvider {
|
|
6436
6819
|
liquid;
|
|
6437
6820
|
sandbox;
|
|
@@ -6446,61 +6829,7 @@ var MemoryCheckProvider = class extends CheckProvider {
|
|
|
6446
6829
|
* Create a secure sandbox for JavaScript execution
|
|
6447
6830
|
*/
|
|
6448
6831
|
createSecureSandbox() {
|
|
6449
|
-
|
|
6450
|
-
...Sandbox3.SAFE_GLOBALS,
|
|
6451
|
-
Math,
|
|
6452
|
-
console: {
|
|
6453
|
-
log: console.log,
|
|
6454
|
-
warn: console.warn,
|
|
6455
|
-
error: console.error
|
|
6456
|
-
}
|
|
6457
|
-
};
|
|
6458
|
-
const prototypeWhitelist = new Map(Sandbox3.SAFE_PROTOTYPES);
|
|
6459
|
-
const arrayMethods = /* @__PURE__ */ new Set([
|
|
6460
|
-
"some",
|
|
6461
|
-
"every",
|
|
6462
|
-
"filter",
|
|
6463
|
-
"map",
|
|
6464
|
-
"reduce",
|
|
6465
|
-
"find",
|
|
6466
|
-
"includes",
|
|
6467
|
-
"indexOf",
|
|
6468
|
-
"length",
|
|
6469
|
-
"slice",
|
|
6470
|
-
"concat",
|
|
6471
|
-
"join",
|
|
6472
|
-
"push",
|
|
6473
|
-
"pop",
|
|
6474
|
-
"shift",
|
|
6475
|
-
"unshift",
|
|
6476
|
-
"sort",
|
|
6477
|
-
"reverse"
|
|
6478
|
-
]);
|
|
6479
|
-
prototypeWhitelist.set(Array.prototype, arrayMethods);
|
|
6480
|
-
const stringMethods = /* @__PURE__ */ new Set([
|
|
6481
|
-
"toLowerCase",
|
|
6482
|
-
"toUpperCase",
|
|
6483
|
-
"includes",
|
|
6484
|
-
"indexOf",
|
|
6485
|
-
"startsWith",
|
|
6486
|
-
"endsWith",
|
|
6487
|
-
"slice",
|
|
6488
|
-
"substring",
|
|
6489
|
-
"length",
|
|
6490
|
-
"trim",
|
|
6491
|
-
"split",
|
|
6492
|
-
"replace",
|
|
6493
|
-
"match",
|
|
6494
|
-
"padStart",
|
|
6495
|
-
"padEnd"
|
|
6496
|
-
]);
|
|
6497
|
-
prototypeWhitelist.set(String.prototype, stringMethods);
|
|
6498
|
-
const objectMethods = /* @__PURE__ */ new Set(["hasOwnProperty", "toString", "valueOf"]);
|
|
6499
|
-
prototypeWhitelist.set(Object.prototype, objectMethods);
|
|
6500
|
-
return new Sandbox3({
|
|
6501
|
-
globals,
|
|
6502
|
-
prototypeWhitelist
|
|
6503
|
-
});
|
|
6832
|
+
return createSecureSandbox();
|
|
6504
6833
|
}
|
|
6505
6834
|
getName() {
|
|
6506
6835
|
return "memory";
|
|
@@ -6792,15 +7121,12 @@ var MemoryCheckProvider = class extends CheckProvider {
|
|
|
6792
7121
|
this.sandbox = this.createSecureSandbox();
|
|
6793
7122
|
}
|
|
6794
7123
|
try {
|
|
6795
|
-
const
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
};
|
|
6802
|
-
const exec = this.sandbox.compile(`return (${expression});`);
|
|
6803
|
-
return exec(scope).run();
|
|
7124
|
+
const scope = { ...context };
|
|
7125
|
+
return compileAndRun(this.sandbox, `return (${expression});`, scope, {
|
|
7126
|
+
injectLog: true,
|
|
7127
|
+
wrapFunction: false,
|
|
7128
|
+
logPrefix: "[memory:value_js]"
|
|
7129
|
+
});
|
|
6804
7130
|
} catch (error) {
|
|
6805
7131
|
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
6806
7132
|
throw new Error(`Failed to evaluate value_js: ${errorMsg}`);
|
|
@@ -6811,20 +7137,16 @@ var MemoryCheckProvider = class extends CheckProvider {
|
|
|
6811
7137
|
* Unlike evaluateJavaScript, this supports full scripts with statements, not just expressions
|
|
6812
7138
|
*/
|
|
6813
7139
|
evaluateJavaScriptBlock(script, context) {
|
|
6814
|
-
if (!this.sandbox) {
|
|
6815
|
-
this.sandbox = this.createSecureSandbox();
|
|
6816
|
-
}
|
|
6817
|
-
try {
|
|
6818
|
-
const
|
|
6819
|
-
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
};
|
|
6825
|
-
const exec = this.sandbox.compile(script);
|
|
6826
|
-
const result = exec(scope).run();
|
|
6827
|
-
return result;
|
|
7140
|
+
if (!this.sandbox) {
|
|
7141
|
+
this.sandbox = this.createSecureSandbox();
|
|
7142
|
+
}
|
|
7143
|
+
try {
|
|
7144
|
+
const scope = { ...context };
|
|
7145
|
+
return compileAndRun(this.sandbox, script, scope, {
|
|
7146
|
+
injectLog: true,
|
|
7147
|
+
wrapFunction: false,
|
|
7148
|
+
logPrefix: "[memory:exec_js]"
|
|
7149
|
+
});
|
|
6828
7150
|
} catch (error) {
|
|
6829
7151
|
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
6830
7152
|
logger.error(`[memory-js] Script execution error: ${errorMsg}`);
|
|
@@ -6916,7 +7238,6 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
|
6916
7238
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
6917
7239
|
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
6918
7240
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
6919
|
-
import Sandbox4 from "@nyariv/sandboxjs";
|
|
6920
7241
|
var McpCheckProvider = class extends CheckProvider {
|
|
6921
7242
|
liquid;
|
|
6922
7243
|
sandbox;
|
|
@@ -6935,67 +7256,7 @@ var McpCheckProvider = class extends CheckProvider {
|
|
|
6935
7256
|
* - No access to filesystem, network, or system resources
|
|
6936
7257
|
*/
|
|
6937
7258
|
createSecureSandbox() {
|
|
6938
|
-
|
|
6939
|
-
logger.debug(`[transform_js] ${args.map((a) => JSON.stringify(a)).join(" ")}`);
|
|
6940
|
-
};
|
|
6941
|
-
const globals = {
|
|
6942
|
-
...Sandbox4.SAFE_GLOBALS,
|
|
6943
|
-
// Excludes Function, eval, require, process, etc.
|
|
6944
|
-
Math,
|
|
6945
|
-
JSON,
|
|
6946
|
-
console: {
|
|
6947
|
-
log: log2
|
|
6948
|
-
}
|
|
6949
|
-
};
|
|
6950
|
-
const prototypeWhitelist = new Map(Sandbox4.SAFE_PROTOTYPES);
|
|
6951
|
-
const arrayMethods = /* @__PURE__ */ new Set([
|
|
6952
|
-
"some",
|
|
6953
|
-
"every",
|
|
6954
|
-
"filter",
|
|
6955
|
-
"map",
|
|
6956
|
-
"reduce",
|
|
6957
|
-
"find",
|
|
6958
|
-
"includes",
|
|
6959
|
-
"indexOf",
|
|
6960
|
-
"length",
|
|
6961
|
-
"slice",
|
|
6962
|
-
"concat",
|
|
6963
|
-
"join",
|
|
6964
|
-
"push",
|
|
6965
|
-
"pop",
|
|
6966
|
-
"shift",
|
|
6967
|
-
"unshift",
|
|
6968
|
-
"sort",
|
|
6969
|
-
"reverse",
|
|
6970
|
-
"flat",
|
|
6971
|
-
"flatMap"
|
|
6972
|
-
]);
|
|
6973
|
-
prototypeWhitelist.set(Array.prototype, arrayMethods);
|
|
6974
|
-
const stringMethods = /* @__PURE__ */ new Set([
|
|
6975
|
-
"toLowerCase",
|
|
6976
|
-
"toUpperCase",
|
|
6977
|
-
"includes",
|
|
6978
|
-
"indexOf",
|
|
6979
|
-
"startsWith",
|
|
6980
|
-
"endsWith",
|
|
6981
|
-
"slice",
|
|
6982
|
-
"substring",
|
|
6983
|
-
"length",
|
|
6984
|
-
"trim",
|
|
6985
|
-
"split",
|
|
6986
|
-
"replace",
|
|
6987
|
-
// String.prototype.replace for text manipulation (e.g., "hello".replace("h", "H"))
|
|
6988
|
-
"match",
|
|
6989
|
-
"padStart",
|
|
6990
|
-
"padEnd"
|
|
6991
|
-
]);
|
|
6992
|
-
prototypeWhitelist.set(String.prototype, stringMethods);
|
|
6993
|
-
const objectMethods = /* @__PURE__ */ new Set(["hasOwnProperty", "toString", "valueOf", "keys", "values"]);
|
|
6994
|
-
prototypeWhitelist.set(Object.prototype, objectMethods);
|
|
6995
|
-
return new Sandbox4({
|
|
6996
|
-
globals,
|
|
6997
|
-
prototypeWhitelist
|
|
6998
|
-
});
|
|
7259
|
+
return createSecureSandbox();
|
|
6999
7260
|
}
|
|
7000
7261
|
getName() {
|
|
7001
7262
|
return "mcp";
|
|
@@ -7124,8 +7385,12 @@ var McpCheckProvider = class extends CheckProvider {
|
|
|
7124
7385
|
outputs: templateContext.outputs,
|
|
7125
7386
|
env: templateContext.env
|
|
7126
7387
|
};
|
|
7127
|
-
|
|
7128
|
-
|
|
7388
|
+
finalOutput = compileAndRun(
|
|
7389
|
+
this.sandbox,
|
|
7390
|
+
`return (${cfg.transform_js});`,
|
|
7391
|
+
scope,
|
|
7392
|
+
{ injectLog: true, wrapFunction: false, logPrefix: "[mcp:transform_js]" }
|
|
7393
|
+
);
|
|
7129
7394
|
} catch (error) {
|
|
7130
7395
|
logger.error(`Failed to apply JavaScript transform: ${error}`);
|
|
7131
7396
|
return {
|
|
@@ -7490,6 +7755,484 @@ var McpCheckProvider = class extends CheckProvider {
|
|
|
7490
7755
|
}
|
|
7491
7756
|
};
|
|
7492
7757
|
|
|
7758
|
+
// src/utils/interactive-prompt.ts
|
|
7759
|
+
import * as readline from "readline";
|
|
7760
|
+
var colors = {
|
|
7761
|
+
reset: "\x1B[0m",
|
|
7762
|
+
dim: "\x1B[2m",
|
|
7763
|
+
bold: "\x1B[1m",
|
|
7764
|
+
cyan: "\x1B[36m",
|
|
7765
|
+
green: "\x1B[32m",
|
|
7766
|
+
yellow: "\x1B[33m",
|
|
7767
|
+
gray: "\x1B[90m"
|
|
7768
|
+
};
|
|
7769
|
+
var supportsUnicode = process.env.LANG?.includes("UTF-8") || process.platform === "darwin";
|
|
7770
|
+
var box = supportsUnicode ? {
|
|
7771
|
+
topLeft: "\u250C",
|
|
7772
|
+
topRight: "\u2510",
|
|
7773
|
+
bottomLeft: "\u2514",
|
|
7774
|
+
bottomRight: "\u2518",
|
|
7775
|
+
horizontal: "\u2500",
|
|
7776
|
+
vertical: "\u2502",
|
|
7777
|
+
leftT: "\u251C",
|
|
7778
|
+
rightT: "\u2524"
|
|
7779
|
+
} : {
|
|
7780
|
+
topLeft: "+",
|
|
7781
|
+
topRight: "+",
|
|
7782
|
+
bottomLeft: "+",
|
|
7783
|
+
bottomRight: "+",
|
|
7784
|
+
horizontal: "-",
|
|
7785
|
+
vertical: "|",
|
|
7786
|
+
leftT: "+",
|
|
7787
|
+
rightT: "+"
|
|
7788
|
+
};
|
|
7789
|
+
function formatTime(ms) {
|
|
7790
|
+
const seconds = Math.ceil(ms / 1e3);
|
|
7791
|
+
const mins = Math.floor(seconds / 60);
|
|
7792
|
+
const secs = seconds % 60;
|
|
7793
|
+
return `${mins}:${secs.toString().padStart(2, "0")}`;
|
|
7794
|
+
}
|
|
7795
|
+
function drawLine(char, width) {
|
|
7796
|
+
return char.repeat(width);
|
|
7797
|
+
}
|
|
7798
|
+
function wrapText(text, width) {
|
|
7799
|
+
const words = text.split(" ");
|
|
7800
|
+
const lines = [];
|
|
7801
|
+
let currentLine = "";
|
|
7802
|
+
for (const word of words) {
|
|
7803
|
+
if (currentLine.length + word.length + 1 <= width) {
|
|
7804
|
+
currentLine += (currentLine ? " " : "") + word;
|
|
7805
|
+
} else {
|
|
7806
|
+
if (currentLine) lines.push(currentLine);
|
|
7807
|
+
currentLine = word;
|
|
7808
|
+
}
|
|
7809
|
+
}
|
|
7810
|
+
if (currentLine) lines.push(currentLine);
|
|
7811
|
+
return lines;
|
|
7812
|
+
}
|
|
7813
|
+
function displayPromptUI(options, remainingMs) {
|
|
7814
|
+
const width = Math.min(process.stdout.columns || 80, 80) - 4;
|
|
7815
|
+
const icon = supportsUnicode ? "\u{1F4AC}" : ">";
|
|
7816
|
+
console.log("\n");
|
|
7817
|
+
console.log(`${box.topLeft}${drawLine(box.horizontal, width + 2)}${box.topRight}`);
|
|
7818
|
+
console.log(
|
|
7819
|
+
`${box.vertical} ${colors.bold}${icon} Human Input Required${colors.reset}${" ".repeat(
|
|
7820
|
+
width - 22
|
|
7821
|
+
)} ${box.vertical}`
|
|
7822
|
+
);
|
|
7823
|
+
console.log(`${box.leftT}${drawLine(box.horizontal, width + 2)}${box.rightT}`);
|
|
7824
|
+
console.log(`${box.vertical} ${" ".repeat(width)} ${box.vertical}`);
|
|
7825
|
+
const promptLines = wrapText(options.prompt, width - 2);
|
|
7826
|
+
for (const line of promptLines) {
|
|
7827
|
+
console.log(
|
|
7828
|
+
`${box.vertical} ${colors.cyan}${line}${colors.reset}${" ".repeat(
|
|
7829
|
+
width - line.length
|
|
7830
|
+
)} ${box.vertical}`
|
|
7831
|
+
);
|
|
7832
|
+
}
|
|
7833
|
+
console.log(`${box.vertical} ${" ".repeat(width)} ${box.vertical}`);
|
|
7834
|
+
const instruction = options.multiline ? "(Type your response, press Ctrl+D when done)" : "(Type your response and press Enter)";
|
|
7835
|
+
console.log(
|
|
7836
|
+
`${box.vertical} ${colors.dim}${instruction}${colors.reset}${" ".repeat(
|
|
7837
|
+
width - instruction.length
|
|
7838
|
+
)} ${box.vertical}`
|
|
7839
|
+
);
|
|
7840
|
+
if (options.placeholder && !options.multiline) {
|
|
7841
|
+
console.log(
|
|
7842
|
+
`${box.vertical} ${colors.dim}${options.placeholder}${colors.reset}${" ".repeat(
|
|
7843
|
+
width - options.placeholder.length
|
|
7844
|
+
)} ${box.vertical}`
|
|
7845
|
+
);
|
|
7846
|
+
}
|
|
7847
|
+
console.log(`${box.vertical} ${" ".repeat(width)} ${box.vertical}`);
|
|
7848
|
+
if (remainingMs !== void 0 && options.timeout) {
|
|
7849
|
+
const timeIcon = supportsUnicode ? "\u23F1 " : "Time: ";
|
|
7850
|
+
const timeStr = `${timeIcon} ${formatTime(remainingMs)} remaining`;
|
|
7851
|
+
console.log(
|
|
7852
|
+
`${box.vertical} ${colors.yellow}${timeStr}${colors.reset}${" ".repeat(
|
|
7853
|
+
width - timeStr.length
|
|
7854
|
+
)} ${box.vertical}`
|
|
7855
|
+
);
|
|
7856
|
+
}
|
|
7857
|
+
console.log(`${box.bottomLeft}${drawLine(box.horizontal, width + 2)}${box.bottomRight}`);
|
|
7858
|
+
console.log("");
|
|
7859
|
+
process.stdout.write(`${colors.green}>${colors.reset} `);
|
|
7860
|
+
}
|
|
7861
|
+
async function interactivePrompt(options) {
|
|
7862
|
+
return new Promise((resolve4, reject) => {
|
|
7863
|
+
let input = "";
|
|
7864
|
+
let timeoutId;
|
|
7865
|
+
let countdownInterval;
|
|
7866
|
+
let remainingMs = options.timeout;
|
|
7867
|
+
const rl = readline.createInterface({
|
|
7868
|
+
input: process.stdin,
|
|
7869
|
+
output: process.stdout,
|
|
7870
|
+
terminal: true
|
|
7871
|
+
});
|
|
7872
|
+
displayPromptUI(options, remainingMs);
|
|
7873
|
+
const cleanup = () => {
|
|
7874
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
7875
|
+
if (countdownInterval) clearInterval(countdownInterval);
|
|
7876
|
+
rl.close();
|
|
7877
|
+
};
|
|
7878
|
+
const finish = (value) => {
|
|
7879
|
+
cleanup();
|
|
7880
|
+
console.log("");
|
|
7881
|
+
resolve4(value);
|
|
7882
|
+
};
|
|
7883
|
+
if (options.timeout) {
|
|
7884
|
+
timeoutId = setTimeout(() => {
|
|
7885
|
+
cleanup();
|
|
7886
|
+
console.log(`
|
|
7887
|
+
${colors.yellow}\u23F1 Timeout reached${colors.reset}`);
|
|
7888
|
+
if (options.defaultValue !== void 0) {
|
|
7889
|
+
console.log(
|
|
7890
|
+
`${colors.gray}Using default value: ${options.defaultValue}${colors.reset}
|
|
7891
|
+
`
|
|
7892
|
+
);
|
|
7893
|
+
resolve4(options.defaultValue);
|
|
7894
|
+
} else {
|
|
7895
|
+
reject(new Error("Input timeout"));
|
|
7896
|
+
}
|
|
7897
|
+
}, options.timeout);
|
|
7898
|
+
if (remainingMs) {
|
|
7899
|
+
countdownInterval = setInterval(() => {
|
|
7900
|
+
remainingMs = remainingMs - 1e3;
|
|
7901
|
+
if (remainingMs <= 0) {
|
|
7902
|
+
if (countdownInterval) clearInterval(countdownInterval);
|
|
7903
|
+
}
|
|
7904
|
+
}, 1e3);
|
|
7905
|
+
}
|
|
7906
|
+
}
|
|
7907
|
+
if (options.multiline) {
|
|
7908
|
+
rl.on("line", (line) => {
|
|
7909
|
+
input += (input ? "\n" : "") + line;
|
|
7910
|
+
});
|
|
7911
|
+
rl.on("close", () => {
|
|
7912
|
+
cleanup();
|
|
7913
|
+
const trimmed = input.trim();
|
|
7914
|
+
if (!trimmed && !options.allowEmpty) {
|
|
7915
|
+
console.log(`${colors.yellow}\u26A0 Empty input not allowed${colors.reset}`);
|
|
7916
|
+
reject(new Error("Empty input not allowed"));
|
|
7917
|
+
} else {
|
|
7918
|
+
finish(trimmed);
|
|
7919
|
+
}
|
|
7920
|
+
});
|
|
7921
|
+
} else {
|
|
7922
|
+
rl.question("", (answer) => {
|
|
7923
|
+
const trimmed = answer.trim();
|
|
7924
|
+
if (!trimmed && !options.allowEmpty && !options.defaultValue) {
|
|
7925
|
+
cleanup();
|
|
7926
|
+
console.log(`${colors.yellow}\u26A0 Empty input not allowed${colors.reset}`);
|
|
7927
|
+
reject(new Error("Empty input not allowed"));
|
|
7928
|
+
} else {
|
|
7929
|
+
finish(trimmed || options.defaultValue || "");
|
|
7930
|
+
}
|
|
7931
|
+
});
|
|
7932
|
+
}
|
|
7933
|
+
rl.on("SIGINT", () => {
|
|
7934
|
+
cleanup();
|
|
7935
|
+
console.log("\n\n" + colors.yellow + "\u26A0 Cancelled by user" + colors.reset);
|
|
7936
|
+
reject(new Error("Cancelled by user"));
|
|
7937
|
+
});
|
|
7938
|
+
});
|
|
7939
|
+
}
|
|
7940
|
+
async function simplePrompt(prompt) {
|
|
7941
|
+
return new Promise((resolve4) => {
|
|
7942
|
+
const rl = readline.createInterface({
|
|
7943
|
+
input: process.stdin,
|
|
7944
|
+
output: process.stdout
|
|
7945
|
+
});
|
|
7946
|
+
rl.question(`${prompt}
|
|
7947
|
+
> `, (answer) => {
|
|
7948
|
+
rl.close();
|
|
7949
|
+
resolve4(answer.trim());
|
|
7950
|
+
});
|
|
7951
|
+
});
|
|
7952
|
+
}
|
|
7953
|
+
|
|
7954
|
+
// src/utils/stdin-reader.ts
|
|
7955
|
+
function isStdinAvailable() {
|
|
7956
|
+
return !process.stdin.isTTY;
|
|
7957
|
+
}
|
|
7958
|
+
async function readStdin(timeout, maxSize = 1024 * 1024) {
|
|
7959
|
+
return new Promise((resolve4, reject) => {
|
|
7960
|
+
let data = "";
|
|
7961
|
+
let timeoutId;
|
|
7962
|
+
if (timeout) {
|
|
7963
|
+
timeoutId = setTimeout(() => {
|
|
7964
|
+
cleanup();
|
|
7965
|
+
reject(new Error(`Stdin read timeout after ${timeout}ms`));
|
|
7966
|
+
}, timeout);
|
|
7967
|
+
}
|
|
7968
|
+
const cleanup = () => {
|
|
7969
|
+
if (timeoutId) {
|
|
7970
|
+
clearTimeout(timeoutId);
|
|
7971
|
+
}
|
|
7972
|
+
process.stdin.removeListener("data", onData);
|
|
7973
|
+
process.stdin.removeListener("end", onEnd);
|
|
7974
|
+
process.stdin.removeListener("error", onError);
|
|
7975
|
+
process.stdin.pause();
|
|
7976
|
+
};
|
|
7977
|
+
const onData = (chunk) => {
|
|
7978
|
+
data += chunk.toString();
|
|
7979
|
+
if (data.length > maxSize) {
|
|
7980
|
+
cleanup();
|
|
7981
|
+
reject(new Error(`Input exceeds maximum size of ${maxSize} bytes`));
|
|
7982
|
+
}
|
|
7983
|
+
};
|
|
7984
|
+
const onEnd = () => {
|
|
7985
|
+
cleanup();
|
|
7986
|
+
resolve4(data.trim());
|
|
7987
|
+
};
|
|
7988
|
+
const onError = (err) => {
|
|
7989
|
+
cleanup();
|
|
7990
|
+
reject(err);
|
|
7991
|
+
};
|
|
7992
|
+
process.stdin.setEncoding("utf8");
|
|
7993
|
+
process.stdin.on("data", onData);
|
|
7994
|
+
process.stdin.on("end", onEnd);
|
|
7995
|
+
process.stdin.on("error", onError);
|
|
7996
|
+
process.stdin.resume();
|
|
7997
|
+
});
|
|
7998
|
+
}
|
|
7999
|
+
async function tryReadStdin(timeout, maxSize = 1024 * 1024) {
|
|
8000
|
+
if (!isStdinAvailable()) {
|
|
8001
|
+
return null;
|
|
8002
|
+
}
|
|
8003
|
+
try {
|
|
8004
|
+
return await readStdin(timeout, maxSize);
|
|
8005
|
+
} catch {
|
|
8006
|
+
return null;
|
|
8007
|
+
}
|
|
8008
|
+
}
|
|
8009
|
+
|
|
8010
|
+
// src/providers/human-input-check-provider.ts
|
|
8011
|
+
import * as fs6 from "fs";
|
|
8012
|
+
import * as path8 from "path";
|
|
8013
|
+
var HumanInputCheckProvider = class _HumanInputCheckProvider extends CheckProvider {
|
|
8014
|
+
/**
|
|
8015
|
+
* @deprecated Use ExecutionContext.cliMessage instead
|
|
8016
|
+
* Kept for backward compatibility
|
|
8017
|
+
*/
|
|
8018
|
+
static cliMessage;
|
|
8019
|
+
/**
|
|
8020
|
+
* @deprecated Use ExecutionContext.hooks instead
|
|
8021
|
+
* Kept for backward compatibility
|
|
8022
|
+
*/
|
|
8023
|
+
static hooks = {};
|
|
8024
|
+
/**
|
|
8025
|
+
* Set the CLI message value (from --message argument)
|
|
8026
|
+
* @deprecated Use ExecutionContext.cliMessage instead
|
|
8027
|
+
*/
|
|
8028
|
+
static setCLIMessage(message) {
|
|
8029
|
+
_HumanInputCheckProvider.cliMessage = message;
|
|
8030
|
+
}
|
|
8031
|
+
/**
|
|
8032
|
+
* Get the current CLI message value
|
|
8033
|
+
* @deprecated Use ExecutionContext.cliMessage instead
|
|
8034
|
+
*/
|
|
8035
|
+
static getCLIMessage() {
|
|
8036
|
+
return _HumanInputCheckProvider.cliMessage;
|
|
8037
|
+
}
|
|
8038
|
+
/**
|
|
8039
|
+
* Set hooks for SDK mode
|
|
8040
|
+
* @deprecated Use ExecutionContext.hooks instead
|
|
8041
|
+
*/
|
|
8042
|
+
static setHooks(hooks) {
|
|
8043
|
+
_HumanInputCheckProvider.hooks = hooks;
|
|
8044
|
+
}
|
|
8045
|
+
getName() {
|
|
8046
|
+
return "human-input";
|
|
8047
|
+
}
|
|
8048
|
+
getDescription() {
|
|
8049
|
+
return "Prompts for human input during workflow execution (CLI interactive or SDK hook)";
|
|
8050
|
+
}
|
|
8051
|
+
async validateConfig(config) {
|
|
8052
|
+
if (!config || typeof config !== "object") {
|
|
8053
|
+
return false;
|
|
8054
|
+
}
|
|
8055
|
+
const cfg = config;
|
|
8056
|
+
if (cfg.type !== "human-input") {
|
|
8057
|
+
return false;
|
|
8058
|
+
}
|
|
8059
|
+
if (!cfg.prompt || typeof cfg.prompt !== "string") {
|
|
8060
|
+
console.error('human-input check requires a "prompt" field');
|
|
8061
|
+
return false;
|
|
8062
|
+
}
|
|
8063
|
+
return true;
|
|
8064
|
+
}
|
|
8065
|
+
/**
|
|
8066
|
+
* Check if a string looks like a file path
|
|
8067
|
+
*/
|
|
8068
|
+
looksLikePath(str) {
|
|
8069
|
+
return str.includes("/") || str.includes("\\");
|
|
8070
|
+
}
|
|
8071
|
+
/**
|
|
8072
|
+
* Sanitize user input to prevent injection attacks in dependent checks
|
|
8073
|
+
* Removes potentially dangerous characters while preserving useful input
|
|
8074
|
+
*/
|
|
8075
|
+
sanitizeInput(input) {
|
|
8076
|
+
let sanitized = input.replace(/\0/g, "");
|
|
8077
|
+
sanitized = sanitized.replace(/[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F]/g, "");
|
|
8078
|
+
const maxLength = 100 * 1024;
|
|
8079
|
+
if (sanitized.length > maxLength) {
|
|
8080
|
+
sanitized = sanitized.substring(0, maxLength);
|
|
8081
|
+
}
|
|
8082
|
+
return sanitized;
|
|
8083
|
+
}
|
|
8084
|
+
/**
|
|
8085
|
+
* Try to read message from file if it exists
|
|
8086
|
+
* Validates path to prevent directory traversal attacks
|
|
8087
|
+
*/
|
|
8088
|
+
async tryReadFile(filePath) {
|
|
8089
|
+
try {
|
|
8090
|
+
const absolutePath = path8.isAbsolute(filePath) ? filePath : path8.resolve(process.cwd(), filePath);
|
|
8091
|
+
const normalizedPath = path8.normalize(absolutePath);
|
|
8092
|
+
const cwd = process.cwd();
|
|
8093
|
+
if (!normalizedPath.startsWith(cwd + path8.sep) && normalizedPath !== cwd) {
|
|
8094
|
+
return null;
|
|
8095
|
+
}
|
|
8096
|
+
try {
|
|
8097
|
+
await fs6.promises.access(normalizedPath, fs6.constants.R_OK);
|
|
8098
|
+
const stats = await fs6.promises.stat(normalizedPath);
|
|
8099
|
+
if (!stats.isFile()) {
|
|
8100
|
+
return null;
|
|
8101
|
+
}
|
|
8102
|
+
const content = await fs6.promises.readFile(normalizedPath, "utf-8");
|
|
8103
|
+
return content.trim();
|
|
8104
|
+
} catch {
|
|
8105
|
+
return null;
|
|
8106
|
+
}
|
|
8107
|
+
} catch {
|
|
8108
|
+
}
|
|
8109
|
+
return null;
|
|
8110
|
+
}
|
|
8111
|
+
/**
|
|
8112
|
+
* Get user input through various methods
|
|
8113
|
+
*/
|
|
8114
|
+
async getUserInput(checkName, config, context) {
|
|
8115
|
+
const prompt = config.prompt || "Please provide input:";
|
|
8116
|
+
const placeholder = config.placeholder || "Enter your response...";
|
|
8117
|
+
const allowEmpty = config.allow_empty ?? false;
|
|
8118
|
+
const multiline = config.multiline ?? false;
|
|
8119
|
+
const timeout = config.timeout ? config.timeout * 1e3 : void 0;
|
|
8120
|
+
const defaultValue = config.default;
|
|
8121
|
+
const cliMessage = context?.cliMessage ?? _HumanInputCheckProvider.cliMessage;
|
|
8122
|
+
if (cliMessage !== void 0) {
|
|
8123
|
+
const message = cliMessage;
|
|
8124
|
+
if (this.looksLikePath(message)) {
|
|
8125
|
+
const fileContent = await this.tryReadFile(message);
|
|
8126
|
+
if (fileContent !== null) {
|
|
8127
|
+
return fileContent;
|
|
8128
|
+
}
|
|
8129
|
+
}
|
|
8130
|
+
return message;
|
|
8131
|
+
}
|
|
8132
|
+
const stdinInput = await tryReadStdin(timeout);
|
|
8133
|
+
if (stdinInput !== null && stdinInput.length > 0) {
|
|
8134
|
+
return stdinInput;
|
|
8135
|
+
}
|
|
8136
|
+
const hooks = context?.hooks ?? _HumanInputCheckProvider.hooks;
|
|
8137
|
+
if (hooks?.onHumanInput) {
|
|
8138
|
+
const request = {
|
|
8139
|
+
checkId: checkName,
|
|
8140
|
+
prompt,
|
|
8141
|
+
placeholder,
|
|
8142
|
+
allowEmpty,
|
|
8143
|
+
multiline,
|
|
8144
|
+
timeout,
|
|
8145
|
+
default: defaultValue
|
|
8146
|
+
};
|
|
8147
|
+
try {
|
|
8148
|
+
const result = await hooks.onHumanInput(request);
|
|
8149
|
+
return result;
|
|
8150
|
+
} catch (error) {
|
|
8151
|
+
throw new Error(
|
|
8152
|
+
`Hook onHumanInput failed: ${error instanceof Error ? error.message : String(error)}`
|
|
8153
|
+
);
|
|
8154
|
+
}
|
|
8155
|
+
}
|
|
8156
|
+
if (process.stdin.isTTY) {
|
|
8157
|
+
try {
|
|
8158
|
+
const result = await interactivePrompt({
|
|
8159
|
+
prompt,
|
|
8160
|
+
placeholder,
|
|
8161
|
+
multiline,
|
|
8162
|
+
timeout,
|
|
8163
|
+
defaultValue,
|
|
8164
|
+
allowEmpty
|
|
8165
|
+
});
|
|
8166
|
+
return result;
|
|
8167
|
+
} catch (error) {
|
|
8168
|
+
throw new Error(
|
|
8169
|
+
`Interactive prompt failed: ${error instanceof Error ? error.message : String(error)}`
|
|
8170
|
+
);
|
|
8171
|
+
}
|
|
8172
|
+
}
|
|
8173
|
+
try {
|
|
8174
|
+
const result = await simplePrompt(prompt);
|
|
8175
|
+
if (!result && !allowEmpty && !defaultValue) {
|
|
8176
|
+
throw new Error("Empty input not allowed");
|
|
8177
|
+
}
|
|
8178
|
+
return result || defaultValue || "";
|
|
8179
|
+
} catch (error) {
|
|
8180
|
+
throw new Error(
|
|
8181
|
+
`Simple prompt failed: ${error instanceof Error ? error.message : String(error)}`
|
|
8182
|
+
);
|
|
8183
|
+
}
|
|
8184
|
+
}
|
|
8185
|
+
async execute(_prInfo, config, _dependencyResults, context) {
|
|
8186
|
+
const checkName = config.checkName || "human-input";
|
|
8187
|
+
try {
|
|
8188
|
+
const userInput = await this.getUserInput(checkName, config, context);
|
|
8189
|
+
const sanitizedInput = this.sanitizeInput(userInput);
|
|
8190
|
+
return {
|
|
8191
|
+
issues: [],
|
|
8192
|
+
output: sanitizedInput
|
|
8193
|
+
};
|
|
8194
|
+
} catch (error) {
|
|
8195
|
+
return {
|
|
8196
|
+
issues: [
|
|
8197
|
+
{
|
|
8198
|
+
file: "",
|
|
8199
|
+
line: 0,
|
|
8200
|
+
ruleId: "human-input-error",
|
|
8201
|
+
message: `Failed to get user input: ${error instanceof Error ? error.message : String(error)}`,
|
|
8202
|
+
severity: "error",
|
|
8203
|
+
category: "logic"
|
|
8204
|
+
}
|
|
8205
|
+
]
|
|
8206
|
+
};
|
|
8207
|
+
}
|
|
8208
|
+
}
|
|
8209
|
+
getSupportedConfigKeys() {
|
|
8210
|
+
return [
|
|
8211
|
+
"type",
|
|
8212
|
+
"prompt",
|
|
8213
|
+
"placeholder",
|
|
8214
|
+
"allow_empty",
|
|
8215
|
+
"multiline",
|
|
8216
|
+
"timeout",
|
|
8217
|
+
"default",
|
|
8218
|
+
"depends_on",
|
|
8219
|
+
"on",
|
|
8220
|
+
"if",
|
|
8221
|
+
"group"
|
|
8222
|
+
];
|
|
8223
|
+
}
|
|
8224
|
+
async isAvailable() {
|
|
8225
|
+
return true;
|
|
8226
|
+
}
|
|
8227
|
+
getRequirements() {
|
|
8228
|
+
return [
|
|
8229
|
+
"No external dependencies required",
|
|
8230
|
+
"Works in CLI mode with --message argument, piped stdin, or interactive prompts",
|
|
8231
|
+
"SDK mode requires onHumanInput hook to be configured"
|
|
8232
|
+
];
|
|
8233
|
+
}
|
|
8234
|
+
};
|
|
8235
|
+
|
|
7493
8236
|
// src/providers/check-provider-registry.ts
|
|
7494
8237
|
var CheckProviderRegistry = class _CheckProviderRegistry {
|
|
7495
8238
|
providers = /* @__PURE__ */ new Map();
|
|
@@ -7519,6 +8262,7 @@ var CheckProviderRegistry = class _CheckProviderRegistry {
|
|
|
7519
8262
|
this.register(new LogCheckProvider());
|
|
7520
8263
|
this.register(new MemoryCheckProvider());
|
|
7521
8264
|
this.register(new GitHubOpsProvider());
|
|
8265
|
+
this.register(new HumanInputCheckProvider());
|
|
7522
8266
|
try {
|
|
7523
8267
|
this.register(new ClaudeCodeCheckProvider());
|
|
7524
8268
|
} catch (error) {
|
|
@@ -7823,7 +8567,6 @@ var DependencyResolver = class {
|
|
|
7823
8567
|
};
|
|
7824
8568
|
|
|
7825
8569
|
// src/failure-condition-evaluator.ts
|
|
7826
|
-
import Sandbox5 from "@nyariv/sandboxjs";
|
|
7827
8570
|
var FailureConditionEvaluator = class _FailureConditionEvaluator {
|
|
7828
8571
|
sandbox;
|
|
7829
8572
|
constructor() {
|
|
@@ -7832,54 +8575,7 @@ var FailureConditionEvaluator = class _FailureConditionEvaluator {
|
|
|
7832
8575
|
* Create a secure sandbox with whitelisted functions and globals
|
|
7833
8576
|
*/
|
|
7834
8577
|
createSecureSandbox() {
|
|
7835
|
-
|
|
7836
|
-
...Sandbox5.SAFE_GLOBALS,
|
|
7837
|
-
// Allow Math for calculations
|
|
7838
|
-
Math,
|
|
7839
|
-
// Allow console for debugging (in controlled environment)
|
|
7840
|
-
console: {
|
|
7841
|
-
log: console.log,
|
|
7842
|
-
warn: console.warn,
|
|
7843
|
-
error: console.error
|
|
7844
|
-
}
|
|
7845
|
-
};
|
|
7846
|
-
const prototypeWhitelist = new Map(Sandbox5.SAFE_PROTOTYPES);
|
|
7847
|
-
const arrayMethods = /* @__PURE__ */ new Set([
|
|
7848
|
-
"some",
|
|
7849
|
-
"every",
|
|
7850
|
-
"filter",
|
|
7851
|
-
"map",
|
|
7852
|
-
"reduce",
|
|
7853
|
-
"find",
|
|
7854
|
-
"includes",
|
|
7855
|
-
"indexOf",
|
|
7856
|
-
"length",
|
|
7857
|
-
"slice",
|
|
7858
|
-
"concat",
|
|
7859
|
-
"join"
|
|
7860
|
-
]);
|
|
7861
|
-
prototypeWhitelist.set(Array.prototype, arrayMethods);
|
|
7862
|
-
const stringMethods = /* @__PURE__ */ new Set([
|
|
7863
|
-
"toLowerCase",
|
|
7864
|
-
"toUpperCase",
|
|
7865
|
-
"includes",
|
|
7866
|
-
"indexOf",
|
|
7867
|
-
"startsWith",
|
|
7868
|
-
"endsWith",
|
|
7869
|
-
"slice",
|
|
7870
|
-
"substring",
|
|
7871
|
-
"length",
|
|
7872
|
-
"trim",
|
|
7873
|
-
"split",
|
|
7874
|
-
"replace"
|
|
7875
|
-
]);
|
|
7876
|
-
prototypeWhitelist.set(String.prototype, stringMethods);
|
|
7877
|
-
const objectMethods = /* @__PURE__ */ new Set(["hasOwnProperty", "toString", "valueOf"]);
|
|
7878
|
-
prototypeWhitelist.set(Object.prototype, objectMethods);
|
|
7879
|
-
return new Sandbox5({
|
|
7880
|
-
globals,
|
|
7881
|
-
prototypeWhitelist
|
|
7882
|
-
});
|
|
8578
|
+
return createSecureSandbox();
|
|
7883
8579
|
}
|
|
7884
8580
|
/**
|
|
7885
8581
|
* Evaluate simple fail_if condition
|
|
@@ -8152,7 +8848,7 @@ var FailureConditionEvaluator = class _FailureConditionEvaluator {
|
|
|
8152
8848
|
*/
|
|
8153
8849
|
evaluateExpression(condition, context) {
|
|
8154
8850
|
try {
|
|
8155
|
-
const
|
|
8851
|
+
const normalize2 = (expr) => {
|
|
8156
8852
|
const trimmed = expr.trim();
|
|
8157
8853
|
if (!/[\n;]/.test(trimmed)) return trimmed;
|
|
8158
8854
|
const parts = trimmed.split(/[\n;]+/).map((s) => s.trim()).filter((s) => s.length > 0 && !s.startsWith("//"));
|
|
@@ -8295,7 +8991,7 @@ var FailureConditionEvaluator = class _FailureConditionEvaluator {
|
|
|
8295
8991
|
try {
|
|
8296
8992
|
exec = this.sandbox.compile(`return (${raw});`);
|
|
8297
8993
|
} catch {
|
|
8298
|
-
const normalizedExpr =
|
|
8994
|
+
const normalizedExpr = normalize2(condition);
|
|
8299
8995
|
exec = this.sandbox.compile(`return (${normalizedExpr});`);
|
|
8300
8996
|
}
|
|
8301
8997
|
const result = exec(scope).run();
|
|
@@ -8981,8 +9677,8 @@ Please check your configuration and try again.`
|
|
|
8981
9677
|
|
|
8982
9678
|
// src/check-execution-engine.ts
|
|
8983
9679
|
init_logger();
|
|
8984
|
-
import Sandbox6 from "@nyariv/sandboxjs";
|
|
8985
9680
|
init_fallback_ndjson();
|
|
9681
|
+
import { trace as trace4, context as otContext4 } from "@opentelemetry/api";
|
|
8986
9682
|
function getSafeEnvironmentVariables() {
|
|
8987
9683
|
const safeEnvVars = [
|
|
8988
9684
|
"CI",
|
|
@@ -9024,6 +9720,8 @@ var CheckExecutionEngine = class _CheckExecutionEngine {
|
|
|
9024
9720
|
outputHistory = /* @__PURE__ */ new Map();
|
|
9025
9721
|
// Event override to simulate alternate event (used during routing goto)
|
|
9026
9722
|
routingEventOverride;
|
|
9723
|
+
// Execution context for providers (CLI message, hooks, etc.)
|
|
9724
|
+
executionContext;
|
|
9027
9725
|
// Cached GitHub context for context elevation when running in Actions
|
|
9028
9726
|
actionContext;
|
|
9029
9727
|
constructor(workingDirectory, octokit) {
|
|
@@ -9053,19 +9751,19 @@ var CheckExecutionEngine = class _CheckExecutionEngine {
|
|
|
9053
9751
|
}
|
|
9054
9752
|
return baseContext;
|
|
9055
9753
|
}
|
|
9754
|
+
/**
|
|
9755
|
+
* Set execution context for providers (CLI message, hooks, etc.)
|
|
9756
|
+
* This allows passing state without using static properties
|
|
9757
|
+
*/
|
|
9758
|
+
setExecutionContext(context) {
|
|
9759
|
+
this.executionContext = context;
|
|
9760
|
+
}
|
|
9056
9761
|
/**
|
|
9057
9762
|
* Lazily create a secure sandbox for routing JS (goto_js, run_js)
|
|
9058
9763
|
*/
|
|
9059
9764
|
getRoutingSandbox() {
|
|
9060
9765
|
if (this.routingSandbox) return this.routingSandbox;
|
|
9061
|
-
|
|
9062
|
-
...Sandbox6.SAFE_GLOBALS,
|
|
9063
|
-
Math,
|
|
9064
|
-
JSON,
|
|
9065
|
-
console: { log: console.log }
|
|
9066
|
-
};
|
|
9067
|
-
const prototypeWhitelist = new Map(Sandbox6.SAFE_PROTOTYPES);
|
|
9068
|
-
this.routingSandbox = new Sandbox6({ globals, prototypeWhitelist });
|
|
9766
|
+
this.routingSandbox = createSecureSandbox();
|
|
9069
9767
|
return this.routingSandbox;
|
|
9070
9768
|
}
|
|
9071
9769
|
redact(str, limit = 200) {
|
|
@@ -9077,7 +9775,7 @@ var CheckExecutionEngine = class _CheckExecutionEngine {
|
|
|
9077
9775
|
}
|
|
9078
9776
|
}
|
|
9079
9777
|
async sleep(ms) {
|
|
9080
|
-
return new Promise((
|
|
9778
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
9081
9779
|
}
|
|
9082
9780
|
deterministicJitter(baseMs, seedStr) {
|
|
9083
9781
|
let h = 2166136261;
|
|
@@ -9138,16 +9836,16 @@ var CheckExecutionEngine = class _CheckExecutionEngine {
|
|
|
9138
9836
|
),
|
|
9139
9837
|
event: eventObj
|
|
9140
9838
|
};
|
|
9141
|
-
const
|
|
9142
|
-
|
|
9143
|
-
|
|
9144
|
-
|
|
9145
|
-
|
|
9146
|
-
|
|
9147
|
-
|
|
9148
|
-
|
|
9149
|
-
|
|
9150
|
-
const res =
|
|
9839
|
+
const prelude = `const step = scope.step; const attempt = scope.attempt; const loop = scope.loop; const error = scope.error; const foreach = scope.foreach; const outputs = scope.outputs; const output = scope.output; const pr = scope.pr; const files = scope.files; const env = scope.env; const event = scope.event; const hasMinPermission = scope.permissions.hasMinPermission; const isOwner = scope.permissions.isOwner; const isMember = scope.permissions.isMember; const isCollaborator = scope.permissions.isCollaborator; const isContributor = scope.permissions.isContributor; const isFirstTimer = scope.permissions.isFirstTimer;`;
|
|
9840
|
+
const code = `${prelude}
|
|
9841
|
+
${expr}`;
|
|
9842
|
+
const result = compileAndRun(
|
|
9843
|
+
sandbox,
|
|
9844
|
+
code,
|
|
9845
|
+
{ scope },
|
|
9846
|
+
{ injectLog: false, wrapFunction: true }
|
|
9847
|
+
);
|
|
9848
|
+
const res = Array.isArray(result) ? result : result ? [result] : [];
|
|
9151
9849
|
if (debug) {
|
|
9152
9850
|
log2(`\u{1F527} Debug: run_js evaluated \u2192 [${this.redact(res)}]`);
|
|
9153
9851
|
}
|
|
@@ -9191,16 +9889,15 @@ ${expr}
|
|
|
9191
9889
|
),
|
|
9192
9890
|
event: eventObj
|
|
9193
9891
|
};
|
|
9194
|
-
const
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
const res = exec({ scope }).run();
|
|
9892
|
+
const prelude2 = `const step = scope.step; const attempt = scope.attempt; const loop = scope.loop; const error = scope.error; const foreach = scope.foreach; const outputs = scope.outputs; const output = scope.output; const pr = scope.pr; const files = scope.files; const env = scope.env; const event = scope.event; const hasMinPermission = scope.permissions.hasMinPermission; const isOwner = scope.permissions.isOwner; const isMember = scope.permissions.isMember; const isCollaborator = scope.permissions.isCollaborator; const isContributor = scope.permissions.isContributor; const isFirstTimer = scope.permissions.isFirstTimer;`;
|
|
9893
|
+
const code2 = `${prelude2}
|
|
9894
|
+
${expr}`;
|
|
9895
|
+
const res = compileAndRun(
|
|
9896
|
+
sandbox,
|
|
9897
|
+
code2,
|
|
9898
|
+
{ scope },
|
|
9899
|
+
{ injectLog: false, wrapFunction: true }
|
|
9900
|
+
);
|
|
9204
9901
|
if (debug) {
|
|
9205
9902
|
log2(`\u{1F527} Debug: goto_js evaluated \u2192 ${this.redact(res)}`);
|
|
9206
9903
|
}
|
|
@@ -9320,7 +10017,11 @@ ${expr}
|
|
|
9320
10017
|
}
|
|
9321
10018
|
let r;
|
|
9322
10019
|
try {
|
|
9323
|
-
|
|
10020
|
+
const inlineContext = {
|
|
10021
|
+
...sessionInfo,
|
|
10022
|
+
...this.executionContext
|
|
10023
|
+
};
|
|
10024
|
+
r = await prov.execute(prInfoForInline, provCfg, depResults, inlineContext);
|
|
9324
10025
|
} finally {
|
|
9325
10026
|
this.routingEventOverride = prevEventOverride;
|
|
9326
10027
|
}
|
|
@@ -9351,7 +10052,31 @@ ${expr}
|
|
|
9351
10052
|
});
|
|
9352
10053
|
} catch {
|
|
9353
10054
|
}
|
|
9354
|
-
const
|
|
10055
|
+
const context = {
|
|
10056
|
+
...sessionInfo,
|
|
10057
|
+
...this.executionContext
|
|
10058
|
+
};
|
|
10059
|
+
const res = await withActiveSpan(
|
|
10060
|
+
`visor.check.${checkName}`,
|
|
10061
|
+
{
|
|
10062
|
+
"visor.check.id": checkName,
|
|
10063
|
+
"visor.check.type": providerConfig.type || "ai",
|
|
10064
|
+
"visor.check.attempt": attempt
|
|
10065
|
+
},
|
|
10066
|
+
async () => {
|
|
10067
|
+
try {
|
|
10068
|
+
return await provider.execute(prInfo, providerConfig, dependencyResults, context);
|
|
10069
|
+
} finally {
|
|
10070
|
+
try {
|
|
10071
|
+
emitNdjsonSpanWithEvents("visor.check", { "visor.check.id": checkName }, [
|
|
10072
|
+
{ name: "check.started" },
|
|
10073
|
+
{ name: "check.completed" }
|
|
10074
|
+
]);
|
|
10075
|
+
} catch {
|
|
10076
|
+
}
|
|
10077
|
+
}
|
|
10078
|
+
}
|
|
10079
|
+
);
|
|
9355
10080
|
try {
|
|
9356
10081
|
currentRouteOutput = res?.output;
|
|
9357
10082
|
} catch {
|
|
@@ -9979,7 +10704,7 @@ ${expr}
|
|
|
9979
10704
|
/**
|
|
9980
10705
|
* Execute review checks and return grouped results with statistics for new architecture
|
|
9981
10706
|
*/
|
|
9982
|
-
async executeGroupedChecks(prInfo, checks, timeout, config, outputFormat, debug, maxParallelism, failFast, tagFilter) {
|
|
10707
|
+
async executeGroupedChecks(prInfo, checks, timeout, config, outputFormat, debug, maxParallelism, failFast, tagFilter, _pauseGate) {
|
|
9983
10708
|
const logFn = outputFormat === "json" || outputFormat === "sarif" ? debug ? console.error : () => {
|
|
9984
10709
|
} : console.log;
|
|
9985
10710
|
if (debug) {
|
|
@@ -10052,7 +10777,8 @@ ${expr}
|
|
|
10052
10777
|
logFn,
|
|
10053
10778
|
debug,
|
|
10054
10779
|
maxParallelism,
|
|
10055
|
-
failFast
|
|
10780
|
+
failFast,
|
|
10781
|
+
_pauseGate
|
|
10056
10782
|
);
|
|
10057
10783
|
}
|
|
10058
10784
|
if (checks.length === 1) {
|
|
@@ -10065,7 +10791,8 @@ ${expr}
|
|
|
10065
10791
|
timeout,
|
|
10066
10792
|
config,
|
|
10067
10793
|
logFn,
|
|
10068
|
-
debug
|
|
10794
|
+
debug,
|
|
10795
|
+
_pauseGate
|
|
10069
10796
|
);
|
|
10070
10797
|
const groupedResults = {};
|
|
10071
10798
|
groupedResults[checkResult.group] = [checkResult];
|
|
@@ -10082,7 +10809,7 @@ ${expr}
|
|
|
10082
10809
|
/**
|
|
10083
10810
|
* Execute single check and return grouped result
|
|
10084
10811
|
*/
|
|
10085
|
-
async executeSingleGroupedCheck(prInfo, checkName, timeout, config, logFn, debug) {
|
|
10812
|
+
async executeSingleGroupedCheck(prInfo, checkName, timeout, config, logFn, debug, _pauseGate) {
|
|
10086
10813
|
if (!config?.checks?.[checkName]) {
|
|
10087
10814
|
throw new Error(`No configuration found for check: ${checkName}`);
|
|
10088
10815
|
}
|
|
@@ -10096,6 +10823,8 @@ ${expr}
|
|
|
10096
10823
|
focus: checkConfig.focus || this.mapCheckNameToFocus(checkName),
|
|
10097
10824
|
schema: checkConfig.schema,
|
|
10098
10825
|
group: checkConfig.group,
|
|
10826
|
+
checkName,
|
|
10827
|
+
// propagate for fallback NDJSON attribution
|
|
10099
10828
|
eventContext: this.enrichEventContext(prInfo.eventContext),
|
|
10100
10829
|
ai: {
|
|
10101
10830
|
timeout: timeout || 6e5,
|
|
@@ -10210,7 +10939,7 @@ ${expr}
|
|
|
10210
10939
|
/**
|
|
10211
10940
|
* Execute multiple checks with dependency awareness - return grouped results with statistics
|
|
10212
10941
|
*/
|
|
10213
|
-
async executeGroupedDependencyAwareChecks(prInfo, checks, timeout, config, logFn, debug, maxParallelism, failFast) {
|
|
10942
|
+
async executeGroupedDependencyAwareChecks(prInfo, checks, timeout, config, logFn, debug, maxParallelism, failFast, pauseGate) {
|
|
10214
10943
|
const reviewSummary = await this.executeDependencyAwareChecks(
|
|
10215
10944
|
prInfo,
|
|
10216
10945
|
checks,
|
|
@@ -10219,7 +10948,8 @@ ${expr}
|
|
|
10219
10948
|
logFn,
|
|
10220
10949
|
debug,
|
|
10221
10950
|
maxParallelism,
|
|
10222
|
-
failFast
|
|
10951
|
+
failFast,
|
|
10952
|
+
pauseGate
|
|
10223
10953
|
);
|
|
10224
10954
|
const executionStatistics = this.buildExecutionStatistics();
|
|
10225
10955
|
const groupedResults = await this.convertReviewSummaryToGroupedResults(
|
|
@@ -10320,7 +11050,7 @@ ${expr}
|
|
|
10320
11050
|
* - Enforcing .liquid file extension
|
|
10321
11051
|
*/
|
|
10322
11052
|
async validateTemplatePath(templatePath) {
|
|
10323
|
-
const
|
|
11053
|
+
const path9 = await import("path");
|
|
10324
11054
|
if (!templatePath || typeof templatePath !== "string" || templatePath.trim() === "") {
|
|
10325
11055
|
throw new Error("Template path must be a non-empty string");
|
|
10326
11056
|
}
|
|
@@ -10330,7 +11060,7 @@ ${expr}
|
|
|
10330
11060
|
if (!templatePath.endsWith(".liquid")) {
|
|
10331
11061
|
throw new Error("Template file must have .liquid extension");
|
|
10332
11062
|
}
|
|
10333
|
-
if (
|
|
11063
|
+
if (path9.isAbsolute(templatePath)) {
|
|
10334
11064
|
throw new Error("Template path must be relative to project directory");
|
|
10335
11065
|
}
|
|
10336
11066
|
if (templatePath.includes("..")) {
|
|
@@ -10344,14 +11074,14 @@ ${expr}
|
|
|
10344
11074
|
if (!projectRoot || typeof projectRoot !== "string") {
|
|
10345
11075
|
throw new Error("Unable to determine project root directory");
|
|
10346
11076
|
}
|
|
10347
|
-
const resolvedPath =
|
|
10348
|
-
const resolvedProjectRoot =
|
|
11077
|
+
const resolvedPath = path9.resolve(projectRoot, templatePath);
|
|
11078
|
+
const resolvedProjectRoot = path9.resolve(projectRoot);
|
|
10349
11079
|
if (!resolvedPath || !resolvedProjectRoot || resolvedPath === "" || resolvedProjectRoot === "") {
|
|
10350
11080
|
throw new Error(
|
|
10351
11081
|
`Unable to resolve template path: projectRoot="${projectRoot}", templatePath="${templatePath}", resolvedPath="${resolvedPath}", resolvedProjectRoot="${resolvedProjectRoot}"`
|
|
10352
11082
|
);
|
|
10353
11083
|
}
|
|
10354
|
-
if (!resolvedPath.startsWith(resolvedProjectRoot +
|
|
11084
|
+
if (!resolvedPath.startsWith(resolvedProjectRoot + path9.sep) && resolvedPath !== resolvedProjectRoot) {
|
|
10355
11085
|
throw new Error("Template path escapes project directory");
|
|
10356
11086
|
}
|
|
10357
11087
|
return resolvedPath;
|
|
@@ -10394,9 +11124,9 @@ ${expr}
|
|
|
10394
11124
|
if (typeof directContent === "string" && directContent.trim()) {
|
|
10395
11125
|
return directContent.trim();
|
|
10396
11126
|
}
|
|
10397
|
-
const { createExtendedLiquid: createExtendedLiquid2 } = await import("./liquid-extensions-
|
|
10398
|
-
const
|
|
10399
|
-
const
|
|
11127
|
+
const { createExtendedLiquid: createExtendedLiquid2 } = await import("./liquid-extensions-KVL4MKRH.mjs");
|
|
11128
|
+
const fs7 = await import("fs/promises");
|
|
11129
|
+
const path9 = await import("path");
|
|
10400
11130
|
const liquid = createExtendedLiquid2({
|
|
10401
11131
|
trimTagLeft: false,
|
|
10402
11132
|
trimTagRight: false,
|
|
@@ -10419,7 +11149,7 @@ ${expr}
|
|
|
10419
11149
|
templateContent = checkConfig.template.content;
|
|
10420
11150
|
} else if (checkConfig.template.file) {
|
|
10421
11151
|
const validatedPath = await this.validateTemplatePath(checkConfig.template.file);
|
|
10422
|
-
templateContent = await
|
|
11152
|
+
templateContent = await fs7.readFile(validatedPath, "utf-8");
|
|
10423
11153
|
} else {
|
|
10424
11154
|
throw new Error('Custom template must specify either "file" or "content"');
|
|
10425
11155
|
}
|
|
@@ -10430,8 +11160,8 @@ ${expr}
|
|
|
10430
11160
|
if (!sanitizedSchema) {
|
|
10431
11161
|
throw new Error("Invalid schema name");
|
|
10432
11162
|
}
|
|
10433
|
-
const templatePath =
|
|
10434
|
-
templateContent = await
|
|
11163
|
+
const templatePath = path9.join(__dirname, `output/${sanitizedSchema}/template.liquid`);
|
|
11164
|
+
templateContent = await fs7.readFile(templatePath, "utf-8");
|
|
10435
11165
|
if (sanitizedSchema === "issue-assistant") {
|
|
10436
11166
|
enrichAssistantContext = true;
|
|
10437
11167
|
}
|
|
@@ -10463,7 +11193,7 @@ ${expr}
|
|
|
10463
11193
|
templateData.authorAssociation = authorAssociation;
|
|
10464
11194
|
templateData.event = { name: eventName, action: eventAction };
|
|
10465
11195
|
}
|
|
10466
|
-
const { withPermissionsContext } = await import("./liquid-extensions-
|
|
11196
|
+
const { withPermissionsContext } = await import("./liquid-extensions-KVL4MKRH.mjs");
|
|
10467
11197
|
let authorAssociationForFilters;
|
|
10468
11198
|
try {
|
|
10469
11199
|
const anyInfo = _prInfo;
|
|
@@ -10487,7 +11217,7 @@ ${expr}
|
|
|
10487
11217
|
}
|
|
10488
11218
|
const finalRendered = rendered.trim();
|
|
10489
11219
|
try {
|
|
10490
|
-
const { emitMermaidFromMarkdown } = await import("./mermaid-telemetry-
|
|
11220
|
+
const { emitMermaidFromMarkdown } = await import("./mermaid-telemetry-FBF6D35S.mjs");
|
|
10491
11221
|
emitMermaidFromMarkdown(checkName, finalRendered, "content");
|
|
10492
11222
|
} catch {
|
|
10493
11223
|
}
|
|
@@ -10539,7 +11269,7 @@ ${expr}
|
|
|
10539
11269
|
/**
|
|
10540
11270
|
* Execute multiple checks with dependency awareness - intelligently parallel and sequential
|
|
10541
11271
|
*/
|
|
10542
|
-
async executeDependencyAwareChecks(prInfo, checks, timeout, config, logFn, debug, maxParallelism, failFast) {
|
|
11272
|
+
async executeDependencyAwareChecks(prInfo, checks, timeout, config, logFn, debug, maxParallelism, failFast, pauseGate) {
|
|
10543
11273
|
const log2 = logFn || console.error;
|
|
10544
11274
|
if (debug) {
|
|
10545
11275
|
log2(`\u{1F527} Debug: Starting dependency-aware execution of ${checks.length} checks`);
|
|
@@ -10674,6 +11404,13 @@ ${expr}
|
|
|
10674
11404
|
}
|
|
10675
11405
|
const levelChecks = executionGroup.parallel.filter((name) => !results.has(name));
|
|
10676
11406
|
const levelTaskFunctions = levelChecks.map((checkName) => async () => {
|
|
11407
|
+
if (pauseGate) {
|
|
11408
|
+
try {
|
|
11409
|
+
await pauseGate();
|
|
11410
|
+
} catch {
|
|
11411
|
+
return { checkName, error: "__STOP__", result: null, skipped: true };
|
|
11412
|
+
}
|
|
11413
|
+
}
|
|
10677
11414
|
if (results.has(checkName)) {
|
|
10678
11415
|
if (debug) log2(`\u{1F527} Debug: Skipping ${checkName} (already satisfied earlier)`);
|
|
10679
11416
|
return { checkName, error: null, result: results.get(checkName) };
|
|
@@ -10762,7 +11499,7 @@ ${expr}
|
|
|
10762
11499
|
});
|
|
10763
11500
|
if (!hasFatalFailure && config && (config.fail_if || config.checks[depId]?.fail_if)) {
|
|
10764
11501
|
try {
|
|
10765
|
-
hasFatalFailure = await this.failIfTriggered(depId, depRes, config);
|
|
11502
|
+
hasFatalFailure = await this.failIfTriggered(depId, depRes, config, results);
|
|
10766
11503
|
} catch {
|
|
10767
11504
|
}
|
|
10768
11505
|
}
|
|
@@ -10995,7 +11732,9 @@ ${expr}
|
|
|
10995
11732
|
const fRes = await this.evaluateFailureConditions(
|
|
10996
11733
|
childName,
|
|
10997
11734
|
childItemRes,
|
|
10998
|
-
config
|
|
11735
|
+
config,
|
|
11736
|
+
prInfo,
|
|
11737
|
+
results
|
|
10999
11738
|
);
|
|
11000
11739
|
if (fRes.length > 0) {
|
|
11001
11740
|
const fIssues = fRes.filter((f) => f.failed).map((f) => ({
|
|
@@ -11038,6 +11777,13 @@ ${expr}
|
|
|
11038
11777
|
}
|
|
11039
11778
|
};
|
|
11040
11779
|
const itemTasks = forEachItems.map((item, itemIndex) => async () => {
|
|
11780
|
+
if (pauseGate) {
|
|
11781
|
+
try {
|
|
11782
|
+
await pauseGate();
|
|
11783
|
+
} catch {
|
|
11784
|
+
throw new Error("__STOP__");
|
|
11785
|
+
}
|
|
11786
|
+
}
|
|
11041
11787
|
try {
|
|
11042
11788
|
emitNdjsonSpanWithEvents(
|
|
11043
11789
|
"visor.foreach.item",
|
|
@@ -11050,6 +11796,13 @@ ${expr}
|
|
|
11050
11796
|
);
|
|
11051
11797
|
} catch {
|
|
11052
11798
|
}
|
|
11799
|
+
try {
|
|
11800
|
+
const span = trace4.getSpan(otContext4.active());
|
|
11801
|
+
if (span) {
|
|
11802
|
+
captureForEachState(span, forEachItems, itemIndex, item);
|
|
11803
|
+
}
|
|
11804
|
+
} catch {
|
|
11805
|
+
}
|
|
11053
11806
|
const forEachDependencyResults = /* @__PURE__ */ new Map();
|
|
11054
11807
|
for (const [depName, depResult] of dependencyResults) {
|
|
11055
11808
|
if (forEachParents.includes(depName)) {
|
|
@@ -11100,7 +11853,9 @@ ${expr}
|
|
|
11100
11853
|
const depFailures = await this.evaluateFailureConditions(
|
|
11101
11854
|
depId,
|
|
11102
11855
|
depItemRes,
|
|
11103
|
-
config
|
|
11856
|
+
config,
|
|
11857
|
+
prInfo,
|
|
11858
|
+
results
|
|
11104
11859
|
);
|
|
11105
11860
|
hasFatalDepFailure = depFailures.some((f) => f.failed);
|
|
11106
11861
|
} catch {
|
|
@@ -11176,7 +11931,9 @@ ${expr}
|
|
|
11176
11931
|
const itemFailures = await this.evaluateFailureConditions(
|
|
11177
11932
|
checkName,
|
|
11178
11933
|
itemResult,
|
|
11179
|
-
config
|
|
11934
|
+
config,
|
|
11935
|
+
prInfo,
|
|
11936
|
+
results
|
|
11180
11937
|
);
|
|
11181
11938
|
if (itemFailures.length > 0) {
|
|
11182
11939
|
const failureIssues = itemFailures.filter((f) => f.failed).map((f) => ({
|
|
@@ -11346,7 +12103,13 @@ ${expr}
|
|
|
11346
12103
|
{ index: itemIndex, total: forEachItems.length, parent: forEachParentName }
|
|
11347
12104
|
);
|
|
11348
12105
|
if (config && (config.fail_if || nodeCfg.fail_if)) {
|
|
11349
|
-
const fRes = await this.evaluateFailureConditions(
|
|
12106
|
+
const fRes = await this.evaluateFailureConditions(
|
|
12107
|
+
node,
|
|
12108
|
+
nodeItemRes,
|
|
12109
|
+
config,
|
|
12110
|
+
prInfo,
|
|
12111
|
+
results
|
|
12112
|
+
);
|
|
11350
12113
|
if (fRes.length > 0) {
|
|
11351
12114
|
const fIssues = fRes.filter((f) => f.failed).map((f) => ({
|
|
11352
12115
|
file: "system",
|
|
@@ -11441,7 +12204,13 @@ ${expr}
|
|
|
11441
12204
|
rForEval = { ...r, output: parsed };
|
|
11442
12205
|
}
|
|
11443
12206
|
}
|
|
11444
|
-
const failures = await this.evaluateFailureConditions(
|
|
12207
|
+
const failures = await this.evaluateFailureConditions(
|
|
12208
|
+
parent,
|
|
12209
|
+
rForEval,
|
|
12210
|
+
config,
|
|
12211
|
+
prInfo,
|
|
12212
|
+
results
|
|
12213
|
+
);
|
|
11445
12214
|
if (failures.some((f) => f.failed)) {
|
|
11446
12215
|
}
|
|
11447
12216
|
if (failures.some((f) => f.failed)) return true;
|
|
@@ -11560,7 +12329,9 @@ ${expr}
|
|
|
11560
12329
|
const failures = await this.evaluateFailureConditions(
|
|
11561
12330
|
checkName,
|
|
11562
12331
|
r,
|
|
11563
|
-
config
|
|
12332
|
+
config,
|
|
12333
|
+
prInfo,
|
|
12334
|
+
results
|
|
11564
12335
|
);
|
|
11565
12336
|
hadFatal = failures.some((f) => f.failed);
|
|
11566
12337
|
} catch {
|
|
@@ -11667,7 +12438,9 @@ ${expr}
|
|
|
11667
12438
|
const failureResults = await this.evaluateFailureConditions(
|
|
11668
12439
|
checkName,
|
|
11669
12440
|
finalResult,
|
|
11670
|
-
config
|
|
12441
|
+
config,
|
|
12442
|
+
prInfo,
|
|
12443
|
+
results
|
|
11671
12444
|
);
|
|
11672
12445
|
if (failureResults.length > 0) {
|
|
11673
12446
|
const failureIssues = failureResults.filter((f) => f.failed).map((f) => ({
|
|
@@ -11782,6 +12555,14 @@ ${error.stack || ""}` : String(error);
|
|
|
11782
12555
|
const checkName = levelChecksList[i];
|
|
11783
12556
|
const result = levelResults[i];
|
|
11784
12557
|
const checkConfig = config.checks[checkName];
|
|
12558
|
+
if (result.status === "fulfilled" && result.value?.error === "__STOP__") {
|
|
12559
|
+
shouldStopExecution = true;
|
|
12560
|
+
break;
|
|
12561
|
+
}
|
|
12562
|
+
if (result.status === "rejected" && result.reason instanceof Error && result.reason.message === "__STOP__") {
|
|
12563
|
+
shouldStopExecution = true;
|
|
12564
|
+
break;
|
|
12565
|
+
}
|
|
11785
12566
|
if (result.status === "fulfilled" && result.value.result && !result.value.error) {
|
|
11786
12567
|
if (result.value.skipped) {
|
|
11787
12568
|
if (debug) {
|
|
@@ -11842,6 +12623,21 @@ ${error.stack || ""}` : String(error);
|
|
|
11842
12623
|
this.trackOutputHistory(checkName, reviewResultWithOutput.output);
|
|
11843
12624
|
}
|
|
11844
12625
|
results.set(checkName, reviewResult);
|
|
12626
|
+
try {
|
|
12627
|
+
const span = trace4.getSpan(otContext4.active());
|
|
12628
|
+
if (span) {
|
|
12629
|
+
const allOutputs = {};
|
|
12630
|
+
results.forEach((result2, name) => {
|
|
12631
|
+
if (result2.output !== void 0) {
|
|
12632
|
+
allOutputs[name] = result2.output;
|
|
12633
|
+
}
|
|
12634
|
+
});
|
|
12635
|
+
const memoryStore = MemoryStore.getInstance();
|
|
12636
|
+
const memoryData = await memoryStore.getAll();
|
|
12637
|
+
captureStateSnapshot(span, checkName, allOutputs, memoryData);
|
|
12638
|
+
}
|
|
12639
|
+
} catch {
|
|
12640
|
+
}
|
|
11845
12641
|
} else {
|
|
11846
12642
|
const errorSummary = {
|
|
11847
12643
|
issues: [
|
|
@@ -12560,13 +13356,14 @@ ${result.value.result.debug.rawResponse}`;
|
|
|
12560
13356
|
/**
|
|
12561
13357
|
* Evaluate failure conditions for a check result
|
|
12562
13358
|
*/
|
|
12563
|
-
async evaluateFailureConditions(checkName, reviewSummary, config, prInfo) {
|
|
13359
|
+
async evaluateFailureConditions(checkName, reviewSummary, config, prInfo, previousOutputs) {
|
|
12564
13360
|
if (!config) {
|
|
12565
13361
|
return [];
|
|
12566
13362
|
}
|
|
12567
13363
|
const checkConfig = config.checks[checkName];
|
|
12568
13364
|
const checkSchema = typeof checkConfig?.schema === "object" ? "custom" : checkConfig?.schema || "";
|
|
12569
13365
|
const checkGroup = checkConfig?.group || "";
|
|
13366
|
+
const outputsRecord = previousOutputs ? previousOutputs instanceof Map ? Object.fromEntries(previousOutputs.entries()) : previousOutputs : void 0;
|
|
12570
13367
|
const globalFailIf = config.fail_if;
|
|
12571
13368
|
const checkFailIf = checkConfig?.fail_if;
|
|
12572
13369
|
if (globalFailIf || checkFailIf) {
|
|
@@ -12577,7 +13374,8 @@ ${result.value.result.debug.rawResponse}`;
|
|
|
12577
13374
|
checkSchema,
|
|
12578
13375
|
checkGroup,
|
|
12579
13376
|
reviewSummary,
|
|
12580
|
-
globalFailIf
|
|
13377
|
+
globalFailIf,
|
|
13378
|
+
outputsRecord
|
|
12581
13379
|
);
|
|
12582
13380
|
try {
|
|
12583
13381
|
addEvent("fail_if.evaluated", {
|
|
@@ -12642,7 +13440,8 @@ ${result.value.result.debug.rawResponse}`;
|
|
|
12642
13440
|
checkSchema,
|
|
12643
13441
|
checkGroup,
|
|
12644
13442
|
reviewSummary,
|
|
12645
|
-
checkFailIf
|
|
13443
|
+
checkFailIf,
|
|
13444
|
+
outputsRecord
|
|
12646
13445
|
);
|
|
12647
13446
|
try {
|
|
12648
13447
|
addEvent("fail_if.evaluated", {
|
|
@@ -13171,9 +13970,15 @@ ${result.value.result.debug.rawResponse}`;
|
|
|
13171
13970
|
if (!issues || issues.length === 0) return false;
|
|
13172
13971
|
return issues.some((i) => this.isFatalRule(i.ruleId || "", i.severity));
|
|
13173
13972
|
}
|
|
13174
|
-
async failIfTriggered(checkName, result, config) {
|
|
13973
|
+
async failIfTriggered(checkName, result, config, previousOutputs) {
|
|
13175
13974
|
if (!config) return false;
|
|
13176
|
-
const failures = await this.evaluateFailureConditions(
|
|
13975
|
+
const failures = await this.evaluateFailureConditions(
|
|
13976
|
+
checkName,
|
|
13977
|
+
result,
|
|
13978
|
+
config,
|
|
13979
|
+
void 0,
|
|
13980
|
+
previousOutputs
|
|
13981
|
+
);
|
|
13177
13982
|
return failures.some((f) => f.failed);
|
|
13178
13983
|
}
|
|
13179
13984
|
/**
|
|
@@ -13286,4 +14091,4 @@ ${result.value.result.debug.rawResponse}`;
|
|
|
13286
14091
|
export {
|
|
13287
14092
|
CheckExecutionEngine
|
|
13288
14093
|
};
|
|
13289
|
-
//# sourceMappingURL=chunk-
|
|
14094
|
+
//# sourceMappingURL=chunk-X2JKUOE5.mjs.map
|