@probelabs/visor 0.1.90 → 0.1.93
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 +68 -2
- package/action.yml +1 -1
- package/defaults/.visor.yaml +133 -168
- package/dist/13.index.js +82 -0
- package/dist/159.index.js +38 -0
- package/dist/168.index.js +82 -0
- package/dist/201.index.js +82 -0
- package/dist/262.index.js +48 -0
- package/dist/272.index.js +82 -0
- package/dist/273.index.js +48 -0
- package/dist/320.index.js +38 -0
- package/dist/34.index.js +81 -0
- package/dist/421.index.js +48 -0
- package/dist/437.index.js +82 -0
- package/dist/441.index.js +81 -0
- package/dist/450.index.js +82 -0
- package/dist/54.index.js +81 -0
- package/dist/544.index.js +38 -0
- package/dist/558.index.js +82 -0
- package/dist/715.index.js +38 -0
- package/dist/737.index.js +82 -0
- package/dist/834.index.js +48 -0
- package/dist/85.index.js +82 -0
- package/dist/861.index.js +48 -0
- package/dist/878.index.js +81 -0
- package/dist/940.index.js +38 -0
- package/dist/989.index.js +81 -0
- package/dist/996.index.js +82 -0
- package/dist/ai-review-service.d.ts +11 -1
- package/dist/ai-review-service.d.ts.map +1 -1
- package/dist/check-execution-engine.d.ts +15 -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/defaults/.visor.yaml +133 -168
- package/dist/failure-condition-evaluator.d.ts +3 -2
- package/dist/failure-condition-evaluator.d.ts.map +1 -1
- package/dist/generated/config-schema.d.ts +89 -2
- package/dist/generated/config-schema.d.ts.map +1 -1
- package/dist/generated/config-schema.json +109 -1
- package/dist/git-repository-analyzer.d.ts +17 -1
- package/dist/git-repository-analyzer.d.ts.map +1 -1
- package/dist/github-comments.d.ts.map +1 -1
- package/dist/github-reactions.d.ts +36 -0
- package/dist/github-reactions.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +173112 -46133
- package/dist/liquid-extensions.d.ts +15 -0
- package/dist/liquid-extensions.d.ts.map +1 -1
- package/dist/memory-store.d.ts +129 -0
- package/dist/memory-store.d.ts.map +1 -0
- package/dist/output/issue-assistant/schema.json +42 -0
- package/dist/output/issue-assistant/template.liquid +1 -0
- package/dist/output/overview/schema.json +33 -0
- package/dist/output/overview/template.liquid +16 -0
- package/dist/output-formatters.d.ts +1 -0
- package/dist/output-formatters.d.ts.map +1 -1
- package/dist/pr-analyzer.d.ts +2 -0
- package/dist/pr-analyzer.d.ts.map +1 -1
- package/dist/proto/channelz.proto +564 -0
- package/dist/providers/check-provider-registry.d.ts.map +1 -1
- package/dist/providers/command-check-provider.d.ts.map +1 -1
- package/dist/providers/github-ops-provider.d.ts +18 -0
- package/dist/providers/github-ops-provider.d.ts.map +1 -0
- package/dist/providers/memory-check-provider.d.ts +56 -0
- package/dist/providers/memory-check-provider.d.ts.map +1 -0
- package/dist/reviewer.d.ts +2 -0
- package/dist/reviewer.d.ts.map +1 -1
- package/dist/sdk/check-execution-engine-RORGGGGP.mjs +11 -0
- package/dist/sdk/chunk-I3GQJIR7.mjs +752 -0
- package/dist/sdk/chunk-I3GQJIR7.mjs.map +1 -0
- package/dist/sdk/chunk-KVHVCGY6.mjs +103 -0
- package/dist/sdk/chunk-KVHVCGY6.mjs.map +1 -0
- package/dist/sdk/chunk-TWJKAYT6.mjs +1124 -0
- package/dist/sdk/chunk-TWJKAYT6.mjs.map +1 -0
- package/dist/sdk/{chunk-N2PPFOSF.mjs → chunk-Z47UECAT.mjs} +2593 -264
- package/dist/sdk/chunk-Z47UECAT.mjs.map +1 -0
- package/dist/sdk/liquid-extensions-GMEGEGC3.mjs +18 -0
- package/dist/sdk/mermaid-telemetry-LZGDD35I.mjs +61 -0
- package/dist/sdk/mermaid-telemetry-LZGDD35I.mjs.map +1 -0
- package/dist/sdk/sdk.d.mts +48 -2
- package/dist/sdk/sdk.d.ts +48 -2
- package/dist/sdk/sdk.js +5372 -781
- package/dist/sdk/sdk.js.map +1 -1
- package/dist/sdk/sdk.mjs +112 -7
- package/dist/sdk/sdk.mjs.map +1 -1
- package/dist/sdk/tracer-init-O7RLXMJ3.mjs +10 -0
- package/dist/sdk/tracer-init-O7RLXMJ3.mjs.map +1 -0
- package/dist/session-registry.d.ts +19 -5
- package/dist/session-registry.d.ts.map +1 -1
- package/dist/telemetry/fallback-ndjson.d.ts +7 -0
- package/dist/telemetry/fallback-ndjson.d.ts.map +1 -0
- package/dist/telemetry/file-span-exporter.d.ts +17 -0
- package/dist/telemetry/file-span-exporter.d.ts.map +1 -0
- package/dist/telemetry/metrics.d.ts +13 -0
- package/dist/telemetry/metrics.d.ts.map +1 -0
- package/dist/telemetry/opentelemetry.d.ts +26 -0
- package/dist/telemetry/opentelemetry.d.ts.map +1 -0
- package/dist/telemetry/trace-helpers.d.ts +9 -0
- package/dist/telemetry/trace-helpers.d.ts.map +1 -0
- package/dist/telemetry/trace-report-exporter.d.ts +17 -0
- package/dist/telemetry/trace-report-exporter.d.ts.map +1 -0
- package/dist/traces/run-2025-10-15T11-54-04-087Z.ndjson +33 -0
- package/dist/traces/run-2025-10-15T11-54-14-046Z.ndjson +33 -0
- package/dist/traces/run-2025-10-15T11-54-14-575Z.ndjson +33 -0
- package/dist/traces/run-2025-10-15T11-54-15-082Z.ndjson +33 -0
- package/dist/traces/run-2025-10-15T11-54-15-561Z.ndjson +12 -0
- package/dist/types/cli.d.ts +4 -0
- package/dist/types/cli.d.ts.map +1 -1
- package/dist/types/config.d.ts +56 -2
- package/dist/types/config.d.ts.map +1 -1
- package/dist/utils/author-permissions.d.ts +74 -0
- package/dist/utils/author-permissions.d.ts.map +1 -0
- package/dist/utils/head-sha.d.ts +8 -0
- package/dist/utils/head-sha.d.ts.map +1 -0
- package/dist/utils/mermaid-telemetry.d.ts +3 -0
- package/dist/utils/mermaid-telemetry.d.ts.map +1 -0
- package/dist/utils/tracer-init.d.ts +12 -0
- package/dist/utils/tracer-init.d.ts.map +1 -0
- package/dist/utils/ui-helpers.d.ts +3 -0
- package/dist/utils/ui-helpers.d.ts.map +1 -0
- package/package.json +2 -2
- package/dist/sdk/check-execution-engine-Z2USLMN5.mjs +0 -9
- package/dist/sdk/chunk-FIL2OGF6.mjs +0 -68
- package/dist/sdk/chunk-FIL2OGF6.mjs.map +0 -1
- package/dist/sdk/chunk-N2PPFOSF.mjs.map +0 -1
- package/dist/sdk/liquid-extensions-KDECAJTV.mjs +0 -12
- /package/dist/sdk/{check-execution-engine-Z2USLMN5.mjs.map → check-execution-engine-RORGGGGP.mjs.map} +0 -0
- /package/dist/sdk/{liquid-extensions-KDECAJTV.mjs.map → liquid-extensions-GMEGEGC3.mjs.map} +0 -0
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
init_tracer_init,
|
|
3
|
+
initializeTracer
|
|
4
|
+
} from "./chunk-KVHVCGY6.mjs";
|
|
5
|
+
import {
|
|
6
|
+
MemoryStore,
|
|
7
|
+
createExtendedLiquid,
|
|
8
|
+
createPermissionHelpers,
|
|
9
|
+
detectLocalMode,
|
|
10
|
+
init_logger,
|
|
11
|
+
logger,
|
|
12
|
+
logger_exports,
|
|
13
|
+
resolveAssociationFromEvent
|
|
14
|
+
} from "./chunk-I3GQJIR7.mjs";
|
|
15
|
+
import {
|
|
16
|
+
addEvent,
|
|
17
|
+
addFailIfTriggered,
|
|
18
|
+
emitNdjsonFallback,
|
|
19
|
+
emitNdjsonSpanWithEvents,
|
|
20
|
+
fallback_ndjson_exports,
|
|
21
|
+
init_fallback_ndjson
|
|
22
|
+
} from "./chunk-TWJKAYT6.mjs";
|
|
4
23
|
import {
|
|
5
24
|
__esm,
|
|
6
25
|
__export,
|
|
@@ -95,43 +114,61 @@ var init_session_registry = __esm({
|
|
|
95
114
|
return this.sessions.has(sessionId);
|
|
96
115
|
}
|
|
97
116
|
/**
|
|
98
|
-
* Clone a session with a new session ID
|
|
99
|
-
*
|
|
117
|
+
* Clone a session with a new session ID using ProbeAgent's official clone() method
|
|
118
|
+
* This uses ProbeAgent's built-in cloning which automatically handles:
|
|
119
|
+
* - Intelligent filtering of internal messages (schema reminders, tool prompts, etc.)
|
|
120
|
+
* - Preserving system message for cache efficiency
|
|
121
|
+
* - Deep copying conversation history
|
|
122
|
+
* - Copying agent configuration
|
|
100
123
|
*/
|
|
101
|
-
async cloneSession(sourceSessionId, newSessionId) {
|
|
124
|
+
async cloneSession(sourceSessionId, newSessionId, checkName) {
|
|
102
125
|
const sourceAgent = this.sessions.get(sourceSessionId);
|
|
103
126
|
if (!sourceAgent) {
|
|
104
127
|
console.error(`\u26A0\uFE0F Cannot clone session: ${sourceSessionId} not found`);
|
|
105
128
|
return void 0;
|
|
106
129
|
}
|
|
107
130
|
try {
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
131
|
+
const clonedAgent = sourceAgent.clone({
|
|
132
|
+
sessionId: newSessionId,
|
|
133
|
+
stripInternalMessages: true,
|
|
134
|
+
// Remove schema reminders, tool prompts, etc.
|
|
135
|
+
keepSystemMessage: true,
|
|
136
|
+
// Keep for cache efficiency
|
|
137
|
+
deepCopy: true
|
|
138
|
+
// Safe deep copy of history
|
|
114
139
|
});
|
|
115
|
-
if (
|
|
140
|
+
if (sourceAgent.debug && checkName) {
|
|
116
141
|
try {
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
142
|
+
const { initializeTracer: initializeTracer2 } = await import("./tracer-init-O7RLXMJ3.mjs");
|
|
143
|
+
const tracerResult = await initializeTracer2(newSessionId, checkName);
|
|
144
|
+
if (tracerResult) {
|
|
145
|
+
clonedAgent.tracer = tracerResult.tracer;
|
|
146
|
+
clonedAgent._telemetryConfig = tracerResult.telemetryConfig;
|
|
147
|
+
clonedAgent._traceFilePath = tracerResult.filePath;
|
|
148
|
+
}
|
|
149
|
+
} catch (traceError) {
|
|
123
150
|
console.error(
|
|
124
|
-
|
|
151
|
+
"\u26A0\uFE0F Warning: Failed to initialize tracing for cloned session:",
|
|
152
|
+
traceError
|
|
125
153
|
);
|
|
126
|
-
clonedAgent.conversationHistory = [...sourceHistory];
|
|
127
154
|
}
|
|
128
|
-
} else {
|
|
129
|
-
console.error(`\u{1F4CB} Cloned session ${sourceSessionId} \u2192 ${newSessionId} (no history)`);
|
|
130
155
|
}
|
|
156
|
+
if (sourceAgent._mcpInitialized && typeof clonedAgent.initialize === "function") {
|
|
157
|
+
try {
|
|
158
|
+
await clonedAgent.initialize();
|
|
159
|
+
console.error(`\u{1F527} Initialized MCP tools for cloned session`);
|
|
160
|
+
} catch (initError) {
|
|
161
|
+
console.error(`\u26A0\uFE0F Warning: Failed to initialize cloned agent: ${initError}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const historyLength = clonedAgent.history?.length || 0;
|
|
165
|
+
console.error(
|
|
166
|
+
`\u{1F4CB} Cloned session ${sourceSessionId} \u2192 ${newSessionId} using ProbeAgent.clone() (${historyLength} messages, internal messages filtered)`
|
|
167
|
+
);
|
|
131
168
|
this.registerSession(newSessionId, clonedAgent);
|
|
132
169
|
return clonedAgent;
|
|
133
170
|
} catch (error) {
|
|
134
|
-
console.error(`\u26A0\uFE0F Failed to clone session ${sourceSessionId}
|
|
171
|
+
console.error(`\u26A0\uFE0F Failed to clone session ${sourceSessionId}:`, error);
|
|
135
172
|
return void 0;
|
|
136
173
|
}
|
|
137
174
|
}
|
|
@@ -177,113 +214,8 @@ var init_session_registry = __esm({
|
|
|
177
214
|
}
|
|
178
215
|
});
|
|
179
216
|
|
|
180
|
-
// src/logger.ts
|
|
181
|
-
var logger_exports = {};
|
|
182
|
-
__export(logger_exports, {
|
|
183
|
-
configureLoggerFromCli: () => configureLoggerFromCli,
|
|
184
|
-
logger: () => logger
|
|
185
|
-
});
|
|
186
|
-
function levelToNumber(level) {
|
|
187
|
-
switch (level) {
|
|
188
|
-
case "silent":
|
|
189
|
-
return 0;
|
|
190
|
-
case "error":
|
|
191
|
-
return 10;
|
|
192
|
-
case "warn":
|
|
193
|
-
return 20;
|
|
194
|
-
case "info":
|
|
195
|
-
return 30;
|
|
196
|
-
case "verbose":
|
|
197
|
-
return 40;
|
|
198
|
-
case "debug":
|
|
199
|
-
return 50;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
function configureLoggerFromCli(options) {
|
|
203
|
-
logger.configure({
|
|
204
|
-
outputFormat: options.output,
|
|
205
|
-
debug: options.debug,
|
|
206
|
-
verbose: options.verbose,
|
|
207
|
-
quiet: options.quiet
|
|
208
|
-
});
|
|
209
|
-
try {
|
|
210
|
-
if (options.output) process.env.VISOR_OUTPUT_FORMAT = String(options.output);
|
|
211
|
-
if (typeof options.debug === "boolean") {
|
|
212
|
-
process.env.VISOR_DEBUG = options.debug ? "true" : "false";
|
|
213
|
-
}
|
|
214
|
-
} catch {
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
var Logger, logger;
|
|
218
|
-
var init_logger = __esm({
|
|
219
|
-
"src/logger.ts"() {
|
|
220
|
-
"use strict";
|
|
221
|
-
Logger = class {
|
|
222
|
-
level = "info";
|
|
223
|
-
isJsonLike = false;
|
|
224
|
-
isTTY = typeof process !== "undefined" ? !!process.stderr.isTTY : false;
|
|
225
|
-
configure(opts = {}) {
|
|
226
|
-
let lvl = "info";
|
|
227
|
-
if (opts.debug || process.env.VISOR_DEBUG === "true") {
|
|
228
|
-
lvl = "debug";
|
|
229
|
-
} else if (opts.verbose || process.env.VISOR_LOG_LEVEL === "verbose") {
|
|
230
|
-
lvl = "verbose";
|
|
231
|
-
} else if (opts.quiet || process.env.VISOR_LOG_LEVEL === "quiet") {
|
|
232
|
-
lvl = "warn";
|
|
233
|
-
} else if (opts.level) {
|
|
234
|
-
lvl = opts.level;
|
|
235
|
-
} else if (process.env.VISOR_LOG_LEVEL) {
|
|
236
|
-
const envLvl = process.env.VISOR_LOG_LEVEL;
|
|
237
|
-
if (["silent", "error", "warn", "info", "verbose", "debug"].includes(envLvl)) {
|
|
238
|
-
lvl = envLvl;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
this.level = lvl;
|
|
242
|
-
const output = opts.outputFormat || process.env.VISOR_OUTPUT_FORMAT || "table";
|
|
243
|
-
this.isJsonLike = output === "json" || output === "sarif";
|
|
244
|
-
}
|
|
245
|
-
shouldLog(level) {
|
|
246
|
-
const desired = levelToNumber(level);
|
|
247
|
-
const current = levelToNumber(this.level);
|
|
248
|
-
if (desired > current) return false;
|
|
249
|
-
if (this.isJsonLike && desired < levelToNumber("error") && this.level !== "debug" && this.level !== "verbose") {
|
|
250
|
-
return false;
|
|
251
|
-
}
|
|
252
|
-
return true;
|
|
253
|
-
}
|
|
254
|
-
write(msg) {
|
|
255
|
-
try {
|
|
256
|
-
process.stderr.write(msg + "\n");
|
|
257
|
-
} catch {
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
info(msg) {
|
|
261
|
-
if (this.shouldLog("info")) this.write(msg);
|
|
262
|
-
}
|
|
263
|
-
warn(msg) {
|
|
264
|
-
if (this.shouldLog("warn")) this.write(msg);
|
|
265
|
-
}
|
|
266
|
-
error(msg) {
|
|
267
|
-
if (this.shouldLog("error")) this.write(msg);
|
|
268
|
-
}
|
|
269
|
-
verbose(msg) {
|
|
270
|
-
if (this.shouldLog("verbose")) this.write(msg);
|
|
271
|
-
}
|
|
272
|
-
debug(msg) {
|
|
273
|
-
if (this.shouldLog("debug")) this.write(msg);
|
|
274
|
-
}
|
|
275
|
-
step(msg) {
|
|
276
|
-
if (this.shouldLog("info")) this.write(`\u25B6 ${msg}`);
|
|
277
|
-
}
|
|
278
|
-
success(msg) {
|
|
279
|
-
if (this.shouldLog("info")) this.write(`\u2714 ${msg}`);
|
|
280
|
-
}
|
|
281
|
-
};
|
|
282
|
-
logger = new Logger();
|
|
283
|
-
}
|
|
284
|
-
});
|
|
285
|
-
|
|
286
217
|
// src/github-comments.ts
|
|
218
|
+
init_logger();
|
|
287
219
|
import { v4 as uuidv4 } from "uuid";
|
|
288
220
|
var CommentManager = class {
|
|
289
221
|
octokit;
|
|
@@ -363,6 +295,9 @@ var CommentManager = class {
|
|
|
363
295
|
comment_id: existingComment.id,
|
|
364
296
|
body: formattedContent
|
|
365
297
|
});
|
|
298
|
+
logger.info(
|
|
299
|
+
`\u2705 Successfully updated comment (ID: ${commentId}, GitHub ID: ${existingComment.id}) on PR #${prNumber} in ${owner}/${repo}`
|
|
300
|
+
);
|
|
366
301
|
return updatedComment.data;
|
|
367
302
|
} else {
|
|
368
303
|
const newComment = await this.octokit.rest.issues.createComment({
|
|
@@ -371,6 +306,9 @@ var CommentManager = class {
|
|
|
371
306
|
issue_number: prNumber,
|
|
372
307
|
body: formattedContent
|
|
373
308
|
});
|
|
309
|
+
logger.info(
|
|
310
|
+
`\u2705 Successfully created comment (ID: ${commentId}, GitHub ID: ${newComment.data.id}) on PR #${prNumber} in ${owner}/${repo}`
|
|
311
|
+
);
|
|
374
312
|
return newComment.data;
|
|
375
313
|
}
|
|
376
314
|
});
|
|
@@ -384,6 +322,10 @@ var CommentManager = class {
|
|
|
384
322
|
return `<!-- visor-comment-id:${commentId} -->
|
|
385
323
|
${content}
|
|
386
324
|
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
*Powered by [Visor](https://probelabs.com/visor) from [Probelabs](https://probelabs.com)*
|
|
328
|
+
|
|
387
329
|
*Last updated: ${lastUpdated} | Triggered by: ${triggeredBy}${commitInfo}*
|
|
388
330
|
<!-- /visor-comment-id:${commentId} -->`;
|
|
389
331
|
}
|
|
@@ -564,6 +506,7 @@ ${content}
|
|
|
564
506
|
// src/ai-review-service.ts
|
|
565
507
|
init_session_registry();
|
|
566
508
|
init_logger();
|
|
509
|
+
init_tracer_init();
|
|
567
510
|
import { ProbeAgent } from "@probelabs/probe";
|
|
568
511
|
function log(...args) {
|
|
569
512
|
logger.debug(args.join(" "));
|
|
@@ -711,18 +654,24 @@ var AIReviewService = class {
|
|
|
711
654
|
`Session not found for reuse: ${parentSessionId}. Ensure the parent check completed successfully.`
|
|
712
655
|
);
|
|
713
656
|
}
|
|
714
|
-
const prompt = await this.buildCustomPrompt(prInfo, customPrompt, schema
|
|
657
|
+
const prompt = await this.buildCustomPrompt(prInfo, customPrompt, schema, {
|
|
658
|
+
skipPRContext: true
|
|
659
|
+
});
|
|
715
660
|
let agentToUse;
|
|
716
661
|
let currentSessionId;
|
|
717
662
|
if (sessionMode === "clone") {
|
|
718
|
-
currentSessionId = `${
|
|
719
|
-
log(
|
|
663
|
+
currentSessionId = `${checkName}-session-${Date.now()}`;
|
|
664
|
+
log(
|
|
665
|
+
`\u{1F4CB} Cloning AI session ${parentSessionId} \u2192 ${currentSessionId} for ${checkName} check...`
|
|
666
|
+
);
|
|
720
667
|
const clonedAgent = await this.sessionRegistry.cloneSession(
|
|
721
668
|
parentSessionId,
|
|
722
|
-
currentSessionId
|
|
669
|
+
currentSessionId,
|
|
670
|
+
checkName
|
|
671
|
+
// Pass checkName for tracing
|
|
723
672
|
);
|
|
724
673
|
if (!clonedAgent) {
|
|
725
|
-
throw new Error(`Failed to clone session ${parentSessionId}
|
|
674
|
+
throw new Error(`Failed to clone session ${parentSessionId}`);
|
|
726
675
|
}
|
|
727
676
|
agentToUse = clonedAgent;
|
|
728
677
|
} else {
|
|
@@ -731,7 +680,13 @@ var AIReviewService = class {
|
|
|
731
680
|
currentSessionId = parentSessionId;
|
|
732
681
|
}
|
|
733
682
|
log(`\u{1F527} Debug: Raw schema parameter: ${JSON.stringify(schema)} (type: ${typeof schema})`);
|
|
734
|
-
log(
|
|
683
|
+
log(`\u{1F4CB} Schema for this check: ${schema || "none (no schema)"}`);
|
|
684
|
+
if (sessionMode === "clone") {
|
|
685
|
+
log(`\u2705 Cloned agent will use NEW schema (${schema}) - parent schema does not persist`);
|
|
686
|
+
log(`\u{1F504} Clone operation ensures fresh agent with copied history but new configuration`);
|
|
687
|
+
} else {
|
|
688
|
+
log(`\u{1F504} Append mode - using existing agent instance with shared history and configuration`);
|
|
689
|
+
}
|
|
735
690
|
let debugInfo;
|
|
736
691
|
if (this.config.debug) {
|
|
737
692
|
debugInfo = {
|
|
@@ -809,11 +764,17 @@ var AIReviewService = class {
|
|
|
809
764
|
/**
|
|
810
765
|
* Build a custom prompt for AI review with XML-formatted data
|
|
811
766
|
*/
|
|
812
|
-
async buildCustomPrompt(prInfo, customInstructions, schema) {
|
|
813
|
-
const
|
|
767
|
+
async buildCustomPrompt(prInfo, customInstructions, schema, options) {
|
|
768
|
+
const skipPRContext = options?.skipPRContext === true;
|
|
769
|
+
const prContext = skipPRContext ? "" : this.formatPRContext(prInfo);
|
|
814
770
|
const isIssue = prInfo.isIssue === true;
|
|
815
771
|
const isCodeReviewSchema = schema === "code-review";
|
|
816
772
|
if (isIssue) {
|
|
773
|
+
if (skipPRContext) {
|
|
774
|
+
return `<instructions>
|
|
775
|
+
${customInstructions}
|
|
776
|
+
</instructions>`;
|
|
777
|
+
}
|
|
817
778
|
return `<review_request>
|
|
818
779
|
<instructions>
|
|
819
780
|
${customInstructions}
|
|
@@ -836,6 +797,19 @@ ${prContext}
|
|
|
836
797
|
}
|
|
837
798
|
if (isCodeReviewSchema) {
|
|
838
799
|
const analysisType = prInfo.isIncremental ? "INCREMENTAL" : "FULL";
|
|
800
|
+
if (skipPRContext) {
|
|
801
|
+
return `<instructions>
|
|
802
|
+
${customInstructions}
|
|
803
|
+
</instructions>
|
|
804
|
+
|
|
805
|
+
<reminder>
|
|
806
|
+
<rule>The code context and diff were provided in the previous message</rule>
|
|
807
|
+
<rule>Focus on the new analysis instructions above</rule>
|
|
808
|
+
<rule>Only analyze code that appears with + (additions) or - (deletions) in the diff sections</rule>
|
|
809
|
+
<rule>STRICT OUTPUT POLICY: Report only actual problems, risks, or deficiencies</rule>
|
|
810
|
+
<rule>SEVERITY ASSIGNMENT: Assign severity ONLY to problems introduced or left unresolved by this change</rule>
|
|
811
|
+
</reminder>`;
|
|
812
|
+
}
|
|
839
813
|
return `<review_request>
|
|
840
814
|
<analysis_type>${analysisType}</analysis_type>
|
|
841
815
|
|
|
@@ -864,6 +838,11 @@ ${prContext}
|
|
|
864
838
|
<rule>SEVERITY ASSIGNMENT: Assign severity ONLY to problems introduced or left unresolved by this change (critical/error/warning/info as appropriate). Do NOT create issue entries solely to acknowledge improvements; if no problems exist, return zero issues.</rule>
|
|
865
839
|
</rules>
|
|
866
840
|
</review_request>`;
|
|
841
|
+
}
|
|
842
|
+
if (skipPRContext) {
|
|
843
|
+
return `<instructions>
|
|
844
|
+
${customInstructions}
|
|
845
|
+
</instructions>`;
|
|
867
846
|
}
|
|
868
847
|
return `<instructions>
|
|
869
848
|
${customInstructions}
|
|
@@ -1123,9 +1102,308 @@ ${schemaString}`);
|
|
|
1123
1102
|
log(`\u{1F3AF} Schema options passed to ProbeAgent.answer() (session reuse):`);
|
|
1124
1103
|
log(JSON.stringify(schemaOptions, null, 2));
|
|
1125
1104
|
}
|
|
1126
|
-
|
|
1105
|
+
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
1106
|
+
try {
|
|
1107
|
+
const fs5 = __require("fs");
|
|
1108
|
+
const path5 = __require("path");
|
|
1109
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1110
|
+
const provider = this.config.provider || "auto";
|
|
1111
|
+
const model = this.config.model || "default";
|
|
1112
|
+
let conversationHistory = [];
|
|
1113
|
+
try {
|
|
1114
|
+
const agentAny2 = agent;
|
|
1115
|
+
if (agentAny2.history) {
|
|
1116
|
+
conversationHistory = agentAny2.history;
|
|
1117
|
+
} else if (agentAny2.messages) {
|
|
1118
|
+
conversationHistory = agentAny2.messages;
|
|
1119
|
+
} else if (agentAny2._messages) {
|
|
1120
|
+
conversationHistory = agentAny2._messages;
|
|
1121
|
+
}
|
|
1122
|
+
} catch {
|
|
1123
|
+
}
|
|
1124
|
+
const debugData = {
|
|
1125
|
+
timestamp,
|
|
1126
|
+
checkName: _checkName || "unknown",
|
|
1127
|
+
provider,
|
|
1128
|
+
model,
|
|
1129
|
+
schema: effectiveSchema,
|
|
1130
|
+
schemaOptions: schemaOptions || "none",
|
|
1131
|
+
sessionInfo: {
|
|
1132
|
+
isSessionReuse: true,
|
|
1133
|
+
historyMessageCount: conversationHistory.length
|
|
1134
|
+
},
|
|
1135
|
+
currentPromptLength: prompt.length,
|
|
1136
|
+
currentPrompt: prompt,
|
|
1137
|
+
conversationHistory
|
|
1138
|
+
};
|
|
1139
|
+
const debugJson = JSON.stringify(debugData, null, 2);
|
|
1140
|
+
let readableVersion = `=============================================================
|
|
1141
|
+
`;
|
|
1142
|
+
readableVersion += `VISOR DEBUG REPORT - SESSION REUSE
|
|
1143
|
+
`;
|
|
1144
|
+
readableVersion += `=============================================================
|
|
1145
|
+
`;
|
|
1146
|
+
readableVersion += `Timestamp: ${timestamp}
|
|
1147
|
+
`;
|
|
1148
|
+
readableVersion += `Check Name: ${_checkName || "unknown"}
|
|
1149
|
+
`;
|
|
1150
|
+
readableVersion += `Provider: ${provider}
|
|
1151
|
+
`;
|
|
1152
|
+
readableVersion += `Model: ${model}
|
|
1153
|
+
`;
|
|
1154
|
+
readableVersion += `Schema: ${effectiveSchema}
|
|
1155
|
+
`;
|
|
1156
|
+
readableVersion += `Schema Options: ${schemaOptions ? "provided" : "none"}
|
|
1157
|
+
`;
|
|
1158
|
+
readableVersion += `History Messages: ${conversationHistory.length}
|
|
1159
|
+
`;
|
|
1160
|
+
readableVersion += `=============================================================
|
|
1161
|
+
|
|
1162
|
+
`;
|
|
1163
|
+
if (schemaOptions) {
|
|
1164
|
+
readableVersion += `
|
|
1165
|
+
${"=".repeat(60)}
|
|
1166
|
+
`;
|
|
1167
|
+
readableVersion += `SCHEMA CONFIGURATION
|
|
1168
|
+
`;
|
|
1169
|
+
readableVersion += `${"=".repeat(60)}
|
|
1170
|
+
`;
|
|
1171
|
+
readableVersion += JSON.stringify(schemaOptions, null, 2);
|
|
1172
|
+
readableVersion += `
|
|
1173
|
+
`;
|
|
1174
|
+
}
|
|
1175
|
+
if (conversationHistory.length > 0) {
|
|
1176
|
+
readableVersion += `
|
|
1177
|
+
${"=".repeat(60)}
|
|
1178
|
+
`;
|
|
1179
|
+
readableVersion += `CONVERSATION HISTORY (${conversationHistory.length} messages)
|
|
1180
|
+
`;
|
|
1181
|
+
readableVersion += `${"=".repeat(60)}
|
|
1182
|
+
`;
|
|
1183
|
+
conversationHistory.forEach((msg, index) => {
|
|
1184
|
+
readableVersion += `
|
|
1185
|
+
${"-".repeat(60)}
|
|
1186
|
+
`;
|
|
1187
|
+
readableVersion += `MESSAGE #${index + 1}
|
|
1188
|
+
`;
|
|
1189
|
+
readableVersion += `Role: ${msg.role || "unknown"}
|
|
1190
|
+
`;
|
|
1191
|
+
if (msg.content) {
|
|
1192
|
+
const contentStr = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content, null, 2);
|
|
1193
|
+
readableVersion += `Length: ${contentStr.length} characters
|
|
1194
|
+
`;
|
|
1195
|
+
readableVersion += `${"-".repeat(60)}
|
|
1196
|
+
`;
|
|
1197
|
+
readableVersion += `${contentStr}
|
|
1198
|
+
`;
|
|
1199
|
+
}
|
|
1200
|
+
});
|
|
1201
|
+
}
|
|
1202
|
+
readableVersion += `
|
|
1203
|
+
${"=".repeat(60)}
|
|
1204
|
+
`;
|
|
1205
|
+
readableVersion += `CURRENT PROMPT (NEW MESSAGE)
|
|
1206
|
+
`;
|
|
1207
|
+
readableVersion += `${"=".repeat(60)}
|
|
1208
|
+
`;
|
|
1209
|
+
readableVersion += `Length: ${prompt.length} characters
|
|
1210
|
+
`;
|
|
1211
|
+
readableVersion += `${"-".repeat(60)}
|
|
1212
|
+
`;
|
|
1213
|
+
readableVersion += `${prompt}
|
|
1214
|
+
`;
|
|
1215
|
+
readableVersion += `
|
|
1216
|
+
${"=".repeat(60)}
|
|
1217
|
+
`;
|
|
1218
|
+
readableVersion += `END OF DEBUG REPORT
|
|
1219
|
+
`;
|
|
1220
|
+
readableVersion += `${"=".repeat(60)}
|
|
1221
|
+
`;
|
|
1222
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path5.join(process.cwd(), "debug-artifacts");
|
|
1223
|
+
if (!fs5.existsSync(debugArtifactsDir)) {
|
|
1224
|
+
fs5.mkdirSync(debugArtifactsDir, { recursive: true });
|
|
1225
|
+
}
|
|
1226
|
+
const debugFile = path5.join(
|
|
1227
|
+
debugArtifactsDir,
|
|
1228
|
+
`prompt-${_checkName || "unknown"}-${timestamp}.json`
|
|
1229
|
+
);
|
|
1230
|
+
fs5.writeFileSync(debugFile, debugJson, "utf-8");
|
|
1231
|
+
const readableFile = path5.join(
|
|
1232
|
+
debugArtifactsDir,
|
|
1233
|
+
`prompt-${_checkName || "unknown"}-${timestamp}.txt`
|
|
1234
|
+
);
|
|
1235
|
+
fs5.writeFileSync(readableFile, readableVersion, "utf-8");
|
|
1236
|
+
log(`
|
|
1237
|
+
\u{1F4BE} Full debug info saved to:`);
|
|
1238
|
+
log(` JSON: ${debugFile}`);
|
|
1239
|
+
log(` TXT: ${readableFile}`);
|
|
1240
|
+
log(` - Includes: full conversation history, schema, current prompt`);
|
|
1241
|
+
} catch (error) {
|
|
1242
|
+
log(`\u26A0\uFE0F Could not save debug file: ${error}`);
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
const agentAny = agent;
|
|
1246
|
+
let response;
|
|
1247
|
+
if (agentAny.tracer && typeof agentAny.tracer.withSpan === "function") {
|
|
1248
|
+
response = await agentAny.tracer.withSpan(
|
|
1249
|
+
"visor.ai_check_reuse",
|
|
1250
|
+
async () => {
|
|
1251
|
+
return await agent.answer(prompt, void 0, schemaOptions);
|
|
1252
|
+
},
|
|
1253
|
+
{
|
|
1254
|
+
"check.name": _checkName || "unknown",
|
|
1255
|
+
"check.mode": "session_reuse",
|
|
1256
|
+
"prompt.length": prompt.length,
|
|
1257
|
+
"schema.type": effectiveSchema || "none"
|
|
1258
|
+
}
|
|
1259
|
+
);
|
|
1260
|
+
} else {
|
|
1261
|
+
response = await agent.answer(prompt, void 0, schemaOptions);
|
|
1262
|
+
}
|
|
1127
1263
|
log("\u2705 ProbeAgent session reuse completed successfully");
|
|
1128
1264
|
log(`\u{1F4E4} Response length: ${response.length} characters`);
|
|
1265
|
+
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
1266
|
+
try {
|
|
1267
|
+
const fs5 = __require("fs");
|
|
1268
|
+
const path5 = __require("path");
|
|
1269
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1270
|
+
const agentAny2 = agent;
|
|
1271
|
+
let fullHistory = [];
|
|
1272
|
+
if (agentAny2.history) {
|
|
1273
|
+
fullHistory = agentAny2.history;
|
|
1274
|
+
} else if (agentAny2.messages) {
|
|
1275
|
+
fullHistory = agentAny2.messages;
|
|
1276
|
+
} else if (agentAny2._messages) {
|
|
1277
|
+
fullHistory = agentAny2._messages;
|
|
1278
|
+
}
|
|
1279
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path5.join(process.cwd(), "debug-artifacts");
|
|
1280
|
+
const sessionFile = path5.join(
|
|
1281
|
+
debugArtifactsDir,
|
|
1282
|
+
`session-${_checkName || "unknown"}-${timestamp}.json`
|
|
1283
|
+
);
|
|
1284
|
+
const sessionData = {
|
|
1285
|
+
timestamp,
|
|
1286
|
+
checkName: _checkName || "unknown",
|
|
1287
|
+
provider: this.config.provider || "auto",
|
|
1288
|
+
model: this.config.model || "default",
|
|
1289
|
+
schema: effectiveSchema,
|
|
1290
|
+
fullConversationHistory: fullHistory,
|
|
1291
|
+
totalMessages: fullHistory.length,
|
|
1292
|
+
latestResponse: response
|
|
1293
|
+
};
|
|
1294
|
+
fs5.writeFileSync(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
1295
|
+
const sessionTxtFile = path5.join(
|
|
1296
|
+
debugArtifactsDir,
|
|
1297
|
+
`session-${_checkName || "unknown"}-${timestamp}.txt`
|
|
1298
|
+
);
|
|
1299
|
+
let readable = `=============================================================
|
|
1300
|
+
`;
|
|
1301
|
+
readable += `COMPLETE AI SESSION HISTORY (AFTER RESPONSE)
|
|
1302
|
+
`;
|
|
1303
|
+
readable += `=============================================================
|
|
1304
|
+
`;
|
|
1305
|
+
readable += `Timestamp: ${timestamp}
|
|
1306
|
+
`;
|
|
1307
|
+
readable += `Check: ${_checkName || "unknown"}
|
|
1308
|
+
`;
|
|
1309
|
+
readable += `Total Messages: ${fullHistory.length}
|
|
1310
|
+
`;
|
|
1311
|
+
readable += `=============================================================
|
|
1312
|
+
|
|
1313
|
+
`;
|
|
1314
|
+
fullHistory.forEach((msg, idx) => {
|
|
1315
|
+
readable += `
|
|
1316
|
+
${"=".repeat(60)}
|
|
1317
|
+
`;
|
|
1318
|
+
readable += `MESSAGE ${idx + 1}/${fullHistory.length}
|
|
1319
|
+
`;
|
|
1320
|
+
readable += `Role: ${msg.role || "unknown"}
|
|
1321
|
+
`;
|
|
1322
|
+
readable += `${"=".repeat(60)}
|
|
1323
|
+
`;
|
|
1324
|
+
const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content, null, 2);
|
|
1325
|
+
readable += content + "\n";
|
|
1326
|
+
});
|
|
1327
|
+
fs5.writeFileSync(sessionTxtFile, readable, "utf-8");
|
|
1328
|
+
log(`\u{1F4BE} Complete session history saved:`);
|
|
1329
|
+
log(` JSON: ${sessionFile}`);
|
|
1330
|
+
log(` TXT: ${sessionTxtFile}`);
|
|
1331
|
+
log(` - Contains ALL ${fullHistory.length} messages (prompts + responses)`);
|
|
1332
|
+
} catch (error) {
|
|
1333
|
+
log(`\u26A0\uFE0F Could not save complete session history: ${error}`);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
1337
|
+
try {
|
|
1338
|
+
const fs5 = __require("fs");
|
|
1339
|
+
const path5 = __require("path");
|
|
1340
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1341
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path5.join(process.cwd(), "debug-artifacts");
|
|
1342
|
+
const responseFile = path5.join(
|
|
1343
|
+
debugArtifactsDir,
|
|
1344
|
+
`response-${_checkName || "unknown"}-${timestamp}.txt`
|
|
1345
|
+
);
|
|
1346
|
+
let responseContent = `=============================================================
|
|
1347
|
+
`;
|
|
1348
|
+
responseContent += `VISOR AI RESPONSE - SESSION REUSE
|
|
1349
|
+
`;
|
|
1350
|
+
responseContent += `=============================================================
|
|
1351
|
+
`;
|
|
1352
|
+
responseContent += `Timestamp: ${timestamp}
|
|
1353
|
+
`;
|
|
1354
|
+
responseContent += `Check Name: ${_checkName || "unknown"}
|
|
1355
|
+
`;
|
|
1356
|
+
responseContent += `Response Length: ${response.length} characters
|
|
1357
|
+
`;
|
|
1358
|
+
responseContent += `=============================================================
|
|
1359
|
+
|
|
1360
|
+
`;
|
|
1361
|
+
responseContent += `${"=".repeat(60)}
|
|
1362
|
+
`;
|
|
1363
|
+
responseContent += `AI RESPONSE
|
|
1364
|
+
`;
|
|
1365
|
+
responseContent += `${"=".repeat(60)}
|
|
1366
|
+
`;
|
|
1367
|
+
responseContent += response;
|
|
1368
|
+
responseContent += `
|
|
1369
|
+
${"=".repeat(60)}
|
|
1370
|
+
`;
|
|
1371
|
+
responseContent += `END OF RESPONSE
|
|
1372
|
+
`;
|
|
1373
|
+
responseContent += `${"=".repeat(60)}
|
|
1374
|
+
`;
|
|
1375
|
+
fs5.writeFileSync(responseFile, responseContent, "utf-8");
|
|
1376
|
+
log(`\u{1F4BE} Response saved to: ${responseFile}`);
|
|
1377
|
+
} catch (error) {
|
|
1378
|
+
log(`\u26A0\uFE0F Could not save response file: ${error}`);
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
if (agentAny._traceFilePath && agentAny._telemetryConfig) {
|
|
1382
|
+
try {
|
|
1383
|
+
if (agentAny.tracer && typeof agentAny.tracer.flush === "function") {
|
|
1384
|
+
await agentAny.tracer.flush();
|
|
1385
|
+
log(`\u{1F504} Flushed tracer spans for cloned session`);
|
|
1386
|
+
}
|
|
1387
|
+
if (agentAny._telemetryConfig && typeof agentAny._telemetryConfig.shutdown === "function") {
|
|
1388
|
+
await agentAny._telemetryConfig.shutdown();
|
|
1389
|
+
log(`\u{1F4CA} OpenTelemetry trace saved to: ${agentAny._traceFilePath}`);
|
|
1390
|
+
if (process.env.GITHUB_ACTIONS) {
|
|
1391
|
+
const fs5 = __require("fs");
|
|
1392
|
+
if (fs5.existsSync(agentAny._traceFilePath)) {
|
|
1393
|
+
const stats = fs5.statSync(agentAny._traceFilePath);
|
|
1394
|
+
console.log(
|
|
1395
|
+
`::notice title=AI Trace Saved::${agentAny._traceFilePath} (${stats.size} bytes)`
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
} else if (agentAny.tracer && typeof agentAny.tracer.shutdown === "function") {
|
|
1400
|
+
await agentAny.tracer.shutdown();
|
|
1401
|
+
log(`\u{1F4CA} Trace saved to: ${agentAny._traceFilePath}`);
|
|
1402
|
+
}
|
|
1403
|
+
} catch (exportError) {
|
|
1404
|
+
console.error("\u26A0\uFE0F Warning: Failed to export trace for cloned session:", exportError);
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1129
1407
|
return { response, effectiveSchema };
|
|
1130
1408
|
} catch (error) {
|
|
1131
1409
|
console.error("\u274C ProbeAgent session reuse failed:", error);
|
|
@@ -1176,6 +1454,16 @@ ${schemaString}`);
|
|
|
1176
1454
|
// We don't want the agent to modify files
|
|
1177
1455
|
debug: this.config.debug || false
|
|
1178
1456
|
};
|
|
1457
|
+
let traceFilePath = "";
|
|
1458
|
+
let telemetryConfig = null;
|
|
1459
|
+
if (this.config.debug) {
|
|
1460
|
+
const tracerResult = await initializeTracer(sessionId, _checkName);
|
|
1461
|
+
if (tracerResult) {
|
|
1462
|
+
options.tracer = tracerResult.tracer;
|
|
1463
|
+
telemetryConfig = tracerResult.telemetryConfig;
|
|
1464
|
+
traceFilePath = tracerResult.filePath;
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1179
1467
|
if (this.config.mcpServers && Object.keys(this.config.mcpServers).length > 0) {
|
|
1180
1468
|
options.enableMcp = true;
|
|
1181
1469
|
options.mcpConfig = { mcpServers: this.config.mcpServers };
|
|
@@ -1220,36 +1508,289 @@ ${schemaString}`);
|
|
|
1220
1508
|
}
|
|
1221
1509
|
const provider = this.config.provider || "auto";
|
|
1222
1510
|
const model = this.config.model || "default";
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1511
|
+
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
1512
|
+
try {
|
|
1513
|
+
const fs5 = __require("fs");
|
|
1514
|
+
const path5 = __require("path");
|
|
1515
|
+
const os = __require("os");
|
|
1516
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1517
|
+
const debugData = {
|
|
1518
|
+
timestamp,
|
|
1519
|
+
checkName: _checkName || "unknown",
|
|
1520
|
+
provider,
|
|
1521
|
+
model,
|
|
1522
|
+
schema: effectiveSchema,
|
|
1523
|
+
schemaOptions: schemaOptions || "none",
|
|
1524
|
+
sessionInfo: {
|
|
1525
|
+
isSessionReuse: false,
|
|
1526
|
+
isNewSession: true
|
|
1527
|
+
},
|
|
1528
|
+
promptLength: prompt.length,
|
|
1529
|
+
prompt
|
|
1530
|
+
};
|
|
1531
|
+
const debugJson = JSON.stringify(debugData, null, 2);
|
|
1532
|
+
let readableVersion = `=============================================================
|
|
1533
|
+
`;
|
|
1534
|
+
readableVersion += `VISOR DEBUG REPORT - NEW SESSION
|
|
1535
|
+
`;
|
|
1536
|
+
readableVersion += `=============================================================
|
|
1537
|
+
`;
|
|
1538
|
+
readableVersion += `Timestamp: ${timestamp}
|
|
1539
|
+
`;
|
|
1540
|
+
readableVersion += `Check Name: ${_checkName || "unknown"}
|
|
1541
|
+
`;
|
|
1542
|
+
readableVersion += `Provider: ${provider}
|
|
1543
|
+
`;
|
|
1544
|
+
readableVersion += `Model: ${model}
|
|
1545
|
+
`;
|
|
1546
|
+
readableVersion += `Schema: ${effectiveSchema}
|
|
1547
|
+
`;
|
|
1548
|
+
readableVersion += `Schema Options: ${schemaOptions ? "provided" : "none"}
|
|
1549
|
+
`;
|
|
1550
|
+
readableVersion += `Session Type: New Session (no history)
|
|
1551
|
+
`;
|
|
1552
|
+
readableVersion += `=============================================================
|
|
1553
|
+
|
|
1554
|
+
`;
|
|
1555
|
+
if (schemaOptions) {
|
|
1556
|
+
readableVersion += `
|
|
1557
|
+
${"=".repeat(60)}
|
|
1558
|
+
`;
|
|
1559
|
+
readableVersion += `SCHEMA CONFIGURATION
|
|
1560
|
+
`;
|
|
1561
|
+
readableVersion += `${"=".repeat(60)}
|
|
1562
|
+
`;
|
|
1563
|
+
readableVersion += JSON.stringify(schemaOptions, null, 2);
|
|
1564
|
+
readableVersion += `
|
|
1565
|
+
`;
|
|
1566
|
+
}
|
|
1567
|
+
readableVersion += `
|
|
1568
|
+
${"=".repeat(60)}
|
|
1569
|
+
`;
|
|
1570
|
+
readableVersion += `PROMPT
|
|
1571
|
+
`;
|
|
1572
|
+
readableVersion += `${"=".repeat(60)}
|
|
1573
|
+
`;
|
|
1574
|
+
readableVersion += `Length: ${prompt.length} characters
|
|
1575
|
+
`;
|
|
1576
|
+
readableVersion += `${"-".repeat(60)}
|
|
1577
|
+
`;
|
|
1578
|
+
readableVersion += `${prompt}
|
|
1579
|
+
`;
|
|
1580
|
+
readableVersion += `
|
|
1581
|
+
${"=".repeat(60)}
|
|
1582
|
+
`;
|
|
1583
|
+
readableVersion += `END OF DEBUG REPORT
|
|
1584
|
+
`;
|
|
1585
|
+
readableVersion += `${"=".repeat(60)}
|
|
1586
|
+
`;
|
|
1587
|
+
const tempDir = os.tmpdir();
|
|
1588
|
+
const promptFile = path5.join(tempDir, `visor-prompt-${timestamp}.txt`);
|
|
1589
|
+
fs5.writeFileSync(promptFile, prompt, "utf-8");
|
|
1590
|
+
log(`
|
|
1232
1591
|
\u{1F4BE} Prompt saved to: ${promptFile}`);
|
|
1233
|
-
|
|
1592
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path5.join(process.cwd(), "debug-artifacts");
|
|
1593
|
+
try {
|
|
1594
|
+
if (!fs5.existsSync(debugArtifactsDir)) {
|
|
1595
|
+
fs5.mkdirSync(debugArtifactsDir, { recursive: true });
|
|
1596
|
+
}
|
|
1597
|
+
const debugFile = path5.join(
|
|
1598
|
+
debugArtifactsDir,
|
|
1599
|
+
`prompt-${_checkName || "unknown"}-${timestamp}.json`
|
|
1600
|
+
);
|
|
1601
|
+
fs5.writeFileSync(debugFile, debugJson, "utf-8");
|
|
1602
|
+
const readableFile = path5.join(
|
|
1603
|
+
debugArtifactsDir,
|
|
1604
|
+
`prompt-${_checkName || "unknown"}-${timestamp}.txt`
|
|
1605
|
+
);
|
|
1606
|
+
fs5.writeFileSync(readableFile, readableVersion, "utf-8");
|
|
1607
|
+
log(`
|
|
1608
|
+
\u{1F4BE} Full debug info saved to:`);
|
|
1609
|
+
log(` JSON: ${debugFile}`);
|
|
1610
|
+
log(` TXT: ${readableFile}`);
|
|
1611
|
+
log(` - Includes: prompt, schema, provider, model, and schema options`);
|
|
1612
|
+
} catch {
|
|
1613
|
+
}
|
|
1614
|
+
log(`
|
|
1234
1615
|
\u{1F4DD} To reproduce locally, run:`);
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1616
|
+
let cliCommand = `npx @probelabs/probe@latest agent`;
|
|
1617
|
+
cliCommand += ` --provider ${provider}`;
|
|
1618
|
+
if (model !== "default") {
|
|
1619
|
+
cliCommand += ` --model ${model}`;
|
|
1620
|
+
}
|
|
1621
|
+
if (schema) {
|
|
1622
|
+
cliCommand += ` --schema output/${schema}/schema.json`;
|
|
1623
|
+
}
|
|
1624
|
+
cliCommand += ` "${promptFile}"`;
|
|
1625
|
+
log(`
|
|
1245
1626
|
$ ${cliCommand}
|
|
1246
1627
|
`);
|
|
1247
|
-
|
|
1248
|
-
|
|
1628
|
+
} catch (error) {
|
|
1629
|
+
log(`\u26A0\uFE0F Could not save prompt file: ${error}`);
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
let response;
|
|
1633
|
+
const tracer = options.tracer;
|
|
1634
|
+
if (tracer && typeof tracer.withSpan === "function") {
|
|
1635
|
+
response = await tracer.withSpan(
|
|
1636
|
+
"visor.ai_check",
|
|
1637
|
+
async () => {
|
|
1638
|
+
return await agent.answer(prompt, void 0, schemaOptions);
|
|
1639
|
+
},
|
|
1640
|
+
{
|
|
1641
|
+
"check.name": _checkName || "unknown",
|
|
1642
|
+
"check.session_id": sessionId,
|
|
1643
|
+
"prompt.length": prompt.length,
|
|
1644
|
+
"schema.type": effectiveSchema || "none"
|
|
1645
|
+
}
|
|
1646
|
+
);
|
|
1647
|
+
} else {
|
|
1648
|
+
response = await agent.answer(prompt, void 0, schemaOptions);
|
|
1249
1649
|
}
|
|
1250
|
-
const response = await agent.answer(prompt, void 0, schemaOptions);
|
|
1251
1650
|
log("\u2705 ProbeAgent completed successfully");
|
|
1252
1651
|
log(`\u{1F4E4} Response length: ${response.length} characters`);
|
|
1652
|
+
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
1653
|
+
try {
|
|
1654
|
+
const fs5 = __require("fs");
|
|
1655
|
+
const path5 = __require("path");
|
|
1656
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1657
|
+
const agentAny = agent;
|
|
1658
|
+
let fullHistory = [];
|
|
1659
|
+
if (agentAny.history) {
|
|
1660
|
+
fullHistory = agentAny.history;
|
|
1661
|
+
} else if (agentAny.messages) {
|
|
1662
|
+
fullHistory = agentAny.messages;
|
|
1663
|
+
} else if (agentAny._messages) {
|
|
1664
|
+
fullHistory = agentAny._messages;
|
|
1665
|
+
}
|
|
1666
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path5.join(process.cwd(), "debug-artifacts");
|
|
1667
|
+
const sessionFile = path5.join(
|
|
1668
|
+
debugArtifactsDir,
|
|
1669
|
+
`session-${_checkName || "unknown"}-${timestamp}.json`
|
|
1670
|
+
);
|
|
1671
|
+
const sessionData = {
|
|
1672
|
+
timestamp,
|
|
1673
|
+
checkName: _checkName || "unknown",
|
|
1674
|
+
provider: this.config.provider || "auto",
|
|
1675
|
+
model: this.config.model || "default",
|
|
1676
|
+
schema: effectiveSchema,
|
|
1677
|
+
fullConversationHistory: fullHistory,
|
|
1678
|
+
totalMessages: fullHistory.length,
|
|
1679
|
+
latestResponse: response
|
|
1680
|
+
};
|
|
1681
|
+
fs5.writeFileSync(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
1682
|
+
const sessionTxtFile = path5.join(
|
|
1683
|
+
debugArtifactsDir,
|
|
1684
|
+
`session-${_checkName || "unknown"}-${timestamp}.txt`
|
|
1685
|
+
);
|
|
1686
|
+
let readable = `=============================================================
|
|
1687
|
+
`;
|
|
1688
|
+
readable += `COMPLETE AI SESSION HISTORY (AFTER RESPONSE)
|
|
1689
|
+
`;
|
|
1690
|
+
readable += `=============================================================
|
|
1691
|
+
`;
|
|
1692
|
+
readable += `Timestamp: ${timestamp}
|
|
1693
|
+
`;
|
|
1694
|
+
readable += `Check: ${_checkName || "unknown"}
|
|
1695
|
+
`;
|
|
1696
|
+
readable += `Total Messages: ${fullHistory.length}
|
|
1697
|
+
`;
|
|
1698
|
+
readable += `=============================================================
|
|
1699
|
+
|
|
1700
|
+
`;
|
|
1701
|
+
fullHistory.forEach((msg, idx) => {
|
|
1702
|
+
readable += `
|
|
1703
|
+
${"=".repeat(60)}
|
|
1704
|
+
`;
|
|
1705
|
+
readable += `MESSAGE ${idx + 1}/${fullHistory.length}
|
|
1706
|
+
`;
|
|
1707
|
+
readable += `Role: ${msg.role || "unknown"}
|
|
1708
|
+
`;
|
|
1709
|
+
readable += `${"=".repeat(60)}
|
|
1710
|
+
`;
|
|
1711
|
+
const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content, null, 2);
|
|
1712
|
+
readable += content + "\n";
|
|
1713
|
+
});
|
|
1714
|
+
fs5.writeFileSync(sessionTxtFile, readable, "utf-8");
|
|
1715
|
+
log(`\u{1F4BE} Complete session history saved:`);
|
|
1716
|
+
log(` JSON: ${sessionFile}`);
|
|
1717
|
+
log(` TXT: ${sessionTxtFile}`);
|
|
1718
|
+
log(` - Contains ALL ${fullHistory.length} messages (prompts + responses)`);
|
|
1719
|
+
} catch (error) {
|
|
1720
|
+
log(`\u26A0\uFE0F Could not save complete session history: ${error}`);
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
1724
|
+
try {
|
|
1725
|
+
const fs5 = __require("fs");
|
|
1726
|
+
const path5 = __require("path");
|
|
1727
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
1728
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path5.join(process.cwd(), "debug-artifacts");
|
|
1729
|
+
const responseFile = path5.join(
|
|
1730
|
+
debugArtifactsDir,
|
|
1731
|
+
`response-${_checkName || "unknown"}-${timestamp}.txt`
|
|
1732
|
+
);
|
|
1733
|
+
let responseContent = `=============================================================
|
|
1734
|
+
`;
|
|
1735
|
+
responseContent += `VISOR AI RESPONSE - NEW SESSION
|
|
1736
|
+
`;
|
|
1737
|
+
responseContent += `=============================================================
|
|
1738
|
+
`;
|
|
1739
|
+
responseContent += `Timestamp: ${timestamp}
|
|
1740
|
+
`;
|
|
1741
|
+
responseContent += `Check Name: ${_checkName || "unknown"}
|
|
1742
|
+
`;
|
|
1743
|
+
responseContent += `Response Length: ${response.length} characters
|
|
1744
|
+
`;
|
|
1745
|
+
responseContent += `=============================================================
|
|
1746
|
+
|
|
1747
|
+
`;
|
|
1748
|
+
responseContent += `${"=".repeat(60)}
|
|
1749
|
+
`;
|
|
1750
|
+
responseContent += `AI RESPONSE
|
|
1751
|
+
`;
|
|
1752
|
+
responseContent += `${"=".repeat(60)}
|
|
1753
|
+
`;
|
|
1754
|
+
responseContent += response;
|
|
1755
|
+
responseContent += `
|
|
1756
|
+
${"=".repeat(60)}
|
|
1757
|
+
`;
|
|
1758
|
+
responseContent += `END OF RESPONSE
|
|
1759
|
+
`;
|
|
1760
|
+
responseContent += `${"=".repeat(60)}
|
|
1761
|
+
`;
|
|
1762
|
+
fs5.writeFileSync(responseFile, responseContent, "utf-8");
|
|
1763
|
+
log(`\u{1F4BE} Response saved to: ${responseFile}`);
|
|
1764
|
+
} catch (error) {
|
|
1765
|
+
log(`\u26A0\uFE0F Could not save response file: ${error}`);
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
if (traceFilePath && telemetryConfig) {
|
|
1769
|
+
try {
|
|
1770
|
+
if (tracer && typeof tracer.flush === "function") {
|
|
1771
|
+
await tracer.flush();
|
|
1772
|
+
log(`\u{1F504} Flushed tracer spans`);
|
|
1773
|
+
}
|
|
1774
|
+
if (telemetryConfig && typeof telemetryConfig.shutdown === "function") {
|
|
1775
|
+
await telemetryConfig.shutdown();
|
|
1776
|
+
log(`\u{1F4CA} OpenTelemetry trace saved to: ${traceFilePath}`);
|
|
1777
|
+
if (process.env.GITHUB_ACTIONS) {
|
|
1778
|
+
const fs5 = __require("fs");
|
|
1779
|
+
if (fs5.existsSync(traceFilePath)) {
|
|
1780
|
+
const stats = fs5.statSync(traceFilePath);
|
|
1781
|
+
console.log(
|
|
1782
|
+
`::notice title=AI Trace Saved::OpenTelemetry trace file size: ${stats.size} bytes`
|
|
1783
|
+
);
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
} else if (tracer && typeof tracer.shutdown === "function") {
|
|
1787
|
+
await tracer.shutdown();
|
|
1788
|
+
log(`\u{1F4CA} Trace saved to: ${traceFilePath}`);
|
|
1789
|
+
}
|
|
1790
|
+
} catch (exportError) {
|
|
1791
|
+
console.error("\u26A0\uFE0F Warning: Failed to export trace:", exportError);
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1253
1794
|
if (_checkName) {
|
|
1254
1795
|
this.registerSession(sessionId, agent);
|
|
1255
1796
|
log(`\u{1F527} Debug: Registered AI session for potential reuse: ${sessionId}`);
|
|
@@ -1637,7 +2178,7 @@ var PRReviewer = class {
|
|
|
1637
2178
|
async reviewPR(owner, repo, prNumber, prInfo, options = {}) {
|
|
1638
2179
|
const { debug = false, config, checks } = options;
|
|
1639
2180
|
if (config && checks && checks.length > 0) {
|
|
1640
|
-
const { CheckExecutionEngine: CheckExecutionEngine2 } = await import("./check-execution-engine-
|
|
2181
|
+
const { CheckExecutionEngine: CheckExecutionEngine2 } = await import("./check-execution-engine-RORGGGGP.mjs");
|
|
1641
2182
|
const engine = new CheckExecutionEngine2();
|
|
1642
2183
|
const { results } = await engine.executeGroupedChecks(
|
|
1643
2184
|
prInfo,
|
|
@@ -1645,7 +2186,10 @@ var PRReviewer = class {
|
|
|
1645
2186
|
void 0,
|
|
1646
2187
|
config,
|
|
1647
2188
|
void 0,
|
|
1648
|
-
debug
|
|
2189
|
+
debug,
|
|
2190
|
+
void 0,
|
|
2191
|
+
void 0,
|
|
2192
|
+
options.tagFilter
|
|
1649
2193
|
);
|
|
1650
2194
|
return results;
|
|
1651
2195
|
}
|
|
@@ -1655,7 +2199,16 @@ var PRReviewer = class {
|
|
|
1655
2199
|
}
|
|
1656
2200
|
async postReviewComment(owner, repo, prNumber, groupedResults, options = {}) {
|
|
1657
2201
|
for (const [groupName, checkResults] of Object.entries(groupedResults)) {
|
|
1658
|
-
const
|
|
2202
|
+
const filteredResults = options.config ? checkResults.filter((r) => {
|
|
2203
|
+
const cfg = options.config.checks?.[r.checkName];
|
|
2204
|
+
const t = cfg?.type || "";
|
|
2205
|
+
const isGitHubOps = t === "github" || r.group === "github" || t === "noop" || t === "command";
|
|
2206
|
+
return !isGitHubOps;
|
|
2207
|
+
}) : checkResults;
|
|
2208
|
+
if (!filteredResults || filteredResults.length === 0) {
|
|
2209
|
+
continue;
|
|
2210
|
+
}
|
|
2211
|
+
const comment = await this.formatGroupComment(filteredResults, options, {
|
|
1659
2212
|
owner,
|
|
1660
2213
|
repo,
|
|
1661
2214
|
prNumber,
|
|
@@ -1668,6 +2221,7 @@ var PRReviewer = class {
|
|
|
1668
2221
|
} else {
|
|
1669
2222
|
commentId = options.commentId ? `${options.commentId}-${groupName}` : `visor-review-${groupName}`;
|
|
1670
2223
|
}
|
|
2224
|
+
if (!comment || !comment.trim()) continue;
|
|
1671
2225
|
await this.commentManager.updateOrCreateComment(owner, repo, prNumber, comment, {
|
|
1672
2226
|
commentId,
|
|
1673
2227
|
triggeredBy: options.triggeredBy || "unknown",
|
|
@@ -1677,22 +2231,33 @@ var PRReviewer = class {
|
|
|
1677
2231
|
}
|
|
1678
2232
|
}
|
|
1679
2233
|
async formatGroupComment(checkResults, _options, _githubContext) {
|
|
2234
|
+
const normalize = (s) => s.replace(/\\n/g, "\n");
|
|
2235
|
+
const checkContents = checkResults.map((result) => {
|
|
2236
|
+
const trimmed = result.content?.trim();
|
|
2237
|
+
if (trimmed) return normalize(trimmed);
|
|
2238
|
+
const out = result.output;
|
|
2239
|
+
if (out) {
|
|
2240
|
+
if (typeof out === "string" && out.trim()) return normalize(out.trim());
|
|
2241
|
+
if (typeof out === "object") {
|
|
2242
|
+
const txt = out.text || out.response || out.message;
|
|
2243
|
+
if (typeof txt === "string" && txt.trim()) return normalize(txt.trim());
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2246
|
+
return "";
|
|
2247
|
+
}).filter((content) => content && content.trim());
|
|
2248
|
+
const debugInfo = checkResults.find((result) => result.debug)?.debug;
|
|
2249
|
+
if (checkContents.length === 0 && !debugInfo) {
|
|
2250
|
+
return "";
|
|
2251
|
+
}
|
|
1680
2252
|
let comment = "";
|
|
1681
2253
|
comment += `## \u{1F50D} Code Analysis Results
|
|
1682
2254
|
|
|
1683
2255
|
`;
|
|
1684
|
-
const checkContents = checkResults.map((result) => result.content).filter((content) => content.trim());
|
|
1685
2256
|
comment += checkContents.join("\n\n");
|
|
1686
|
-
const debugInfo = checkResults.find((result) => result.debug)?.debug;
|
|
1687
2257
|
if (debugInfo) {
|
|
1688
2258
|
comment += "\n\n" + this.formatDebugSection(debugInfo);
|
|
1689
2259
|
comment += "\n\n";
|
|
1690
2260
|
}
|
|
1691
|
-
comment += `
|
|
1692
|
-
|
|
1693
|
-
---
|
|
1694
|
-
|
|
1695
|
-
*Powered by [Visor](https://probelabs.com/visor) from [Probelabs](https://probelabs.com)*`;
|
|
1696
2261
|
return comment;
|
|
1697
2262
|
}
|
|
1698
2263
|
formatDebugSection(debug) {
|
|
@@ -1813,6 +2378,7 @@ var PRReviewer = class {
|
|
|
1813
2378
|
import { simpleGit } from "simple-git";
|
|
1814
2379
|
import * as path from "path";
|
|
1815
2380
|
import * as fs from "fs";
|
|
2381
|
+
var MAX_PATCH_SIZE = 50 * 1024;
|
|
1816
2382
|
var GitRepositoryAnalyzer = class {
|
|
1817
2383
|
git;
|
|
1818
2384
|
cwd;
|
|
@@ -1823,30 +2389,48 @@ var GitRepositoryAnalyzer = class {
|
|
|
1823
2389
|
/**
|
|
1824
2390
|
* Analyze the current git repository state and return data compatible with PRInfo interface
|
|
1825
2391
|
*/
|
|
1826
|
-
async analyzeRepository(includeContext = true) {
|
|
2392
|
+
async analyzeRepository(includeContext = true, enableBranchDiff = false) {
|
|
1827
2393
|
const isRepo = await this.isGitRepository();
|
|
1828
2394
|
if (!isRepo) {
|
|
1829
2395
|
return this.createEmptyRepositoryInfo("Not a git repository");
|
|
1830
2396
|
}
|
|
1831
2397
|
try {
|
|
1832
|
-
const [status, currentBranch] = await Promise.all([
|
|
2398
|
+
const [status, currentBranch, baseBranch] = await Promise.all([
|
|
1833
2399
|
this.git.status(),
|
|
1834
|
-
this.getCurrentBranch()
|
|
2400
|
+
this.getCurrentBranch(),
|
|
2401
|
+
this.getBaseBranch()
|
|
1835
2402
|
]);
|
|
1836
|
-
const
|
|
2403
|
+
const isFeatureBranch = currentBranch !== baseBranch && currentBranch !== "main" && currentBranch !== "master";
|
|
2404
|
+
let uncommittedFiles = await this.getUncommittedChanges(includeContext);
|
|
2405
|
+
if (isFeatureBranch && includeContext && enableBranchDiff) {
|
|
2406
|
+
if (uncommittedFiles.length > 0) {
|
|
2407
|
+
console.error(`\u{1F4CA} Feature branch detected: ${currentBranch}`);
|
|
2408
|
+
console.error(
|
|
2409
|
+
`\u26A0\uFE0F Ignoring ${uncommittedFiles.length} uncommitted file(s) due to --analyze-branch-diff flag`
|
|
2410
|
+
);
|
|
2411
|
+
} else {
|
|
2412
|
+
console.error(`\u{1F4CA} Feature branch detected: ${currentBranch}`);
|
|
2413
|
+
}
|
|
2414
|
+
console.error(
|
|
2415
|
+
`\u{1F4C2} Analyzing diff vs ${baseBranch} (${uncommittedFiles.length > 0 ? "forced by --analyze-branch-diff" : "auto-enabled for code-review schemas"})`
|
|
2416
|
+
);
|
|
2417
|
+
uncommittedFiles = await this.getBranchDiff(baseBranch, includeContext);
|
|
2418
|
+
} else if (uncommittedFiles.length > 0) {
|
|
2419
|
+
console.error(`\u{1F4DD} Analyzing uncommitted changes (${uncommittedFiles.length} files)`);
|
|
2420
|
+
}
|
|
1837
2421
|
let lastCommit = null;
|
|
1838
2422
|
try {
|
|
1839
2423
|
const recentCommits = await this.git.log({ maxCount: 1 });
|
|
1840
2424
|
lastCommit = recentCommits.latest;
|
|
1841
2425
|
} catch {
|
|
1842
|
-
console.
|
|
2426
|
+
console.error("\u{1F4DD} Repository has no commits yet, analyzing uncommitted changes");
|
|
1843
2427
|
}
|
|
1844
2428
|
let author = lastCommit?.author_name;
|
|
1845
2429
|
if (!author) {
|
|
1846
2430
|
try {
|
|
1847
2431
|
const [userName, userEmail] = await Promise.all([
|
|
1848
|
-
this.git.raw(["config", "user.name"]).catch(() => null),
|
|
1849
|
-
this.git.raw(["config", "user.email"]).catch(() => null)
|
|
2432
|
+
this.git.raw(["config", "--local", "user.name"]).catch(() => null),
|
|
2433
|
+
this.git.raw(["config", "--local", "user.email"]).catch(() => null)
|
|
1850
2434
|
]);
|
|
1851
2435
|
author = userName?.trim() || userEmail?.trim() || "unknown";
|
|
1852
2436
|
} catch {
|
|
@@ -1857,7 +2441,7 @@ var GitRepositoryAnalyzer = class {
|
|
|
1857
2441
|
title: this.generateTitle(status, currentBranch),
|
|
1858
2442
|
body: this.generateDescription(status, lastCommit),
|
|
1859
2443
|
author,
|
|
1860
|
-
base:
|
|
2444
|
+
base: baseBranch,
|
|
1861
2445
|
head: currentBranch,
|
|
1862
2446
|
files: uncommittedFiles,
|
|
1863
2447
|
totalAdditions: uncommittedFiles.reduce((sum, file) => sum + file.additions, 0),
|
|
@@ -1935,6 +2519,51 @@ ${file.patch}`).join("\n\n");
|
|
|
1935
2519
|
return "main";
|
|
1936
2520
|
}
|
|
1937
2521
|
}
|
|
2522
|
+
/**
|
|
2523
|
+
* Check if a file should be excluded from analysis
|
|
2524
|
+
* This includes:
|
|
2525
|
+
* - Files in .gitignore (even if force-added)
|
|
2526
|
+
* - Built/generated files (dist/, build/, .next/, etc.)
|
|
2527
|
+
*/
|
|
2528
|
+
async shouldExcludeFile(filename) {
|
|
2529
|
+
const excludePatterns = [
|
|
2530
|
+
/^dist\//,
|
|
2531
|
+
/^build\//,
|
|
2532
|
+
/^\.next\//,
|
|
2533
|
+
/^out\//,
|
|
2534
|
+
/^node_modules\//,
|
|
2535
|
+
/^coverage\//,
|
|
2536
|
+
/^\.turbo\//
|
|
2537
|
+
];
|
|
2538
|
+
for (const pattern of excludePatterns) {
|
|
2539
|
+
if (pattern.test(filename)) {
|
|
2540
|
+
return true;
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
try {
|
|
2544
|
+
const result = await this.git.raw(["check-ignore", filename]);
|
|
2545
|
+
return result.trim().length > 0;
|
|
2546
|
+
} catch {
|
|
2547
|
+
return false;
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
/**
|
|
2551
|
+
* Truncate a patch if it exceeds MAX_PATCH_SIZE
|
|
2552
|
+
*/
|
|
2553
|
+
truncatePatch(patch, filename) {
|
|
2554
|
+
const patchSize = Buffer.byteLength(patch, "utf8");
|
|
2555
|
+
if (patchSize <= MAX_PATCH_SIZE) {
|
|
2556
|
+
return { patch, truncated: false };
|
|
2557
|
+
}
|
|
2558
|
+
const truncated = patch.substring(0, MAX_PATCH_SIZE);
|
|
2559
|
+
const truncatedPatch = `${truncated}
|
|
2560
|
+
|
|
2561
|
+
... [TRUNCATED: Diff too large (${(patchSize / 1024).toFixed(1)}KB), showing first ${(MAX_PATCH_SIZE / 1024).toFixed(0)}KB] ...`;
|
|
2562
|
+
console.error(
|
|
2563
|
+
`\u26A0\uFE0F Truncated diff for ${filename} (${(patchSize / 1024).toFixed(1)}KB \u2192 ${(MAX_PATCH_SIZE / 1024).toFixed(0)}KB)`
|
|
2564
|
+
);
|
|
2565
|
+
return { patch: truncatedPatch, truncated: true };
|
|
2566
|
+
}
|
|
1938
2567
|
async getRemoteInfo() {
|
|
1939
2568
|
try {
|
|
1940
2569
|
const remotes = await this.git.getRemotes(true);
|
|
@@ -1958,6 +2587,10 @@ ${file.patch}`).join("\n\n");
|
|
|
1958
2587
|
}))
|
|
1959
2588
|
];
|
|
1960
2589
|
for (const { file, status: status2 } of fileChanges) {
|
|
2590
|
+
if (await this.shouldExcludeFile(file)) {
|
|
2591
|
+
console.error(`\u23ED\uFE0F Skipping excluded file: ${file}`);
|
|
2592
|
+
continue;
|
|
2593
|
+
}
|
|
1961
2594
|
const filePath = path.join(this.cwd, file);
|
|
1962
2595
|
const fileChange = await this.analyzeFileChange(file, status2, filePath, includeContext);
|
|
1963
2596
|
changes.push(fileChange);
|
|
@@ -1968,16 +2601,78 @@ ${file.patch}`).join("\n\n");
|
|
|
1968
2601
|
return [];
|
|
1969
2602
|
}
|
|
1970
2603
|
}
|
|
2604
|
+
/**
|
|
2605
|
+
* Get diff between current branch and base branch (for feature branch analysis)
|
|
2606
|
+
*/
|
|
2607
|
+
async getBranchDiff(baseBranch, includeContext = true) {
|
|
2608
|
+
try {
|
|
2609
|
+
const diffSummary = await this.git.diffSummary([baseBranch]);
|
|
2610
|
+
const changes = [];
|
|
2611
|
+
if (!diffSummary || !diffSummary.files) {
|
|
2612
|
+
return [];
|
|
2613
|
+
}
|
|
2614
|
+
for (const file of diffSummary.files) {
|
|
2615
|
+
if (await this.shouldExcludeFile(file.file)) {
|
|
2616
|
+
console.error(`\u23ED\uFE0F Skipping excluded file: ${file.file}`);
|
|
2617
|
+
continue;
|
|
2618
|
+
}
|
|
2619
|
+
const isBinary = "binary" in file && file.binary;
|
|
2620
|
+
const insertions = "insertions" in file ? file.insertions : 0;
|
|
2621
|
+
const deletions = "deletions" in file ? file.deletions : 0;
|
|
2622
|
+
const fileChanges = "changes" in file ? file.changes : 0;
|
|
2623
|
+
let status;
|
|
2624
|
+
if (isBinary) {
|
|
2625
|
+
status = "modified";
|
|
2626
|
+
} else if (insertions > 0 && deletions === 0) {
|
|
2627
|
+
status = "added";
|
|
2628
|
+
} else if (insertions === 0 && deletions > 0) {
|
|
2629
|
+
status = "removed";
|
|
2630
|
+
} else {
|
|
2631
|
+
status = "modified";
|
|
2632
|
+
}
|
|
2633
|
+
let patch;
|
|
2634
|
+
let truncated = false;
|
|
2635
|
+
if (includeContext && !isBinary) {
|
|
2636
|
+
try {
|
|
2637
|
+
const rawPatch = await this.git.diff([baseBranch, "--", file.file]);
|
|
2638
|
+
if (rawPatch) {
|
|
2639
|
+
const result = this.truncatePatch(rawPatch, file.file);
|
|
2640
|
+
patch = result.patch;
|
|
2641
|
+
truncated = result.truncated;
|
|
2642
|
+
}
|
|
2643
|
+
} catch {
|
|
2644
|
+
}
|
|
2645
|
+
}
|
|
2646
|
+
const fileChange = {
|
|
2647
|
+
filename: file.file,
|
|
2648
|
+
additions: insertions,
|
|
2649
|
+
deletions,
|
|
2650
|
+
changes: fileChanges,
|
|
2651
|
+
status,
|
|
2652
|
+
patch,
|
|
2653
|
+
truncated
|
|
2654
|
+
};
|
|
2655
|
+
changes.push(fileChange);
|
|
2656
|
+
}
|
|
2657
|
+
return changes;
|
|
2658
|
+
} catch (error) {
|
|
2659
|
+
console.error("Error getting branch diff:", error);
|
|
2660
|
+
return [];
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
1971
2663
|
async analyzeFileChange(filename, status, filePath, includeContext = true) {
|
|
1972
2664
|
let additions = 0;
|
|
1973
2665
|
let deletions = 0;
|
|
1974
2666
|
let patch;
|
|
1975
2667
|
let content;
|
|
2668
|
+
let truncated = false;
|
|
1976
2669
|
try {
|
|
1977
2670
|
if (includeContext && status !== "added" && fs.existsSync(filePath)) {
|
|
1978
2671
|
const diff = await this.git.diff(["--", filename]).catch(() => "");
|
|
1979
2672
|
if (diff) {
|
|
1980
|
-
|
|
2673
|
+
const result = this.truncatePatch(diff, filename);
|
|
2674
|
+
patch = result.patch;
|
|
2675
|
+
truncated = result.truncated;
|
|
1981
2676
|
const lines = diff.split("\n");
|
|
1982
2677
|
additions = lines.filter((line) => line.startsWith("+")).length;
|
|
1983
2678
|
deletions = lines.filter((line) => line.startsWith("-")).length;
|
|
@@ -1996,7 +2691,9 @@ ${file.patch}`).join("\n\n");
|
|
|
1996
2691
|
if (stats.isFile() && stats.size < 1024 * 1024) {
|
|
1997
2692
|
if (includeContext) {
|
|
1998
2693
|
content = fs.readFileSync(filePath, "utf8");
|
|
1999
|
-
|
|
2694
|
+
const result = this.truncatePatch(content, filename);
|
|
2695
|
+
patch = result.patch;
|
|
2696
|
+
truncated = result.truncated;
|
|
2000
2697
|
}
|
|
2001
2698
|
const fileContent = includeContext ? content : fs.readFileSync(filePath, "utf8");
|
|
2002
2699
|
additions = fileContent.split("\n").length;
|
|
@@ -2017,7 +2714,8 @@ ${file.patch}`).join("\n\n");
|
|
|
2017
2714
|
deletions,
|
|
2018
2715
|
changes: additions + deletions,
|
|
2019
2716
|
content,
|
|
2020
|
-
patch
|
|
2717
|
+
patch,
|
|
2718
|
+
truncated
|
|
2021
2719
|
};
|
|
2022
2720
|
}
|
|
2023
2721
|
generateTitle(status, branch) {
|
|
@@ -2079,6 +2777,172 @@ ${file.patch}`).join("\n\n");
|
|
|
2079
2777
|
}
|
|
2080
2778
|
};
|
|
2081
2779
|
|
|
2780
|
+
// src/pr-analyzer.ts
|
|
2781
|
+
var PRAnalyzer = class {
|
|
2782
|
+
constructor(octokit, maxRetries = 3) {
|
|
2783
|
+
this.octokit = octokit;
|
|
2784
|
+
this.maxRetries = maxRetries;
|
|
2785
|
+
}
|
|
2786
|
+
/**
|
|
2787
|
+
* Fetch commit diff for incremental analysis
|
|
2788
|
+
*/
|
|
2789
|
+
async fetchCommitDiff(owner, repo, commitSha) {
|
|
2790
|
+
try {
|
|
2791
|
+
const { data: commit } = await this.withRetry(
|
|
2792
|
+
() => this.octokit.rest.repos.getCommit({
|
|
2793
|
+
owner,
|
|
2794
|
+
repo,
|
|
2795
|
+
ref: commitSha
|
|
2796
|
+
})
|
|
2797
|
+
);
|
|
2798
|
+
const patches = commit.files?.filter((file) => file.patch).map((file) => `--- ${file.filename}
|
|
2799
|
+
${file.patch}`).join("\n\n") || "";
|
|
2800
|
+
return patches;
|
|
2801
|
+
} catch (error) {
|
|
2802
|
+
console.warn(`Failed to fetch commit diff for ${commitSha}:`, error);
|
|
2803
|
+
return "";
|
|
2804
|
+
}
|
|
2805
|
+
}
|
|
2806
|
+
/**
|
|
2807
|
+
* Generate unified diff for all PR files
|
|
2808
|
+
*/
|
|
2809
|
+
generateFullDiff(files) {
|
|
2810
|
+
return files.filter((file) => file.patch).map((file) => `--- ${file.filename}
|
|
2811
|
+
${file.patch}`).join("\n\n");
|
|
2812
|
+
}
|
|
2813
|
+
async fetchPRDiff(owner, repo, prNumber, commitSha, eventType) {
|
|
2814
|
+
const [prData, filesData] = await Promise.all([
|
|
2815
|
+
this.withRetry(
|
|
2816
|
+
() => this.octokit.rest.pulls.get({
|
|
2817
|
+
owner,
|
|
2818
|
+
repo,
|
|
2819
|
+
pull_number: prNumber
|
|
2820
|
+
})
|
|
2821
|
+
),
|
|
2822
|
+
this.withRetry(
|
|
2823
|
+
() => this.octokit.rest.pulls.listFiles({
|
|
2824
|
+
owner,
|
|
2825
|
+
repo,
|
|
2826
|
+
pull_number: prNumber
|
|
2827
|
+
})
|
|
2828
|
+
)
|
|
2829
|
+
]);
|
|
2830
|
+
const pr = prData?.data;
|
|
2831
|
+
const files = filesData?.data || [];
|
|
2832
|
+
if (!pr) {
|
|
2833
|
+
throw new Error("Invalid or missing pull request data");
|
|
2834
|
+
}
|
|
2835
|
+
const title = typeof pr.title === "string" ? pr.title : pr.title ? String(pr.title) : "MISSING";
|
|
2836
|
+
const body = typeof pr.body === "string" ? pr.body : pr.body ? String(pr.body) : "";
|
|
2837
|
+
const author = pr.user && typeof pr.user === "object" && pr.user.login ? typeof pr.user.login === "string" ? pr.user.login : String(pr.user.login) : "unknown";
|
|
2838
|
+
const authorAssociation = pr.author_association && typeof pr.author_association === "string" ? pr.author_association : void 0;
|
|
2839
|
+
const base = pr.base && typeof pr.base === "object" && pr.base.ref ? typeof pr.base.ref === "string" ? pr.base.ref : String(pr.base.ref) : "main";
|
|
2840
|
+
const head = pr.head && typeof pr.head === "object" && pr.head.ref ? typeof pr.head.ref === "string" ? pr.head.ref : String(pr.head.ref) : "feature";
|
|
2841
|
+
const validFiles = files ? files.filter((file) => file && typeof file === "object" && file.filename).map((file) => ({
|
|
2842
|
+
filename: typeof file.filename === "string" ? file.filename : String(file.filename || "unknown"),
|
|
2843
|
+
additions: typeof file.additions === "number" ? Math.max(0, file.additions) : 0,
|
|
2844
|
+
deletions: typeof file.deletions === "number" ? Math.max(0, file.deletions) : 0,
|
|
2845
|
+
changes: typeof file.changes === "number" ? Math.max(0, file.changes) : 0,
|
|
2846
|
+
patch: typeof file.patch === "string" ? file.patch : void 0,
|
|
2847
|
+
status: ["added", "removed", "modified", "renamed"].includes(file.status) ? file.status : "modified"
|
|
2848
|
+
})).filter((file) => file.filename.length > 0) : [];
|
|
2849
|
+
const prInfo = {
|
|
2850
|
+
number: typeof pr.number === "number" ? pr.number : parseInt(String(pr.number || 1), 10),
|
|
2851
|
+
title,
|
|
2852
|
+
body,
|
|
2853
|
+
author,
|
|
2854
|
+
authorAssociation,
|
|
2855
|
+
base,
|
|
2856
|
+
head,
|
|
2857
|
+
files: validFiles,
|
|
2858
|
+
totalAdditions: validFiles.reduce((sum, file) => sum + file.additions, 0),
|
|
2859
|
+
totalDeletions: validFiles.reduce((sum, file) => sum + file.deletions, 0),
|
|
2860
|
+
fullDiff: this.generateFullDiff(validFiles),
|
|
2861
|
+
eventType
|
|
2862
|
+
};
|
|
2863
|
+
try {
|
|
2864
|
+
console.log(`\u{1F4AC} Fetching comment history for PR #${prInfo.number}`);
|
|
2865
|
+
const comments = await this.fetchPRComments(owner, repo, prInfo.number);
|
|
2866
|
+
prInfo.comments = comments;
|
|
2867
|
+
console.log(`\u2705 Retrieved ${comments.length} comments`);
|
|
2868
|
+
} catch (error) {
|
|
2869
|
+
console.warn(
|
|
2870
|
+
`\u26A0\uFE0F Could not fetch comments: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2871
|
+
);
|
|
2872
|
+
prInfo.comments = [];
|
|
2873
|
+
}
|
|
2874
|
+
if (commitSha) {
|
|
2875
|
+
console.log(`\u{1F527} Fetching incremental diff for commit: ${commitSha}`);
|
|
2876
|
+
prInfo.commitDiff = await this.fetchCommitDiff(owner, repo, commitSha);
|
|
2877
|
+
prInfo.isIncremental = true;
|
|
2878
|
+
if (!prInfo.commitDiff || prInfo.commitDiff.length === 0) {
|
|
2879
|
+
console.warn(
|
|
2880
|
+
`\u26A0\uFE0F No commit diff retrieved for ${commitSha}, will use full diff as fallback`
|
|
2881
|
+
);
|
|
2882
|
+
} else {
|
|
2883
|
+
console.log(`\u2705 Incremental diff retrieved (${prInfo.commitDiff.length} chars)`);
|
|
2884
|
+
}
|
|
2885
|
+
} else {
|
|
2886
|
+
prInfo.isIncremental = false;
|
|
2887
|
+
}
|
|
2888
|
+
return prInfo;
|
|
2889
|
+
}
|
|
2890
|
+
async fetchPRComments(owner, repo, prNumber) {
|
|
2891
|
+
const { data: comments } = await this.withRetry(
|
|
2892
|
+
() => this.octokit.rest.issues.listComments({
|
|
2893
|
+
owner,
|
|
2894
|
+
repo,
|
|
2895
|
+
issue_number: prNumber
|
|
2896
|
+
})
|
|
2897
|
+
);
|
|
2898
|
+
return comments.map((comment) => ({
|
|
2899
|
+
id: comment.id,
|
|
2900
|
+
author: comment.user?.login || "unknown",
|
|
2901
|
+
body: comment.body || "",
|
|
2902
|
+
createdAt: comment.created_at,
|
|
2903
|
+
updatedAt: comment.updated_at
|
|
2904
|
+
}));
|
|
2905
|
+
}
|
|
2906
|
+
async withRetry(operation) {
|
|
2907
|
+
let lastError = new Error("Unknown error");
|
|
2908
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
2909
|
+
try {
|
|
2910
|
+
return await operation();
|
|
2911
|
+
} catch (error) {
|
|
2912
|
+
if (error instanceof Error) {
|
|
2913
|
+
lastError = error;
|
|
2914
|
+
} else if (typeof error === "object" && error !== null) {
|
|
2915
|
+
const errorObj = error;
|
|
2916
|
+
const message = errorObj.message || errorObj.code || "Unknown error";
|
|
2917
|
+
lastError = new Error(String(message));
|
|
2918
|
+
Object.assign(lastError, error);
|
|
2919
|
+
} else {
|
|
2920
|
+
lastError = new Error(String(error));
|
|
2921
|
+
}
|
|
2922
|
+
if (attempt === this.maxRetries) {
|
|
2923
|
+
break;
|
|
2924
|
+
}
|
|
2925
|
+
if (this.isRetryableError(error)) {
|
|
2926
|
+
const delay = Math.min(1e3 * Math.pow(2, attempt), 5e3);
|
|
2927
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2928
|
+
} else {
|
|
2929
|
+
throw error;
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
throw lastError;
|
|
2934
|
+
}
|
|
2935
|
+
isRetryableError(error) {
|
|
2936
|
+
const retryableErrors = ["ETIMEDOUT", "ECONNRESET", "ECONNREFUSED", "ENOTFOUND", "EAI_AGAIN"];
|
|
2937
|
+
const retryableStatuses = [408, 429, 500, 502, 503, 504];
|
|
2938
|
+
if (typeof error !== "object" || error === null) {
|
|
2939
|
+
return false;
|
|
2940
|
+
}
|
|
2941
|
+
const err = error;
|
|
2942
|
+
return err.code !== void 0 && retryableErrors.includes(err.code) || err.status !== void 0 && retryableStatuses.includes(err.status) || err.response?.status !== void 0 && retryableStatuses.includes(err.response.status);
|
|
2943
|
+
}
|
|
2944
|
+
};
|
|
2945
|
+
|
|
2082
2946
|
// src/providers/check-provider.interface.ts
|
|
2083
2947
|
var CheckProvider = class {
|
|
2084
2948
|
};
|
|
@@ -3451,6 +4315,270 @@ var LogCheckProvider = class extends CheckProvider {
|
|
|
3451
4315
|
}
|
|
3452
4316
|
};
|
|
3453
4317
|
|
|
4318
|
+
// src/providers/github-ops-provider.ts
|
|
4319
|
+
import Sandbox from "@nyariv/sandboxjs";
|
|
4320
|
+
var GitHubOpsProvider = class extends CheckProvider {
|
|
4321
|
+
sandbox;
|
|
4322
|
+
getName() {
|
|
4323
|
+
return "github";
|
|
4324
|
+
}
|
|
4325
|
+
getDescription() {
|
|
4326
|
+
return "Native GitHub operations (labels, comments, reviewers) executed via Octokit";
|
|
4327
|
+
}
|
|
4328
|
+
async validateConfig(config) {
|
|
4329
|
+
if (!config || typeof config !== "object") return false;
|
|
4330
|
+
const cfg = config;
|
|
4331
|
+
return typeof cfg.op === "string" && cfg.op.length > 0;
|
|
4332
|
+
}
|
|
4333
|
+
getSupportedConfigKeys() {
|
|
4334
|
+
return ["op", "values", "value", "value_js"];
|
|
4335
|
+
}
|
|
4336
|
+
async isAvailable() {
|
|
4337
|
+
return Boolean(
|
|
4338
|
+
process.env.GITHUB_TOKEN || process.env["INPUT_GITHUB-TOKEN"] || process.env.GITHUB_REPOSITORY
|
|
4339
|
+
);
|
|
4340
|
+
}
|
|
4341
|
+
getRequirements() {
|
|
4342
|
+
return ["GITHUB_TOKEN or INPUT_GITHUB-TOKEN", "GITHUB_REPOSITORY"];
|
|
4343
|
+
}
|
|
4344
|
+
async execute(prInfo, config, dependencyResults) {
|
|
4345
|
+
const cfg = config;
|
|
4346
|
+
let octokit = config.eventContext?.octokit;
|
|
4347
|
+
if (!octokit) {
|
|
4348
|
+
const token = process.env["INPUT_GITHUB-TOKEN"] || process.env["GITHUB_TOKEN"];
|
|
4349
|
+
if (!token) {
|
|
4350
|
+
return {
|
|
4351
|
+
issues: [
|
|
4352
|
+
{
|
|
4353
|
+
file: "system",
|
|
4354
|
+
line: 0,
|
|
4355
|
+
ruleId: "github/missing_token",
|
|
4356
|
+
message: "No GitHub token available; set GITHUB_TOKEN or pass github-token input for native GitHub operations",
|
|
4357
|
+
severity: "error",
|
|
4358
|
+
category: "logic"
|
|
4359
|
+
}
|
|
4360
|
+
]
|
|
4361
|
+
};
|
|
4362
|
+
}
|
|
4363
|
+
const { Octokit } = await import("@octokit/rest");
|
|
4364
|
+
octokit = new Octokit({ auth: token });
|
|
4365
|
+
}
|
|
4366
|
+
const repoEnv = process.env.GITHUB_REPOSITORY || "";
|
|
4367
|
+
const [owner, repo] = repoEnv.split("/");
|
|
4368
|
+
if (!owner || !repo || !prInfo?.number) {
|
|
4369
|
+
return {
|
|
4370
|
+
issues: [
|
|
4371
|
+
{
|
|
4372
|
+
file: "system",
|
|
4373
|
+
line: 0,
|
|
4374
|
+
ruleId: "github/missing_context",
|
|
4375
|
+
message: "Missing owner/repo or PR number; GitHub operations require Action context",
|
|
4376
|
+
severity: "error",
|
|
4377
|
+
category: "logic"
|
|
4378
|
+
}
|
|
4379
|
+
]
|
|
4380
|
+
};
|
|
4381
|
+
}
|
|
4382
|
+
let valuesRaw = [];
|
|
4383
|
+
if (Array.isArray(cfg.values)) valuesRaw = cfg.values.map((v) => String(v));
|
|
4384
|
+
else if (typeof cfg.values === "string") valuesRaw = [cfg.values];
|
|
4385
|
+
else if (typeof cfg.value === "string") valuesRaw = [cfg.value];
|
|
4386
|
+
const renderValues = async (arr) => {
|
|
4387
|
+
if (!arr || arr.length === 0) return [];
|
|
4388
|
+
const liq = createExtendedLiquid({
|
|
4389
|
+
cache: false,
|
|
4390
|
+
strictFilters: false,
|
|
4391
|
+
strictVariables: false
|
|
4392
|
+
});
|
|
4393
|
+
const outputs = {};
|
|
4394
|
+
if (dependencyResults) {
|
|
4395
|
+
for (const [name, result] of dependencyResults.entries()) {
|
|
4396
|
+
const summary = result;
|
|
4397
|
+
outputs[name] = summary.output !== void 0 ? summary.output : summary;
|
|
4398
|
+
}
|
|
4399
|
+
}
|
|
4400
|
+
const ctx = {
|
|
4401
|
+
pr: {
|
|
4402
|
+
number: prInfo.number,
|
|
4403
|
+
title: prInfo.title,
|
|
4404
|
+
author: prInfo.author,
|
|
4405
|
+
branch: prInfo.head,
|
|
4406
|
+
base: prInfo.base,
|
|
4407
|
+
authorAssociation: prInfo.authorAssociation
|
|
4408
|
+
},
|
|
4409
|
+
outputs
|
|
4410
|
+
};
|
|
4411
|
+
const out = [];
|
|
4412
|
+
for (const item of arr) {
|
|
4413
|
+
if (typeof item === "string" && (item.includes("{{") || item.includes("{%"))) {
|
|
4414
|
+
try {
|
|
4415
|
+
const rendered = await liq.parseAndRender(item, ctx);
|
|
4416
|
+
out.push(rendered);
|
|
4417
|
+
} catch (e) {
|
|
4418
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
4419
|
+
return Promise.reject({
|
|
4420
|
+
issues: [
|
|
4421
|
+
{
|
|
4422
|
+
file: "system",
|
|
4423
|
+
line: 0,
|
|
4424
|
+
ruleId: "github/liquid_render_error",
|
|
4425
|
+
message: `Failed to render template: ${msg}`,
|
|
4426
|
+
severity: "error",
|
|
4427
|
+
category: "logic"
|
|
4428
|
+
}
|
|
4429
|
+
]
|
|
4430
|
+
});
|
|
4431
|
+
}
|
|
4432
|
+
} else {
|
|
4433
|
+
out.push(String(item));
|
|
4434
|
+
}
|
|
4435
|
+
}
|
|
4436
|
+
return out;
|
|
4437
|
+
};
|
|
4438
|
+
let values = await renderValues(valuesRaw);
|
|
4439
|
+
if (cfg.value_js && cfg.value_js.trim()) {
|
|
4440
|
+
try {
|
|
4441
|
+
const sandbox = this.getSecureSandbox();
|
|
4442
|
+
const code = `
|
|
4443
|
+
const __fn = () => {
|
|
4444
|
+
${cfg.value_js}
|
|
4445
|
+
};
|
|
4446
|
+
return __fn();
|
|
4447
|
+
`;
|
|
4448
|
+
const exec = sandbox.compile(code);
|
|
4449
|
+
const res = exec({ pr: prInfo, values });
|
|
4450
|
+
if (typeof res === "string") values = [res];
|
|
4451
|
+
else if (Array.isArray(res)) values = res.map((v) => String(v));
|
|
4452
|
+
} catch (e) {
|
|
4453
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
4454
|
+
return {
|
|
4455
|
+
issues: [
|
|
4456
|
+
{
|
|
4457
|
+
file: "system",
|
|
4458
|
+
line: 0,
|
|
4459
|
+
ruleId: "github/value_js_error",
|
|
4460
|
+
message: `value_js evaluation failed: ${msg}`,
|
|
4461
|
+
severity: "error",
|
|
4462
|
+
category: "logic"
|
|
4463
|
+
}
|
|
4464
|
+
]
|
|
4465
|
+
};
|
|
4466
|
+
}
|
|
4467
|
+
}
|
|
4468
|
+
values = values.map((v) => v.trim()).filter((v) => v.length > 0);
|
|
4469
|
+
values = Array.from(new Set(values));
|
|
4470
|
+
try {
|
|
4471
|
+
switch (cfg.op) {
|
|
4472
|
+
case "labels.add": {
|
|
4473
|
+
if (values.length === 0) break;
|
|
4474
|
+
await octokit.rest.issues.addLabels({
|
|
4475
|
+
owner,
|
|
4476
|
+
repo,
|
|
4477
|
+
issue_number: prInfo.number,
|
|
4478
|
+
labels: values
|
|
4479
|
+
});
|
|
4480
|
+
break;
|
|
4481
|
+
}
|
|
4482
|
+
case "labels.remove": {
|
|
4483
|
+
for (const l of values) {
|
|
4484
|
+
await octokit.rest.issues.removeLabel({
|
|
4485
|
+
owner,
|
|
4486
|
+
repo,
|
|
4487
|
+
issue_number: prInfo.number,
|
|
4488
|
+
name: l
|
|
4489
|
+
});
|
|
4490
|
+
}
|
|
4491
|
+
break;
|
|
4492
|
+
}
|
|
4493
|
+
case "comment.create": {
|
|
4494
|
+
const body = values.join("\n").trim();
|
|
4495
|
+
if (body)
|
|
4496
|
+
await octokit.rest.issues.createComment({
|
|
4497
|
+
owner,
|
|
4498
|
+
repo,
|
|
4499
|
+
issue_number: prInfo.number,
|
|
4500
|
+
body
|
|
4501
|
+
});
|
|
4502
|
+
break;
|
|
4503
|
+
}
|
|
4504
|
+
default:
|
|
4505
|
+
return {
|
|
4506
|
+
issues: [
|
|
4507
|
+
{
|
|
4508
|
+
file: "system",
|
|
4509
|
+
line: 0,
|
|
4510
|
+
ruleId: "github/unsupported_op",
|
|
4511
|
+
message: `Unsupported GitHub op: ${cfg.op}`,
|
|
4512
|
+
severity: "error",
|
|
4513
|
+
category: "logic"
|
|
4514
|
+
}
|
|
4515
|
+
]
|
|
4516
|
+
};
|
|
4517
|
+
}
|
|
4518
|
+
return { issues: [] };
|
|
4519
|
+
} catch (e) {
|
|
4520
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
4521
|
+
return {
|
|
4522
|
+
issues: [
|
|
4523
|
+
{
|
|
4524
|
+
file: "system",
|
|
4525
|
+
line: 0,
|
|
4526
|
+
ruleId: "github/op_failed",
|
|
4527
|
+
message: `GitHub operation failed (${cfg.op}): ${msg}`,
|
|
4528
|
+
severity: "error",
|
|
4529
|
+
category: "logic"
|
|
4530
|
+
}
|
|
4531
|
+
]
|
|
4532
|
+
};
|
|
4533
|
+
}
|
|
4534
|
+
}
|
|
4535
|
+
/**
|
|
4536
|
+
* Create a secure sandbox for evaluating small expressions without access to process/env
|
|
4537
|
+
*/
|
|
4538
|
+
getSecureSandbox() {
|
|
4539
|
+
if (this.sandbox) return this.sandbox;
|
|
4540
|
+
const globals = {
|
|
4541
|
+
...Sandbox.SAFE_GLOBALS,
|
|
4542
|
+
Math
|
|
4543
|
+
};
|
|
4544
|
+
const prototypeWhitelist = new Map(Sandbox.SAFE_PROTOTYPES);
|
|
4545
|
+
const arrayMethods = /* @__PURE__ */ new Set([
|
|
4546
|
+
"some",
|
|
4547
|
+
"every",
|
|
4548
|
+
"filter",
|
|
4549
|
+
"map",
|
|
4550
|
+
"reduce",
|
|
4551
|
+
"find",
|
|
4552
|
+
"includes",
|
|
4553
|
+
"indexOf",
|
|
4554
|
+
"length",
|
|
4555
|
+
"slice",
|
|
4556
|
+
"concat",
|
|
4557
|
+
"join"
|
|
4558
|
+
]);
|
|
4559
|
+
prototypeWhitelist.set(Array.prototype, arrayMethods);
|
|
4560
|
+
const stringMethods = /* @__PURE__ */ new Set([
|
|
4561
|
+
"toLowerCase",
|
|
4562
|
+
"toUpperCase",
|
|
4563
|
+
"includes",
|
|
4564
|
+
"indexOf",
|
|
4565
|
+
"startsWith",
|
|
4566
|
+
"endsWith",
|
|
4567
|
+
"slice",
|
|
4568
|
+
"substring",
|
|
4569
|
+
"length",
|
|
4570
|
+
"trim",
|
|
4571
|
+
"split",
|
|
4572
|
+
"replace"
|
|
4573
|
+
]);
|
|
4574
|
+
prototypeWhitelist.set(String.prototype, stringMethods);
|
|
4575
|
+
const objectMethods = /* @__PURE__ */ new Set(["hasOwnProperty", "toString", "valueOf"]);
|
|
4576
|
+
prototypeWhitelist.set(Object.prototype, objectMethods);
|
|
4577
|
+
this.sandbox = new Sandbox({ globals, prototypeWhitelist });
|
|
4578
|
+
return this.sandbox;
|
|
4579
|
+
}
|
|
4580
|
+
};
|
|
4581
|
+
|
|
3454
4582
|
// src/providers/claude-code-check-provider.ts
|
|
3455
4583
|
import fs4 from "fs/promises";
|
|
3456
4584
|
import path4 from "path";
|
|
@@ -3905,7 +5033,7 @@ var ClaudeCodeCheckProvider = class extends CheckProvider {
|
|
|
3905
5033
|
};
|
|
3906
5034
|
|
|
3907
5035
|
// src/providers/command-check-provider.ts
|
|
3908
|
-
import
|
|
5036
|
+
import Sandbox2 from "@nyariv/sandboxjs";
|
|
3909
5037
|
init_logger();
|
|
3910
5038
|
var CommandCheckProvider = class extends CheckProvider {
|
|
3911
5039
|
liquid;
|
|
@@ -3920,12 +5048,12 @@ var CommandCheckProvider = class extends CheckProvider {
|
|
|
3920
5048
|
}
|
|
3921
5049
|
createSecureSandbox() {
|
|
3922
5050
|
const globals = {
|
|
3923
|
-
...
|
|
5051
|
+
...Sandbox2.SAFE_GLOBALS,
|
|
3924
5052
|
console,
|
|
3925
5053
|
JSON
|
|
3926
5054
|
};
|
|
3927
|
-
const prototypeWhitelist = new Map(
|
|
3928
|
-
return new
|
|
5055
|
+
const prototypeWhitelist = new Map(Sandbox2.SAFE_PROTOTYPES);
|
|
5056
|
+
return new Sandbox2({ globals, prototypeWhitelist });
|
|
3929
5057
|
}
|
|
3930
5058
|
getName() {
|
|
3931
5059
|
return "command";
|
|
@@ -3995,7 +5123,20 @@ var CommandCheckProvider = class extends CheckProvider {
|
|
|
3995
5123
|
const execAsync = promisify(exec);
|
|
3996
5124
|
const timeoutSeconds = config.timeout || 60;
|
|
3997
5125
|
const timeoutMs = timeoutSeconds * 1e3;
|
|
3998
|
-
const
|
|
5126
|
+
const normalizeNodeEval = (cmd) => {
|
|
5127
|
+
const re = /^(?<prefix>\s*(?:\/usr\/bin\/env\s+)?node(?:\.exe)?\s+(?:-e|--eval)\s+)(['"])([\s\S]*?)\2(?<suffix>\s|$)/;
|
|
5128
|
+
const m = cmd.match(re);
|
|
5129
|
+
if (!m || !m.groups) return cmd;
|
|
5130
|
+
const prefix = m.groups.prefix;
|
|
5131
|
+
const quote = m[2];
|
|
5132
|
+
const code = m[3];
|
|
5133
|
+
const suffix = m.groups.suffix || "";
|
|
5134
|
+
if (!code.includes("\n")) return cmd;
|
|
5135
|
+
const escaped = code.replace(/\n/g, "\\n");
|
|
5136
|
+
return cmd.replace(re, `${prefix}${quote}${escaped}${quote}${suffix}`);
|
|
5137
|
+
};
|
|
5138
|
+
const safeCommand = normalizeNodeEval(renderedCommand);
|
|
5139
|
+
const { stdout, stderr } = await execAsync(safeCommand, {
|
|
3999
5140
|
env: scriptEnv,
|
|
4000
5141
|
timeout: timeoutMs,
|
|
4001
5142
|
maxBuffer: 10 * 1024 * 1024
|
|
@@ -4077,7 +5218,11 @@ var CommandCheckProvider = class extends CheckProvider {
|
|
|
4077
5218
|
pr: templateContext.pr,
|
|
4078
5219
|
files: templateContext.files,
|
|
4079
5220
|
outputs: this.makeOutputsJsonSmart(templateContext.outputs),
|
|
4080
|
-
env: templateContext.env
|
|
5221
|
+
env: templateContext.env,
|
|
5222
|
+
permissions: createPermissionHelpers(
|
|
5223
|
+
resolveAssociationFromEvent(prInfo.eventContext, prInfo.authorAssociation),
|
|
5224
|
+
detectLocalMode()
|
|
5225
|
+
)
|
|
4081
5226
|
};
|
|
4082
5227
|
const trimmedTransform = transformJs.trim();
|
|
4083
5228
|
const buildBodyWithReturn = (raw) => {
|
|
@@ -4104,6 +5249,12 @@ return (${lastExpr});`;
|
|
|
4104
5249
|
const outputs = scope.outputs;
|
|
4105
5250
|
const env = scope.env;
|
|
4106
5251
|
const log = (...args) => { console.log('\u{1F50D} Debug:', ...args); };
|
|
5252
|
+
const hasMinPermission = scope.permissions.hasMinPermission;
|
|
5253
|
+
const isOwner = scope.permissions.isOwner;
|
|
5254
|
+
const isMember = scope.permissions.isMember;
|
|
5255
|
+
const isCollaborator = scope.permissions.isCollaborator;
|
|
5256
|
+
const isContributor = scope.permissions.isContributor;
|
|
5257
|
+
const isFirstTimer = scope.permissions.isFirstTimer;
|
|
4107
5258
|
const __result = (function(){
|
|
4108
5259
|
${bodyWithReturn}
|
|
4109
5260
|
})();
|
|
@@ -4121,6 +5272,12 @@ ${bodyWithReturn}
|
|
|
4121
5272
|
const outputs = scope.outputs;
|
|
4122
5273
|
const env = scope.env;
|
|
4123
5274
|
const log = (...args) => { console.log('\u{1F50D} Debug:', ...args); };
|
|
5275
|
+
const hasMinPermission = scope.permissions.hasMinPermission;
|
|
5276
|
+
const isOwner = scope.permissions.isOwner;
|
|
5277
|
+
const isMember = scope.permissions.isMember;
|
|
5278
|
+
const isCollaborator = scope.permissions.isCollaborator;
|
|
5279
|
+
const isContributor = scope.permissions.isContributor;
|
|
5280
|
+
const isFirstTimer = scope.permissions.isFirstTimer;
|
|
4124
5281
|
const __ret = (function(){
|
|
4125
5282
|
${bodyWithReturn}
|
|
4126
5283
|
})();
|
|
@@ -5103,6 +6260,475 @@ ${stderrOutput}` : `Command execution failed: ${errorMessage}`;
|
|
|
5103
6260
|
}
|
|
5104
6261
|
};
|
|
5105
6262
|
|
|
6263
|
+
// src/providers/memory-check-provider.ts
|
|
6264
|
+
init_logger();
|
|
6265
|
+
import Sandbox3 from "@nyariv/sandboxjs";
|
|
6266
|
+
var MemoryCheckProvider = class extends CheckProvider {
|
|
6267
|
+
liquid;
|
|
6268
|
+
sandbox;
|
|
6269
|
+
constructor() {
|
|
6270
|
+
super();
|
|
6271
|
+
this.liquid = createExtendedLiquid({
|
|
6272
|
+
strictVariables: false,
|
|
6273
|
+
strictFilters: false
|
|
6274
|
+
});
|
|
6275
|
+
}
|
|
6276
|
+
/**
|
|
6277
|
+
* Create a secure sandbox for JavaScript execution
|
|
6278
|
+
*/
|
|
6279
|
+
createSecureSandbox() {
|
|
6280
|
+
const globals = {
|
|
6281
|
+
...Sandbox3.SAFE_GLOBALS,
|
|
6282
|
+
Math,
|
|
6283
|
+
console: {
|
|
6284
|
+
log: console.log,
|
|
6285
|
+
warn: console.warn,
|
|
6286
|
+
error: console.error
|
|
6287
|
+
}
|
|
6288
|
+
};
|
|
6289
|
+
const prototypeWhitelist = new Map(Sandbox3.SAFE_PROTOTYPES);
|
|
6290
|
+
const arrayMethods = /* @__PURE__ */ new Set([
|
|
6291
|
+
"some",
|
|
6292
|
+
"every",
|
|
6293
|
+
"filter",
|
|
6294
|
+
"map",
|
|
6295
|
+
"reduce",
|
|
6296
|
+
"find",
|
|
6297
|
+
"includes",
|
|
6298
|
+
"indexOf",
|
|
6299
|
+
"length",
|
|
6300
|
+
"slice",
|
|
6301
|
+
"concat",
|
|
6302
|
+
"join",
|
|
6303
|
+
"push",
|
|
6304
|
+
"pop",
|
|
6305
|
+
"shift",
|
|
6306
|
+
"unshift",
|
|
6307
|
+
"sort",
|
|
6308
|
+
"reverse"
|
|
6309
|
+
]);
|
|
6310
|
+
prototypeWhitelist.set(Array.prototype, arrayMethods);
|
|
6311
|
+
const stringMethods = /* @__PURE__ */ new Set([
|
|
6312
|
+
"toLowerCase",
|
|
6313
|
+
"toUpperCase",
|
|
6314
|
+
"includes",
|
|
6315
|
+
"indexOf",
|
|
6316
|
+
"startsWith",
|
|
6317
|
+
"endsWith",
|
|
6318
|
+
"slice",
|
|
6319
|
+
"substring",
|
|
6320
|
+
"length",
|
|
6321
|
+
"trim",
|
|
6322
|
+
"split",
|
|
6323
|
+
"replace",
|
|
6324
|
+
"match",
|
|
6325
|
+
"padStart",
|
|
6326
|
+
"padEnd"
|
|
6327
|
+
]);
|
|
6328
|
+
prototypeWhitelist.set(String.prototype, stringMethods);
|
|
6329
|
+
const objectMethods = /* @__PURE__ */ new Set(["hasOwnProperty", "toString", "valueOf"]);
|
|
6330
|
+
prototypeWhitelist.set(Object.prototype, objectMethods);
|
|
6331
|
+
return new Sandbox3({
|
|
6332
|
+
globals,
|
|
6333
|
+
prototypeWhitelist
|
|
6334
|
+
});
|
|
6335
|
+
}
|
|
6336
|
+
getName() {
|
|
6337
|
+
return "memory";
|
|
6338
|
+
}
|
|
6339
|
+
getDescription() {
|
|
6340
|
+
return "Memory/state management provider for persistent key-value storage across checks";
|
|
6341
|
+
}
|
|
6342
|
+
async validateConfig(config) {
|
|
6343
|
+
if (!config || typeof config !== "object") {
|
|
6344
|
+
return false;
|
|
6345
|
+
}
|
|
6346
|
+
const cfg = config;
|
|
6347
|
+
if (cfg.type !== "memory") {
|
|
6348
|
+
return false;
|
|
6349
|
+
}
|
|
6350
|
+
if (!cfg.operation || typeof cfg.operation !== "string") {
|
|
6351
|
+
return false;
|
|
6352
|
+
}
|
|
6353
|
+
const operation = cfg.operation;
|
|
6354
|
+
const validOps = ["get", "set", "append", "increment", "delete", "clear", "list", "exec_js"];
|
|
6355
|
+
if (!validOps.includes(operation)) {
|
|
6356
|
+
return false;
|
|
6357
|
+
}
|
|
6358
|
+
if (["get", "set", "append", "increment", "delete"].includes(operation)) {
|
|
6359
|
+
if (!cfg.key || typeof cfg.key !== "string") {
|
|
6360
|
+
return false;
|
|
6361
|
+
}
|
|
6362
|
+
}
|
|
6363
|
+
if (["set", "append"].includes(operation)) {
|
|
6364
|
+
if (cfg.value === void 0 && !cfg.value_js) {
|
|
6365
|
+
return false;
|
|
6366
|
+
}
|
|
6367
|
+
}
|
|
6368
|
+
if (operation === "exec_js") {
|
|
6369
|
+
if (!cfg.memory_js || typeof cfg.memory_js !== "string") {
|
|
6370
|
+
return false;
|
|
6371
|
+
}
|
|
6372
|
+
}
|
|
6373
|
+
return true;
|
|
6374
|
+
}
|
|
6375
|
+
async execute(prInfo, config, dependencyResults, _sessionInfo) {
|
|
6376
|
+
const operation = config.operation;
|
|
6377
|
+
const key = config.key;
|
|
6378
|
+
const namespace = config.namespace;
|
|
6379
|
+
const memoryStore = MemoryStore.getInstance();
|
|
6380
|
+
const templateContext = this.buildTemplateContext(prInfo, dependencyResults, memoryStore);
|
|
6381
|
+
let result;
|
|
6382
|
+
try {
|
|
6383
|
+
switch (operation) {
|
|
6384
|
+
case "get":
|
|
6385
|
+
result = await this.handleGet(memoryStore, key, namespace);
|
|
6386
|
+
break;
|
|
6387
|
+
case "set":
|
|
6388
|
+
result = await this.handleSet(memoryStore, key, config, namespace, templateContext);
|
|
6389
|
+
break;
|
|
6390
|
+
case "append":
|
|
6391
|
+
result = await this.handleAppend(memoryStore, key, config, namespace, templateContext);
|
|
6392
|
+
break;
|
|
6393
|
+
case "increment":
|
|
6394
|
+
result = await this.handleIncrement(
|
|
6395
|
+
memoryStore,
|
|
6396
|
+
key,
|
|
6397
|
+
config,
|
|
6398
|
+
namespace,
|
|
6399
|
+
templateContext
|
|
6400
|
+
);
|
|
6401
|
+
break;
|
|
6402
|
+
case "delete":
|
|
6403
|
+
result = await this.handleDelete(memoryStore, key, namespace);
|
|
6404
|
+
break;
|
|
6405
|
+
case "clear":
|
|
6406
|
+
result = await this.handleClear(memoryStore, namespace);
|
|
6407
|
+
break;
|
|
6408
|
+
case "list":
|
|
6409
|
+
result = await this.handleList(memoryStore, namespace);
|
|
6410
|
+
break;
|
|
6411
|
+
case "exec_js":
|
|
6412
|
+
result = await this.handleExecJs(memoryStore, config, templateContext);
|
|
6413
|
+
break;
|
|
6414
|
+
default:
|
|
6415
|
+
throw new Error(`Unknown memory operation: ${operation}`);
|
|
6416
|
+
}
|
|
6417
|
+
return {
|
|
6418
|
+
issues: [],
|
|
6419
|
+
output: result
|
|
6420
|
+
};
|
|
6421
|
+
} catch (error) {
|
|
6422
|
+
const errorMsg = error instanceof Error ? error.message : "Unknown error in memory operation";
|
|
6423
|
+
logger.error(`Memory operation failed: ${errorMsg}`);
|
|
6424
|
+
return {
|
|
6425
|
+
issues: [],
|
|
6426
|
+
output: null,
|
|
6427
|
+
error: errorMsg
|
|
6428
|
+
};
|
|
6429
|
+
}
|
|
6430
|
+
}
|
|
6431
|
+
async handleGet(store, key, namespace) {
|
|
6432
|
+
const value = store.get(key, namespace);
|
|
6433
|
+
logger.debug(
|
|
6434
|
+
`Memory GET: ${namespace || store.getDefaultNamespace()}.${key} = ${JSON.stringify(value)}`
|
|
6435
|
+
);
|
|
6436
|
+
return value;
|
|
6437
|
+
}
|
|
6438
|
+
async handleSet(store, key, config, namespace, context) {
|
|
6439
|
+
const value = await this.computeValue(config, context);
|
|
6440
|
+
await store.set(key, value, namespace);
|
|
6441
|
+
logger.debug(
|
|
6442
|
+
`Memory SET: ${namespace || store.getDefaultNamespace()}.${key} = ${JSON.stringify(value)}`
|
|
6443
|
+
);
|
|
6444
|
+
return value;
|
|
6445
|
+
}
|
|
6446
|
+
async handleAppend(store, key, config, namespace, context) {
|
|
6447
|
+
const value = await this.computeValue(config, context);
|
|
6448
|
+
await store.append(key, value, namespace);
|
|
6449
|
+
const result = store.get(key, namespace);
|
|
6450
|
+
logger.debug(
|
|
6451
|
+
`Memory APPEND: ${namespace || store.getDefaultNamespace()}.${key} += ${JSON.stringify(value)} (now: ${JSON.stringify(result)})`
|
|
6452
|
+
);
|
|
6453
|
+
return result;
|
|
6454
|
+
}
|
|
6455
|
+
async handleIncrement(store, key, config, namespace, context) {
|
|
6456
|
+
let amount = 1;
|
|
6457
|
+
if (config.value !== void 0 || config.value_js) {
|
|
6458
|
+
const computedValue = await this.computeValue(config, context);
|
|
6459
|
+
if (typeof computedValue === "number") {
|
|
6460
|
+
amount = computedValue;
|
|
6461
|
+
} else {
|
|
6462
|
+
throw new Error(`Increment amount must be a number, got ${typeof computedValue}`);
|
|
6463
|
+
}
|
|
6464
|
+
}
|
|
6465
|
+
const result = await store.increment(key, amount, namespace);
|
|
6466
|
+
logger.debug(
|
|
6467
|
+
`Memory INCREMENT: ${namespace || store.getDefaultNamespace()}.${key} += ${amount} (now: ${result})`
|
|
6468
|
+
);
|
|
6469
|
+
return result;
|
|
6470
|
+
}
|
|
6471
|
+
async handleDelete(store, key, namespace) {
|
|
6472
|
+
const deleted = await store.delete(key, namespace);
|
|
6473
|
+
logger.debug(
|
|
6474
|
+
`Memory DELETE: ${namespace || store.getDefaultNamespace()}.${key} (deleted: ${deleted})`
|
|
6475
|
+
);
|
|
6476
|
+
return deleted;
|
|
6477
|
+
}
|
|
6478
|
+
async handleClear(store, namespace) {
|
|
6479
|
+
await store.clear(namespace);
|
|
6480
|
+
logger.debug(`Memory CLEAR: ${namespace ? `namespace ${namespace}` : "all namespaces"}`);
|
|
6481
|
+
}
|
|
6482
|
+
async handleList(store, namespace) {
|
|
6483
|
+
const keys = store.list(namespace);
|
|
6484
|
+
logger.debug(`Memory LIST: ${namespace || store.getDefaultNamespace()} (${keys.length} keys)`);
|
|
6485
|
+
return keys;
|
|
6486
|
+
}
|
|
6487
|
+
async handleExecJs(store, config, context) {
|
|
6488
|
+
const script = config.memory_js;
|
|
6489
|
+
const pendingOps = [];
|
|
6490
|
+
const enhancedContext = {
|
|
6491
|
+
...context,
|
|
6492
|
+
memory: {
|
|
6493
|
+
get: (key, ns) => store.get(key, ns),
|
|
6494
|
+
set: (key, value, ns) => {
|
|
6495
|
+
const nsName = ns || store.getDefaultNamespace();
|
|
6496
|
+
if (!store["data"].has(nsName)) {
|
|
6497
|
+
store["data"].set(nsName, /* @__PURE__ */ new Map());
|
|
6498
|
+
}
|
|
6499
|
+
store["data"].get(nsName).set(key, value);
|
|
6500
|
+
pendingOps.push(async () => {
|
|
6501
|
+
if (store.getConfig().storage === "file" && store.getConfig().auto_save) {
|
|
6502
|
+
await store.save();
|
|
6503
|
+
}
|
|
6504
|
+
});
|
|
6505
|
+
return value;
|
|
6506
|
+
},
|
|
6507
|
+
append: (key, value, ns) => {
|
|
6508
|
+
const existing = store.get(key, ns);
|
|
6509
|
+
let newValue;
|
|
6510
|
+
if (existing === void 0) {
|
|
6511
|
+
newValue = [value];
|
|
6512
|
+
} else if (Array.isArray(existing)) {
|
|
6513
|
+
newValue = [...existing, value];
|
|
6514
|
+
} else {
|
|
6515
|
+
newValue = [existing, value];
|
|
6516
|
+
}
|
|
6517
|
+
const nsName = ns || store.getDefaultNamespace();
|
|
6518
|
+
if (!store["data"].has(nsName)) {
|
|
6519
|
+
store["data"].set(nsName, /* @__PURE__ */ new Map());
|
|
6520
|
+
}
|
|
6521
|
+
store["data"].get(nsName).set(key, newValue);
|
|
6522
|
+
pendingOps.push(async () => {
|
|
6523
|
+
if (store.getConfig().storage === "file" && store.getConfig().auto_save) {
|
|
6524
|
+
await store.save();
|
|
6525
|
+
}
|
|
6526
|
+
});
|
|
6527
|
+
return newValue;
|
|
6528
|
+
},
|
|
6529
|
+
increment: (key, amount = 1, ns) => {
|
|
6530
|
+
const existing = store.get(key, ns);
|
|
6531
|
+
let newValue;
|
|
6532
|
+
if (existing === void 0 || existing === null) {
|
|
6533
|
+
newValue = amount;
|
|
6534
|
+
} else if (typeof existing === "number") {
|
|
6535
|
+
newValue = existing + amount;
|
|
6536
|
+
} else {
|
|
6537
|
+
throw new Error(
|
|
6538
|
+
`Cannot increment non-numeric value at key '${key}' (type: ${typeof existing})`
|
|
6539
|
+
);
|
|
6540
|
+
}
|
|
6541
|
+
const nsName = ns || store.getDefaultNamespace();
|
|
6542
|
+
if (!store["data"].has(nsName)) {
|
|
6543
|
+
store["data"].set(nsName, /* @__PURE__ */ new Map());
|
|
6544
|
+
}
|
|
6545
|
+
store["data"].get(nsName).set(key, newValue);
|
|
6546
|
+
pendingOps.push(async () => {
|
|
6547
|
+
if (store.getConfig().storage === "file" && store.getConfig().auto_save) {
|
|
6548
|
+
await store.save();
|
|
6549
|
+
}
|
|
6550
|
+
});
|
|
6551
|
+
return newValue;
|
|
6552
|
+
},
|
|
6553
|
+
delete: (key, ns) => {
|
|
6554
|
+
const nsName = ns || store.getDefaultNamespace();
|
|
6555
|
+
const nsData = store["data"].get(nsName);
|
|
6556
|
+
const deleted = nsData?.delete(key) || false;
|
|
6557
|
+
if (deleted) {
|
|
6558
|
+
pendingOps.push(async () => {
|
|
6559
|
+
if (store.getConfig().storage === "file" && store.getConfig().auto_save) {
|
|
6560
|
+
await store.save();
|
|
6561
|
+
}
|
|
6562
|
+
});
|
|
6563
|
+
}
|
|
6564
|
+
return deleted;
|
|
6565
|
+
},
|
|
6566
|
+
clear: (ns) => {
|
|
6567
|
+
if (ns) {
|
|
6568
|
+
store["data"].delete(ns);
|
|
6569
|
+
} else {
|
|
6570
|
+
store["data"].clear();
|
|
6571
|
+
}
|
|
6572
|
+
pendingOps.push(async () => {
|
|
6573
|
+
if (store.getConfig().storage === "file" && store.getConfig().auto_save) {
|
|
6574
|
+
await store.save();
|
|
6575
|
+
}
|
|
6576
|
+
});
|
|
6577
|
+
},
|
|
6578
|
+
list: (ns) => store.list(ns),
|
|
6579
|
+
has: (key, ns) => store.has(key, ns),
|
|
6580
|
+
getAll: (ns) => store.getAll(ns),
|
|
6581
|
+
listNamespaces: () => store.listNamespaces()
|
|
6582
|
+
}
|
|
6583
|
+
};
|
|
6584
|
+
const result = this.evaluateJavaScriptBlock(script, enhancedContext);
|
|
6585
|
+
if (pendingOps.length > 0 && store.getConfig().storage === "file" && store.getConfig().auto_save) {
|
|
6586
|
+
await store.save();
|
|
6587
|
+
}
|
|
6588
|
+
logger.debug(`Memory EXEC_JS: Executed custom script with ${pendingOps.length} operations`);
|
|
6589
|
+
return result;
|
|
6590
|
+
}
|
|
6591
|
+
/**
|
|
6592
|
+
* Compute value from config using value, value_js, transform, or transform_js
|
|
6593
|
+
*/
|
|
6594
|
+
async computeValue(config, context) {
|
|
6595
|
+
let value;
|
|
6596
|
+
if (config.value_js && typeof config.value_js === "string") {
|
|
6597
|
+
value = this.evaluateJavaScript(config.value_js, context);
|
|
6598
|
+
} else {
|
|
6599
|
+
value = config.value;
|
|
6600
|
+
}
|
|
6601
|
+
if (config.transform && typeof config.transform === "string") {
|
|
6602
|
+
const rendered = await this.liquid.parseAndRender(config.transform, {
|
|
6603
|
+
...context,
|
|
6604
|
+
value
|
|
6605
|
+
});
|
|
6606
|
+
value = rendered;
|
|
6607
|
+
}
|
|
6608
|
+
if (config.transform_js && typeof config.transform_js === "string") {
|
|
6609
|
+
value = this.evaluateJavaScript(config.transform_js, { ...context, value });
|
|
6610
|
+
}
|
|
6611
|
+
return value;
|
|
6612
|
+
}
|
|
6613
|
+
/**
|
|
6614
|
+
* Evaluate JavaScript expression in context using SandboxJS for secure execution
|
|
6615
|
+
*/
|
|
6616
|
+
evaluateJavaScript(expression, context) {
|
|
6617
|
+
if (!this.sandbox) {
|
|
6618
|
+
this.sandbox = this.createSecureSandbox();
|
|
6619
|
+
}
|
|
6620
|
+
try {
|
|
6621
|
+
const log2 = (...args) => {
|
|
6622
|
+
logger.info(`\u{1F50D} [memory-js] ${args.map((a) => JSON.stringify(a)).join(" ")}`);
|
|
6623
|
+
};
|
|
6624
|
+
const scope = {
|
|
6625
|
+
...context,
|
|
6626
|
+
log: log2
|
|
6627
|
+
};
|
|
6628
|
+
const exec = this.sandbox.compile(`return (${expression});`);
|
|
6629
|
+
return exec(scope).run();
|
|
6630
|
+
} catch (error) {
|
|
6631
|
+
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
6632
|
+
throw new Error(`Failed to evaluate value_js: ${errorMsg}`);
|
|
6633
|
+
}
|
|
6634
|
+
}
|
|
6635
|
+
/**
|
|
6636
|
+
* Evaluate JavaScript block (multi-line script) using SandboxJS for secure execution
|
|
6637
|
+
* Unlike evaluateJavaScript, this supports full scripts with statements, not just expressions
|
|
6638
|
+
*/
|
|
6639
|
+
evaluateJavaScriptBlock(script, context) {
|
|
6640
|
+
if (!this.sandbox) {
|
|
6641
|
+
this.sandbox = this.createSecureSandbox();
|
|
6642
|
+
}
|
|
6643
|
+
try {
|
|
6644
|
+
const log2 = (...args) => {
|
|
6645
|
+
logger.info(`\u{1F50D} [memory-js] ${args.map((a) => JSON.stringify(a)).join(" ")}`);
|
|
6646
|
+
};
|
|
6647
|
+
const scope = {
|
|
6648
|
+
...context,
|
|
6649
|
+
log: log2
|
|
6650
|
+
};
|
|
6651
|
+
const exec = this.sandbox.compile(script);
|
|
6652
|
+
const result = exec(scope).run();
|
|
6653
|
+
return result;
|
|
6654
|
+
} catch (error) {
|
|
6655
|
+
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
6656
|
+
logger.error(`[memory-js] Script execution error: ${errorMsg}`);
|
|
6657
|
+
throw new Error(`Failed to execute memory_js: ${errorMsg}`);
|
|
6658
|
+
}
|
|
6659
|
+
}
|
|
6660
|
+
/**
|
|
6661
|
+
* Build template context for Liquid and JS evaluation
|
|
6662
|
+
*/
|
|
6663
|
+
buildTemplateContext(prInfo, dependencyResults, memoryStore) {
|
|
6664
|
+
const context = {};
|
|
6665
|
+
context.pr = {
|
|
6666
|
+
number: prInfo.number,
|
|
6667
|
+
title: prInfo.title,
|
|
6668
|
+
body: prInfo.body,
|
|
6669
|
+
author: prInfo.author,
|
|
6670
|
+
base: prInfo.base,
|
|
6671
|
+
head: prInfo.head,
|
|
6672
|
+
totalAdditions: prInfo.totalAdditions,
|
|
6673
|
+
totalDeletions: prInfo.totalDeletions,
|
|
6674
|
+
files: prInfo.files.map((f) => ({
|
|
6675
|
+
filename: f.filename,
|
|
6676
|
+
status: f.status,
|
|
6677
|
+
additions: f.additions,
|
|
6678
|
+
deletions: f.deletions,
|
|
6679
|
+
changes: f.changes
|
|
6680
|
+
}))
|
|
6681
|
+
};
|
|
6682
|
+
if (dependencyResults) {
|
|
6683
|
+
const outputs = {};
|
|
6684
|
+
for (const [checkName, result] of dependencyResults.entries()) {
|
|
6685
|
+
const summary = result;
|
|
6686
|
+
outputs[checkName] = summary.output !== void 0 ? summary.output : summary;
|
|
6687
|
+
}
|
|
6688
|
+
context.outputs = outputs;
|
|
6689
|
+
}
|
|
6690
|
+
if (memoryStore) {
|
|
6691
|
+
context.memory = {
|
|
6692
|
+
get: (key, ns) => memoryStore.get(key, ns),
|
|
6693
|
+
has: (key, ns) => memoryStore.has(key, ns),
|
|
6694
|
+
list: (ns) => memoryStore.list(ns),
|
|
6695
|
+
getAll: (ns) => memoryStore.getAll(ns)
|
|
6696
|
+
};
|
|
6697
|
+
}
|
|
6698
|
+
return context;
|
|
6699
|
+
}
|
|
6700
|
+
getSupportedConfigKeys() {
|
|
6701
|
+
return [
|
|
6702
|
+
"type",
|
|
6703
|
+
"operation",
|
|
6704
|
+
"key",
|
|
6705
|
+
"value",
|
|
6706
|
+
"value_js",
|
|
6707
|
+
"memory_js",
|
|
6708
|
+
"transform",
|
|
6709
|
+
"transform_js",
|
|
6710
|
+
"namespace",
|
|
6711
|
+
"depends_on",
|
|
6712
|
+
"group",
|
|
6713
|
+
"command",
|
|
6714
|
+
"on",
|
|
6715
|
+
"if",
|
|
6716
|
+
"fail_if",
|
|
6717
|
+
"on_fail",
|
|
6718
|
+
"on_success"
|
|
6719
|
+
];
|
|
6720
|
+
}
|
|
6721
|
+
async isAvailable() {
|
|
6722
|
+
return true;
|
|
6723
|
+
}
|
|
6724
|
+
getRequirements() {
|
|
6725
|
+
return [
|
|
6726
|
+
"No external dependencies required",
|
|
6727
|
+
"Used for state management and persistent storage across checks"
|
|
6728
|
+
];
|
|
6729
|
+
}
|
|
6730
|
+
};
|
|
6731
|
+
|
|
5106
6732
|
// src/providers/check-provider-registry.ts
|
|
5107
6733
|
var CheckProviderRegistry = class _CheckProviderRegistry {
|
|
5108
6734
|
providers = /* @__PURE__ */ new Map();
|
|
@@ -5130,6 +6756,8 @@ var CheckProviderRegistry = class _CheckProviderRegistry {
|
|
|
5130
6756
|
this.register(new HttpClientProvider());
|
|
5131
6757
|
this.register(new NoopCheckProvider());
|
|
5132
6758
|
this.register(new LogCheckProvider());
|
|
6759
|
+
this.register(new MemoryCheckProvider());
|
|
6760
|
+
this.register(new GitHubOpsProvider());
|
|
5133
6761
|
try {
|
|
5134
6762
|
this.register(new ClaudeCodeCheckProvider());
|
|
5135
6763
|
} catch (error) {
|
|
@@ -5427,7 +7055,7 @@ var DependencyResolver = class {
|
|
|
5427
7055
|
};
|
|
5428
7056
|
|
|
5429
7057
|
// src/failure-condition-evaluator.ts
|
|
5430
|
-
import
|
|
7058
|
+
import Sandbox4 from "@nyariv/sandboxjs";
|
|
5431
7059
|
var FailureConditionEvaluator = class _FailureConditionEvaluator {
|
|
5432
7060
|
sandbox;
|
|
5433
7061
|
constructor() {
|
|
@@ -5437,7 +7065,7 @@ var FailureConditionEvaluator = class _FailureConditionEvaluator {
|
|
|
5437
7065
|
*/
|
|
5438
7066
|
createSecureSandbox() {
|
|
5439
7067
|
const globals = {
|
|
5440
|
-
...
|
|
7068
|
+
...Sandbox4.SAFE_GLOBALS,
|
|
5441
7069
|
// Allow Math for calculations
|
|
5442
7070
|
Math,
|
|
5443
7071
|
// Allow console for debugging (in controlled environment)
|
|
@@ -5447,7 +7075,7 @@ var FailureConditionEvaluator = class _FailureConditionEvaluator {
|
|
|
5447
7075
|
error: console.error
|
|
5448
7076
|
}
|
|
5449
7077
|
};
|
|
5450
|
-
const prototypeWhitelist = new Map(
|
|
7078
|
+
const prototypeWhitelist = new Map(Sandbox4.SAFE_PROTOTYPES);
|
|
5451
7079
|
const arrayMethods = /* @__PURE__ */ new Set([
|
|
5452
7080
|
"some",
|
|
5453
7081
|
"every",
|
|
@@ -5480,7 +7108,7 @@ var FailureConditionEvaluator = class _FailureConditionEvaluator {
|
|
|
5480
7108
|
prototypeWhitelist.set(String.prototype, stringMethods);
|
|
5481
7109
|
const objectMethods = /* @__PURE__ */ new Set(["hasOwnProperty", "toString", "valueOf"]);
|
|
5482
7110
|
prototypeWhitelist.set(Object.prototype, objectMethods);
|
|
5483
|
-
return new
|
|
7111
|
+
return new Sandbox4({
|
|
5484
7112
|
globals,
|
|
5485
7113
|
prototypeWhitelist
|
|
5486
7114
|
});
|
|
@@ -5488,13 +7116,14 @@ var FailureConditionEvaluator = class _FailureConditionEvaluator {
|
|
|
5488
7116
|
/**
|
|
5489
7117
|
* Evaluate simple fail_if condition
|
|
5490
7118
|
*/
|
|
5491
|
-
async evaluateSimpleCondition(checkName, checkSchema, checkGroup, reviewSummary, expression, previousOutputs) {
|
|
7119
|
+
async evaluateSimpleCondition(checkName, checkSchema, checkGroup, reviewSummary, expression, previousOutputs, authorAssociation) {
|
|
5492
7120
|
const context = this.buildEvaluationContext(
|
|
5493
7121
|
checkName,
|
|
5494
7122
|
checkSchema,
|
|
5495
7123
|
checkGroup,
|
|
5496
7124
|
reviewSummary,
|
|
5497
|
-
previousOutputs
|
|
7125
|
+
previousOutputs,
|
|
7126
|
+
authorAssociation
|
|
5498
7127
|
);
|
|
5499
7128
|
try {
|
|
5500
7129
|
try {
|
|
@@ -5509,6 +7138,38 @@ var FailureConditionEvaluator = class _FailureConditionEvaluator {
|
|
|
5509
7138
|
} catch {
|
|
5510
7139
|
}
|
|
5511
7140
|
const res = this.evaluateExpression(expression, context);
|
|
7141
|
+
if (res === true) {
|
|
7142
|
+
try {
|
|
7143
|
+
addEvent("fail_if.triggered", {
|
|
7144
|
+
check: checkName,
|
|
7145
|
+
scope: "check",
|
|
7146
|
+
name: `${checkName}_fail_if`,
|
|
7147
|
+
expression,
|
|
7148
|
+
severity: "error"
|
|
7149
|
+
});
|
|
7150
|
+
} catch {
|
|
7151
|
+
}
|
|
7152
|
+
try {
|
|
7153
|
+
const { emitNdjsonSpanWithEvents: emitNdjsonSpanWithEvents2 } = (init_fallback_ndjson(), __toCommonJS(fallback_ndjson_exports));
|
|
7154
|
+
emitNdjsonSpanWithEvents2(
|
|
7155
|
+
"visor.fail_if",
|
|
7156
|
+
{ check: checkName, scope: "check", name: `${checkName}_fail_if` },
|
|
7157
|
+
[
|
|
7158
|
+
{
|
|
7159
|
+
name: "fail_if.triggered",
|
|
7160
|
+
attrs: {
|
|
7161
|
+
check: checkName,
|
|
7162
|
+
scope: "check",
|
|
7163
|
+
name: `${checkName}_fail_if`,
|
|
7164
|
+
expression,
|
|
7165
|
+
severity: "error"
|
|
7166
|
+
}
|
|
7167
|
+
}
|
|
7168
|
+
]
|
|
7169
|
+
);
|
|
7170
|
+
} catch {
|
|
7171
|
+
}
|
|
7172
|
+
}
|
|
5512
7173
|
return res;
|
|
5513
7174
|
} catch (error) {
|
|
5514
7175
|
console.warn(`Failed to evaluate fail_if expression: ${error}`);
|
|
@@ -5566,6 +7227,8 @@ var FailureConditionEvaluator = class _FailureConditionEvaluator {
|
|
|
5566
7227
|
output: {
|
|
5567
7228
|
issues: []
|
|
5568
7229
|
},
|
|
7230
|
+
// Author association (used by permission helpers)
|
|
7231
|
+
authorAssociation: contextData?.authorAssociation,
|
|
5569
7232
|
// Utility metadata
|
|
5570
7233
|
metadata: {
|
|
5571
7234
|
checkName,
|
|
@@ -5591,13 +7254,14 @@ var FailureConditionEvaluator = class _FailureConditionEvaluator {
|
|
|
5591
7254
|
/**
|
|
5592
7255
|
* Evaluate all failure conditions for a check result
|
|
5593
7256
|
*/
|
|
5594
|
-
async evaluateConditions(checkName, checkSchema, checkGroup, reviewSummary, globalConditions, checkConditions, previousOutputs) {
|
|
7257
|
+
async evaluateConditions(checkName, checkSchema, checkGroup, reviewSummary, globalConditions, checkConditions, previousOutputs, authorAssociation) {
|
|
5595
7258
|
const context = this.buildEvaluationContext(
|
|
5596
7259
|
checkName,
|
|
5597
7260
|
checkSchema,
|
|
5598
7261
|
checkGroup,
|
|
5599
7262
|
reviewSummary,
|
|
5600
|
-
previousOutputs
|
|
7263
|
+
previousOutputs,
|
|
7264
|
+
authorAssociation
|
|
5601
7265
|
);
|
|
5602
7266
|
const results = [];
|
|
5603
7267
|
if (globalConditions) {
|
|
@@ -5631,9 +7295,54 @@ var FailureConditionEvaluator = class _FailureConditionEvaluator {
|
|
|
5631
7295
|
async evaluateConditionSet(conditions, context, source) {
|
|
5632
7296
|
const results = [];
|
|
5633
7297
|
for (const [conditionName, condition] of Object.entries(conditions)) {
|
|
7298
|
+
try {
|
|
7299
|
+
addEvent("fail_if.evaluated", {
|
|
7300
|
+
check: context.checkName,
|
|
7301
|
+
scope: source,
|
|
7302
|
+
name: conditionName,
|
|
7303
|
+
expression: this.extractExpression(condition)
|
|
7304
|
+
});
|
|
7305
|
+
} catch {
|
|
7306
|
+
}
|
|
7307
|
+
try {
|
|
7308
|
+
const { emitNdjsonSpanWithEvents: emitNdjsonSpanWithEvents2 } = (init_fallback_ndjson(), __toCommonJS(fallback_ndjson_exports));
|
|
7309
|
+
emitNdjsonSpanWithEvents2(
|
|
7310
|
+
"visor.fail_if",
|
|
7311
|
+
{ check: context.checkName || "unknown", scope: source, name: conditionName },
|
|
7312
|
+
[
|
|
7313
|
+
{
|
|
7314
|
+
name: "fail_if.evaluated",
|
|
7315
|
+
attrs: {
|
|
7316
|
+
check: context.checkName,
|
|
7317
|
+
scope: source,
|
|
7318
|
+
name: conditionName,
|
|
7319
|
+
expression: this.extractExpression(condition)
|
|
7320
|
+
}
|
|
7321
|
+
}
|
|
7322
|
+
]
|
|
7323
|
+
);
|
|
7324
|
+
} catch {
|
|
7325
|
+
}
|
|
5634
7326
|
try {
|
|
5635
7327
|
const result = await this.evaluateSingleCondition(conditionName, condition, context);
|
|
5636
7328
|
results.push(result);
|
|
7329
|
+
if (result.failed) {
|
|
7330
|
+
try {
|
|
7331
|
+
addEvent("fail_if.triggered", {
|
|
7332
|
+
check: context.checkName,
|
|
7333
|
+
scope: source,
|
|
7334
|
+
name: conditionName,
|
|
7335
|
+
expression: result.expression,
|
|
7336
|
+
severity: result.severity,
|
|
7337
|
+
halt_execution: result.haltExecution
|
|
7338
|
+
});
|
|
7339
|
+
} catch {
|
|
7340
|
+
}
|
|
7341
|
+
try {
|
|
7342
|
+
addFailIfTriggered(context.checkName || "unknown", source);
|
|
7343
|
+
} catch {
|
|
7344
|
+
}
|
|
7345
|
+
}
|
|
5637
7346
|
} catch (error) {
|
|
5638
7347
|
results.push({
|
|
5639
7348
|
conditionName,
|
|
@@ -5721,6 +7430,16 @@ var FailureConditionEvaluator = class _FailureConditionEvaluator {
|
|
|
5721
7430
|
};
|
|
5722
7431
|
const hasIssueWith = hasIssue;
|
|
5723
7432
|
const hasFileWith = hasFileMatching;
|
|
7433
|
+
const permissionHelpers = createPermissionHelpers(
|
|
7434
|
+
context.authorAssociation,
|
|
7435
|
+
detectLocalMode()
|
|
7436
|
+
);
|
|
7437
|
+
const hasMinPermission = permissionHelpers.hasMinPermission;
|
|
7438
|
+
const isOwner = permissionHelpers.isOwner;
|
|
7439
|
+
const isMember = permissionHelpers.isMember;
|
|
7440
|
+
const isCollaborator = permissionHelpers.isCollaborator;
|
|
7441
|
+
const isContributor = permissionHelpers.isContributor;
|
|
7442
|
+
const isFirstTimer = permissionHelpers.isFirstTimer;
|
|
5724
7443
|
const output = context.output || {};
|
|
5725
7444
|
const issues = output.issues || [];
|
|
5726
7445
|
const suggestions = [];
|
|
@@ -5751,11 +7470,20 @@ var FailureConditionEvaluator = class _FailureConditionEvaluator {
|
|
|
5751
7470
|
const env = context.env || {};
|
|
5752
7471
|
const outputs = context.outputs || {};
|
|
5753
7472
|
const debugData = context.debug || null;
|
|
7473
|
+
const memoryStore = MemoryStore.getInstance();
|
|
7474
|
+
const memoryAccessor = {
|
|
7475
|
+
get: (key, ns) => memoryStore.get(key, ns),
|
|
7476
|
+
has: (key, ns) => memoryStore.has(key, ns),
|
|
7477
|
+
list: (ns) => memoryStore.list(ns),
|
|
7478
|
+
getAll: (ns) => memoryStore.getAll(ns)
|
|
7479
|
+
};
|
|
5754
7480
|
const scope = {
|
|
5755
7481
|
// Primary context variables
|
|
5756
7482
|
output,
|
|
5757
7483
|
outputs,
|
|
5758
7484
|
debug: debugData,
|
|
7485
|
+
// Memory accessor for fail_if expressions
|
|
7486
|
+
memory: memoryAccessor,
|
|
5759
7487
|
// Legacy compatibility variables
|
|
5760
7488
|
issues,
|
|
5761
7489
|
suggestions,
|
|
@@ -5789,7 +7517,14 @@ var FailureConditionEvaluator = class _FailureConditionEvaluator {
|
|
|
5789
7517
|
hasFileMatching,
|
|
5790
7518
|
hasSuggestion,
|
|
5791
7519
|
hasIssueWith,
|
|
5792
|
-
hasFileWith
|
|
7520
|
+
hasFileWith,
|
|
7521
|
+
// Permission helpers
|
|
7522
|
+
hasMinPermission,
|
|
7523
|
+
isOwner,
|
|
7524
|
+
isMember,
|
|
7525
|
+
isCollaborator,
|
|
7526
|
+
isContributor,
|
|
7527
|
+
isFirstTimer
|
|
5793
7528
|
};
|
|
5794
7529
|
const raw = condition.trim();
|
|
5795
7530
|
if (!this.sandbox) {
|
|
@@ -5838,7 +7573,7 @@ var FailureConditionEvaluator = class _FailureConditionEvaluator {
|
|
|
5838
7573
|
/**
|
|
5839
7574
|
* Build the evaluation context for expressions
|
|
5840
7575
|
*/
|
|
5841
|
-
buildEvaluationContext(checkName, checkSchema, checkGroup, reviewSummary, previousOutputs) {
|
|
7576
|
+
buildEvaluationContext(checkName, checkSchema, checkGroup, reviewSummary, previousOutputs, authorAssociation) {
|
|
5842
7577
|
const { issues, debug } = reviewSummary;
|
|
5843
7578
|
const reviewSummaryWithOutput = reviewSummary;
|
|
5844
7579
|
const {
|
|
@@ -5936,6 +7671,7 @@ var FailureConditionEvaluator = class _FailureConditionEvaluator {
|
|
|
5936
7671
|
}
|
|
5937
7672
|
} catch {
|
|
5938
7673
|
}
|
|
7674
|
+
const memoryStore = MemoryStore.getInstance();
|
|
5939
7675
|
const context = {
|
|
5940
7676
|
output: aggregatedOutput,
|
|
5941
7677
|
outputs: (() => {
|
|
@@ -5947,10 +7683,18 @@ var FailureConditionEvaluator = class _FailureConditionEvaluator {
|
|
|
5947
7683
|
}
|
|
5948
7684
|
return outputs;
|
|
5949
7685
|
})(),
|
|
7686
|
+
// Add memory accessor for fail_if expressions
|
|
7687
|
+
memory: {
|
|
7688
|
+
get: (key, ns) => memoryStore.get(key, ns),
|
|
7689
|
+
has: (key, ns) => memoryStore.has(key, ns),
|
|
7690
|
+
list: (ns) => memoryStore.list(ns),
|
|
7691
|
+
getAll: (ns) => memoryStore.getAll(ns)
|
|
7692
|
+
},
|
|
5950
7693
|
// Add basic context info for failure conditions
|
|
5951
7694
|
checkName,
|
|
5952
7695
|
schema: checkSchema,
|
|
5953
|
-
group: checkGroup
|
|
7696
|
+
group: checkGroup,
|
|
7697
|
+
authorAssociation
|
|
5954
7698
|
};
|
|
5955
7699
|
if (debug) {
|
|
5956
7700
|
context.debug = {
|
|
@@ -6278,7 +8022,7 @@ Please check your configuration and try again.`
|
|
|
6278
8022
|
sections.push("---");
|
|
6279
8023
|
sections.push("");
|
|
6280
8024
|
sections.push(
|
|
6281
|
-
"*
|
|
8025
|
+
"*Powered by [Visor](https://probelabs.com/visor) from [Probelabs](https://probelabs.com)*"
|
|
6282
8026
|
);
|
|
6283
8027
|
return sections.join("\n");
|
|
6284
8028
|
}
|
|
@@ -6480,7 +8224,8 @@ Please check your configuration and try again.`
|
|
|
6480
8224
|
|
|
6481
8225
|
// src/check-execution-engine.ts
|
|
6482
8226
|
init_logger();
|
|
6483
|
-
import
|
|
8227
|
+
import Sandbox5 from "@nyariv/sandboxjs";
|
|
8228
|
+
init_fallback_ndjson();
|
|
6484
8229
|
function getSafeEnvironmentVariables() {
|
|
6485
8230
|
const safeEnvVars = [
|
|
6486
8231
|
"CI",
|
|
@@ -6518,27 +8263,50 @@ var CheckExecutionEngine = class _CheckExecutionEngine {
|
|
|
6518
8263
|
webhookContext;
|
|
6519
8264
|
routingSandbox;
|
|
6520
8265
|
executionStats = /* @__PURE__ */ new Map();
|
|
6521
|
-
|
|
8266
|
+
// Event override to simulate alternate event (used during routing goto)
|
|
8267
|
+
routingEventOverride;
|
|
8268
|
+
// Cached GitHub context for context elevation when running in Actions
|
|
8269
|
+
actionContext;
|
|
8270
|
+
constructor(workingDirectory, octokit) {
|
|
6522
8271
|
this.workingDirectory = workingDirectory || process.cwd();
|
|
6523
8272
|
this.gitAnalyzer = new GitRepositoryAnalyzer(this.workingDirectory);
|
|
6524
8273
|
this.providerRegistry = CheckProviderRegistry.getInstance();
|
|
6525
8274
|
this.failureEvaluator = new FailureConditionEvaluator();
|
|
8275
|
+
if (octokit) {
|
|
8276
|
+
const repoEnv = process.env.GITHUB_REPOSITORY || "";
|
|
8277
|
+
const [owner, repo] = repoEnv.split("/");
|
|
8278
|
+
if (owner && repo) {
|
|
8279
|
+
this.actionContext = { owner, repo, octokit };
|
|
8280
|
+
}
|
|
8281
|
+
}
|
|
6526
8282
|
this.mockOctokit = this.createMockOctokit();
|
|
6527
8283
|
this.reviewer = new PRReviewer(this.mockOctokit);
|
|
6528
8284
|
}
|
|
8285
|
+
/**
|
|
8286
|
+
* Enrich event context with authenticated octokit instance
|
|
8287
|
+
* @param eventContext - The event context to enrich
|
|
8288
|
+
* @returns Enriched event context with octokit if available
|
|
8289
|
+
*/
|
|
8290
|
+
enrichEventContext(eventContext) {
|
|
8291
|
+
const baseContext = eventContext || {};
|
|
8292
|
+
if (this.actionContext?.octokit) {
|
|
8293
|
+
return { ...baseContext, octokit: this.actionContext.octokit };
|
|
8294
|
+
}
|
|
8295
|
+
return baseContext;
|
|
8296
|
+
}
|
|
6529
8297
|
/**
|
|
6530
8298
|
* Lazily create a secure sandbox for routing JS (goto_js, run_js)
|
|
6531
8299
|
*/
|
|
6532
8300
|
getRoutingSandbox() {
|
|
6533
8301
|
if (this.routingSandbox) return this.routingSandbox;
|
|
6534
8302
|
const globals = {
|
|
6535
|
-
...
|
|
8303
|
+
...Sandbox5.SAFE_GLOBALS,
|
|
6536
8304
|
Math,
|
|
6537
8305
|
JSON,
|
|
6538
8306
|
console: { log: console.log }
|
|
6539
8307
|
};
|
|
6540
|
-
const prototypeWhitelist = new Map(
|
|
6541
|
-
this.routingSandbox = new
|
|
8308
|
+
const prototypeWhitelist = new Map(Sandbox5.SAFE_PROTOTYPES);
|
|
8309
|
+
this.routingSandbox = new Sandbox5({ globals, prototypeWhitelist });
|
|
6542
8310
|
return this.routingSandbox;
|
|
6543
8311
|
}
|
|
6544
8312
|
redact(str, limit = 200) {
|
|
@@ -6578,10 +8346,12 @@ var CheckExecutionEngine = class _CheckExecutionEngine {
|
|
|
6578
8346
|
let loopCount = 0;
|
|
6579
8347
|
const seed = `${checkName}-${prInfo.number || "local"}`;
|
|
6580
8348
|
const allAncestors = DependencyResolver.getAllDependencies(checkName, dependencyGraph.nodes);
|
|
8349
|
+
let currentRouteOutput = void 0;
|
|
6581
8350
|
const evalRunJs = async (expr, error) => {
|
|
6582
8351
|
if (!expr) return [];
|
|
6583
8352
|
try {
|
|
6584
8353
|
const sandbox = this.getRoutingSandbox();
|
|
8354
|
+
const eventObj = { name: prInfo.eventType || "manual" };
|
|
6585
8355
|
const scope = {
|
|
6586
8356
|
step: { id: checkName, tags: checkConfig.tags || [], group: checkConfig.group },
|
|
6587
8357
|
attempt,
|
|
@@ -6593,6 +8363,7 @@ var CheckExecutionEngine = class _CheckExecutionEngine {
|
|
|
6593
8363
|
parent: foreachContext.parent
|
|
6594
8364
|
} : null,
|
|
6595
8365
|
outputs: Object.fromEntries((dependencyResults || /* @__PURE__ */ new Map()).entries()),
|
|
8366
|
+
output: currentRouteOutput,
|
|
6596
8367
|
pr: {
|
|
6597
8368
|
number: prInfo.number,
|
|
6598
8369
|
title: prInfo.title,
|
|
@@ -6601,10 +8372,15 @@ var CheckExecutionEngine = class _CheckExecutionEngine {
|
|
|
6601
8372
|
base: prInfo.base
|
|
6602
8373
|
},
|
|
6603
8374
|
files: prInfo.files,
|
|
6604
|
-
env: getSafeEnvironmentVariables()
|
|
8375
|
+
env: getSafeEnvironmentVariables(),
|
|
8376
|
+
permissions: createPermissionHelpers(
|
|
8377
|
+
resolveAssociationFromEvent(prInfo.eventContext, prInfo.authorAssociation),
|
|
8378
|
+
detectLocalMode()
|
|
8379
|
+
),
|
|
8380
|
+
event: eventObj
|
|
6605
8381
|
};
|
|
6606
8382
|
const code = `
|
|
6607
|
-
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 pr = scope.pr; const files = scope.files; const env = scope.env; const log = (...a)=>console.log('\u{1F50D} Debug:',...a);
|
|
8383
|
+
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 log = (...a)=>console.log('\u{1F50D} Debug:',...a); 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;
|
|
6608
8384
|
const __fn = () => {
|
|
6609
8385
|
${expr}
|
|
6610
8386
|
};
|
|
@@ -6628,6 +8404,7 @@ ${expr}
|
|
|
6628
8404
|
if (!expr) return null;
|
|
6629
8405
|
try {
|
|
6630
8406
|
const sandbox = this.getRoutingSandbox();
|
|
8407
|
+
const eventObj = { name: prInfo.eventType || "manual" };
|
|
6631
8408
|
const scope = {
|
|
6632
8409
|
step: { id: checkName, tags: checkConfig.tags || [], group: checkConfig.group },
|
|
6633
8410
|
attempt,
|
|
@@ -6639,6 +8416,7 @@ ${expr}
|
|
|
6639
8416
|
parent: foreachContext.parent
|
|
6640
8417
|
} : null,
|
|
6641
8418
|
outputs: Object.fromEntries((dependencyResults || /* @__PURE__ */ new Map()).entries()),
|
|
8419
|
+
output: currentRouteOutput,
|
|
6642
8420
|
pr: {
|
|
6643
8421
|
number: prInfo.number,
|
|
6644
8422
|
title: prInfo.title,
|
|
@@ -6647,10 +8425,15 @@ ${expr}
|
|
|
6647
8425
|
base: prInfo.base
|
|
6648
8426
|
},
|
|
6649
8427
|
files: prInfo.files,
|
|
6650
|
-
env: getSafeEnvironmentVariables()
|
|
8428
|
+
env: getSafeEnvironmentVariables(),
|
|
8429
|
+
permissions: createPermissionHelpers(
|
|
8430
|
+
resolveAssociationFromEvent(prInfo.eventContext, prInfo.authorAssociation),
|
|
8431
|
+
detectLocalMode()
|
|
8432
|
+
),
|
|
8433
|
+
event: eventObj
|
|
6651
8434
|
};
|
|
6652
8435
|
const code = `
|
|
6653
|
-
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 pr = scope.pr; const files = scope.files; const env = scope.env; const log = (...a)=>console.log('\u{1F50D} Debug:',...a);
|
|
8436
|
+
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 log = (...a)=>console.log('\u{1F50D} Debug:',...a); 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;
|
|
6654
8437
|
const __fn = () => {
|
|
6655
8438
|
${expr}
|
|
6656
8439
|
};
|
|
@@ -6686,7 +8469,7 @@ ${expr}
|
|
|
6686
8469
|
dfs(name);
|
|
6687
8470
|
return Array.from(new Set(acc));
|
|
6688
8471
|
};
|
|
6689
|
-
const executeNamedCheckInline = async (target) => {
|
|
8472
|
+
const executeNamedCheckInline = async (target, opts) => {
|
|
6690
8473
|
const targetCfg = config?.checks?.[target];
|
|
6691
8474
|
if (!targetCfg) {
|
|
6692
8475
|
throw new Error(`on_* referenced unknown check '${target}'`);
|
|
@@ -6718,15 +8501,17 @@ ${expr}
|
|
|
6718
8501
|
schema: targetCfg.schema,
|
|
6719
8502
|
group: targetCfg.group,
|
|
6720
8503
|
checkName: target,
|
|
6721
|
-
eventContext: prInfo.eventContext,
|
|
8504
|
+
eventContext: this.enrichEventContext(prInfo.eventContext),
|
|
6722
8505
|
transform: targetCfg.transform,
|
|
6723
8506
|
transform_js: targetCfg.transform_js,
|
|
6724
8507
|
env: targetCfg.env,
|
|
6725
8508
|
forEach: targetCfg.forEach,
|
|
8509
|
+
// Include provider-specific keys (e.g., op/values for github)
|
|
8510
|
+
...targetCfg,
|
|
6726
8511
|
ai: {
|
|
8512
|
+
...targetCfg.ai || {},
|
|
6727
8513
|
timeout: providerConfig.ai?.timeout || 6e5,
|
|
6728
|
-
debug: !!debug
|
|
6729
|
-
...targetCfg.ai || {}
|
|
8514
|
+
debug: !!debug
|
|
6730
8515
|
}
|
|
6731
8516
|
};
|
|
6732
8517
|
const targetDeps = getAllDepsFromConfig(target);
|
|
@@ -6750,7 +8535,34 @@ ${expr}
|
|
|
6750
8535
|
const execStr = provCfg.exec;
|
|
6751
8536
|
if (execStr) log2(`\u{1F527} Debug: inline exec '${target}' command: ${execStr}`);
|
|
6752
8537
|
}
|
|
6753
|
-
|
|
8538
|
+
let prInfoForInline = prInfo;
|
|
8539
|
+
const prevEventOverride = this.routingEventOverride;
|
|
8540
|
+
if (opts?.eventOverride) {
|
|
8541
|
+
const elevated = await this.elevateContextToPullRequest(
|
|
8542
|
+
{ ...prInfo, eventType: opts.eventOverride },
|
|
8543
|
+
opts.eventOverride,
|
|
8544
|
+
log2,
|
|
8545
|
+
debug
|
|
8546
|
+
);
|
|
8547
|
+
if (elevated) {
|
|
8548
|
+
prInfoForInline = elevated;
|
|
8549
|
+
} else {
|
|
8550
|
+
prInfoForInline = { ...prInfo, eventType: opts.eventOverride };
|
|
8551
|
+
}
|
|
8552
|
+
this.routingEventOverride = opts.eventOverride;
|
|
8553
|
+
const msg = `\u21AA goto_event: inline '${target}' with event=${opts.eventOverride}${elevated ? " (elevated to PR context)" : ""}`;
|
|
8554
|
+
if (debug) log2(`\u{1F527} Debug: ${msg}`);
|
|
8555
|
+
try {
|
|
8556
|
+
(init_logger(), __toCommonJS(logger_exports)).logger.info(msg);
|
|
8557
|
+
} catch {
|
|
8558
|
+
}
|
|
8559
|
+
}
|
|
8560
|
+
let r;
|
|
8561
|
+
try {
|
|
8562
|
+
r = await prov.execute(prInfoForInline, provCfg, depResults, sessionInfo);
|
|
8563
|
+
} finally {
|
|
8564
|
+
this.routingEventOverride = prevEventOverride;
|
|
8565
|
+
}
|
|
6754
8566
|
const enrichedIssues = (r.issues || []).map((issue) => ({
|
|
6755
8567
|
...issue,
|
|
6756
8568
|
checkName: target,
|
|
@@ -6767,7 +8579,18 @@ ${expr}
|
|
|
6767
8579
|
};
|
|
6768
8580
|
while (true) {
|
|
6769
8581
|
try {
|
|
8582
|
+
try {
|
|
8583
|
+
emitNdjsonFallback("visor.provider", {
|
|
8584
|
+
"visor.check.id": checkName,
|
|
8585
|
+
"visor.provider.type": providerConfig.type || "ai"
|
|
8586
|
+
});
|
|
8587
|
+
} catch {
|
|
8588
|
+
}
|
|
6770
8589
|
const res = await provider.execute(prInfo, providerConfig, dependencyResults, sessionInfo);
|
|
8590
|
+
try {
|
|
8591
|
+
currentRouteOutput = res?.output;
|
|
8592
|
+
} catch {
|
|
8593
|
+
}
|
|
6771
8594
|
const hasSoftFailure = (res.issues || []).some(
|
|
6772
8595
|
(i) => i.severity === "error" || i.severity === "critical"
|
|
6773
8596
|
);
|
|
@@ -6786,6 +8609,12 @@ ${expr}
|
|
|
6786
8609
|
runList = Array.from(new Set(runList));
|
|
6787
8610
|
if (debug) log2(`\u{1F527} Debug: on_fail.run (soft) list = [${runList.join(", ")}]`);
|
|
6788
8611
|
if (runList.length > 0) {
|
|
8612
|
+
try {
|
|
8613
|
+
(init_logger(), __toCommonJS(logger_exports)).logger.info(
|
|
8614
|
+
`\u25B6 on_fail.run: scheduling [${runList.join(", ")}] after '${checkName}'`
|
|
8615
|
+
);
|
|
8616
|
+
} catch {
|
|
8617
|
+
}
|
|
6789
8618
|
loopCount++;
|
|
6790
8619
|
if (loopCount > maxLoops) {
|
|
6791
8620
|
throw new Error(
|
|
@@ -6801,6 +8630,12 @@ ${expr}
|
|
|
6801
8630
|
if (!target && onFail.goto) target = onFail.goto;
|
|
6802
8631
|
if (debug) log2(`\u{1F527} Debug: on_fail.goto (soft) target = ${target}`);
|
|
6803
8632
|
if (target) {
|
|
8633
|
+
try {
|
|
8634
|
+
(init_logger(), __toCommonJS(logger_exports)).logger.info(
|
|
8635
|
+
`\u21AA on_fail.goto: jumping to '${target}' from '${checkName}'`
|
|
8636
|
+
);
|
|
8637
|
+
} catch {
|
|
8638
|
+
}
|
|
6804
8639
|
if (!allAncestors.includes(target)) {
|
|
6805
8640
|
if (debug)
|
|
6806
8641
|
log2(
|
|
@@ -6813,7 +8648,7 @@ ${expr}
|
|
|
6813
8648
|
`Routing loop budget exceeded (max_loops=${maxLoops}) during on_fail goto`
|
|
6814
8649
|
);
|
|
6815
8650
|
}
|
|
6816
|
-
await executeNamedCheckInline(target);
|
|
8651
|
+
await executeNamedCheckInline(target, { eventOverride: onFail.goto_event });
|
|
6817
8652
|
}
|
|
6818
8653
|
}
|
|
6819
8654
|
const retryMax = onFail.retry?.max ?? 0;
|
|
@@ -6836,10 +8671,17 @@ ${expr}
|
|
|
6836
8671
|
return res;
|
|
6837
8672
|
}
|
|
6838
8673
|
let needRerun = false;
|
|
8674
|
+
let rerunEventOverride;
|
|
6839
8675
|
if (onSuccess) {
|
|
6840
8676
|
const dynamicRun = await evalRunJs(onSuccess.run_js);
|
|
6841
8677
|
const runList = [...onSuccess.run || [], ...dynamicRun].filter(Boolean);
|
|
6842
8678
|
if (runList.length > 0) {
|
|
8679
|
+
try {
|
|
8680
|
+
(init_logger(), __toCommonJS(logger_exports)).logger.info(
|
|
8681
|
+
`\u25B6 on_success.run: scheduling [${Array.from(new Set(runList)).join(", ")}] after '${checkName}'`
|
|
8682
|
+
);
|
|
8683
|
+
} catch {
|
|
8684
|
+
}
|
|
6843
8685
|
loopCount++;
|
|
6844
8686
|
if (loopCount > maxLoops) {
|
|
6845
8687
|
throw new Error(
|
|
@@ -6849,15 +8691,106 @@ ${expr}
|
|
|
6849
8691
|
for (const stepId of Array.from(new Set(runList))) {
|
|
6850
8692
|
await executeNamedCheckInline(stepId);
|
|
6851
8693
|
}
|
|
8694
|
+
} else {
|
|
8695
|
+
try {
|
|
8696
|
+
const assoc = resolveAssociationFromEvent(
|
|
8697
|
+
prInfo?.eventContext,
|
|
8698
|
+
prInfo.authorAssociation
|
|
8699
|
+
);
|
|
8700
|
+
const perms = createPermissionHelpers(assoc, detectLocalMode());
|
|
8701
|
+
const allowedMember = perms.hasMinPermission("MEMBER");
|
|
8702
|
+
let intent;
|
|
8703
|
+
try {
|
|
8704
|
+
intent = res?.output?.intent;
|
|
8705
|
+
} catch {
|
|
8706
|
+
}
|
|
8707
|
+
(init_logger(), __toCommonJS(logger_exports)).logger.info(
|
|
8708
|
+
`\u23ED on_success.run: none after '${checkName}' (event=${prInfo.eventType || "manual"}, intent=${intent || "n/a"}, assoc=${assoc || "unknown"}, memberOrHigher=${allowedMember})`
|
|
8709
|
+
);
|
|
8710
|
+
} catch {
|
|
8711
|
+
}
|
|
6852
8712
|
}
|
|
6853
8713
|
let target = await evalGotoJs(onSuccess.goto_js);
|
|
6854
8714
|
if (!target && onSuccess.goto) target = onSuccess.goto;
|
|
6855
8715
|
if (target) {
|
|
8716
|
+
try {
|
|
8717
|
+
(init_logger(), __toCommonJS(logger_exports)).logger.info(
|
|
8718
|
+
`\u21AA on_success.goto: jumping to '${target}' from '${checkName}'`
|
|
8719
|
+
);
|
|
8720
|
+
} catch {
|
|
8721
|
+
}
|
|
6856
8722
|
if (!allAncestors.includes(target)) {
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
8723
|
+
const prevEventOverride2 = this.routingEventOverride;
|
|
8724
|
+
if (onSuccess.goto_event) {
|
|
8725
|
+
this.routingEventOverride = onSuccess.goto_event;
|
|
8726
|
+
}
|
|
8727
|
+
try {
|
|
8728
|
+
const cfgChecks = config?.checks || {};
|
|
8729
|
+
const forwardSet = /* @__PURE__ */ new Set();
|
|
8730
|
+
if (cfgChecks[target]) forwardSet.add(target);
|
|
8731
|
+
const dependsOn = (name, root) => {
|
|
8732
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8733
|
+
const dfs = (n) => {
|
|
8734
|
+
if (seen.has(n)) return false;
|
|
8735
|
+
seen.add(n);
|
|
8736
|
+
const deps = cfgChecks[n]?.depends_on || [];
|
|
8737
|
+
if (deps.includes(root)) return true;
|
|
8738
|
+
return deps.some((d) => dfs(d));
|
|
8739
|
+
};
|
|
8740
|
+
return dfs(name);
|
|
8741
|
+
};
|
|
8742
|
+
const ev = onSuccess.goto_event || prInfo.eventType || "issue_comment";
|
|
8743
|
+
for (const name of Object.keys(cfgChecks)) {
|
|
8744
|
+
if (name === target) continue;
|
|
8745
|
+
const onArr = cfgChecks[name]?.on;
|
|
8746
|
+
const eventMatches = !onArr || Array.isArray(onArr) && onArr.includes(ev);
|
|
8747
|
+
if (!eventMatches) continue;
|
|
8748
|
+
if (dependsOn(name, target)) forwardSet.add(name);
|
|
8749
|
+
}
|
|
8750
|
+
const order = [];
|
|
8751
|
+
const inSet = (n) => forwardSet.has(n);
|
|
8752
|
+
const tempMarks = /* @__PURE__ */ new Set();
|
|
8753
|
+
const permMarks = /* @__PURE__ */ new Set();
|
|
8754
|
+
const stack = [];
|
|
8755
|
+
const visit = (n) => {
|
|
8756
|
+
if (permMarks.has(n)) return;
|
|
8757
|
+
if (tempMarks.has(n)) {
|
|
8758
|
+
const idx = stack.indexOf(n);
|
|
8759
|
+
const cyclePath = idx >= 0 ? [...stack.slice(idx), n] : [n];
|
|
8760
|
+
throw new Error(
|
|
8761
|
+
`Cycle detected in forward-run dependency subset: ${cyclePath.join(" -> ")}`
|
|
8762
|
+
);
|
|
8763
|
+
}
|
|
8764
|
+
tempMarks.add(n);
|
|
8765
|
+
stack.push(n);
|
|
8766
|
+
const deps = (cfgChecks[n]?.depends_on || []).filter(inSet);
|
|
8767
|
+
for (const d of deps) visit(d);
|
|
8768
|
+
stack.pop();
|
|
8769
|
+
tempMarks.delete(n);
|
|
8770
|
+
permMarks.add(n);
|
|
8771
|
+
order.push(n);
|
|
8772
|
+
};
|
|
8773
|
+
for (const n of forwardSet) visit(n);
|
|
8774
|
+
for (const stepId of order) {
|
|
8775
|
+
if (!this.executionStats.has(stepId)) this.initializeCheckStats(stepId);
|
|
8776
|
+
const childStart = this.recordIterationStart(stepId);
|
|
8777
|
+
const childRes = await executeNamedCheckInline(stepId, {
|
|
8778
|
+
eventOverride: onSuccess.goto_event
|
|
8779
|
+
});
|
|
8780
|
+
const childIssues = (childRes.issues || []).map((i) => ({ ...i }));
|
|
8781
|
+
const childSuccess = !this.hasFatal(childIssues);
|
|
8782
|
+
const childOutput = childRes?.output;
|
|
8783
|
+
this.recordIterationComplete(
|
|
8784
|
+
stepId,
|
|
8785
|
+
childStart,
|
|
8786
|
+
childSuccess,
|
|
8787
|
+
childIssues,
|
|
8788
|
+
childOutput
|
|
8789
|
+
);
|
|
8790
|
+
}
|
|
8791
|
+
} finally {
|
|
8792
|
+
this.routingEventOverride = prevEventOverride2;
|
|
8793
|
+
}
|
|
6861
8794
|
} else {
|
|
6862
8795
|
loopCount++;
|
|
6863
8796
|
if (loopCount > maxLoops) {
|
|
@@ -6865,14 +8798,22 @@ ${expr}
|
|
|
6865
8798
|
`Routing loop budget exceeded (max_loops=${maxLoops}) during on_success goto`
|
|
6866
8799
|
);
|
|
6867
8800
|
}
|
|
6868
|
-
await executeNamedCheckInline(target);
|
|
8801
|
+
await executeNamedCheckInline(target, { eventOverride: onSuccess.goto_event });
|
|
6869
8802
|
needRerun = true;
|
|
8803
|
+
rerunEventOverride = onSuccess.goto_event;
|
|
6870
8804
|
}
|
|
6871
8805
|
}
|
|
6872
8806
|
}
|
|
6873
8807
|
if (needRerun) {
|
|
6874
8808
|
if (debug) log2(`\u{1F504} Debug: Re-running '${checkName}' after on_success.goto`);
|
|
8809
|
+
try {
|
|
8810
|
+
(init_logger(), __toCommonJS(logger_exports)).logger.info(`\u{1F501} Re-running '${checkName}' after goto`);
|
|
8811
|
+
} catch {
|
|
8812
|
+
}
|
|
8813
|
+
const prev = this.routingEventOverride;
|
|
8814
|
+
if (rerunEventOverride) this.routingEventOverride = rerunEventOverride;
|
|
6875
8815
|
attempt++;
|
|
8816
|
+
this.routingEventOverride = prev;
|
|
6876
8817
|
continue;
|
|
6877
8818
|
}
|
|
6878
8819
|
return res;
|
|
@@ -6885,6 +8826,12 @@ ${expr}
|
|
|
6885
8826
|
let runList = [...onFail.run || [], ...dynamicRun].filter(Boolean);
|
|
6886
8827
|
runList = Array.from(new Set(runList));
|
|
6887
8828
|
if (runList.length > 0) {
|
|
8829
|
+
try {
|
|
8830
|
+
(init_logger(), __toCommonJS(logger_exports)).logger.info(
|
|
8831
|
+
`\u25B6 on_fail.run: scheduling [${runList.join(", ")}] after '${checkName}'`
|
|
8832
|
+
);
|
|
8833
|
+
} catch {
|
|
8834
|
+
}
|
|
6888
8835
|
loopCount++;
|
|
6889
8836
|
if (loopCount > maxLoops) {
|
|
6890
8837
|
throw new Error(
|
|
@@ -6899,6 +8846,12 @@ ${expr}
|
|
|
6899
8846
|
let target = await evalGotoJs(onFail.goto_js, lastError);
|
|
6900
8847
|
if (!target && onFail.goto) target = onFail.goto;
|
|
6901
8848
|
if (target) {
|
|
8849
|
+
try {
|
|
8850
|
+
(init_logger(), __toCommonJS(logger_exports)).logger.info(
|
|
8851
|
+
`\u21AA on_fail.goto: jumping to '${target}' from '${checkName}'`
|
|
8852
|
+
);
|
|
8853
|
+
} catch {
|
|
8854
|
+
}
|
|
6902
8855
|
if (!allAncestors.includes(target)) {
|
|
6903
8856
|
if (debug)
|
|
6904
8857
|
log2(
|
|
@@ -6911,7 +8864,7 @@ ${expr}
|
|
|
6911
8864
|
`Routing loop budget exceeded (max_loops=${maxLoops}) during on_fail goto`
|
|
6912
8865
|
);
|
|
6913
8866
|
}
|
|
6914
|
-
await executeNamedCheckInline(target);
|
|
8867
|
+
await executeNamedCheckInline(target, { eventOverride: onFail.goto_event });
|
|
6915
8868
|
}
|
|
6916
8869
|
}
|
|
6917
8870
|
const retryMax = onFail.retry?.max ?? 0;
|
|
@@ -6988,6 +8941,11 @@ ${expr}
|
|
|
6988
8941
|
const startTime = Date.now();
|
|
6989
8942
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
6990
8943
|
try {
|
|
8944
|
+
if (options.config?.memory) {
|
|
8945
|
+
const memoryStore = MemoryStore.getInstance(options.config.memory);
|
|
8946
|
+
await memoryStore.initialize();
|
|
8947
|
+
logger.debug("Memory store initialized");
|
|
8948
|
+
}
|
|
6991
8949
|
this.webhookContext = options.webhookContext;
|
|
6992
8950
|
const logFn = (msg) => logger.info(msg);
|
|
6993
8951
|
if (options.githubChecks?.enabled && options.githubChecks.octokit) {
|
|
@@ -7184,8 +9142,7 @@ ${expr}
|
|
|
7184
9142
|
const providerConfig = {
|
|
7185
9143
|
type: checks[0],
|
|
7186
9144
|
prompt: "all",
|
|
7187
|
-
eventContext: prInfo.eventContext,
|
|
7188
|
-
// Pass event context for templates
|
|
9145
|
+
eventContext: this.enrichEventContext(prInfo.eventContext),
|
|
7189
9146
|
ai: timeout ? { timeout } : void 0
|
|
7190
9147
|
};
|
|
7191
9148
|
const result = await provider.execute(prInfo, providerConfig);
|
|
@@ -7219,8 +9176,7 @@ ${expr}
|
|
|
7219
9176
|
type: "ai",
|
|
7220
9177
|
prompt: focus2,
|
|
7221
9178
|
focus: focus2,
|
|
7222
|
-
eventContext: prInfo.eventContext,
|
|
7223
|
-
// Pass event context for templates
|
|
9179
|
+
eventContext: this.enrichEventContext(prInfo.eventContext),
|
|
7224
9180
|
ai: timeout ? { timeout } : void 0,
|
|
7225
9181
|
// Inherit global AI provider and model settings if config is available
|
|
7226
9182
|
ai_provider: config?.ai_provider,
|
|
@@ -7284,6 +9240,21 @@ ${expr}
|
|
|
7284
9240
|
);
|
|
7285
9241
|
}
|
|
7286
9242
|
checks = tagFilteredChecks;
|
|
9243
|
+
if (!this.actionContext) {
|
|
9244
|
+
try {
|
|
9245
|
+
const repoEnv = process.env.GITHUB_REPOSITORY || "";
|
|
9246
|
+
const [owner, repo] = repoEnv.split("/");
|
|
9247
|
+
const token = process.env["INPUT_GITHUB-TOKEN"] || process.env["GITHUB_TOKEN"];
|
|
9248
|
+
if (owner && repo) {
|
|
9249
|
+
this.actionContext = { owner, repo };
|
|
9250
|
+
if (token) {
|
|
9251
|
+
const { Octokit } = await import("@octokit/rest");
|
|
9252
|
+
this.actionContext.octokit = new Octokit({ auth: token });
|
|
9253
|
+
}
|
|
9254
|
+
}
|
|
9255
|
+
} catch {
|
|
9256
|
+
}
|
|
9257
|
+
}
|
|
7287
9258
|
if (checks.length === 0) {
|
|
7288
9259
|
logger.warn("\u26A0\uFE0F No checks remain after tag filtering");
|
|
7289
9260
|
return {
|
|
@@ -7298,10 +9269,14 @@ ${expr}
|
|
|
7298
9269
|
const checkConfig = config.checks[checkName];
|
|
7299
9270
|
return checkConfig?.depends_on && checkConfig.depends_on.length > 0;
|
|
7300
9271
|
});
|
|
7301
|
-
|
|
9272
|
+
const hasRouting = checks.some((checkName) => {
|
|
9273
|
+
const c = config.checks[checkName];
|
|
9274
|
+
return Boolean(c?.on_success || c?.on_fail);
|
|
9275
|
+
});
|
|
9276
|
+
if (checks.length > 1 || hasDependencies || hasRouting) {
|
|
7302
9277
|
if (debug) {
|
|
7303
9278
|
logger.debug(
|
|
7304
|
-
`\u{1F527} Debug: Using grouped dependency-aware execution for ${checks.length} checks (has dependencies: ${hasDependencies})`
|
|
9279
|
+
`\u{1F527} Debug: Using grouped dependency-aware execution for ${checks.length} checks (has dependencies: ${hasDependencies}, has routing: ${hasRouting})`
|
|
7305
9280
|
);
|
|
7306
9281
|
}
|
|
7307
9282
|
return await this.executeGroupedDependencyAwareChecks(
|
|
@@ -7356,8 +9331,7 @@ ${expr}
|
|
|
7356
9331
|
focus: checkConfig.focus || this.mapCheckNameToFocus(checkName),
|
|
7357
9332
|
schema: checkConfig.schema,
|
|
7358
9333
|
group: checkConfig.group,
|
|
7359
|
-
eventContext: prInfo.eventContext,
|
|
7360
|
-
// Pass event context for templates
|
|
9334
|
+
eventContext: this.enrichEventContext(prInfo.eventContext),
|
|
7361
9335
|
ai: {
|
|
7362
9336
|
timeout: timeout || 6e5,
|
|
7363
9337
|
debug,
|
|
@@ -7384,7 +9358,12 @@ ${expr}
|
|
|
7384
9358
|
}
|
|
7385
9359
|
}
|
|
7386
9360
|
if (config && (config.fail_if || checkConfig.fail_if)) {
|
|
7387
|
-
const failureResults = await this.evaluateFailureConditions(
|
|
9361
|
+
const failureResults = await this.evaluateFailureConditions(
|
|
9362
|
+
checkName,
|
|
9363
|
+
result,
|
|
9364
|
+
config,
|
|
9365
|
+
prInfo
|
|
9366
|
+
);
|
|
7388
9367
|
if (failureResults.length > 0) {
|
|
7389
9368
|
const failureIssues = failureResults.filter((f) => f.failed).map((f) => ({
|
|
7390
9369
|
file: "system",
|
|
@@ -7402,6 +9381,7 @@ ${expr}
|
|
|
7402
9381
|
checkName,
|
|
7403
9382
|
content,
|
|
7404
9383
|
group: checkConfig.group || "default",
|
|
9384
|
+
output: result.output,
|
|
7405
9385
|
debug: result.debug,
|
|
7406
9386
|
issues: result.issues
|
|
7407
9387
|
// Include structured issues
|
|
@@ -7487,8 +9467,24 @@ ${expr}
|
|
|
7487
9467
|
*/
|
|
7488
9468
|
async convertReviewSummaryToGroupedResults(reviewSummary, checks, config, prInfo) {
|
|
7489
9469
|
const groupedResults = {};
|
|
7490
|
-
const
|
|
7491
|
-
|
|
9470
|
+
const agg = reviewSummary;
|
|
9471
|
+
const contentMap = agg.__contents;
|
|
9472
|
+
const outputMap = agg.__outputs;
|
|
9473
|
+
const allCheckNames = [];
|
|
9474
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9475
|
+
const pushUnique = (n) => {
|
|
9476
|
+
if (!n) return;
|
|
9477
|
+
if (!seen.has(n)) {
|
|
9478
|
+
seen.add(n);
|
|
9479
|
+
allCheckNames.push(n);
|
|
9480
|
+
}
|
|
9481
|
+
};
|
|
9482
|
+
for (const n of checks) pushUnique(n);
|
|
9483
|
+
if (contentMap) for (const n of Object.keys(contentMap)) pushUnique(n);
|
|
9484
|
+
if (outputMap) for (const n of Object.keys(outputMap)) pushUnique(n);
|
|
9485
|
+
for (const issue of reviewSummary.issues || []) pushUnique(issue.checkName);
|
|
9486
|
+
if (Array.isArray(agg.__executed)) for (const n of agg.__executed) pushUnique(n);
|
|
9487
|
+
for (const checkName of allCheckNames) {
|
|
7492
9488
|
const checkConfig = config?.checks?.[checkName];
|
|
7493
9489
|
if (!checkConfig) continue;
|
|
7494
9490
|
const checkIssues = (reviewSummary.issues || []).filter(
|
|
@@ -7499,17 +9495,38 @@ ${expr}
|
|
|
7499
9495
|
debug: reviewSummary.debug
|
|
7500
9496
|
};
|
|
7501
9497
|
if (contentMap?.[checkName]) {
|
|
7502
|
-
|
|
7503
|
-
|
|
9498
|
+
checkSummary.content = contentMap[checkName];
|
|
9499
|
+
}
|
|
9500
|
+
if (outputMap && Object.prototype.hasOwnProperty.call(outputMap, checkName)) {
|
|
9501
|
+
checkSummary.output = outputMap[checkName];
|
|
9502
|
+
}
|
|
9503
|
+
let content = "";
|
|
9504
|
+
let issuesForCheck = [...checkIssues];
|
|
9505
|
+
try {
|
|
9506
|
+
content = await this.renderCheckContent(checkName, checkSummary, checkConfig, prInfo);
|
|
9507
|
+
} catch (e) {
|
|
9508
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
9509
|
+
console.error(`\u274C Failed to render content for check '${checkName}': ${msg}`);
|
|
9510
|
+
issuesForCheck = [
|
|
9511
|
+
...issuesForCheck,
|
|
9512
|
+
{
|
|
9513
|
+
file: "system",
|
|
9514
|
+
line: 0,
|
|
9515
|
+
ruleId: `${checkName}/render-error`,
|
|
9516
|
+
message: `Template rendering failed: ${msg}`,
|
|
9517
|
+
severity: "error",
|
|
9518
|
+
category: "logic"
|
|
9519
|
+
}
|
|
9520
|
+
];
|
|
7504
9521
|
}
|
|
7505
|
-
const content = await this.renderCheckContent(checkName, checkSummary, checkConfig, prInfo);
|
|
7506
9522
|
const checkResult = {
|
|
7507
9523
|
checkName,
|
|
7508
9524
|
content,
|
|
7509
9525
|
group: checkConfig.group || "default",
|
|
9526
|
+
output: checkSummary.output,
|
|
7510
9527
|
debug: reviewSummary.debug,
|
|
7511
|
-
issues:
|
|
7512
|
-
// Include structured issues
|
|
9528
|
+
issues: issuesForCheck
|
|
9529
|
+
// Include structured issues + rendering error if any
|
|
7513
9530
|
};
|
|
7514
9531
|
const group = checkResult.group;
|
|
7515
9532
|
if (!groupedResults[group]) {
|
|
@@ -7575,13 +9592,20 @@ ${expr}
|
|
|
7575
9592
|
* @returns true if the check should run, false if it should be skipped
|
|
7576
9593
|
*/
|
|
7577
9594
|
async evaluateCheckCondition(checkName, condition, prInfo, results, debug) {
|
|
9595
|
+
const override = this.routingEventOverride;
|
|
9596
|
+
const eventName = override ? override.startsWith("pr_") ? "pull_request" : override === "issue_comment" ? "issue_comment" : override.startsWith("issue_") ? "issues" : "manual" : "issue_comment";
|
|
9597
|
+
const commenterAssoc = resolveAssociationFromEvent(
|
|
9598
|
+
prInfo?.eventContext,
|
|
9599
|
+
prInfo.authorAssociation
|
|
9600
|
+
);
|
|
7578
9601
|
const shouldRun = await this.failureEvaluator.evaluateIfCondition(checkName, condition, {
|
|
7579
9602
|
branch: prInfo.head,
|
|
7580
9603
|
baseBranch: prInfo.base,
|
|
7581
9604
|
filesChanged: prInfo.files.map((f) => f.filename),
|
|
7582
|
-
event:
|
|
9605
|
+
event: eventName,
|
|
7583
9606
|
environment: getSafeEnvironmentVariables(),
|
|
7584
|
-
previousResults: results
|
|
9607
|
+
previousResults: results,
|
|
9608
|
+
authorAssociation: commenterAssoc
|
|
7585
9609
|
});
|
|
7586
9610
|
if (!shouldRun && debug) {
|
|
7587
9611
|
logger.debug(`\u{1F527} Debug: Skipping check '${checkName}' - if condition evaluated to false`);
|
|
@@ -7596,7 +9620,7 @@ ${expr}
|
|
|
7596
9620
|
if (typeof directContent === "string" && directContent.trim()) {
|
|
7597
9621
|
return directContent.trim();
|
|
7598
9622
|
}
|
|
7599
|
-
const { createExtendedLiquid: createExtendedLiquid2 } = await import("./liquid-extensions-
|
|
9623
|
+
const { createExtendedLiquid: createExtendedLiquid2 } = await import("./liquid-extensions-GMEGEGC3.mjs");
|
|
7600
9624
|
const fs5 = await import("fs/promises");
|
|
7601
9625
|
const path5 = await import("path");
|
|
7602
9626
|
const liquid = createExtendedLiquid2({
|
|
@@ -7615,6 +9639,7 @@ ${expr}
|
|
|
7615
9639
|
schemaName = checkConfig.schema || "plain";
|
|
7616
9640
|
}
|
|
7617
9641
|
let templateContent;
|
|
9642
|
+
let enrichAssistantContext = false;
|
|
7618
9643
|
if (checkConfig.template) {
|
|
7619
9644
|
if (checkConfig.template.content) {
|
|
7620
9645
|
templateContent = checkConfig.template.content;
|
|
@@ -7633,16 +9658,109 @@ ${expr}
|
|
|
7633
9658
|
}
|
|
7634
9659
|
const templatePath = path5.join(__dirname, `../output/${sanitizedSchema}/template.liquid`);
|
|
7635
9660
|
templateContent = await fs5.readFile(templatePath, "utf-8");
|
|
9661
|
+
if (sanitizedSchema === "issue-assistant") {
|
|
9662
|
+
enrichAssistantContext = true;
|
|
9663
|
+
}
|
|
7636
9664
|
}
|
|
7637
9665
|
const filteredIssues = (reviewSummary.issues || []).filter(
|
|
7638
9666
|
(issue) => !(issue.file === "system" && issue.line === 0)
|
|
7639
9667
|
);
|
|
7640
9668
|
const templateData = {
|
|
7641
9669
|
issues: filteredIssues,
|
|
7642
|
-
checkName
|
|
9670
|
+
checkName,
|
|
9671
|
+
// Expose structured output for custom schemas/templates (e.g., overview)
|
|
9672
|
+
// This allows templates to render fields like output.text or output.tags
|
|
9673
|
+
output: reviewSummary.output
|
|
7643
9674
|
};
|
|
7644
|
-
|
|
7645
|
-
|
|
9675
|
+
if (enrichAssistantContext) {
|
|
9676
|
+
let authorAssociation;
|
|
9677
|
+
let eventName = "manual";
|
|
9678
|
+
let eventAction;
|
|
9679
|
+
try {
|
|
9680
|
+
const anyInfo = _prInfo;
|
|
9681
|
+
authorAssociation = resolveAssociationFromEvent(
|
|
9682
|
+
anyInfo?.eventContext,
|
|
9683
|
+
anyInfo?.authorAssociation
|
|
9684
|
+
);
|
|
9685
|
+
eventName = anyInfo?.eventContext?.event_name || anyInfo?.eventType || "manual";
|
|
9686
|
+
eventAction = anyInfo?.eventContext?.action;
|
|
9687
|
+
} catch {
|
|
9688
|
+
}
|
|
9689
|
+
templateData.authorAssociation = authorAssociation;
|
|
9690
|
+
templateData.event = { name: eventName, action: eventAction };
|
|
9691
|
+
}
|
|
9692
|
+
const { withPermissionsContext } = await import("./liquid-extensions-GMEGEGC3.mjs");
|
|
9693
|
+
let authorAssociationForFilters;
|
|
9694
|
+
try {
|
|
9695
|
+
const anyInfo = _prInfo;
|
|
9696
|
+
authorAssociationForFilters = resolveAssociationFromEvent(
|
|
9697
|
+
anyInfo?.eventContext,
|
|
9698
|
+
anyInfo?.authorAssociation
|
|
9699
|
+
);
|
|
9700
|
+
} catch {
|
|
9701
|
+
}
|
|
9702
|
+
let rendered;
|
|
9703
|
+
if (typeof withPermissionsContext === "function") {
|
|
9704
|
+
rendered = await withPermissionsContext(
|
|
9705
|
+
{ authorAssociation: authorAssociationForFilters },
|
|
9706
|
+
async () => await liquid.parseAndRender(templateContent, templateData)
|
|
9707
|
+
);
|
|
9708
|
+
if (rendered === void 0 || rendered === null) {
|
|
9709
|
+
rendered = await liquid.parseAndRender(templateContent, templateData);
|
|
9710
|
+
}
|
|
9711
|
+
} else {
|
|
9712
|
+
rendered = await liquid.parseAndRender(templateContent, templateData);
|
|
9713
|
+
}
|
|
9714
|
+
const finalRendered = rendered.trim();
|
|
9715
|
+
try {
|
|
9716
|
+
const { emitMermaidFromMarkdown } = await import("./mermaid-telemetry-LZGDD35I.mjs");
|
|
9717
|
+
emitMermaidFromMarkdown(checkName, finalRendered, "content");
|
|
9718
|
+
} catch {
|
|
9719
|
+
}
|
|
9720
|
+
return finalRendered;
|
|
9721
|
+
}
|
|
9722
|
+
/**
|
|
9723
|
+
* Attempt to elevate an issue/issue_comment context to full PR context when routing via goto_event.
|
|
9724
|
+
* Returns a new PRInfo with files/diff when possible; otherwise returns null.
|
|
9725
|
+
*/
|
|
9726
|
+
async elevateContextToPullRequest(prInfo, targetEvent, log2, debug) {
|
|
9727
|
+
try {
|
|
9728
|
+
if (targetEvent !== "pr_opened" && targetEvent !== "pr_updated") return null;
|
|
9729
|
+
const isIssueContext = prInfo.isIssue === true;
|
|
9730
|
+
const ctx = prInfo.eventContext || {};
|
|
9731
|
+
const isPRThread = Boolean(ctx?.issue?.pull_request);
|
|
9732
|
+
if (!isIssueContext || !isPRThread) return null;
|
|
9733
|
+
let owner = this.actionContext?.owner;
|
|
9734
|
+
let repo = this.actionContext?.repo;
|
|
9735
|
+
if (!owner || !repo) {
|
|
9736
|
+
const repoEnv = process.env.GITHUB_REPOSITORY || "";
|
|
9737
|
+
[owner, repo] = repoEnv.split("/");
|
|
9738
|
+
}
|
|
9739
|
+
if (!owner || !repo) return null;
|
|
9740
|
+
const prNumber = ctx?.issue?.number || prInfo.number;
|
|
9741
|
+
if (!prNumber) return null;
|
|
9742
|
+
let octokit = this.actionContext?.octokit;
|
|
9743
|
+
if (!octokit) {
|
|
9744
|
+
const token = process.env["INPUT_GITHUB-TOKEN"] || process.env["GITHUB_TOKEN"];
|
|
9745
|
+
if (!token) return null;
|
|
9746
|
+
const { Octokit } = await import("@octokit/rest");
|
|
9747
|
+
octokit = new Octokit({ auth: token });
|
|
9748
|
+
}
|
|
9749
|
+
const analyzer = new PRAnalyzer(octokit);
|
|
9750
|
+
const elevated = await analyzer.fetchPRDiff(owner, repo, prNumber, void 0, targetEvent);
|
|
9751
|
+
elevated.eventContext = prInfo.eventContext || ctx;
|
|
9752
|
+
elevated.isPRContext = true;
|
|
9753
|
+
elevated.includeCodeContext = true;
|
|
9754
|
+
if (debug)
|
|
9755
|
+
log2?.(`\u{1F527} Debug: Elevated context to PR #${prNumber} for goto_event=${targetEvent}`);
|
|
9756
|
+
return elevated;
|
|
9757
|
+
} catch (e) {
|
|
9758
|
+
if (debug) {
|
|
9759
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
9760
|
+
log2?.(`\u26A0\uFE0F Debug: Context elevation to PR failed: ${msg}`);
|
|
9761
|
+
}
|
|
9762
|
+
return null;
|
|
9763
|
+
}
|
|
7646
9764
|
}
|
|
7647
9765
|
/**
|
|
7648
9766
|
* Execute multiple checks with dependency awareness - intelligently parallel and sequential
|
|
@@ -7817,8 +9935,7 @@ ${expr}
|
|
|
7817
9935
|
group: checkConfig.group,
|
|
7818
9936
|
checkName,
|
|
7819
9937
|
// Add checkName for sessionID
|
|
7820
|
-
eventContext: prInfo.eventContext,
|
|
7821
|
-
// Pass event context for templates
|
|
9938
|
+
eventContext: this.enrichEventContext(prInfo.eventContext),
|
|
7822
9939
|
transform: checkConfig.transform,
|
|
7823
9940
|
transform_js: checkConfig.transform_js,
|
|
7824
9941
|
// Important: pass through provider-level timeout from check config
|
|
@@ -7828,10 +9945,12 @@ ${expr}
|
|
|
7828
9945
|
message: extendedCheckConfig.message,
|
|
7829
9946
|
env: checkConfig.env,
|
|
7830
9947
|
forEach: checkConfig.forEach,
|
|
9948
|
+
// Pass through any provider-specific keys (e.g., op/values for github provider)
|
|
9949
|
+
...checkConfig,
|
|
7831
9950
|
ai: {
|
|
9951
|
+
...checkConfig.ai || {},
|
|
7832
9952
|
timeout: timeout || 6e5,
|
|
7833
|
-
debug
|
|
7834
|
-
...checkConfig.ai || {}
|
|
9953
|
+
debug
|
|
7835
9954
|
}
|
|
7836
9955
|
};
|
|
7837
9956
|
const dependencyResults = /* @__PURE__ */ new Map();
|
|
@@ -8003,17 +10122,26 @@ ${expr}
|
|
|
8003
10122
|
schema: childCfg.schema,
|
|
8004
10123
|
group: childCfg.group,
|
|
8005
10124
|
checkName: childName,
|
|
8006
|
-
eventContext: prInfo.eventContext,
|
|
10125
|
+
eventContext: this.enrichEventContext(prInfo.eventContext),
|
|
8007
10126
|
transform: childCfg.transform,
|
|
8008
10127
|
transform_js: childCfg.transform_js,
|
|
8009
10128
|
env: childCfg.env,
|
|
8010
10129
|
forEach: childCfg.forEach,
|
|
10130
|
+
// Include provider-specific keys like op/values for non-AI providers
|
|
10131
|
+
...childCfg,
|
|
8011
10132
|
ai: {
|
|
10133
|
+
...childCfg.ai || {},
|
|
8012
10134
|
timeout: timeout || 6e5,
|
|
8013
|
-
debug
|
|
8014
|
-
...childCfg.ai || {}
|
|
10135
|
+
debug
|
|
8015
10136
|
}
|
|
8016
10137
|
};
|
|
10138
|
+
try {
|
|
10139
|
+
emitNdjsonSpanWithEvents("visor.check", { "visor.check.id": checkName }, [
|
|
10140
|
+
{ name: "check.started" },
|
|
10141
|
+
{ name: "check.completed" }
|
|
10142
|
+
]);
|
|
10143
|
+
} catch {
|
|
10144
|
+
}
|
|
8017
10145
|
const childDepResults = /* @__PURE__ */ new Map();
|
|
8018
10146
|
const childAllDeps = DependencyResolver.getAllDependencies(
|
|
8019
10147
|
childName,
|
|
@@ -8136,6 +10264,18 @@ ${expr}
|
|
|
8136
10264
|
}
|
|
8137
10265
|
};
|
|
8138
10266
|
const itemTasks = forEachItems.map((item, itemIndex) => async () => {
|
|
10267
|
+
try {
|
|
10268
|
+
emitNdjsonSpanWithEvents(
|
|
10269
|
+
"visor.foreach.item",
|
|
10270
|
+
{
|
|
10271
|
+
"visor.check.id": checkName,
|
|
10272
|
+
"visor.foreach.index": itemIndex,
|
|
10273
|
+
"visor.foreach.total": forEachItems.length
|
|
10274
|
+
},
|
|
10275
|
+
[]
|
|
10276
|
+
);
|
|
10277
|
+
} catch {
|
|
10278
|
+
}
|
|
8139
10279
|
const forEachDependencyResults = /* @__PURE__ */ new Map();
|
|
8140
10280
|
for (const [depName, depResult] of dependencyResults) {
|
|
8141
10281
|
if (forEachParents.includes(depName)) {
|
|
@@ -8376,7 +10516,7 @@ ${expr}
|
|
|
8376
10516
|
schema: nodeCfg.schema,
|
|
8377
10517
|
group: nodeCfg.group,
|
|
8378
10518
|
checkName: node,
|
|
8379
|
-
eventContext: prInfo.eventContext,
|
|
10519
|
+
eventContext: this.enrichEventContext(prInfo.eventContext),
|
|
8380
10520
|
transform: nodeCfg.transform,
|
|
8381
10521
|
transform_js: nodeCfg.transform_js,
|
|
8382
10522
|
env: nodeCfg.env,
|
|
@@ -8738,6 +10878,13 @@ ${expr}
|
|
|
8738
10878
|
debug,
|
|
8739
10879
|
results
|
|
8740
10880
|
);
|
|
10881
|
+
try {
|
|
10882
|
+
emitNdjsonSpanWithEvents("visor.check", { "visor.check.id": checkName }, [
|
|
10883
|
+
{ name: "check.started" },
|
|
10884
|
+
{ name: "check.completed" }
|
|
10885
|
+
]);
|
|
10886
|
+
} catch {
|
|
10887
|
+
}
|
|
8741
10888
|
if (config && (config.fail_if || checkConfig.fail_if)) {
|
|
8742
10889
|
const failureResults = await this.evaluateFailureConditions(
|
|
8743
10890
|
checkName,
|
|
@@ -8905,6 +11052,13 @@ ${error.stack || ""}` : String(error);
|
|
|
8905
11052
|
reviewSummaryWithOutput.forEachItems = normalizedOutput;
|
|
8906
11053
|
reviewSummaryWithOutput.isForEach = true;
|
|
8907
11054
|
}
|
|
11055
|
+
try {
|
|
11056
|
+
emitNdjsonSpanWithEvents("visor.check", { "visor.check.id": checkName }, [
|
|
11057
|
+
{ name: "check.started" },
|
|
11058
|
+
{ name: "check.completed" }
|
|
11059
|
+
]);
|
|
11060
|
+
} catch {
|
|
11061
|
+
}
|
|
8908
11062
|
results.set(checkName, reviewResult);
|
|
8909
11063
|
} else {
|
|
8910
11064
|
const errorSummary = {
|
|
@@ -9018,6 +11172,12 @@ ${error.stack || ""}` : String(error);
|
|
|
9018
11172
|
`\u{1F527} Debug: Starting check: ${checkName} with prompt type: ${typeof checkConfig.prompt}`
|
|
9019
11173
|
);
|
|
9020
11174
|
if (checkConfig.if) {
|
|
11175
|
+
const override = this.routingEventOverride;
|
|
11176
|
+
const eventName = override ? override.startsWith("pr_") ? "pull_request" : override === "issue_comment" ? "issue_comment" : override.startsWith("issue_") ? "issues" : "manual" : "issue_comment";
|
|
11177
|
+
const commenterAssoc = resolveAssociationFromEvent(
|
|
11178
|
+
prInfo?.eventContext,
|
|
11179
|
+
prInfo.authorAssociation
|
|
11180
|
+
);
|
|
9021
11181
|
const shouldRun = await this.failureEvaluator.evaluateIfCondition(
|
|
9022
11182
|
checkName,
|
|
9023
11183
|
checkConfig.if,
|
|
@@ -9025,11 +11185,12 @@ ${error.stack || ""}` : String(error);
|
|
|
9025
11185
|
branch: prInfo.head,
|
|
9026
11186
|
baseBranch: prInfo.base,
|
|
9027
11187
|
filesChanged: prInfo.files.map((f) => f.filename),
|
|
9028
|
-
event:
|
|
9029
|
-
//
|
|
11188
|
+
event: eventName,
|
|
11189
|
+
// honor routing override if present
|
|
9030
11190
|
environment: getSafeEnvironmentVariables(),
|
|
9031
|
-
previousResults: /* @__PURE__ */ new Map()
|
|
11191
|
+
previousResults: /* @__PURE__ */ new Map(),
|
|
9032
11192
|
// No previous results in parallel execution
|
|
11193
|
+
authorAssociation: commenterAssoc
|
|
9033
11194
|
}
|
|
9034
11195
|
);
|
|
9035
11196
|
if (!shouldRun) {
|
|
@@ -9051,8 +11212,7 @@ ${error.stack || ""}` : String(error);
|
|
|
9051
11212
|
focus: checkConfig.focus || this.mapCheckNameToFocus(checkName),
|
|
9052
11213
|
schema: checkConfig.schema,
|
|
9053
11214
|
group: checkConfig.group,
|
|
9054
|
-
eventContext: prInfo.eventContext,
|
|
9055
|
-
// Pass event context for templates
|
|
11215
|
+
eventContext: this.enrichEventContext(prInfo.eventContext),
|
|
9056
11216
|
ai: {
|
|
9057
11217
|
timeout: timeout || 6e5,
|
|
9058
11218
|
debug,
|
|
@@ -9128,8 +11288,7 @@ ${error.stack || ""}` : String(error);
|
|
|
9128
11288
|
focus: checkConfig.focus || this.mapCheckNameToFocus(checkName),
|
|
9129
11289
|
schema: checkConfig.schema,
|
|
9130
11290
|
group: checkConfig.group,
|
|
9131
|
-
eventContext: prInfo.eventContext,
|
|
9132
|
-
// Pass event context for templates
|
|
11291
|
+
eventContext: this.enrichEventContext(prInfo.eventContext),
|
|
9133
11292
|
ai: {
|
|
9134
11293
|
timeout: timeout || 6e5,
|
|
9135
11294
|
...checkConfig.ai || {}
|
|
@@ -9171,6 +11330,7 @@ ${error.stack || ""}` : String(error);
|
|
|
9171
11330
|
const aggregatedIssues = [];
|
|
9172
11331
|
const debugInfo = [];
|
|
9173
11332
|
const contentMap = {};
|
|
11333
|
+
const outputsMap = {};
|
|
9174
11334
|
const stats = DependencyResolver.getExecutionStats(dependencyGraph);
|
|
9175
11335
|
const executionInfo = [
|
|
9176
11336
|
stoppedEarly ? `\u{1F6D1} Dependency-aware execution stopped early (fail-fast):` : `\u{1F50D} Dependency-aware execution completed:`,
|
|
@@ -9182,6 +11342,7 @@ ${error.stack || ""}` : String(error);
|
|
|
9182
11342
|
stoppedEarly ? ` - Stopped early due to fail-fast behavior` : ``
|
|
9183
11343
|
].filter(Boolean);
|
|
9184
11344
|
debugInfo.push(...executionInfo);
|
|
11345
|
+
const processed = /* @__PURE__ */ new Set();
|
|
9185
11346
|
for (const executionGroup of dependencyGraph.executionOrder) {
|
|
9186
11347
|
for (const checkName of executionGroup.parallel) {
|
|
9187
11348
|
const result = results.get(checkName);
|
|
@@ -9199,6 +11360,7 @@ ${error.stack || ""}` : String(error);
|
|
|
9199
11360
|
`\u2705 Check "${checkName}" completed: ${(result.issues || []).length} issues found (level ${executionGroup.level})`
|
|
9200
11361
|
);
|
|
9201
11362
|
}
|
|
11363
|
+
processed.add(checkName);
|
|
9202
11364
|
const nonInternalIssues = (result.issues || []).filter(
|
|
9203
11365
|
(issue) => !issue.ruleId?.endsWith("/__skipped")
|
|
9204
11366
|
);
|
|
@@ -9208,7 +11370,29 @@ ${error.stack || ""}` : String(error);
|
|
|
9208
11370
|
if (typeof resultContent === "string" && resultContent.trim()) {
|
|
9209
11371
|
contentMap[checkName] = resultContent.trim();
|
|
9210
11372
|
}
|
|
11373
|
+
if (resultSummary.output !== void 0) {
|
|
11374
|
+
outputsMap[checkName] = resultSummary.output;
|
|
11375
|
+
}
|
|
11376
|
+
}
|
|
11377
|
+
}
|
|
11378
|
+
for (const [checkName, result] of results.entries()) {
|
|
11379
|
+
if (processed.has(checkName)) continue;
|
|
11380
|
+
if (!result) continue;
|
|
11381
|
+
const nonInternalIssues = (result.issues || []).filter(
|
|
11382
|
+
(issue) => !issue.ruleId?.endsWith("/__skipped")
|
|
11383
|
+
);
|
|
11384
|
+
aggregatedIssues.push(...nonInternalIssues);
|
|
11385
|
+
const resultSummary = result;
|
|
11386
|
+
const resultContent = resultSummary.content;
|
|
11387
|
+
if (typeof resultContent === "string" && resultContent.trim()) {
|
|
11388
|
+
contentMap[checkName] = resultContent.trim();
|
|
11389
|
+
}
|
|
11390
|
+
if (resultSummary.output !== void 0) {
|
|
11391
|
+
outputsMap[checkName] = resultSummary.output;
|
|
9211
11392
|
}
|
|
11393
|
+
debugInfo.push(
|
|
11394
|
+
`\u2705 (dynamic) Check "${checkName}" included: ${(result.issues || []).length} issues found`
|
|
11395
|
+
);
|
|
9212
11396
|
}
|
|
9213
11397
|
if (debug) {
|
|
9214
11398
|
console.error(
|
|
@@ -9267,6 +11451,10 @@ ${result.debug.rawResponse}`).join("\n\n"),
|
|
|
9267
11451
|
if (Object.keys(contentMap).length > 0) {
|
|
9268
11452
|
summary.__contents = contentMap;
|
|
9269
11453
|
}
|
|
11454
|
+
if (Object.keys(outputsMap).length > 0) {
|
|
11455
|
+
summary.__outputs = outputsMap;
|
|
11456
|
+
}
|
|
11457
|
+
summary.__executed = Array.from(results.keys());
|
|
9270
11458
|
return summary;
|
|
9271
11459
|
}
|
|
9272
11460
|
/**
|
|
@@ -9590,7 +11778,7 @@ ${result.value.result.debug.rawResponse}`;
|
|
|
9590
11778
|
/**
|
|
9591
11779
|
* Evaluate failure conditions for a check result
|
|
9592
11780
|
*/
|
|
9593
|
-
async evaluateFailureConditions(checkName, reviewSummary, config) {
|
|
11781
|
+
async evaluateFailureConditions(checkName, reviewSummary, config, prInfo) {
|
|
9594
11782
|
if (!config) {
|
|
9595
11783
|
return [];
|
|
9596
11784
|
}
|
|
@@ -9609,7 +11797,50 @@ ${result.value.result.debug.rawResponse}`;
|
|
|
9609
11797
|
reviewSummary,
|
|
9610
11798
|
globalFailIf
|
|
9611
11799
|
);
|
|
11800
|
+
try {
|
|
11801
|
+
addEvent("fail_if.evaluated", {
|
|
11802
|
+
check: checkName,
|
|
11803
|
+
scope: "global",
|
|
11804
|
+
name: "global_fail_if",
|
|
11805
|
+
expression: globalFailIf
|
|
11806
|
+
});
|
|
11807
|
+
} catch {
|
|
11808
|
+
}
|
|
9612
11809
|
if (failed) {
|
|
11810
|
+
try {
|
|
11811
|
+
addEvent("fail_if.triggered", {
|
|
11812
|
+
check: checkName,
|
|
11813
|
+
scope: "global",
|
|
11814
|
+
name: "global_fail_if",
|
|
11815
|
+
expression: globalFailIf,
|
|
11816
|
+
severity: "error"
|
|
11817
|
+
});
|
|
11818
|
+
} catch {
|
|
11819
|
+
}
|
|
11820
|
+
try {
|
|
11821
|
+
addFailIfTriggered(checkName, "global");
|
|
11822
|
+
} catch {
|
|
11823
|
+
}
|
|
11824
|
+
try {
|
|
11825
|
+
const { emitNdjsonSpanWithEvents: emitNdjsonSpanWithEvents2 } = (init_fallback_ndjson(), __toCommonJS(fallback_ndjson_exports));
|
|
11826
|
+
emitNdjsonSpanWithEvents2(
|
|
11827
|
+
"visor.fail_if",
|
|
11828
|
+
{ check: checkName, scope: "global", name: "global_fail_if" },
|
|
11829
|
+
[
|
|
11830
|
+
{
|
|
11831
|
+
name: "fail_if.triggered",
|
|
11832
|
+
attrs: {
|
|
11833
|
+
check: checkName,
|
|
11834
|
+
scope: "global",
|
|
11835
|
+
name: "global_fail_if",
|
|
11836
|
+
expression: globalFailIf,
|
|
11837
|
+
severity: "error"
|
|
11838
|
+
}
|
|
11839
|
+
}
|
|
11840
|
+
]
|
|
11841
|
+
);
|
|
11842
|
+
} catch {
|
|
11843
|
+
}
|
|
9613
11844
|
logger.warn(`\u26A0\uFE0F Check "${checkName}" - global fail_if condition met: ${globalFailIf}`);
|
|
9614
11845
|
results.push({
|
|
9615
11846
|
conditionName: "global_fail_if",
|
|
@@ -9631,7 +11862,78 @@ ${result.value.result.debug.rawResponse}`;
|
|
|
9631
11862
|
reviewSummary,
|
|
9632
11863
|
checkFailIf
|
|
9633
11864
|
);
|
|
11865
|
+
try {
|
|
11866
|
+
addEvent("fail_if.evaluated", {
|
|
11867
|
+
check: checkName,
|
|
11868
|
+
scope: "check",
|
|
11869
|
+
name: `${checkName}_fail_if`,
|
|
11870
|
+
expression: checkFailIf
|
|
11871
|
+
});
|
|
11872
|
+
} catch {
|
|
11873
|
+
}
|
|
11874
|
+
try {
|
|
11875
|
+
const { emitNdjsonSpanWithEvents: emitNdjsonSpanWithEvents2 } = (init_fallback_ndjson(), __toCommonJS(fallback_ndjson_exports));
|
|
11876
|
+
emitNdjsonSpanWithEvents2(
|
|
11877
|
+
"visor.fail_if",
|
|
11878
|
+
{ check: checkName, scope: "check", name: `${checkName}_fail_if` },
|
|
11879
|
+
[
|
|
11880
|
+
{
|
|
11881
|
+
name: "fail_if.evaluated",
|
|
11882
|
+
attrs: {
|
|
11883
|
+
check: checkName,
|
|
11884
|
+
scope: "check",
|
|
11885
|
+
name: `${checkName}_fail_if`,
|
|
11886
|
+
expression: checkFailIf
|
|
11887
|
+
}
|
|
11888
|
+
}
|
|
11889
|
+
]
|
|
11890
|
+
);
|
|
11891
|
+
} catch {
|
|
11892
|
+
}
|
|
9634
11893
|
if (failed) {
|
|
11894
|
+
try {
|
|
11895
|
+
addEvent("fail_if.triggered", {
|
|
11896
|
+
check: checkName,
|
|
11897
|
+
scope: "check",
|
|
11898
|
+
name: `${checkName}_fail_if`,
|
|
11899
|
+
expression: checkFailIf,
|
|
11900
|
+
severity: "error"
|
|
11901
|
+
});
|
|
11902
|
+
} catch {
|
|
11903
|
+
}
|
|
11904
|
+
try {
|
|
11905
|
+
addEvent("fail_if.evaluated", {
|
|
11906
|
+
check: checkName,
|
|
11907
|
+
scope: "check",
|
|
11908
|
+
name: `${checkName}_fail_if`,
|
|
11909
|
+
expression: checkFailIf
|
|
11910
|
+
});
|
|
11911
|
+
} catch {
|
|
11912
|
+
}
|
|
11913
|
+
try {
|
|
11914
|
+
addFailIfTriggered(checkName, "check");
|
|
11915
|
+
} catch {
|
|
11916
|
+
}
|
|
11917
|
+
try {
|
|
11918
|
+
const { emitNdjsonSpanWithEvents: emitNdjsonSpanWithEvents2 } = (init_fallback_ndjson(), __toCommonJS(fallback_ndjson_exports));
|
|
11919
|
+
emitNdjsonSpanWithEvents2(
|
|
11920
|
+
"visor.fail_if",
|
|
11921
|
+
{ check: checkName, scope: "check", name: `${checkName}_fail_if` },
|
|
11922
|
+
[
|
|
11923
|
+
{
|
|
11924
|
+
name: "fail_if.triggered",
|
|
11925
|
+
attrs: {
|
|
11926
|
+
check: checkName,
|
|
11927
|
+
scope: "check",
|
|
11928
|
+
name: `${checkName}_fail_if`,
|
|
11929
|
+
expression: checkFailIf,
|
|
11930
|
+
severity: "error"
|
|
11931
|
+
}
|
|
11932
|
+
}
|
|
11933
|
+
]
|
|
11934
|
+
);
|
|
11935
|
+
} catch {
|
|
11936
|
+
}
|
|
9635
11937
|
logger.warn(`\u26A0\uFE0F Check "${checkName}" - fail_if condition met: ${checkFailIf}`);
|
|
9636
11938
|
results.push({
|
|
9637
11939
|
conditionName: `${checkName}_fail_if`,
|
|
@@ -9645,6 +11947,31 @@ ${result.value.result.debug.rawResponse}`;
|
|
|
9645
11947
|
logger.debug(`\u2713 Check "${checkName}" - fail_if condition passed`);
|
|
9646
11948
|
}
|
|
9647
11949
|
}
|
|
11950
|
+
try {
|
|
11951
|
+
const { emitNdjsonSpanWithEvents: emitNdjsonSpanWithEvents2 } = (init_fallback_ndjson(), __toCommonJS(fallback_ndjson_exports));
|
|
11952
|
+
const hadTriggered = results.some((r) => r.failed === true);
|
|
11953
|
+
emitNdjsonSpanWithEvents2(
|
|
11954
|
+
"visor.fail_if",
|
|
11955
|
+
{
|
|
11956
|
+
check: checkName,
|
|
11957
|
+
scope: hadTriggered ? checkFailIf ? "check" : "global" : checkFailIf ? "check" : "global"
|
|
11958
|
+
},
|
|
11959
|
+
[
|
|
11960
|
+
{
|
|
11961
|
+
name: "fail_if.evaluated",
|
|
11962
|
+
attrs: { check: checkName, scope: checkFailIf ? "check" : "global" }
|
|
11963
|
+
}
|
|
11964
|
+
].concat(
|
|
11965
|
+
hadTriggered ? [
|
|
11966
|
+
{
|
|
11967
|
+
name: "fail_if.triggered",
|
|
11968
|
+
attrs: { check: checkName, scope: checkFailIf ? "check" : "global" }
|
|
11969
|
+
}
|
|
11970
|
+
] : []
|
|
11971
|
+
)
|
|
11972
|
+
);
|
|
11973
|
+
} catch {
|
|
11974
|
+
}
|
|
9648
11975
|
return results;
|
|
9649
11976
|
}
|
|
9650
11977
|
const globalConditions = config.failure_conditions;
|
|
@@ -9655,7 +11982,10 @@ ${result.value.result.debug.rawResponse}`;
|
|
|
9655
11982
|
checkGroup,
|
|
9656
11983
|
reviewSummary,
|
|
9657
11984
|
globalConditions,
|
|
9658
|
-
checkConditions
|
|
11985
|
+
checkConditions,
|
|
11986
|
+
void 0,
|
|
11987
|
+
// previousOutputs
|
|
11988
|
+
prInfo?.authorAssociation
|
|
9659
11989
|
);
|
|
9660
11990
|
}
|
|
9661
11991
|
/**
|
|
@@ -9778,6 +12108,7 @@ ${result.value.result.debug.rawResponse}`;
|
|
|
9778
12108
|
{ issues: checkIssues },
|
|
9779
12109
|
options.config
|
|
9780
12110
|
);
|
|
12111
|
+
const execErrorIssue = checkIssues.find((i) => i.ruleId?.startsWith("command/"));
|
|
9781
12112
|
await this.githubCheckService.completeCheckRun(
|
|
9782
12113
|
options.githubChecks.owner,
|
|
9783
12114
|
options.githubChecks.repo,
|
|
@@ -9785,7 +12116,7 @@ ${result.value.result.debug.rawResponse}`;
|
|
|
9785
12116
|
checkName,
|
|
9786
12117
|
failureResults,
|
|
9787
12118
|
checkIssues,
|
|
9788
|
-
void 0,
|
|
12119
|
+
execErrorIssue ? execErrorIssue.message : void 0,
|
|
9789
12120
|
// executionError
|
|
9790
12121
|
prInfo.files.map((f) => f.filename),
|
|
9791
12122
|
// filesChangedInCommit
|
|
@@ -10161,8 +12492,6 @@ ${result.value.result.debug.rawResponse}`;
|
|
|
10161
12492
|
};
|
|
10162
12493
|
|
|
10163
12494
|
export {
|
|
10164
|
-
logger,
|
|
10165
|
-
init_logger,
|
|
10166
12495
|
CheckExecutionEngine
|
|
10167
12496
|
};
|
|
10168
|
-
//# sourceMappingURL=chunk-
|
|
12497
|
+
//# sourceMappingURL=chunk-Z47UECAT.mjs.map
|