@probelabs/visor 0.1.90 → 0.1.92
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 +120 -154
- 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 +9 -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 +120 -154
- 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 +172824 -45634
- package/dist/liquid-extensions.d.ts +3 -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 +29 -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-L73PFZQY.mjs +11 -0
- package/dist/sdk/chunk-2U6BIWSY.mjs +748 -0
- package/dist/sdk/chunk-2U6BIWSY.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-N2PPFOSF.mjs → chunk-LJHRU3WQ.mjs} +2577 -250
- package/dist/sdk/chunk-LJHRU3WQ.mjs.map +1 -0
- package/dist/sdk/chunk-TWJKAYT6.mjs +1124 -0
- package/dist/sdk/chunk-TWJKAYT6.mjs.map +1 -0
- package/dist/sdk/liquid-extensions-AFKRYROF.mjs +14 -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 +5352 -767
- 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-15T07-21-47-696Z.ndjson +17 -0
- package/dist/traces/run-2025-10-15T07-21-58-106Z.ndjson +17 -0
- package/dist/traces/run-2025-10-15T07-21-58-693Z.ndjson +17 -0
- package/dist/traces/run-2025-10-15T07-21-59-167Z.ndjson +17 -0
- package/dist/traces/run-2025-10-15T07-21-59-629Z.ndjson +8 -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-L73PFZQY.mjs.map} +0 -0
- /package/dist/sdk/{liquid-extensions-KDECAJTV.mjs.map → liquid-extensions-AFKRYROF.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-2U6BIWSY.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-L73PFZQY.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,11 +8263,22 @@ 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
|
}
|
|
@@ -6532,13 +8288,13 @@ var CheckExecutionEngine = class _CheckExecutionEngine {
|
|
|
6532
8288
|
getRoutingSandbox() {
|
|
6533
8289
|
if (this.routingSandbox) return this.routingSandbox;
|
|
6534
8290
|
const globals = {
|
|
6535
|
-
...
|
|
8291
|
+
...Sandbox5.SAFE_GLOBALS,
|
|
6536
8292
|
Math,
|
|
6537
8293
|
JSON,
|
|
6538
8294
|
console: { log: console.log }
|
|
6539
8295
|
};
|
|
6540
|
-
const prototypeWhitelist = new Map(
|
|
6541
|
-
this.routingSandbox = new
|
|
8296
|
+
const prototypeWhitelist = new Map(Sandbox5.SAFE_PROTOTYPES);
|
|
8297
|
+
this.routingSandbox = new Sandbox5({ globals, prototypeWhitelist });
|
|
6542
8298
|
return this.routingSandbox;
|
|
6543
8299
|
}
|
|
6544
8300
|
redact(str, limit = 200) {
|
|
@@ -6578,10 +8334,12 @@ var CheckExecutionEngine = class _CheckExecutionEngine {
|
|
|
6578
8334
|
let loopCount = 0;
|
|
6579
8335
|
const seed = `${checkName}-${prInfo.number || "local"}`;
|
|
6580
8336
|
const allAncestors = DependencyResolver.getAllDependencies(checkName, dependencyGraph.nodes);
|
|
8337
|
+
let currentRouteOutput = void 0;
|
|
6581
8338
|
const evalRunJs = async (expr, error) => {
|
|
6582
8339
|
if (!expr) return [];
|
|
6583
8340
|
try {
|
|
6584
8341
|
const sandbox = this.getRoutingSandbox();
|
|
8342
|
+
const eventObj = { name: prInfo.eventType || "manual" };
|
|
6585
8343
|
const scope = {
|
|
6586
8344
|
step: { id: checkName, tags: checkConfig.tags || [], group: checkConfig.group },
|
|
6587
8345
|
attempt,
|
|
@@ -6593,6 +8351,7 @@ var CheckExecutionEngine = class _CheckExecutionEngine {
|
|
|
6593
8351
|
parent: foreachContext.parent
|
|
6594
8352
|
} : null,
|
|
6595
8353
|
outputs: Object.fromEntries((dependencyResults || /* @__PURE__ */ new Map()).entries()),
|
|
8354
|
+
output: currentRouteOutput,
|
|
6596
8355
|
pr: {
|
|
6597
8356
|
number: prInfo.number,
|
|
6598
8357
|
title: prInfo.title,
|
|
@@ -6601,10 +8360,15 @@ var CheckExecutionEngine = class _CheckExecutionEngine {
|
|
|
6601
8360
|
base: prInfo.base
|
|
6602
8361
|
},
|
|
6603
8362
|
files: prInfo.files,
|
|
6604
|
-
env: getSafeEnvironmentVariables()
|
|
8363
|
+
env: getSafeEnvironmentVariables(),
|
|
8364
|
+
permissions: createPermissionHelpers(
|
|
8365
|
+
resolveAssociationFromEvent(prInfo.eventContext, prInfo.authorAssociation),
|
|
8366
|
+
detectLocalMode()
|
|
8367
|
+
),
|
|
8368
|
+
event: eventObj
|
|
6605
8369
|
};
|
|
6606
8370
|
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);
|
|
8371
|
+
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
8372
|
const __fn = () => {
|
|
6609
8373
|
${expr}
|
|
6610
8374
|
};
|
|
@@ -6628,6 +8392,7 @@ ${expr}
|
|
|
6628
8392
|
if (!expr) return null;
|
|
6629
8393
|
try {
|
|
6630
8394
|
const sandbox = this.getRoutingSandbox();
|
|
8395
|
+
const eventObj = { name: prInfo.eventType || "manual" };
|
|
6631
8396
|
const scope = {
|
|
6632
8397
|
step: { id: checkName, tags: checkConfig.tags || [], group: checkConfig.group },
|
|
6633
8398
|
attempt,
|
|
@@ -6639,6 +8404,7 @@ ${expr}
|
|
|
6639
8404
|
parent: foreachContext.parent
|
|
6640
8405
|
} : null,
|
|
6641
8406
|
outputs: Object.fromEntries((dependencyResults || /* @__PURE__ */ new Map()).entries()),
|
|
8407
|
+
output: currentRouteOutput,
|
|
6642
8408
|
pr: {
|
|
6643
8409
|
number: prInfo.number,
|
|
6644
8410
|
title: prInfo.title,
|
|
@@ -6647,10 +8413,15 @@ ${expr}
|
|
|
6647
8413
|
base: prInfo.base
|
|
6648
8414
|
},
|
|
6649
8415
|
files: prInfo.files,
|
|
6650
|
-
env: getSafeEnvironmentVariables()
|
|
8416
|
+
env: getSafeEnvironmentVariables(),
|
|
8417
|
+
permissions: createPermissionHelpers(
|
|
8418
|
+
resolveAssociationFromEvent(prInfo.eventContext, prInfo.authorAssociation),
|
|
8419
|
+
detectLocalMode()
|
|
8420
|
+
),
|
|
8421
|
+
event: eventObj
|
|
6651
8422
|
};
|
|
6652
8423
|
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);
|
|
8424
|
+
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
8425
|
const __fn = () => {
|
|
6655
8426
|
${expr}
|
|
6656
8427
|
};
|
|
@@ -6686,7 +8457,7 @@ ${expr}
|
|
|
6686
8457
|
dfs(name);
|
|
6687
8458
|
return Array.from(new Set(acc));
|
|
6688
8459
|
};
|
|
6689
|
-
const executeNamedCheckInline = async (target) => {
|
|
8460
|
+
const executeNamedCheckInline = async (target, opts) => {
|
|
6690
8461
|
const targetCfg = config?.checks?.[target];
|
|
6691
8462
|
if (!targetCfg) {
|
|
6692
8463
|
throw new Error(`on_* referenced unknown check '${target}'`);
|
|
@@ -6710,6 +8481,10 @@ ${expr}
|
|
|
6710
8481
|
const providerType = targetCfg.type || "ai";
|
|
6711
8482
|
const prov = this.providerRegistry.getProviderOrThrow(providerType);
|
|
6712
8483
|
this.setProviderWebhookContext(prov);
|
|
8484
|
+
const enrichedEventContext = {
|
|
8485
|
+
...prInfo.eventContext,
|
|
8486
|
+
...this.actionContext?.octokit ? { octokit: this.actionContext.octokit } : {}
|
|
8487
|
+
};
|
|
6713
8488
|
const provCfg = {
|
|
6714
8489
|
type: providerType,
|
|
6715
8490
|
prompt: targetCfg.prompt,
|
|
@@ -6718,15 +8493,17 @@ ${expr}
|
|
|
6718
8493
|
schema: targetCfg.schema,
|
|
6719
8494
|
group: targetCfg.group,
|
|
6720
8495
|
checkName: target,
|
|
6721
|
-
eventContext:
|
|
8496
|
+
eventContext: enrichedEventContext,
|
|
6722
8497
|
transform: targetCfg.transform,
|
|
6723
8498
|
transform_js: targetCfg.transform_js,
|
|
6724
8499
|
env: targetCfg.env,
|
|
6725
8500
|
forEach: targetCfg.forEach,
|
|
8501
|
+
// Include provider-specific keys (e.g., op/values for github)
|
|
8502
|
+
...targetCfg,
|
|
6726
8503
|
ai: {
|
|
8504
|
+
...targetCfg.ai || {},
|
|
6727
8505
|
timeout: providerConfig.ai?.timeout || 6e5,
|
|
6728
|
-
debug: !!debug
|
|
6729
|
-
...targetCfg.ai || {}
|
|
8506
|
+
debug: !!debug
|
|
6730
8507
|
}
|
|
6731
8508
|
};
|
|
6732
8509
|
const targetDeps = getAllDepsFromConfig(target);
|
|
@@ -6750,7 +8527,34 @@ ${expr}
|
|
|
6750
8527
|
const execStr = provCfg.exec;
|
|
6751
8528
|
if (execStr) log2(`\u{1F527} Debug: inline exec '${target}' command: ${execStr}`);
|
|
6752
8529
|
}
|
|
6753
|
-
|
|
8530
|
+
let prInfoForInline = prInfo;
|
|
8531
|
+
const prevEventOverride = this.routingEventOverride;
|
|
8532
|
+
if (opts?.eventOverride) {
|
|
8533
|
+
const elevated = await this.elevateContextToPullRequest(
|
|
8534
|
+
{ ...prInfo, eventType: opts.eventOverride },
|
|
8535
|
+
opts.eventOverride,
|
|
8536
|
+
log2,
|
|
8537
|
+
debug
|
|
8538
|
+
);
|
|
8539
|
+
if (elevated) {
|
|
8540
|
+
prInfoForInline = elevated;
|
|
8541
|
+
} else {
|
|
8542
|
+
prInfoForInline = { ...prInfo, eventType: opts.eventOverride };
|
|
8543
|
+
}
|
|
8544
|
+
this.routingEventOverride = opts.eventOverride;
|
|
8545
|
+
const msg = `\u21AA goto_event: inline '${target}' with event=${opts.eventOverride}${elevated ? " (elevated to PR context)" : ""}`;
|
|
8546
|
+
if (debug) log2(`\u{1F527} Debug: ${msg}`);
|
|
8547
|
+
try {
|
|
8548
|
+
(init_logger(), __toCommonJS(logger_exports)).logger.info(msg);
|
|
8549
|
+
} catch {
|
|
8550
|
+
}
|
|
8551
|
+
}
|
|
8552
|
+
let r;
|
|
8553
|
+
try {
|
|
8554
|
+
r = await prov.execute(prInfoForInline, provCfg, depResults, sessionInfo);
|
|
8555
|
+
} finally {
|
|
8556
|
+
this.routingEventOverride = prevEventOverride;
|
|
8557
|
+
}
|
|
6754
8558
|
const enrichedIssues = (r.issues || []).map((issue) => ({
|
|
6755
8559
|
...issue,
|
|
6756
8560
|
checkName: target,
|
|
@@ -6767,7 +8571,18 @@ ${expr}
|
|
|
6767
8571
|
};
|
|
6768
8572
|
while (true) {
|
|
6769
8573
|
try {
|
|
8574
|
+
try {
|
|
8575
|
+
emitNdjsonFallback("visor.provider", {
|
|
8576
|
+
"visor.check.id": checkName,
|
|
8577
|
+
"visor.provider.type": providerConfig.type || "ai"
|
|
8578
|
+
});
|
|
8579
|
+
} catch {
|
|
8580
|
+
}
|
|
6770
8581
|
const res = await provider.execute(prInfo, providerConfig, dependencyResults, sessionInfo);
|
|
8582
|
+
try {
|
|
8583
|
+
currentRouteOutput = res?.output;
|
|
8584
|
+
} catch {
|
|
8585
|
+
}
|
|
6771
8586
|
const hasSoftFailure = (res.issues || []).some(
|
|
6772
8587
|
(i) => i.severity === "error" || i.severity === "critical"
|
|
6773
8588
|
);
|
|
@@ -6786,6 +8601,12 @@ ${expr}
|
|
|
6786
8601
|
runList = Array.from(new Set(runList));
|
|
6787
8602
|
if (debug) log2(`\u{1F527} Debug: on_fail.run (soft) list = [${runList.join(", ")}]`);
|
|
6788
8603
|
if (runList.length > 0) {
|
|
8604
|
+
try {
|
|
8605
|
+
(init_logger(), __toCommonJS(logger_exports)).logger.info(
|
|
8606
|
+
`\u25B6 on_fail.run: scheduling [${runList.join(", ")}] after '${checkName}'`
|
|
8607
|
+
);
|
|
8608
|
+
} catch {
|
|
8609
|
+
}
|
|
6789
8610
|
loopCount++;
|
|
6790
8611
|
if (loopCount > maxLoops) {
|
|
6791
8612
|
throw new Error(
|
|
@@ -6801,6 +8622,12 @@ ${expr}
|
|
|
6801
8622
|
if (!target && onFail.goto) target = onFail.goto;
|
|
6802
8623
|
if (debug) log2(`\u{1F527} Debug: on_fail.goto (soft) target = ${target}`);
|
|
6803
8624
|
if (target) {
|
|
8625
|
+
try {
|
|
8626
|
+
(init_logger(), __toCommonJS(logger_exports)).logger.info(
|
|
8627
|
+
`\u21AA on_fail.goto: jumping to '${target}' from '${checkName}'`
|
|
8628
|
+
);
|
|
8629
|
+
} catch {
|
|
8630
|
+
}
|
|
6804
8631
|
if (!allAncestors.includes(target)) {
|
|
6805
8632
|
if (debug)
|
|
6806
8633
|
log2(
|
|
@@ -6813,7 +8640,7 @@ ${expr}
|
|
|
6813
8640
|
`Routing loop budget exceeded (max_loops=${maxLoops}) during on_fail goto`
|
|
6814
8641
|
);
|
|
6815
8642
|
}
|
|
6816
|
-
await executeNamedCheckInline(target);
|
|
8643
|
+
await executeNamedCheckInline(target, { eventOverride: onFail.goto_event });
|
|
6817
8644
|
}
|
|
6818
8645
|
}
|
|
6819
8646
|
const retryMax = onFail.retry?.max ?? 0;
|
|
@@ -6836,10 +8663,17 @@ ${expr}
|
|
|
6836
8663
|
return res;
|
|
6837
8664
|
}
|
|
6838
8665
|
let needRerun = false;
|
|
8666
|
+
let rerunEventOverride;
|
|
6839
8667
|
if (onSuccess) {
|
|
6840
8668
|
const dynamicRun = await evalRunJs(onSuccess.run_js);
|
|
6841
8669
|
const runList = [...onSuccess.run || [], ...dynamicRun].filter(Boolean);
|
|
6842
8670
|
if (runList.length > 0) {
|
|
8671
|
+
try {
|
|
8672
|
+
(init_logger(), __toCommonJS(logger_exports)).logger.info(
|
|
8673
|
+
`\u25B6 on_success.run: scheduling [${Array.from(new Set(runList)).join(", ")}] after '${checkName}'`
|
|
8674
|
+
);
|
|
8675
|
+
} catch {
|
|
8676
|
+
}
|
|
6843
8677
|
loopCount++;
|
|
6844
8678
|
if (loopCount > maxLoops) {
|
|
6845
8679
|
throw new Error(
|
|
@@ -6849,15 +8683,106 @@ ${expr}
|
|
|
6849
8683
|
for (const stepId of Array.from(new Set(runList))) {
|
|
6850
8684
|
await executeNamedCheckInline(stepId);
|
|
6851
8685
|
}
|
|
8686
|
+
} else {
|
|
8687
|
+
try {
|
|
8688
|
+
const assoc = resolveAssociationFromEvent(
|
|
8689
|
+
prInfo?.eventContext,
|
|
8690
|
+
prInfo.authorAssociation
|
|
8691
|
+
);
|
|
8692
|
+
const perms = createPermissionHelpers(assoc, detectLocalMode());
|
|
8693
|
+
const allowedMember = perms.hasMinPermission("MEMBER");
|
|
8694
|
+
let intent;
|
|
8695
|
+
try {
|
|
8696
|
+
intent = res?.output?.intent;
|
|
8697
|
+
} catch {
|
|
8698
|
+
}
|
|
8699
|
+
(init_logger(), __toCommonJS(logger_exports)).logger.info(
|
|
8700
|
+
`\u23ED on_success.run: none after '${checkName}' (event=${prInfo.eventType || "manual"}, intent=${intent || "n/a"}, assoc=${assoc || "unknown"}, memberOrHigher=${allowedMember})`
|
|
8701
|
+
);
|
|
8702
|
+
} catch {
|
|
8703
|
+
}
|
|
6852
8704
|
}
|
|
6853
8705
|
let target = await evalGotoJs(onSuccess.goto_js);
|
|
6854
8706
|
if (!target && onSuccess.goto) target = onSuccess.goto;
|
|
6855
8707
|
if (target) {
|
|
8708
|
+
try {
|
|
8709
|
+
(init_logger(), __toCommonJS(logger_exports)).logger.info(
|
|
8710
|
+
`\u21AA on_success.goto: jumping to '${target}' from '${checkName}'`
|
|
8711
|
+
);
|
|
8712
|
+
} catch {
|
|
8713
|
+
}
|
|
6856
8714
|
if (!allAncestors.includes(target)) {
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
8715
|
+
const prevEventOverride2 = this.routingEventOverride;
|
|
8716
|
+
if (onSuccess.goto_event) {
|
|
8717
|
+
this.routingEventOverride = onSuccess.goto_event;
|
|
8718
|
+
}
|
|
8719
|
+
try {
|
|
8720
|
+
const cfgChecks = config?.checks || {};
|
|
8721
|
+
const forwardSet = /* @__PURE__ */ new Set();
|
|
8722
|
+
if (cfgChecks[target]) forwardSet.add(target);
|
|
8723
|
+
const dependsOn = (name, root) => {
|
|
8724
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8725
|
+
const dfs = (n) => {
|
|
8726
|
+
if (seen.has(n)) return false;
|
|
8727
|
+
seen.add(n);
|
|
8728
|
+
const deps = cfgChecks[n]?.depends_on || [];
|
|
8729
|
+
if (deps.includes(root)) return true;
|
|
8730
|
+
return deps.some((d) => dfs(d));
|
|
8731
|
+
};
|
|
8732
|
+
return dfs(name);
|
|
8733
|
+
};
|
|
8734
|
+
const ev = onSuccess.goto_event || prInfo.eventType || "issue_comment";
|
|
8735
|
+
for (const name of Object.keys(cfgChecks)) {
|
|
8736
|
+
if (name === target) continue;
|
|
8737
|
+
const onArr = cfgChecks[name]?.on;
|
|
8738
|
+
const eventMatches = !onArr || Array.isArray(onArr) && onArr.includes(ev);
|
|
8739
|
+
if (!eventMatches) continue;
|
|
8740
|
+
if (dependsOn(name, target)) forwardSet.add(name);
|
|
8741
|
+
}
|
|
8742
|
+
const order = [];
|
|
8743
|
+
const inSet = (n) => forwardSet.has(n);
|
|
8744
|
+
const tempMarks = /* @__PURE__ */ new Set();
|
|
8745
|
+
const permMarks = /* @__PURE__ */ new Set();
|
|
8746
|
+
const stack = [];
|
|
8747
|
+
const visit = (n) => {
|
|
8748
|
+
if (permMarks.has(n)) return;
|
|
8749
|
+
if (tempMarks.has(n)) {
|
|
8750
|
+
const idx = stack.indexOf(n);
|
|
8751
|
+
const cyclePath = idx >= 0 ? [...stack.slice(idx), n] : [n];
|
|
8752
|
+
throw new Error(
|
|
8753
|
+
`Cycle detected in forward-run dependency subset: ${cyclePath.join(" -> ")}`
|
|
8754
|
+
);
|
|
8755
|
+
}
|
|
8756
|
+
tempMarks.add(n);
|
|
8757
|
+
stack.push(n);
|
|
8758
|
+
const deps = (cfgChecks[n]?.depends_on || []).filter(inSet);
|
|
8759
|
+
for (const d of deps) visit(d);
|
|
8760
|
+
stack.pop();
|
|
8761
|
+
tempMarks.delete(n);
|
|
8762
|
+
permMarks.add(n);
|
|
8763
|
+
order.push(n);
|
|
8764
|
+
};
|
|
8765
|
+
for (const n of forwardSet) visit(n);
|
|
8766
|
+
for (const stepId of order) {
|
|
8767
|
+
if (!this.executionStats.has(stepId)) this.initializeCheckStats(stepId);
|
|
8768
|
+
const childStart = this.recordIterationStart(stepId);
|
|
8769
|
+
const childRes = await executeNamedCheckInline(stepId, {
|
|
8770
|
+
eventOverride: onSuccess.goto_event
|
|
8771
|
+
});
|
|
8772
|
+
const childIssues = (childRes.issues || []).map((i) => ({ ...i }));
|
|
8773
|
+
const childSuccess = !this.hasFatal(childIssues);
|
|
8774
|
+
const childOutput = childRes?.output;
|
|
8775
|
+
this.recordIterationComplete(
|
|
8776
|
+
stepId,
|
|
8777
|
+
childStart,
|
|
8778
|
+
childSuccess,
|
|
8779
|
+
childIssues,
|
|
8780
|
+
childOutput
|
|
8781
|
+
);
|
|
8782
|
+
}
|
|
8783
|
+
} finally {
|
|
8784
|
+
this.routingEventOverride = prevEventOverride2;
|
|
8785
|
+
}
|
|
6861
8786
|
} else {
|
|
6862
8787
|
loopCount++;
|
|
6863
8788
|
if (loopCount > maxLoops) {
|
|
@@ -6865,14 +8790,22 @@ ${expr}
|
|
|
6865
8790
|
`Routing loop budget exceeded (max_loops=${maxLoops}) during on_success goto`
|
|
6866
8791
|
);
|
|
6867
8792
|
}
|
|
6868
|
-
await executeNamedCheckInline(target);
|
|
8793
|
+
await executeNamedCheckInline(target, { eventOverride: onSuccess.goto_event });
|
|
6869
8794
|
needRerun = true;
|
|
8795
|
+
rerunEventOverride = onSuccess.goto_event;
|
|
6870
8796
|
}
|
|
6871
8797
|
}
|
|
6872
8798
|
}
|
|
6873
8799
|
if (needRerun) {
|
|
6874
8800
|
if (debug) log2(`\u{1F504} Debug: Re-running '${checkName}' after on_success.goto`);
|
|
8801
|
+
try {
|
|
8802
|
+
(init_logger(), __toCommonJS(logger_exports)).logger.info(`\u{1F501} Re-running '${checkName}' after goto`);
|
|
8803
|
+
} catch {
|
|
8804
|
+
}
|
|
8805
|
+
const prev = this.routingEventOverride;
|
|
8806
|
+
if (rerunEventOverride) this.routingEventOverride = rerunEventOverride;
|
|
6875
8807
|
attempt++;
|
|
8808
|
+
this.routingEventOverride = prev;
|
|
6876
8809
|
continue;
|
|
6877
8810
|
}
|
|
6878
8811
|
return res;
|
|
@@ -6885,6 +8818,12 @@ ${expr}
|
|
|
6885
8818
|
let runList = [...onFail.run || [], ...dynamicRun].filter(Boolean);
|
|
6886
8819
|
runList = Array.from(new Set(runList));
|
|
6887
8820
|
if (runList.length > 0) {
|
|
8821
|
+
try {
|
|
8822
|
+
(init_logger(), __toCommonJS(logger_exports)).logger.info(
|
|
8823
|
+
`\u25B6 on_fail.run: scheduling [${runList.join(", ")}] after '${checkName}'`
|
|
8824
|
+
);
|
|
8825
|
+
} catch {
|
|
8826
|
+
}
|
|
6888
8827
|
loopCount++;
|
|
6889
8828
|
if (loopCount > maxLoops) {
|
|
6890
8829
|
throw new Error(
|
|
@@ -6899,6 +8838,12 @@ ${expr}
|
|
|
6899
8838
|
let target = await evalGotoJs(onFail.goto_js, lastError);
|
|
6900
8839
|
if (!target && onFail.goto) target = onFail.goto;
|
|
6901
8840
|
if (target) {
|
|
8841
|
+
try {
|
|
8842
|
+
(init_logger(), __toCommonJS(logger_exports)).logger.info(
|
|
8843
|
+
`\u21AA on_fail.goto: jumping to '${target}' from '${checkName}'`
|
|
8844
|
+
);
|
|
8845
|
+
} catch {
|
|
8846
|
+
}
|
|
6902
8847
|
if (!allAncestors.includes(target)) {
|
|
6903
8848
|
if (debug)
|
|
6904
8849
|
log2(
|
|
@@ -6911,7 +8856,7 @@ ${expr}
|
|
|
6911
8856
|
`Routing loop budget exceeded (max_loops=${maxLoops}) during on_fail goto`
|
|
6912
8857
|
);
|
|
6913
8858
|
}
|
|
6914
|
-
await executeNamedCheckInline(target);
|
|
8859
|
+
await executeNamedCheckInline(target, { eventOverride: onFail.goto_event });
|
|
6915
8860
|
}
|
|
6916
8861
|
}
|
|
6917
8862
|
const retryMax = onFail.retry?.max ?? 0;
|
|
@@ -6988,6 +8933,11 @@ ${expr}
|
|
|
6988
8933
|
const startTime = Date.now();
|
|
6989
8934
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
6990
8935
|
try {
|
|
8936
|
+
if (options.config?.memory) {
|
|
8937
|
+
const memoryStore = MemoryStore.getInstance(options.config.memory);
|
|
8938
|
+
await memoryStore.initialize();
|
|
8939
|
+
logger.debug("Memory store initialized");
|
|
8940
|
+
}
|
|
6991
8941
|
this.webhookContext = options.webhookContext;
|
|
6992
8942
|
const logFn = (msg) => logger.info(msg);
|
|
6993
8943
|
if (options.githubChecks?.enabled && options.githubChecks.octokit) {
|
|
@@ -7284,6 +9234,21 @@ ${expr}
|
|
|
7284
9234
|
);
|
|
7285
9235
|
}
|
|
7286
9236
|
checks = tagFilteredChecks;
|
|
9237
|
+
if (!this.actionContext) {
|
|
9238
|
+
try {
|
|
9239
|
+
const repoEnv = process.env.GITHUB_REPOSITORY || "";
|
|
9240
|
+
const [owner, repo] = repoEnv.split("/");
|
|
9241
|
+
const token = process.env["INPUT_GITHUB-TOKEN"] || process.env["GITHUB_TOKEN"];
|
|
9242
|
+
if (owner && repo) {
|
|
9243
|
+
this.actionContext = { owner, repo };
|
|
9244
|
+
if (token) {
|
|
9245
|
+
const { Octokit } = await import("@octokit/rest");
|
|
9246
|
+
this.actionContext.octokit = new Octokit({ auth: token });
|
|
9247
|
+
}
|
|
9248
|
+
}
|
|
9249
|
+
} catch {
|
|
9250
|
+
}
|
|
9251
|
+
}
|
|
7287
9252
|
if (checks.length === 0) {
|
|
7288
9253
|
logger.warn("\u26A0\uFE0F No checks remain after tag filtering");
|
|
7289
9254
|
return {
|
|
@@ -7298,10 +9263,14 @@ ${expr}
|
|
|
7298
9263
|
const checkConfig = config.checks[checkName];
|
|
7299
9264
|
return checkConfig?.depends_on && checkConfig.depends_on.length > 0;
|
|
7300
9265
|
});
|
|
7301
|
-
|
|
9266
|
+
const hasRouting = checks.some((checkName) => {
|
|
9267
|
+
const c = config.checks[checkName];
|
|
9268
|
+
return Boolean(c?.on_success || c?.on_fail);
|
|
9269
|
+
});
|
|
9270
|
+
if (checks.length > 1 || hasDependencies || hasRouting) {
|
|
7302
9271
|
if (debug) {
|
|
7303
9272
|
logger.debug(
|
|
7304
|
-
`\u{1F527} Debug: Using grouped dependency-aware execution for ${checks.length} checks (has dependencies: ${hasDependencies})`
|
|
9273
|
+
`\u{1F527} Debug: Using grouped dependency-aware execution for ${checks.length} checks (has dependencies: ${hasDependencies}, has routing: ${hasRouting})`
|
|
7305
9274
|
);
|
|
7306
9275
|
}
|
|
7307
9276
|
return await this.executeGroupedDependencyAwareChecks(
|
|
@@ -7384,7 +9353,12 @@ ${expr}
|
|
|
7384
9353
|
}
|
|
7385
9354
|
}
|
|
7386
9355
|
if (config && (config.fail_if || checkConfig.fail_if)) {
|
|
7387
|
-
const failureResults = await this.evaluateFailureConditions(
|
|
9356
|
+
const failureResults = await this.evaluateFailureConditions(
|
|
9357
|
+
checkName,
|
|
9358
|
+
result,
|
|
9359
|
+
config,
|
|
9360
|
+
prInfo
|
|
9361
|
+
);
|
|
7388
9362
|
if (failureResults.length > 0) {
|
|
7389
9363
|
const failureIssues = failureResults.filter((f) => f.failed).map((f) => ({
|
|
7390
9364
|
file: "system",
|
|
@@ -7402,6 +9376,7 @@ ${expr}
|
|
|
7402
9376
|
checkName,
|
|
7403
9377
|
content,
|
|
7404
9378
|
group: checkConfig.group || "default",
|
|
9379
|
+
output: result.output,
|
|
7405
9380
|
debug: result.debug,
|
|
7406
9381
|
issues: result.issues
|
|
7407
9382
|
// Include structured issues
|
|
@@ -7487,8 +9462,24 @@ ${expr}
|
|
|
7487
9462
|
*/
|
|
7488
9463
|
async convertReviewSummaryToGroupedResults(reviewSummary, checks, config, prInfo) {
|
|
7489
9464
|
const groupedResults = {};
|
|
7490
|
-
const
|
|
7491
|
-
|
|
9465
|
+
const agg = reviewSummary;
|
|
9466
|
+
const contentMap = agg.__contents;
|
|
9467
|
+
const outputMap = agg.__outputs;
|
|
9468
|
+
const allCheckNames = [];
|
|
9469
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9470
|
+
const pushUnique = (n) => {
|
|
9471
|
+
if (!n) return;
|
|
9472
|
+
if (!seen.has(n)) {
|
|
9473
|
+
seen.add(n);
|
|
9474
|
+
allCheckNames.push(n);
|
|
9475
|
+
}
|
|
9476
|
+
};
|
|
9477
|
+
for (const n of checks) pushUnique(n);
|
|
9478
|
+
if (contentMap) for (const n of Object.keys(contentMap)) pushUnique(n);
|
|
9479
|
+
if (outputMap) for (const n of Object.keys(outputMap)) pushUnique(n);
|
|
9480
|
+
for (const issue of reviewSummary.issues || []) pushUnique(issue.checkName);
|
|
9481
|
+
if (Array.isArray(agg.__executed)) for (const n of agg.__executed) pushUnique(n);
|
|
9482
|
+
for (const checkName of allCheckNames) {
|
|
7492
9483
|
const checkConfig = config?.checks?.[checkName];
|
|
7493
9484
|
if (!checkConfig) continue;
|
|
7494
9485
|
const checkIssues = (reviewSummary.issues || []).filter(
|
|
@@ -7499,17 +9490,38 @@ ${expr}
|
|
|
7499
9490
|
debug: reviewSummary.debug
|
|
7500
9491
|
};
|
|
7501
9492
|
if (contentMap?.[checkName]) {
|
|
7502
|
-
|
|
7503
|
-
|
|
9493
|
+
checkSummary.content = contentMap[checkName];
|
|
9494
|
+
}
|
|
9495
|
+
if (outputMap && Object.prototype.hasOwnProperty.call(outputMap, checkName)) {
|
|
9496
|
+
checkSummary.output = outputMap[checkName];
|
|
9497
|
+
}
|
|
9498
|
+
let content = "";
|
|
9499
|
+
let issuesForCheck = [...checkIssues];
|
|
9500
|
+
try {
|
|
9501
|
+
content = await this.renderCheckContent(checkName, checkSummary, checkConfig, prInfo);
|
|
9502
|
+
} catch (e) {
|
|
9503
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
9504
|
+
console.error(`\u274C Failed to render content for check '${checkName}': ${msg}`);
|
|
9505
|
+
issuesForCheck = [
|
|
9506
|
+
...issuesForCheck,
|
|
9507
|
+
{
|
|
9508
|
+
file: "system",
|
|
9509
|
+
line: 0,
|
|
9510
|
+
ruleId: `${checkName}/render-error`,
|
|
9511
|
+
message: `Template rendering failed: ${msg}`,
|
|
9512
|
+
severity: "error",
|
|
9513
|
+
category: "logic"
|
|
9514
|
+
}
|
|
9515
|
+
];
|
|
7504
9516
|
}
|
|
7505
|
-
const content = await this.renderCheckContent(checkName, checkSummary, checkConfig, prInfo);
|
|
7506
9517
|
const checkResult = {
|
|
7507
9518
|
checkName,
|
|
7508
9519
|
content,
|
|
7509
9520
|
group: checkConfig.group || "default",
|
|
9521
|
+
output: checkSummary.output,
|
|
7510
9522
|
debug: reviewSummary.debug,
|
|
7511
|
-
issues:
|
|
7512
|
-
// Include structured issues
|
|
9523
|
+
issues: issuesForCheck
|
|
9524
|
+
// Include structured issues + rendering error if any
|
|
7513
9525
|
};
|
|
7514
9526
|
const group = checkResult.group;
|
|
7515
9527
|
if (!groupedResults[group]) {
|
|
@@ -7575,13 +9587,20 @@ ${expr}
|
|
|
7575
9587
|
* @returns true if the check should run, false if it should be skipped
|
|
7576
9588
|
*/
|
|
7577
9589
|
async evaluateCheckCondition(checkName, condition, prInfo, results, debug) {
|
|
9590
|
+
const override = this.routingEventOverride;
|
|
9591
|
+
const eventName = override ? override.startsWith("pr_") ? "pull_request" : override === "issue_comment" ? "issue_comment" : override.startsWith("issue_") ? "issues" : "manual" : "issue_comment";
|
|
9592
|
+
const commenterAssoc = resolveAssociationFromEvent(
|
|
9593
|
+
prInfo?.eventContext,
|
|
9594
|
+
prInfo.authorAssociation
|
|
9595
|
+
);
|
|
7578
9596
|
const shouldRun = await this.failureEvaluator.evaluateIfCondition(checkName, condition, {
|
|
7579
9597
|
branch: prInfo.head,
|
|
7580
9598
|
baseBranch: prInfo.base,
|
|
7581
9599
|
filesChanged: prInfo.files.map((f) => f.filename),
|
|
7582
|
-
event:
|
|
9600
|
+
event: eventName,
|
|
7583
9601
|
environment: getSafeEnvironmentVariables(),
|
|
7584
|
-
previousResults: results
|
|
9602
|
+
previousResults: results,
|
|
9603
|
+
authorAssociation: commenterAssoc
|
|
7585
9604
|
});
|
|
7586
9605
|
if (!shouldRun && debug) {
|
|
7587
9606
|
logger.debug(`\u{1F527} Debug: Skipping check '${checkName}' - if condition evaluated to false`);
|
|
@@ -7596,7 +9615,7 @@ ${expr}
|
|
|
7596
9615
|
if (typeof directContent === "string" && directContent.trim()) {
|
|
7597
9616
|
return directContent.trim();
|
|
7598
9617
|
}
|
|
7599
|
-
const { createExtendedLiquid: createExtendedLiquid2 } = await import("./liquid-extensions-
|
|
9618
|
+
const { createExtendedLiquid: createExtendedLiquid2 } = await import("./liquid-extensions-AFKRYROF.mjs");
|
|
7600
9619
|
const fs5 = await import("fs/promises");
|
|
7601
9620
|
const path5 = await import("path");
|
|
7602
9621
|
const liquid = createExtendedLiquid2({
|
|
@@ -7615,6 +9634,7 @@ ${expr}
|
|
|
7615
9634
|
schemaName = checkConfig.schema || "plain";
|
|
7616
9635
|
}
|
|
7617
9636
|
let templateContent;
|
|
9637
|
+
let enrichAssistantContext = false;
|
|
7618
9638
|
if (checkConfig.template) {
|
|
7619
9639
|
if (checkConfig.template.content) {
|
|
7620
9640
|
templateContent = checkConfig.template.content;
|
|
@@ -7633,16 +9653,109 @@ ${expr}
|
|
|
7633
9653
|
}
|
|
7634
9654
|
const templatePath = path5.join(__dirname, `../output/${sanitizedSchema}/template.liquid`);
|
|
7635
9655
|
templateContent = await fs5.readFile(templatePath, "utf-8");
|
|
9656
|
+
if (sanitizedSchema === "issue-assistant") {
|
|
9657
|
+
enrichAssistantContext = true;
|
|
9658
|
+
}
|
|
7636
9659
|
}
|
|
7637
9660
|
const filteredIssues = (reviewSummary.issues || []).filter(
|
|
7638
9661
|
(issue) => !(issue.file === "system" && issue.line === 0)
|
|
7639
9662
|
);
|
|
7640
9663
|
const templateData = {
|
|
7641
9664
|
issues: filteredIssues,
|
|
7642
|
-
checkName
|
|
9665
|
+
checkName,
|
|
9666
|
+
// Expose structured output for custom schemas/templates (e.g., overview)
|
|
9667
|
+
// This allows templates to render fields like output.text or output.tags
|
|
9668
|
+
output: reviewSummary.output
|
|
7643
9669
|
};
|
|
7644
|
-
|
|
7645
|
-
|
|
9670
|
+
if (enrichAssistantContext) {
|
|
9671
|
+
let authorAssociation;
|
|
9672
|
+
let eventName = "manual";
|
|
9673
|
+
let eventAction;
|
|
9674
|
+
try {
|
|
9675
|
+
const anyInfo = _prInfo;
|
|
9676
|
+
authorAssociation = resolveAssociationFromEvent(
|
|
9677
|
+
anyInfo?.eventContext,
|
|
9678
|
+
anyInfo?.authorAssociation
|
|
9679
|
+
);
|
|
9680
|
+
eventName = anyInfo?.eventContext?.event_name || anyInfo?.eventType || "manual";
|
|
9681
|
+
eventAction = anyInfo?.eventContext?.action;
|
|
9682
|
+
} catch {
|
|
9683
|
+
}
|
|
9684
|
+
templateData.authorAssociation = authorAssociation;
|
|
9685
|
+
templateData.event = { name: eventName, action: eventAction };
|
|
9686
|
+
}
|
|
9687
|
+
const { withPermissionsContext } = await import("./liquid-extensions-AFKRYROF.mjs");
|
|
9688
|
+
let authorAssociationForFilters;
|
|
9689
|
+
try {
|
|
9690
|
+
const anyInfo = _prInfo;
|
|
9691
|
+
authorAssociationForFilters = resolveAssociationFromEvent(
|
|
9692
|
+
anyInfo?.eventContext,
|
|
9693
|
+
anyInfo?.authorAssociation
|
|
9694
|
+
);
|
|
9695
|
+
} catch {
|
|
9696
|
+
}
|
|
9697
|
+
let rendered;
|
|
9698
|
+
if (typeof withPermissionsContext === "function") {
|
|
9699
|
+
rendered = await withPermissionsContext(
|
|
9700
|
+
{ authorAssociation: authorAssociationForFilters },
|
|
9701
|
+
async () => await liquid.parseAndRender(templateContent, templateData)
|
|
9702
|
+
);
|
|
9703
|
+
if (rendered === void 0 || rendered === null) {
|
|
9704
|
+
rendered = await liquid.parseAndRender(templateContent, templateData);
|
|
9705
|
+
}
|
|
9706
|
+
} else {
|
|
9707
|
+
rendered = await liquid.parseAndRender(templateContent, templateData);
|
|
9708
|
+
}
|
|
9709
|
+
const finalRendered = rendered.trim();
|
|
9710
|
+
try {
|
|
9711
|
+
const { emitMermaidFromMarkdown } = await import("./mermaid-telemetry-LZGDD35I.mjs");
|
|
9712
|
+
emitMermaidFromMarkdown(checkName, finalRendered, "content");
|
|
9713
|
+
} catch {
|
|
9714
|
+
}
|
|
9715
|
+
return finalRendered;
|
|
9716
|
+
}
|
|
9717
|
+
/**
|
|
9718
|
+
* Attempt to elevate an issue/issue_comment context to full PR context when routing via goto_event.
|
|
9719
|
+
* Returns a new PRInfo with files/diff when possible; otherwise returns null.
|
|
9720
|
+
*/
|
|
9721
|
+
async elevateContextToPullRequest(prInfo, targetEvent, log2, debug) {
|
|
9722
|
+
try {
|
|
9723
|
+
if (targetEvent !== "pr_opened" && targetEvent !== "pr_updated") return null;
|
|
9724
|
+
const isIssueContext = prInfo.isIssue === true;
|
|
9725
|
+
const ctx = prInfo.eventContext || {};
|
|
9726
|
+
const isPRThread = Boolean(ctx?.issue?.pull_request);
|
|
9727
|
+
if (!isIssueContext || !isPRThread) return null;
|
|
9728
|
+
let owner = this.actionContext?.owner;
|
|
9729
|
+
let repo = this.actionContext?.repo;
|
|
9730
|
+
if (!owner || !repo) {
|
|
9731
|
+
const repoEnv = process.env.GITHUB_REPOSITORY || "";
|
|
9732
|
+
[owner, repo] = repoEnv.split("/");
|
|
9733
|
+
}
|
|
9734
|
+
if (!owner || !repo) return null;
|
|
9735
|
+
const prNumber = ctx?.issue?.number || prInfo.number;
|
|
9736
|
+
if (!prNumber) return null;
|
|
9737
|
+
let octokit = this.actionContext?.octokit;
|
|
9738
|
+
if (!octokit) {
|
|
9739
|
+
const token = process.env["INPUT_GITHUB-TOKEN"] || process.env["GITHUB_TOKEN"];
|
|
9740
|
+
if (!token) return null;
|
|
9741
|
+
const { Octokit } = await import("@octokit/rest");
|
|
9742
|
+
octokit = new Octokit({ auth: token });
|
|
9743
|
+
}
|
|
9744
|
+
const analyzer = new PRAnalyzer(octokit);
|
|
9745
|
+
const elevated = await analyzer.fetchPRDiff(owner, repo, prNumber, void 0, targetEvent);
|
|
9746
|
+
elevated.eventContext = prInfo.eventContext || ctx;
|
|
9747
|
+
elevated.isPRContext = true;
|
|
9748
|
+
elevated.includeCodeContext = true;
|
|
9749
|
+
if (debug)
|
|
9750
|
+
log2?.(`\u{1F527} Debug: Elevated context to PR #${prNumber} for goto_event=${targetEvent}`);
|
|
9751
|
+
return elevated;
|
|
9752
|
+
} catch (e) {
|
|
9753
|
+
if (debug) {
|
|
9754
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
9755
|
+
log2?.(`\u26A0\uFE0F Debug: Context elevation to PR failed: ${msg}`);
|
|
9756
|
+
}
|
|
9757
|
+
return null;
|
|
9758
|
+
}
|
|
7646
9759
|
}
|
|
7647
9760
|
/**
|
|
7648
9761
|
* Execute multiple checks with dependency awareness - intelligently parallel and sequential
|
|
@@ -7828,10 +9941,12 @@ ${expr}
|
|
|
7828
9941
|
message: extendedCheckConfig.message,
|
|
7829
9942
|
env: checkConfig.env,
|
|
7830
9943
|
forEach: checkConfig.forEach,
|
|
9944
|
+
// Pass through any provider-specific keys (e.g., op/values for github provider)
|
|
9945
|
+
...checkConfig,
|
|
7831
9946
|
ai: {
|
|
9947
|
+
...checkConfig.ai || {},
|
|
7832
9948
|
timeout: timeout || 6e5,
|
|
7833
|
-
debug
|
|
7834
|
-
...checkConfig.ai || {}
|
|
9949
|
+
debug
|
|
7835
9950
|
}
|
|
7836
9951
|
};
|
|
7837
9952
|
const dependencyResults = /* @__PURE__ */ new Map();
|
|
@@ -8008,12 +10123,21 @@ ${expr}
|
|
|
8008
10123
|
transform_js: childCfg.transform_js,
|
|
8009
10124
|
env: childCfg.env,
|
|
8010
10125
|
forEach: childCfg.forEach,
|
|
10126
|
+
// Include provider-specific keys like op/values for non-AI providers
|
|
10127
|
+
...childCfg,
|
|
8011
10128
|
ai: {
|
|
10129
|
+
...childCfg.ai || {},
|
|
8012
10130
|
timeout: timeout || 6e5,
|
|
8013
|
-
debug
|
|
8014
|
-
...childCfg.ai || {}
|
|
10131
|
+
debug
|
|
8015
10132
|
}
|
|
8016
10133
|
};
|
|
10134
|
+
try {
|
|
10135
|
+
emitNdjsonSpanWithEvents("visor.check", { "visor.check.id": checkName }, [
|
|
10136
|
+
{ name: "check.started" },
|
|
10137
|
+
{ name: "check.completed" }
|
|
10138
|
+
]);
|
|
10139
|
+
} catch {
|
|
10140
|
+
}
|
|
8017
10141
|
const childDepResults = /* @__PURE__ */ new Map();
|
|
8018
10142
|
const childAllDeps = DependencyResolver.getAllDependencies(
|
|
8019
10143
|
childName,
|
|
@@ -8136,6 +10260,18 @@ ${expr}
|
|
|
8136
10260
|
}
|
|
8137
10261
|
};
|
|
8138
10262
|
const itemTasks = forEachItems.map((item, itemIndex) => async () => {
|
|
10263
|
+
try {
|
|
10264
|
+
emitNdjsonSpanWithEvents(
|
|
10265
|
+
"visor.foreach.item",
|
|
10266
|
+
{
|
|
10267
|
+
"visor.check.id": checkName,
|
|
10268
|
+
"visor.foreach.index": itemIndex,
|
|
10269
|
+
"visor.foreach.total": forEachItems.length
|
|
10270
|
+
},
|
|
10271
|
+
[]
|
|
10272
|
+
);
|
|
10273
|
+
} catch {
|
|
10274
|
+
}
|
|
8139
10275
|
const forEachDependencyResults = /* @__PURE__ */ new Map();
|
|
8140
10276
|
for (const [depName, depResult] of dependencyResults) {
|
|
8141
10277
|
if (forEachParents.includes(depName)) {
|
|
@@ -8738,6 +10874,13 @@ ${expr}
|
|
|
8738
10874
|
debug,
|
|
8739
10875
|
results
|
|
8740
10876
|
);
|
|
10877
|
+
try {
|
|
10878
|
+
emitNdjsonSpanWithEvents("visor.check", { "visor.check.id": checkName }, [
|
|
10879
|
+
{ name: "check.started" },
|
|
10880
|
+
{ name: "check.completed" }
|
|
10881
|
+
]);
|
|
10882
|
+
} catch {
|
|
10883
|
+
}
|
|
8741
10884
|
if (config && (config.fail_if || checkConfig.fail_if)) {
|
|
8742
10885
|
const failureResults = await this.evaluateFailureConditions(
|
|
8743
10886
|
checkName,
|
|
@@ -8905,6 +11048,13 @@ ${error.stack || ""}` : String(error);
|
|
|
8905
11048
|
reviewSummaryWithOutput.forEachItems = normalizedOutput;
|
|
8906
11049
|
reviewSummaryWithOutput.isForEach = true;
|
|
8907
11050
|
}
|
|
11051
|
+
try {
|
|
11052
|
+
emitNdjsonSpanWithEvents("visor.check", { "visor.check.id": checkName }, [
|
|
11053
|
+
{ name: "check.started" },
|
|
11054
|
+
{ name: "check.completed" }
|
|
11055
|
+
]);
|
|
11056
|
+
} catch {
|
|
11057
|
+
}
|
|
8908
11058
|
results.set(checkName, reviewResult);
|
|
8909
11059
|
} else {
|
|
8910
11060
|
const errorSummary = {
|
|
@@ -9018,6 +11168,12 @@ ${error.stack || ""}` : String(error);
|
|
|
9018
11168
|
`\u{1F527} Debug: Starting check: ${checkName} with prompt type: ${typeof checkConfig.prompt}`
|
|
9019
11169
|
);
|
|
9020
11170
|
if (checkConfig.if) {
|
|
11171
|
+
const override = this.routingEventOverride;
|
|
11172
|
+
const eventName = override ? override.startsWith("pr_") ? "pull_request" : override === "issue_comment" ? "issue_comment" : override.startsWith("issue_") ? "issues" : "manual" : "issue_comment";
|
|
11173
|
+
const commenterAssoc = resolveAssociationFromEvent(
|
|
11174
|
+
prInfo?.eventContext,
|
|
11175
|
+
prInfo.authorAssociation
|
|
11176
|
+
);
|
|
9021
11177
|
const shouldRun = await this.failureEvaluator.evaluateIfCondition(
|
|
9022
11178
|
checkName,
|
|
9023
11179
|
checkConfig.if,
|
|
@@ -9025,11 +11181,12 @@ ${error.stack || ""}` : String(error);
|
|
|
9025
11181
|
branch: prInfo.head,
|
|
9026
11182
|
baseBranch: prInfo.base,
|
|
9027
11183
|
filesChanged: prInfo.files.map((f) => f.filename),
|
|
9028
|
-
event:
|
|
9029
|
-
//
|
|
11184
|
+
event: eventName,
|
|
11185
|
+
// honor routing override if present
|
|
9030
11186
|
environment: getSafeEnvironmentVariables(),
|
|
9031
|
-
previousResults: /* @__PURE__ */ new Map()
|
|
11187
|
+
previousResults: /* @__PURE__ */ new Map(),
|
|
9032
11188
|
// No previous results in parallel execution
|
|
11189
|
+
authorAssociation: commenterAssoc
|
|
9033
11190
|
}
|
|
9034
11191
|
);
|
|
9035
11192
|
if (!shouldRun) {
|
|
@@ -9171,6 +11328,7 @@ ${error.stack || ""}` : String(error);
|
|
|
9171
11328
|
const aggregatedIssues = [];
|
|
9172
11329
|
const debugInfo = [];
|
|
9173
11330
|
const contentMap = {};
|
|
11331
|
+
const outputsMap = {};
|
|
9174
11332
|
const stats = DependencyResolver.getExecutionStats(dependencyGraph);
|
|
9175
11333
|
const executionInfo = [
|
|
9176
11334
|
stoppedEarly ? `\u{1F6D1} Dependency-aware execution stopped early (fail-fast):` : `\u{1F50D} Dependency-aware execution completed:`,
|
|
@@ -9182,6 +11340,7 @@ ${error.stack || ""}` : String(error);
|
|
|
9182
11340
|
stoppedEarly ? ` - Stopped early due to fail-fast behavior` : ``
|
|
9183
11341
|
].filter(Boolean);
|
|
9184
11342
|
debugInfo.push(...executionInfo);
|
|
11343
|
+
const processed = /* @__PURE__ */ new Set();
|
|
9185
11344
|
for (const executionGroup of dependencyGraph.executionOrder) {
|
|
9186
11345
|
for (const checkName of executionGroup.parallel) {
|
|
9187
11346
|
const result = results.get(checkName);
|
|
@@ -9199,6 +11358,7 @@ ${error.stack || ""}` : String(error);
|
|
|
9199
11358
|
`\u2705 Check "${checkName}" completed: ${(result.issues || []).length} issues found (level ${executionGroup.level})`
|
|
9200
11359
|
);
|
|
9201
11360
|
}
|
|
11361
|
+
processed.add(checkName);
|
|
9202
11362
|
const nonInternalIssues = (result.issues || []).filter(
|
|
9203
11363
|
(issue) => !issue.ruleId?.endsWith("/__skipped")
|
|
9204
11364
|
);
|
|
@@ -9208,7 +11368,29 @@ ${error.stack || ""}` : String(error);
|
|
|
9208
11368
|
if (typeof resultContent === "string" && resultContent.trim()) {
|
|
9209
11369
|
contentMap[checkName] = resultContent.trim();
|
|
9210
11370
|
}
|
|
11371
|
+
if (resultSummary.output !== void 0) {
|
|
11372
|
+
outputsMap[checkName] = resultSummary.output;
|
|
11373
|
+
}
|
|
11374
|
+
}
|
|
11375
|
+
}
|
|
11376
|
+
for (const [checkName, result] of results.entries()) {
|
|
11377
|
+
if (processed.has(checkName)) continue;
|
|
11378
|
+
if (!result) continue;
|
|
11379
|
+
const nonInternalIssues = (result.issues || []).filter(
|
|
11380
|
+
(issue) => !issue.ruleId?.endsWith("/__skipped")
|
|
11381
|
+
);
|
|
11382
|
+
aggregatedIssues.push(...nonInternalIssues);
|
|
11383
|
+
const resultSummary = result;
|
|
11384
|
+
const resultContent = resultSummary.content;
|
|
11385
|
+
if (typeof resultContent === "string" && resultContent.trim()) {
|
|
11386
|
+
contentMap[checkName] = resultContent.trim();
|
|
11387
|
+
}
|
|
11388
|
+
if (resultSummary.output !== void 0) {
|
|
11389
|
+
outputsMap[checkName] = resultSummary.output;
|
|
9211
11390
|
}
|
|
11391
|
+
debugInfo.push(
|
|
11392
|
+
`\u2705 (dynamic) Check "${checkName}" included: ${(result.issues || []).length} issues found`
|
|
11393
|
+
);
|
|
9212
11394
|
}
|
|
9213
11395
|
if (debug) {
|
|
9214
11396
|
console.error(
|
|
@@ -9267,6 +11449,10 @@ ${result.debug.rawResponse}`).join("\n\n"),
|
|
|
9267
11449
|
if (Object.keys(contentMap).length > 0) {
|
|
9268
11450
|
summary.__contents = contentMap;
|
|
9269
11451
|
}
|
|
11452
|
+
if (Object.keys(outputsMap).length > 0) {
|
|
11453
|
+
summary.__outputs = outputsMap;
|
|
11454
|
+
}
|
|
11455
|
+
summary.__executed = Array.from(results.keys());
|
|
9270
11456
|
return summary;
|
|
9271
11457
|
}
|
|
9272
11458
|
/**
|
|
@@ -9590,7 +11776,7 @@ ${result.value.result.debug.rawResponse}`;
|
|
|
9590
11776
|
/**
|
|
9591
11777
|
* Evaluate failure conditions for a check result
|
|
9592
11778
|
*/
|
|
9593
|
-
async evaluateFailureConditions(checkName, reviewSummary, config) {
|
|
11779
|
+
async evaluateFailureConditions(checkName, reviewSummary, config, prInfo) {
|
|
9594
11780
|
if (!config) {
|
|
9595
11781
|
return [];
|
|
9596
11782
|
}
|
|
@@ -9609,7 +11795,50 @@ ${result.value.result.debug.rawResponse}`;
|
|
|
9609
11795
|
reviewSummary,
|
|
9610
11796
|
globalFailIf
|
|
9611
11797
|
);
|
|
11798
|
+
try {
|
|
11799
|
+
addEvent("fail_if.evaluated", {
|
|
11800
|
+
check: checkName,
|
|
11801
|
+
scope: "global",
|
|
11802
|
+
name: "global_fail_if",
|
|
11803
|
+
expression: globalFailIf
|
|
11804
|
+
});
|
|
11805
|
+
} catch {
|
|
11806
|
+
}
|
|
9612
11807
|
if (failed) {
|
|
11808
|
+
try {
|
|
11809
|
+
addEvent("fail_if.triggered", {
|
|
11810
|
+
check: checkName,
|
|
11811
|
+
scope: "global",
|
|
11812
|
+
name: "global_fail_if",
|
|
11813
|
+
expression: globalFailIf,
|
|
11814
|
+
severity: "error"
|
|
11815
|
+
});
|
|
11816
|
+
} catch {
|
|
11817
|
+
}
|
|
11818
|
+
try {
|
|
11819
|
+
addFailIfTriggered(checkName, "global");
|
|
11820
|
+
} catch {
|
|
11821
|
+
}
|
|
11822
|
+
try {
|
|
11823
|
+
const { emitNdjsonSpanWithEvents: emitNdjsonSpanWithEvents2 } = (init_fallback_ndjson(), __toCommonJS(fallback_ndjson_exports));
|
|
11824
|
+
emitNdjsonSpanWithEvents2(
|
|
11825
|
+
"visor.fail_if",
|
|
11826
|
+
{ check: checkName, scope: "global", name: "global_fail_if" },
|
|
11827
|
+
[
|
|
11828
|
+
{
|
|
11829
|
+
name: "fail_if.triggered",
|
|
11830
|
+
attrs: {
|
|
11831
|
+
check: checkName,
|
|
11832
|
+
scope: "global",
|
|
11833
|
+
name: "global_fail_if",
|
|
11834
|
+
expression: globalFailIf,
|
|
11835
|
+
severity: "error"
|
|
11836
|
+
}
|
|
11837
|
+
}
|
|
11838
|
+
]
|
|
11839
|
+
);
|
|
11840
|
+
} catch {
|
|
11841
|
+
}
|
|
9613
11842
|
logger.warn(`\u26A0\uFE0F Check "${checkName}" - global fail_if condition met: ${globalFailIf}`);
|
|
9614
11843
|
results.push({
|
|
9615
11844
|
conditionName: "global_fail_if",
|
|
@@ -9631,7 +11860,78 @@ ${result.value.result.debug.rawResponse}`;
|
|
|
9631
11860
|
reviewSummary,
|
|
9632
11861
|
checkFailIf
|
|
9633
11862
|
);
|
|
11863
|
+
try {
|
|
11864
|
+
addEvent("fail_if.evaluated", {
|
|
11865
|
+
check: checkName,
|
|
11866
|
+
scope: "check",
|
|
11867
|
+
name: `${checkName}_fail_if`,
|
|
11868
|
+
expression: checkFailIf
|
|
11869
|
+
});
|
|
11870
|
+
} catch {
|
|
11871
|
+
}
|
|
11872
|
+
try {
|
|
11873
|
+
const { emitNdjsonSpanWithEvents: emitNdjsonSpanWithEvents2 } = (init_fallback_ndjson(), __toCommonJS(fallback_ndjson_exports));
|
|
11874
|
+
emitNdjsonSpanWithEvents2(
|
|
11875
|
+
"visor.fail_if",
|
|
11876
|
+
{ check: checkName, scope: "check", name: `${checkName}_fail_if` },
|
|
11877
|
+
[
|
|
11878
|
+
{
|
|
11879
|
+
name: "fail_if.evaluated",
|
|
11880
|
+
attrs: {
|
|
11881
|
+
check: checkName,
|
|
11882
|
+
scope: "check",
|
|
11883
|
+
name: `${checkName}_fail_if`,
|
|
11884
|
+
expression: checkFailIf
|
|
11885
|
+
}
|
|
11886
|
+
}
|
|
11887
|
+
]
|
|
11888
|
+
);
|
|
11889
|
+
} catch {
|
|
11890
|
+
}
|
|
9634
11891
|
if (failed) {
|
|
11892
|
+
try {
|
|
11893
|
+
addEvent("fail_if.triggered", {
|
|
11894
|
+
check: checkName,
|
|
11895
|
+
scope: "check",
|
|
11896
|
+
name: `${checkName}_fail_if`,
|
|
11897
|
+
expression: checkFailIf,
|
|
11898
|
+
severity: "error"
|
|
11899
|
+
});
|
|
11900
|
+
} catch {
|
|
11901
|
+
}
|
|
11902
|
+
try {
|
|
11903
|
+
addEvent("fail_if.evaluated", {
|
|
11904
|
+
check: checkName,
|
|
11905
|
+
scope: "check",
|
|
11906
|
+
name: `${checkName}_fail_if`,
|
|
11907
|
+
expression: checkFailIf
|
|
11908
|
+
});
|
|
11909
|
+
} catch {
|
|
11910
|
+
}
|
|
11911
|
+
try {
|
|
11912
|
+
addFailIfTriggered(checkName, "check");
|
|
11913
|
+
} catch {
|
|
11914
|
+
}
|
|
11915
|
+
try {
|
|
11916
|
+
const { emitNdjsonSpanWithEvents: emitNdjsonSpanWithEvents2 } = (init_fallback_ndjson(), __toCommonJS(fallback_ndjson_exports));
|
|
11917
|
+
emitNdjsonSpanWithEvents2(
|
|
11918
|
+
"visor.fail_if",
|
|
11919
|
+
{ check: checkName, scope: "check", name: `${checkName}_fail_if` },
|
|
11920
|
+
[
|
|
11921
|
+
{
|
|
11922
|
+
name: "fail_if.triggered",
|
|
11923
|
+
attrs: {
|
|
11924
|
+
check: checkName,
|
|
11925
|
+
scope: "check",
|
|
11926
|
+
name: `${checkName}_fail_if`,
|
|
11927
|
+
expression: checkFailIf,
|
|
11928
|
+
severity: "error"
|
|
11929
|
+
}
|
|
11930
|
+
}
|
|
11931
|
+
]
|
|
11932
|
+
);
|
|
11933
|
+
} catch {
|
|
11934
|
+
}
|
|
9635
11935
|
logger.warn(`\u26A0\uFE0F Check "${checkName}" - fail_if condition met: ${checkFailIf}`);
|
|
9636
11936
|
results.push({
|
|
9637
11937
|
conditionName: `${checkName}_fail_if`,
|
|
@@ -9645,6 +11945,31 @@ ${result.value.result.debug.rawResponse}`;
|
|
|
9645
11945
|
logger.debug(`\u2713 Check "${checkName}" - fail_if condition passed`);
|
|
9646
11946
|
}
|
|
9647
11947
|
}
|
|
11948
|
+
try {
|
|
11949
|
+
const { emitNdjsonSpanWithEvents: emitNdjsonSpanWithEvents2 } = (init_fallback_ndjson(), __toCommonJS(fallback_ndjson_exports));
|
|
11950
|
+
const hadTriggered = results.some((r) => r.failed === true);
|
|
11951
|
+
emitNdjsonSpanWithEvents2(
|
|
11952
|
+
"visor.fail_if",
|
|
11953
|
+
{
|
|
11954
|
+
check: checkName,
|
|
11955
|
+
scope: hadTriggered ? checkFailIf ? "check" : "global" : checkFailIf ? "check" : "global"
|
|
11956
|
+
},
|
|
11957
|
+
[
|
|
11958
|
+
{
|
|
11959
|
+
name: "fail_if.evaluated",
|
|
11960
|
+
attrs: { check: checkName, scope: checkFailIf ? "check" : "global" }
|
|
11961
|
+
}
|
|
11962
|
+
].concat(
|
|
11963
|
+
hadTriggered ? [
|
|
11964
|
+
{
|
|
11965
|
+
name: "fail_if.triggered",
|
|
11966
|
+
attrs: { check: checkName, scope: checkFailIf ? "check" : "global" }
|
|
11967
|
+
}
|
|
11968
|
+
] : []
|
|
11969
|
+
)
|
|
11970
|
+
);
|
|
11971
|
+
} catch {
|
|
11972
|
+
}
|
|
9648
11973
|
return results;
|
|
9649
11974
|
}
|
|
9650
11975
|
const globalConditions = config.failure_conditions;
|
|
@@ -9655,7 +11980,10 @@ ${result.value.result.debug.rawResponse}`;
|
|
|
9655
11980
|
checkGroup,
|
|
9656
11981
|
reviewSummary,
|
|
9657
11982
|
globalConditions,
|
|
9658
|
-
checkConditions
|
|
11983
|
+
checkConditions,
|
|
11984
|
+
void 0,
|
|
11985
|
+
// previousOutputs
|
|
11986
|
+
prInfo?.authorAssociation
|
|
9659
11987
|
);
|
|
9660
11988
|
}
|
|
9661
11989
|
/**
|
|
@@ -9778,6 +12106,7 @@ ${result.value.result.debug.rawResponse}`;
|
|
|
9778
12106
|
{ issues: checkIssues },
|
|
9779
12107
|
options.config
|
|
9780
12108
|
);
|
|
12109
|
+
const execErrorIssue = checkIssues.find((i) => i.ruleId?.startsWith("command/"));
|
|
9781
12110
|
await this.githubCheckService.completeCheckRun(
|
|
9782
12111
|
options.githubChecks.owner,
|
|
9783
12112
|
options.githubChecks.repo,
|
|
@@ -9785,7 +12114,7 @@ ${result.value.result.debug.rawResponse}`;
|
|
|
9785
12114
|
checkName,
|
|
9786
12115
|
failureResults,
|
|
9787
12116
|
checkIssues,
|
|
9788
|
-
void 0,
|
|
12117
|
+
execErrorIssue ? execErrorIssue.message : void 0,
|
|
9789
12118
|
// executionError
|
|
9790
12119
|
prInfo.files.map((f) => f.filename),
|
|
9791
12120
|
// filesChangedInCommit
|
|
@@ -10161,8 +12490,6 @@ ${result.value.result.debug.rawResponse}`;
|
|
|
10161
12490
|
};
|
|
10162
12491
|
|
|
10163
12492
|
export {
|
|
10164
|
-
logger,
|
|
10165
|
-
init_logger,
|
|
10166
12493
|
CheckExecutionEngine
|
|
10167
12494
|
};
|
|
10168
|
-
//# sourceMappingURL=chunk-
|
|
12495
|
+
//# sourceMappingURL=chunk-LJHRU3WQ.mjs.map
|