@probelabs/visor 0.1.131 → 0.1.132-ee
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 +460 -596
- package/action.yml +2 -2
- package/dist/ai-review-service.d.ts +3 -0
- package/dist/ai-review-service.d.ts.map +1 -1
- package/dist/cli-main.d.ts.map +1 -1
- package/dist/config/config-watcher.d.ts +15 -1
- package/dist/config/config-watcher.d.ts.map +1 -1
- package/dist/enterprise/policy/policy-input-builder.d.ts +2 -0
- package/dist/enterprise/policy/policy-input-builder.d.ts.map +1 -1
- package/dist/frontends/slack-frontend.d.ts.map +1 -1
- package/dist/generated/config-schema.d.ts +404 -96
- package/dist/generated/config-schema.d.ts.map +1 -1
- package/dist/generated/config-schema.json +2875 -0
- package/dist/index.js +24782 -8528
- package/dist/providers/ai-check-provider.d.ts +12 -0
- package/dist/providers/ai-check-provider.d.ts.map +1 -1
- package/dist/providers/workflow-check-provider.d.ts.map +1 -1
- package/dist/providers/workflow-tool-executor.d.ts +5 -1
- package/dist/providers/workflow-tool-executor.d.ts.map +1 -1
- package/dist/sdk/{check-provider-registry-S7BMQ2FC.mjs → check-provider-registry-7TCA3NSG.mjs} +6 -6
- package/dist/sdk/{check-provider-registry-ZOLEYDKM.mjs → check-provider-registry-KUDVEKAC.mjs} +7 -7
- package/dist/sdk/{chunk-OMFPM576.mjs → chunk-27RV5RR2.mjs} +2 -2
- package/dist/sdk/{chunk-6ZZ4DPAA.mjs → chunk-2RNTEWOA.mjs} +236 -47
- package/dist/sdk/chunk-2RNTEWOA.mjs.map +1 -0
- package/dist/sdk/{chunk-2GCSK3PD.mjs → chunk-BGBXLPLL.mjs} +3 -3
- package/dist/sdk/{chunk-LQ5B4T6L.mjs → chunk-U3BLLEW3.mjs} +431 -82
- package/dist/sdk/chunk-U3BLLEW3.mjs.map +1 -0
- package/dist/sdk/{chunk-N4I6ZDCJ.mjs → chunk-VG7FWDC2.mjs} +3 -3
- package/dist/sdk/{chunk-RI77TA6V.mjs.map → chunk-VG7FWDC2.mjs.map} +1 -1
- package/dist/sdk/{chunk-MQ57AB4U.mjs → chunk-XGI47XIH.mjs} +260 -55
- package/dist/sdk/chunk-XGI47XIH.mjs.map +1 -0
- package/dist/sdk/{config-4EG7IQIU.mjs → config-FMIIATKX.mjs} +2 -2
- package/dist/sdk/{failure-condition-evaluator-GLHZZF47.mjs → failure-condition-evaluator-PNONVBXD.mjs} +3 -3
- package/dist/sdk/{github-frontend-F4TE2JY7.mjs → github-frontend-WR4S3NG5.mjs} +3 -3
- package/dist/sdk/{host-SAT6RHDX.mjs → host-TROSAWTE.mjs} +3 -3
- package/dist/sdk/{host-VA3ET7N6.mjs → host-U7V54J2H.mjs} +3 -3
- package/dist/sdk/knex-store-HPXJILBL.mjs +411 -0
- package/dist/sdk/knex-store-HPXJILBL.mjs.map +1 -0
- package/dist/sdk/loader-ZC5G3JGJ.mjs +89 -0
- package/dist/sdk/loader-ZC5G3JGJ.mjs.map +1 -0
- package/dist/sdk/opa-policy-engine-S2S2ULEI.mjs +655 -0
- package/dist/sdk/opa-policy-engine-S2S2ULEI.mjs.map +1 -0
- package/dist/sdk/{routing-KFYQGOYU.mjs → routing-F4FOWVKF.mjs} +4 -4
- package/dist/sdk/{schedule-tool-handler-OQF57URO.mjs → schedule-tool-handler-ULNF7TZW.mjs} +7 -7
- package/dist/sdk/{schedule-tool-handler-PJVKWSYX.mjs → schedule-tool-handler-VFES42DD.mjs} +6 -6
- package/dist/sdk/sdk.d.mts +56 -38
- package/dist/sdk/sdk.d.ts +56 -38
- package/dist/sdk/sdk.js +2275 -388
- package/dist/sdk/sdk.js.map +1 -1
- package/dist/sdk/sdk.mjs +6 -6
- package/dist/sdk/{slack-frontend-LAY45IBR.mjs → slack-frontend-JS2VAZWB.mjs} +95 -4
- package/dist/sdk/slack-frontend-JS2VAZWB.mjs.map +1 -0
- package/dist/sdk/{trace-helpers-R2ETIEC2.mjs → trace-helpers-RDPXIN4S.mjs} +2 -2
- package/dist/sdk/validator-XTZJZZJH.mjs +134 -0
- package/dist/sdk/validator-XTZJZZJH.mjs.map +1 -0
- package/dist/sdk/{workflow-check-provider-LRWD52WN.mjs → workflow-check-provider-4NFWH6YO.mjs} +6 -6
- package/dist/sdk/{workflow-check-provider-N2DRFQDB.mjs → workflow-check-provider-5XS62BCJ.mjs} +7 -7
- package/dist/slack/adapter.d.ts +2 -0
- package/dist/slack/adapter.d.ts.map +1 -1
- package/dist/slack/client.d.ts +3 -0
- package/dist/slack/client.d.ts.map +1 -1
- package/dist/slack/markdown.d.ts +29 -0
- package/dist/slack/markdown.d.ts.map +1 -1
- package/dist/slack/socket-runner.d.ts +2 -0
- package/dist/slack/socket-runner.d.ts.map +1 -1
- package/dist/tui/chat-tui.d.ts +7 -0
- package/dist/tui/chat-tui.d.ts.map +1 -1
- package/dist/tui/components/input-bar.d.ts +11 -0
- package/dist/tui/components/input-bar.d.ts.map +1 -1
- package/dist/tui/components/trace-viewer.d.ts +25 -1
- package/dist/tui/components/trace-viewer.d.ts.map +1 -1
- package/dist/types/bot.d.ts +12 -0
- package/dist/types/bot.d.ts.map +1 -1
- package/dist/types/config.d.ts +4 -1
- package/dist/types/config.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/defaults/.visor.yaml +0 -420
- package/dist/output/traces/run-2026-02-15T19-14-20-379Z.ndjson +0 -138
- package/dist/output/traces/run-2026-02-15T19-15-09-410Z.ndjson +0 -1357
- package/dist/sdk/check-provider-registry-AAPPJ4CP.mjs +0 -28
- package/dist/sdk/chunk-6ZZ4DPAA.mjs.map +0 -1
- package/dist/sdk/chunk-EBTD2D4L.mjs +0 -739
- package/dist/sdk/chunk-LDFUW34H.mjs +0 -39912
- package/dist/sdk/chunk-LDFUW34H.mjs.map +0 -1
- package/dist/sdk/chunk-LQ5B4T6L.mjs.map +0 -1
- package/dist/sdk/chunk-MQ57AB4U.mjs.map +0 -1
- package/dist/sdk/chunk-N4I6ZDCJ.mjs.map +0 -1
- package/dist/sdk/chunk-OMFPM576.mjs.map +0 -1
- package/dist/sdk/chunk-RI77TA6V.mjs +0 -436
- package/dist/sdk/chunk-VO4N6TEL.mjs +0 -1502
- package/dist/sdk/chunk-VO4N6TEL.mjs.map +0 -1
- package/dist/sdk/failure-condition-evaluator-KN55WXRO.mjs +0 -17
- package/dist/sdk/github-frontend-HCOKL53D.mjs +0 -1356
- package/dist/sdk/github-frontend-HCOKL53D.mjs.map +0 -1
- package/dist/sdk/routing-OXQKETSA.mjs +0 -25
- package/dist/sdk/schedule-tool-handler-G353DHS6.mjs +0 -38
- package/dist/sdk/schedule-tool-handler-PJVKWSYX.mjs.map +0 -1
- package/dist/sdk/slack-frontend-LAY45IBR.mjs.map +0 -1
- package/dist/sdk/trace-helpers-LOPBHYYX.mjs +0 -25
- package/dist/sdk/trace-helpers-LOPBHYYX.mjs.map +0 -1
- package/dist/sdk/trace-helpers-R2ETIEC2.mjs.map +0 -1
- package/dist/sdk/workflow-check-provider-57KAR4Y4.mjs +0 -28
- package/dist/sdk/workflow-check-provider-57KAR4Y4.mjs.map +0 -1
- package/dist/sdk/workflow-check-provider-LRWD52WN.mjs.map +0 -1
- package/dist/sdk/workflow-check-provider-N2DRFQDB.mjs.map +0 -1
- package/dist/traces/run-2026-02-15T19-14-20-379Z.ndjson +0 -138
- package/dist/traces/run-2026-02-15T19-15-09-410Z.ndjson +0 -1357
- /package/dist/sdk/{check-provider-registry-AAPPJ4CP.mjs.map → check-provider-registry-7TCA3NSG.mjs.map} +0 -0
- /package/dist/sdk/{check-provider-registry-S7BMQ2FC.mjs.map → check-provider-registry-KUDVEKAC.mjs.map} +0 -0
- /package/dist/sdk/{chunk-EBTD2D4L.mjs.map → chunk-27RV5RR2.mjs.map} +0 -0
- /package/dist/sdk/{chunk-2GCSK3PD.mjs.map → chunk-BGBXLPLL.mjs.map} +0 -0
- /package/dist/sdk/{check-provider-registry-ZOLEYDKM.mjs.map → config-FMIIATKX.mjs.map} +0 -0
- /package/dist/sdk/{config-4EG7IQIU.mjs.map → failure-condition-evaluator-PNONVBXD.mjs.map} +0 -0
- /package/dist/sdk/{github-frontend-F4TE2JY7.mjs.map → github-frontend-WR4S3NG5.mjs.map} +0 -0
- /package/dist/sdk/{host-SAT6RHDX.mjs.map → host-TROSAWTE.mjs.map} +0 -0
- /package/dist/sdk/{host-VA3ET7N6.mjs.map → host-U7V54J2H.mjs.map} +0 -0
- /package/dist/sdk/{failure-condition-evaluator-GLHZZF47.mjs.map → routing-F4FOWVKF.mjs.map} +0 -0
- /package/dist/sdk/{failure-condition-evaluator-KN55WXRO.mjs.map → schedule-tool-handler-ULNF7TZW.mjs.map} +0 -0
- /package/dist/sdk/{routing-KFYQGOYU.mjs.map → schedule-tool-handler-VFES42DD.mjs.map} +0 -0
- /package/dist/sdk/{routing-OXQKETSA.mjs.map → trace-helpers-RDPXIN4S.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-handler-G353DHS6.mjs.map → workflow-check-provider-4NFWH6YO.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-handler-OQF57URO.mjs.map → workflow-check-provider-5XS62BCJ.mjs.map} +0 -0
package/dist/sdk/sdk.js
CHANGED
|
@@ -646,7 +646,7 @@ var require_package = __commonJS({
|
|
|
646
646
|
"package.json"(exports2, module2) {
|
|
647
647
|
module2.exports = {
|
|
648
648
|
name: "@probelabs/visor",
|
|
649
|
-
version: "0.1.
|
|
649
|
+
version: "0.1.42",
|
|
650
650
|
main: "dist/index.js",
|
|
651
651
|
bin: {
|
|
652
652
|
visor: "./dist/index.js"
|
|
@@ -731,7 +731,7 @@ var require_package = __commonJS({
|
|
|
731
731
|
],
|
|
732
732
|
author: "Probe Labs",
|
|
733
733
|
license: "MIT",
|
|
734
|
-
description: "AI
|
|
734
|
+
description: "AI workflow engine for code review, assistants, and automation \u2014 orchestrate checks, MCP tools, and AI providers with YAML-driven pipelines",
|
|
735
735
|
repository: {
|
|
736
736
|
type: "git",
|
|
737
737
|
url: "git+https://github.com/probelabs/visor.git"
|
|
@@ -748,7 +748,7 @@ var require_package = __commonJS({
|
|
|
748
748
|
"@octokit/auth-app": "^8.1.0",
|
|
749
749
|
"@octokit/core": "^7.0.3",
|
|
750
750
|
"@octokit/rest": "^22.0.0",
|
|
751
|
-
"@probelabs/probe": "^0.6.0-
|
|
751
|
+
"@probelabs/probe": "^0.6.0-rc245",
|
|
752
752
|
"@types/commander": "^2.12.0",
|
|
753
753
|
"@types/uuid": "^10.0.0",
|
|
754
754
|
ajv: "^8.17.1",
|
|
@@ -857,11 +857,11 @@ function getTracer() {
|
|
|
857
857
|
}
|
|
858
858
|
async function withActiveSpan(name, attrs, fn) {
|
|
859
859
|
const tracer = getTracer();
|
|
860
|
-
return await new Promise((
|
|
860
|
+
return await new Promise((resolve15, reject) => {
|
|
861
861
|
const callback = async (span) => {
|
|
862
862
|
try {
|
|
863
863
|
const res = await fn(span);
|
|
864
|
-
|
|
864
|
+
resolve15(res);
|
|
865
865
|
} catch (err) {
|
|
866
866
|
try {
|
|
867
867
|
if (err instanceof Error) span.recordException(err);
|
|
@@ -938,19 +938,19 @@ function __getOrCreateNdjsonPath() {
|
|
|
938
938
|
try {
|
|
939
939
|
if (process.env.VISOR_TELEMETRY_SINK && process.env.VISOR_TELEMETRY_SINK !== "file")
|
|
940
940
|
return null;
|
|
941
|
-
const
|
|
942
|
-
const
|
|
941
|
+
const path29 = require("path");
|
|
942
|
+
const fs25 = require("fs");
|
|
943
943
|
if (process.env.VISOR_FALLBACK_TRACE_FILE) {
|
|
944
944
|
__ndjsonPath = process.env.VISOR_FALLBACK_TRACE_FILE;
|
|
945
|
-
const dir =
|
|
946
|
-
if (!
|
|
945
|
+
const dir = path29.dirname(__ndjsonPath);
|
|
946
|
+
if (!fs25.existsSync(dir)) fs25.mkdirSync(dir, { recursive: true });
|
|
947
947
|
return __ndjsonPath;
|
|
948
948
|
}
|
|
949
|
-
const outDir = process.env.VISOR_TRACE_DIR ||
|
|
950
|
-
if (!
|
|
949
|
+
const outDir = process.env.VISOR_TRACE_DIR || path29.join(process.cwd(), "output", "traces");
|
|
950
|
+
if (!fs25.existsSync(outDir)) fs25.mkdirSync(outDir, { recursive: true });
|
|
951
951
|
if (!__ndjsonPath) {
|
|
952
952
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
953
|
-
__ndjsonPath =
|
|
953
|
+
__ndjsonPath = path29.join(outDir, `${ts}.ndjson`);
|
|
954
954
|
}
|
|
955
955
|
return __ndjsonPath;
|
|
956
956
|
} catch {
|
|
@@ -959,11 +959,11 @@ function __getOrCreateNdjsonPath() {
|
|
|
959
959
|
}
|
|
960
960
|
function _appendRunMarker() {
|
|
961
961
|
try {
|
|
962
|
-
const
|
|
962
|
+
const fs25 = require("fs");
|
|
963
963
|
const p = __getOrCreateNdjsonPath();
|
|
964
964
|
if (!p) return;
|
|
965
965
|
const line = { name: "visor.run", attributes: { started: true } };
|
|
966
|
-
|
|
966
|
+
fs25.appendFileSync(p, JSON.stringify(line) + "\n", "utf8");
|
|
967
967
|
} catch {
|
|
968
968
|
}
|
|
969
969
|
}
|
|
@@ -3156,7 +3156,7 @@ var init_failure_condition_evaluator = __esm({
|
|
|
3156
3156
|
*/
|
|
3157
3157
|
evaluateExpression(condition, context2) {
|
|
3158
3158
|
try {
|
|
3159
|
-
const
|
|
3159
|
+
const normalize8 = (expr) => {
|
|
3160
3160
|
const trimmed = expr.trim();
|
|
3161
3161
|
if (!/[\n;]/.test(trimmed)) return trimmed;
|
|
3162
3162
|
const parts = trimmed.split(/[\n;]+/).map((s) => s.trim()).filter((s) => s.length > 0 && !s.startsWith("//"));
|
|
@@ -3314,7 +3314,7 @@ var init_failure_condition_evaluator = __esm({
|
|
|
3314
3314
|
try {
|
|
3315
3315
|
exec2 = this.sandbox.compile(`return (${raw});`);
|
|
3316
3316
|
} catch {
|
|
3317
|
-
const normalizedExpr =
|
|
3317
|
+
const normalizedExpr = normalize8(condition);
|
|
3318
3318
|
exec2 = this.sandbox.compile(`return (${normalizedExpr});`);
|
|
3319
3319
|
}
|
|
3320
3320
|
const result = exec2(scope).run();
|
|
@@ -3697,9 +3697,9 @@ function configureLiquidWithExtensions(liquid) {
|
|
|
3697
3697
|
});
|
|
3698
3698
|
liquid.registerFilter("get", (obj, pathExpr) => {
|
|
3699
3699
|
if (obj == null) return void 0;
|
|
3700
|
-
const
|
|
3701
|
-
if (!
|
|
3702
|
-
const parts =
|
|
3700
|
+
const path29 = typeof pathExpr === "string" ? pathExpr : String(pathExpr || "");
|
|
3701
|
+
if (!path29) return obj;
|
|
3702
|
+
const parts = path29.split(".");
|
|
3703
3703
|
let cur = obj;
|
|
3704
3704
|
for (const p of parts) {
|
|
3705
3705
|
if (cur == null) return void 0;
|
|
@@ -3818,9 +3818,9 @@ function configureLiquidWithExtensions(liquid) {
|
|
|
3818
3818
|
}
|
|
3819
3819
|
}
|
|
3820
3820
|
const defaultRole = typeof rolesCfg.default === "string" && rolesCfg.default.trim() ? rolesCfg.default.trim() : void 0;
|
|
3821
|
-
const getNested = (obj,
|
|
3822
|
-
if (!obj || !
|
|
3823
|
-
const parts =
|
|
3821
|
+
const getNested = (obj, path29) => {
|
|
3822
|
+
if (!obj || !path29) return void 0;
|
|
3823
|
+
const parts = path29.split(".");
|
|
3824
3824
|
let cur = obj;
|
|
3825
3825
|
for (const p of parts) {
|
|
3826
3826
|
if (cur == null) return void 0;
|
|
@@ -6372,8 +6372,8 @@ var init_dependency_gating = __esm({
|
|
|
6372
6372
|
async function renderTemplateContent(checkId, checkConfig, reviewSummary) {
|
|
6373
6373
|
try {
|
|
6374
6374
|
const { createExtendedLiquid: createExtendedLiquid2 } = await Promise.resolve().then(() => (init_liquid_extensions(), liquid_extensions_exports));
|
|
6375
|
-
const
|
|
6376
|
-
const
|
|
6375
|
+
const fs25 = await import("fs/promises");
|
|
6376
|
+
const path29 = await import("path");
|
|
6377
6377
|
const schemaRaw = checkConfig.schema || "plain";
|
|
6378
6378
|
const schema = typeof schemaRaw === "string" ? schemaRaw : "code-review";
|
|
6379
6379
|
let templateContent;
|
|
@@ -6381,24 +6381,24 @@ async function renderTemplateContent(checkId, checkConfig, reviewSummary) {
|
|
|
6381
6381
|
templateContent = String(checkConfig.template.content);
|
|
6382
6382
|
} else if (checkConfig.template && checkConfig.template.file) {
|
|
6383
6383
|
const file = String(checkConfig.template.file);
|
|
6384
|
-
const resolved =
|
|
6385
|
-
templateContent = await
|
|
6384
|
+
const resolved = path29.resolve(process.cwd(), file);
|
|
6385
|
+
templateContent = await fs25.readFile(resolved, "utf-8");
|
|
6386
6386
|
} else if (schema && schema !== "plain") {
|
|
6387
6387
|
const sanitized = String(schema).replace(/[^a-zA-Z0-9-]/g, "");
|
|
6388
6388
|
if (sanitized) {
|
|
6389
6389
|
const candidatePaths = [
|
|
6390
|
-
|
|
6390
|
+
path29.join(__dirname, "output", sanitized, "template.liquid"),
|
|
6391
6391
|
// bundled: dist/output/
|
|
6392
|
-
|
|
6392
|
+
path29.join(__dirname, "..", "..", "output", sanitized, "template.liquid"),
|
|
6393
6393
|
// source: output/
|
|
6394
|
-
|
|
6394
|
+
path29.join(process.cwd(), "output", sanitized, "template.liquid"),
|
|
6395
6395
|
// fallback: cwd/output/
|
|
6396
|
-
|
|
6396
|
+
path29.join(process.cwd(), "dist", "output", sanitized, "template.liquid")
|
|
6397
6397
|
// fallback: cwd/dist/output/
|
|
6398
6398
|
];
|
|
6399
6399
|
for (const p of candidatePaths) {
|
|
6400
6400
|
try {
|
|
6401
|
-
templateContent = await
|
|
6401
|
+
templateContent = await fs25.readFile(p, "utf-8");
|
|
6402
6402
|
if (templateContent) break;
|
|
6403
6403
|
} catch {
|
|
6404
6404
|
}
|
|
@@ -6803,7 +6803,7 @@ async function processDiffWithOutline(diffContent) {
|
|
|
6803
6803
|
}
|
|
6804
6804
|
try {
|
|
6805
6805
|
const originalProbePath = process.env.PROBE_PATH;
|
|
6806
|
-
const
|
|
6806
|
+
const fs25 = require("fs");
|
|
6807
6807
|
const possiblePaths = [
|
|
6808
6808
|
// Relative to current working directory (most common in production)
|
|
6809
6809
|
path6.join(process.cwd(), "node_modules/@probelabs/probe/bin/probe-binary"),
|
|
@@ -6814,7 +6814,7 @@ async function processDiffWithOutline(diffContent) {
|
|
|
6814
6814
|
];
|
|
6815
6815
|
let probeBinaryPath;
|
|
6816
6816
|
for (const candidatePath of possiblePaths) {
|
|
6817
|
-
if (
|
|
6817
|
+
if (fs25.existsSync(candidatePath)) {
|
|
6818
6818
|
probeBinaryPath = candidatePath;
|
|
6819
6819
|
break;
|
|
6820
6820
|
}
|
|
@@ -7739,7 +7739,7 @@ ${this.escapeXml(processedFallbackDiff)}
|
|
|
7739
7739
|
<user>${this.escapeXml(String(m.user || ""))}</user>
|
|
7740
7740
|
<text>${this.escapeXml(String(m.text || ""))}</text>
|
|
7741
7741
|
<timestamp>${this.escapeXml(String(m.timestamp || ""))}</timestamp>
|
|
7742
|
-
<origin>${this.escapeXml(String(m.origin || ""))}</origin
|
|
7742
|
+
<origin>${this.escapeXml(String(m.origin || ""))}</origin>${this.formatFilesXml(m.files)}
|
|
7743
7743
|
</message>`;
|
|
7744
7744
|
}
|
|
7745
7745
|
xml += `
|
|
@@ -7751,7 +7751,7 @@ ${this.escapeXml(processedFallbackDiff)}
|
|
|
7751
7751
|
<user>${this.escapeXml(String(current.user || ""))}</user>
|
|
7752
7752
|
<text>${this.escapeXml(String(current.text || ""))}</text>
|
|
7753
7753
|
<timestamp>${this.escapeXml(String(current.timestamp || ""))}</timestamp>
|
|
7754
|
-
<origin>${this.escapeXml(String(current.origin || ""))}</origin
|
|
7754
|
+
<origin>${this.escapeXml(String(current.origin || ""))}</origin>${this.formatFilesXml(current.files)}
|
|
7755
7755
|
</current>
|
|
7756
7756
|
</slack_context>`;
|
|
7757
7757
|
return xml;
|
|
@@ -7759,6 +7759,25 @@ ${this.escapeXml(processedFallbackDiff)}
|
|
|
7759
7759
|
return "";
|
|
7760
7760
|
}
|
|
7761
7761
|
}
|
|
7762
|
+
/** Render file attachment metadata as XML fragment for a message. */
|
|
7763
|
+
formatFilesXml(files) {
|
|
7764
|
+
if (!Array.isArray(files) || files.length === 0) return "";
|
|
7765
|
+
let xml = `
|
|
7766
|
+
<files>`;
|
|
7767
|
+
for (const f of files) {
|
|
7768
|
+
xml += `
|
|
7769
|
+
<file>
|
|
7770
|
+
<name>${this.escapeXml(String(f.name || ""))}</name>
|
|
7771
|
+
<mimetype>${this.escapeXml(String(f.mimetype || ""))}</mimetype>
|
|
7772
|
+
<filetype>${this.escapeXml(String(f.filetype || ""))}</filetype>
|
|
7773
|
+
<url>${this.escapeXml(String(f.url_private || f.permalink || ""))}</url>
|
|
7774
|
+
<size>${f.size || 0}</size>
|
|
7775
|
+
</file>`;
|
|
7776
|
+
}
|
|
7777
|
+
xml += `
|
|
7778
|
+
</files>`;
|
|
7779
|
+
return xml;
|
|
7780
|
+
}
|
|
7762
7781
|
/**
|
|
7763
7782
|
* Build a normalized ConversationContext for GitHub (PR/issue + comments)
|
|
7764
7783
|
* using the same contract as Slack's ConversationContext. This is exposed
|
|
@@ -7881,8 +7900,8 @@ ${schemaString}`);
|
|
|
7881
7900
|
}
|
|
7882
7901
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
7883
7902
|
try {
|
|
7884
|
-
const
|
|
7885
|
-
const
|
|
7903
|
+
const fs25 = require("fs");
|
|
7904
|
+
const path29 = require("path");
|
|
7886
7905
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
7887
7906
|
const provider = this.config.provider || "auto";
|
|
7888
7907
|
const model = this.config.model || "default";
|
|
@@ -7996,20 +8015,20 @@ ${"=".repeat(60)}
|
|
|
7996
8015
|
`;
|
|
7997
8016
|
readableVersion += `${"=".repeat(60)}
|
|
7998
8017
|
`;
|
|
7999
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
8000
|
-
if (!
|
|
8001
|
-
|
|
8018
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path29.join(process.cwd(), "debug-artifacts");
|
|
8019
|
+
if (!fs25.existsSync(debugArtifactsDir)) {
|
|
8020
|
+
fs25.mkdirSync(debugArtifactsDir, { recursive: true });
|
|
8002
8021
|
}
|
|
8003
|
-
const debugFile =
|
|
8022
|
+
const debugFile = path29.join(
|
|
8004
8023
|
debugArtifactsDir,
|
|
8005
8024
|
`prompt-${_checkName || "unknown"}-${timestamp}.json`
|
|
8006
8025
|
);
|
|
8007
|
-
|
|
8008
|
-
const readableFile =
|
|
8026
|
+
fs25.writeFileSync(debugFile, debugJson, "utf-8");
|
|
8027
|
+
const readableFile = path29.join(
|
|
8009
8028
|
debugArtifactsDir,
|
|
8010
8029
|
`prompt-${_checkName || "unknown"}-${timestamp}.txt`
|
|
8011
8030
|
);
|
|
8012
|
-
|
|
8031
|
+
fs25.writeFileSync(readableFile, readableVersion, "utf-8");
|
|
8013
8032
|
log(`
|
|
8014
8033
|
\u{1F4BE} Full debug info saved to:`);
|
|
8015
8034
|
log(` JSON: ${debugFile}`);
|
|
@@ -8042,8 +8061,8 @@ ${"=".repeat(60)}
|
|
|
8042
8061
|
log(`\u{1F4E4} Response length: ${response.length} characters`);
|
|
8043
8062
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
8044
8063
|
try {
|
|
8045
|
-
const
|
|
8046
|
-
const
|
|
8064
|
+
const fs25 = require("fs");
|
|
8065
|
+
const path29 = require("path");
|
|
8047
8066
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8048
8067
|
const agentAny2 = agent;
|
|
8049
8068
|
let fullHistory = [];
|
|
@@ -8054,8 +8073,8 @@ ${"=".repeat(60)}
|
|
|
8054
8073
|
} else if (agentAny2._messages) {
|
|
8055
8074
|
fullHistory = agentAny2._messages;
|
|
8056
8075
|
}
|
|
8057
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
8058
|
-
const sessionBase =
|
|
8076
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path29.join(process.cwd(), "debug-artifacts");
|
|
8077
|
+
const sessionBase = path29.join(
|
|
8059
8078
|
debugArtifactsDir,
|
|
8060
8079
|
`session-${_checkName || "unknown"}-${timestamp}`
|
|
8061
8080
|
);
|
|
@@ -8067,7 +8086,7 @@ ${"=".repeat(60)}
|
|
|
8067
8086
|
schema: effectiveSchema,
|
|
8068
8087
|
totalMessages: fullHistory.length
|
|
8069
8088
|
};
|
|
8070
|
-
|
|
8089
|
+
fs25.writeFileSync(sessionBase + ".json", JSON.stringify(sessionData, null, 2), "utf-8");
|
|
8071
8090
|
let readable = `=============================================================
|
|
8072
8091
|
`;
|
|
8073
8092
|
readable += `COMPLETE AI SESSION HISTORY (AFTER RESPONSE)
|
|
@@ -8094,7 +8113,7 @@ ${"=".repeat(60)}
|
|
|
8094
8113
|
`;
|
|
8095
8114
|
readable += content + "\n";
|
|
8096
8115
|
});
|
|
8097
|
-
|
|
8116
|
+
fs25.writeFileSync(sessionBase + ".summary.txt", readable, "utf-8");
|
|
8098
8117
|
log(`\u{1F4BE} Complete session history saved:`);
|
|
8099
8118
|
log(` - Contains ALL ${fullHistory.length} messages (prompts + responses)`);
|
|
8100
8119
|
} catch (error) {
|
|
@@ -8103,11 +8122,11 @@ ${"=".repeat(60)}
|
|
|
8103
8122
|
}
|
|
8104
8123
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
8105
8124
|
try {
|
|
8106
|
-
const
|
|
8107
|
-
const
|
|
8125
|
+
const fs25 = require("fs");
|
|
8126
|
+
const path29 = require("path");
|
|
8108
8127
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8109
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
8110
|
-
const responseFile =
|
|
8128
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path29.join(process.cwd(), "debug-artifacts");
|
|
8129
|
+
const responseFile = path29.join(
|
|
8111
8130
|
debugArtifactsDir,
|
|
8112
8131
|
`response-${_checkName || "unknown"}-${timestamp}.txt`
|
|
8113
8132
|
);
|
|
@@ -8140,7 +8159,7 @@ ${"=".repeat(60)}
|
|
|
8140
8159
|
`;
|
|
8141
8160
|
responseContent += `${"=".repeat(60)}
|
|
8142
8161
|
`;
|
|
8143
|
-
|
|
8162
|
+
fs25.writeFileSync(responseFile, responseContent, "utf-8");
|
|
8144
8163
|
log(`\u{1F4BE} Response saved to: ${responseFile}`);
|
|
8145
8164
|
} catch (error) {
|
|
8146
8165
|
log(`\u26A0\uFE0F Could not save response file: ${error}`);
|
|
@@ -8156,9 +8175,9 @@ ${"=".repeat(60)}
|
|
|
8156
8175
|
await agentAny._telemetryConfig.shutdown();
|
|
8157
8176
|
log(`\u{1F4CA} OpenTelemetry trace saved to: ${agentAny._traceFilePath}`);
|
|
8158
8177
|
if (process.env.GITHUB_ACTIONS) {
|
|
8159
|
-
const
|
|
8160
|
-
if (
|
|
8161
|
-
const stats =
|
|
8178
|
+
const fs25 = require("fs");
|
|
8179
|
+
if (fs25.existsSync(agentAny._traceFilePath)) {
|
|
8180
|
+
const stats = fs25.statSync(agentAny._traceFilePath);
|
|
8162
8181
|
console.log(
|
|
8163
8182
|
`::notice title=AI Trace Saved::${agentAny._traceFilePath} (${stats.size} bytes)`
|
|
8164
8183
|
);
|
|
@@ -8264,6 +8283,9 @@ ${"=".repeat(60)}
|
|
|
8264
8283
|
if (this.config.enableTasks !== void 0) {
|
|
8265
8284
|
options.enableTasks = this.config.enableTasks;
|
|
8266
8285
|
}
|
|
8286
|
+
if (this.config.enableExecutePlan !== void 0) {
|
|
8287
|
+
options.enableExecutePlan = this.config.enableExecutePlan;
|
|
8288
|
+
}
|
|
8267
8289
|
if (this.config.retry) {
|
|
8268
8290
|
options.retry = this.config.retry;
|
|
8269
8291
|
}
|
|
@@ -8356,9 +8378,9 @@ ${schemaString}`);
|
|
|
8356
8378
|
const model = this.config.model || "default";
|
|
8357
8379
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
8358
8380
|
try {
|
|
8359
|
-
const
|
|
8360
|
-
const
|
|
8361
|
-
const
|
|
8381
|
+
const fs25 = require("fs");
|
|
8382
|
+
const path29 = require("path");
|
|
8383
|
+
const os3 = require("os");
|
|
8362
8384
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8363
8385
|
const debugData = {
|
|
8364
8386
|
timestamp,
|
|
@@ -8430,19 +8452,19 @@ ${"=".repeat(60)}
|
|
|
8430
8452
|
`;
|
|
8431
8453
|
readableVersion += `${"=".repeat(60)}
|
|
8432
8454
|
`;
|
|
8433
|
-
const tempDir =
|
|
8434
|
-
const promptFile =
|
|
8435
|
-
|
|
8455
|
+
const tempDir = os3.tmpdir();
|
|
8456
|
+
const promptFile = path29.join(tempDir, `visor-prompt-${timestamp}.txt`);
|
|
8457
|
+
fs25.writeFileSync(promptFile, prompt, "utf-8");
|
|
8436
8458
|
log(`
|
|
8437
8459
|
\u{1F4BE} Prompt saved to: ${promptFile}`);
|
|
8438
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
8460
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path29.join(process.cwd(), "debug-artifacts");
|
|
8439
8461
|
try {
|
|
8440
|
-
const base =
|
|
8462
|
+
const base = path29.join(
|
|
8441
8463
|
debugArtifactsDir,
|
|
8442
8464
|
`prompt-${_checkName || "unknown"}-${timestamp}`
|
|
8443
8465
|
);
|
|
8444
|
-
|
|
8445
|
-
|
|
8466
|
+
fs25.writeFileSync(base + ".json", debugJson, "utf-8");
|
|
8467
|
+
fs25.writeFileSync(base + ".summary.txt", readableVersion, "utf-8");
|
|
8446
8468
|
log(`
|
|
8447
8469
|
\u{1F4BE} Full debug info saved to directory: ${debugArtifactsDir}`);
|
|
8448
8470
|
} catch {
|
|
@@ -8487,8 +8509,8 @@ $ ${cliCommand}
|
|
|
8487
8509
|
log(`\u{1F4E4} Response length: ${response.length} characters`);
|
|
8488
8510
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
8489
8511
|
try {
|
|
8490
|
-
const
|
|
8491
|
-
const
|
|
8512
|
+
const fs25 = require("fs");
|
|
8513
|
+
const path29 = require("path");
|
|
8492
8514
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8493
8515
|
const agentAny = agent;
|
|
8494
8516
|
let fullHistory = [];
|
|
@@ -8499,8 +8521,8 @@ $ ${cliCommand}
|
|
|
8499
8521
|
} else if (agentAny._messages) {
|
|
8500
8522
|
fullHistory = agentAny._messages;
|
|
8501
8523
|
}
|
|
8502
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
8503
|
-
const sessionBase =
|
|
8524
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path29.join(process.cwd(), "debug-artifacts");
|
|
8525
|
+
const sessionBase = path29.join(
|
|
8504
8526
|
debugArtifactsDir,
|
|
8505
8527
|
`session-${_checkName || "unknown"}-${timestamp}`
|
|
8506
8528
|
);
|
|
@@ -8512,7 +8534,7 @@ $ ${cliCommand}
|
|
|
8512
8534
|
schema: effectiveSchema,
|
|
8513
8535
|
totalMessages: fullHistory.length
|
|
8514
8536
|
};
|
|
8515
|
-
|
|
8537
|
+
fs25.writeFileSync(sessionBase + ".json", JSON.stringify(sessionData, null, 2), "utf-8");
|
|
8516
8538
|
let readable = `=============================================================
|
|
8517
8539
|
`;
|
|
8518
8540
|
readable += `COMPLETE AI SESSION HISTORY (AFTER RESPONSE)
|
|
@@ -8539,7 +8561,7 @@ ${"=".repeat(60)}
|
|
|
8539
8561
|
`;
|
|
8540
8562
|
readable += content + "\n";
|
|
8541
8563
|
});
|
|
8542
|
-
|
|
8564
|
+
fs25.writeFileSync(sessionBase + ".summary.txt", readable, "utf-8");
|
|
8543
8565
|
log(`\u{1F4BE} Complete session history saved:`);
|
|
8544
8566
|
log(` - Contains ALL ${fullHistory.length} messages (prompts + responses)`);
|
|
8545
8567
|
} catch (error) {
|
|
@@ -8548,11 +8570,11 @@ ${"=".repeat(60)}
|
|
|
8548
8570
|
}
|
|
8549
8571
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
8550
8572
|
try {
|
|
8551
|
-
const
|
|
8552
|
-
const
|
|
8573
|
+
const fs25 = require("fs");
|
|
8574
|
+
const path29 = require("path");
|
|
8553
8575
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8554
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
8555
|
-
const responseFile =
|
|
8576
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path29.join(process.cwd(), "debug-artifacts");
|
|
8577
|
+
const responseFile = path29.join(
|
|
8556
8578
|
debugArtifactsDir,
|
|
8557
8579
|
`response-${_checkName || "unknown"}-${timestamp}.txt`
|
|
8558
8580
|
);
|
|
@@ -8585,7 +8607,7 @@ ${"=".repeat(60)}
|
|
|
8585
8607
|
`;
|
|
8586
8608
|
responseContent += `${"=".repeat(60)}
|
|
8587
8609
|
`;
|
|
8588
|
-
|
|
8610
|
+
fs25.writeFileSync(responseFile, responseContent, "utf-8");
|
|
8589
8611
|
log(`\u{1F4BE} Response saved to: ${responseFile}`);
|
|
8590
8612
|
} catch (error) {
|
|
8591
8613
|
log(`\u26A0\uFE0F Could not save response file: ${error}`);
|
|
@@ -8603,9 +8625,9 @@ ${"=".repeat(60)}
|
|
|
8603
8625
|
await telemetry.shutdown();
|
|
8604
8626
|
log(`\u{1F4CA} OpenTelemetry trace saved to: ${traceFilePath}`);
|
|
8605
8627
|
if (process.env.GITHUB_ACTIONS) {
|
|
8606
|
-
const
|
|
8607
|
-
if (
|
|
8608
|
-
const stats =
|
|
8628
|
+
const fs25 = require("fs");
|
|
8629
|
+
if (fs25.existsSync(traceFilePath)) {
|
|
8630
|
+
const stats = fs25.statSync(traceFilePath);
|
|
8609
8631
|
console.log(
|
|
8610
8632
|
`::notice title=AI Trace Saved::OpenTelemetry trace file size: ${stats.size} bytes`
|
|
8611
8633
|
);
|
|
@@ -8643,8 +8665,8 @@ ${"=".repeat(60)}
|
|
|
8643
8665
|
* Load schema content from schema files or inline definitions
|
|
8644
8666
|
*/
|
|
8645
8667
|
async loadSchemaContent(schema) {
|
|
8646
|
-
const
|
|
8647
|
-
const
|
|
8668
|
+
const fs25 = require("fs").promises;
|
|
8669
|
+
const path29 = require("path");
|
|
8648
8670
|
if (typeof schema === "object" && schema !== null) {
|
|
8649
8671
|
log("\u{1F4CB} Using inline schema object from configuration");
|
|
8650
8672
|
return JSON.stringify(schema);
|
|
@@ -8657,14 +8679,14 @@ ${"=".repeat(60)}
|
|
|
8657
8679
|
}
|
|
8658
8680
|
} catch {
|
|
8659
8681
|
}
|
|
8660
|
-
if ((schema.startsWith("./") || schema.includes(".json")) && !
|
|
8682
|
+
if ((schema.startsWith("./") || schema.includes(".json")) && !path29.isAbsolute(schema)) {
|
|
8661
8683
|
if (schema.includes("..") || schema.includes("\0")) {
|
|
8662
8684
|
throw new Error("Invalid schema path: path traversal not allowed");
|
|
8663
8685
|
}
|
|
8664
8686
|
try {
|
|
8665
|
-
const schemaPath =
|
|
8687
|
+
const schemaPath = path29.resolve(process.cwd(), schema);
|
|
8666
8688
|
log(`\u{1F4CB} Loading custom schema from file: ${schemaPath}`);
|
|
8667
|
-
const schemaContent = await
|
|
8689
|
+
const schemaContent = await fs25.readFile(schemaPath, "utf-8");
|
|
8668
8690
|
return schemaContent.trim();
|
|
8669
8691
|
} catch (error) {
|
|
8670
8692
|
throw new Error(
|
|
@@ -8678,22 +8700,22 @@ ${"=".repeat(60)}
|
|
|
8678
8700
|
}
|
|
8679
8701
|
const candidatePaths = [
|
|
8680
8702
|
// GitHub Action bundle location
|
|
8681
|
-
|
|
8703
|
+
path29.join(__dirname, "output", sanitizedSchemaName, "schema.json"),
|
|
8682
8704
|
// Historical fallback when src/output was inadvertently bundled as output1/
|
|
8683
|
-
|
|
8705
|
+
path29.join(__dirname, "output1", sanitizedSchemaName, "schema.json"),
|
|
8684
8706
|
// Local dev (repo root)
|
|
8685
|
-
|
|
8707
|
+
path29.join(process.cwd(), "output", sanitizedSchemaName, "schema.json")
|
|
8686
8708
|
];
|
|
8687
8709
|
for (const schemaPath of candidatePaths) {
|
|
8688
8710
|
try {
|
|
8689
|
-
const schemaContent = await
|
|
8711
|
+
const schemaContent = await fs25.readFile(schemaPath, "utf-8");
|
|
8690
8712
|
return schemaContent.trim();
|
|
8691
8713
|
} catch {
|
|
8692
8714
|
}
|
|
8693
8715
|
}
|
|
8694
|
-
const distPath =
|
|
8695
|
-
const distAltPath =
|
|
8696
|
-
const cwdPath =
|
|
8716
|
+
const distPath = path29.join(__dirname, "output", sanitizedSchemaName, "schema.json");
|
|
8717
|
+
const distAltPath = path29.join(__dirname, "output1", sanitizedSchemaName, "schema.json");
|
|
8718
|
+
const cwdPath = path29.join(process.cwd(), "output", sanitizedSchemaName, "schema.json");
|
|
8697
8719
|
throw new Error(
|
|
8698
8720
|
`Failed to load schema '${sanitizedSchemaName}'. Tried: ${distPath}, ${distAltPath}, and ${cwdPath}. Ensure build copies 'output/' into dist (build:cli), or provide a custom schema file/path.`
|
|
8699
8721
|
);
|
|
@@ -8711,7 +8733,22 @@ ${"=".repeat(60)}
|
|
|
8711
8733
|
log("\u{1F4CB} Full response preview:", response);
|
|
8712
8734
|
}
|
|
8713
8735
|
try {
|
|
8714
|
-
let reviewData;
|
|
8736
|
+
let reviewData = void 0;
|
|
8737
|
+
const RAW_OUTPUT_RE = /\n<<<RAW_OUTPUT>>>\n([\s\S]*?)\n<<<END_RAW_OUTPUT>>>/g;
|
|
8738
|
+
const rawOutputBlocks = [];
|
|
8739
|
+
let responseForParsing = response;
|
|
8740
|
+
{
|
|
8741
|
+
let rawMatch;
|
|
8742
|
+
while ((rawMatch = RAW_OUTPUT_RE.exec(response)) !== null) {
|
|
8743
|
+
rawOutputBlocks.push(rawMatch[1]);
|
|
8744
|
+
}
|
|
8745
|
+
if (rawOutputBlocks.length > 0) {
|
|
8746
|
+
responseForParsing = response.replace(RAW_OUTPUT_RE, "");
|
|
8747
|
+
log(
|
|
8748
|
+
`\u{1F4E6} Extracted ${rawOutputBlocks.length} RAW_OUTPUT blocks (${rawOutputBlocks.reduce((s, b) => s + b.length, 0)} chars) from response`
|
|
8749
|
+
);
|
|
8750
|
+
}
|
|
8751
|
+
}
|
|
8715
8752
|
if (_schema === "plain" || !_schema) {
|
|
8716
8753
|
log(
|
|
8717
8754
|
`\u{1F4CB} ${_schema === "plain" ? "Plain" : "No"} schema detected - treating raw response as text output`
|
|
@@ -8728,7 +8765,7 @@ ${"=".repeat(60)}
|
|
|
8728
8765
|
}
|
|
8729
8766
|
{
|
|
8730
8767
|
log("\u{1F50D} Extracting JSON from AI response...");
|
|
8731
|
-
const sanitizedResponse =
|
|
8768
|
+
const sanitizedResponse = responseForParsing.replace(/^\uFEFF/, "").replace(/[\u200B-\u200D\uFEFF\u00A0]/g, "").replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "").trim();
|
|
8732
8769
|
try {
|
|
8733
8770
|
reviewData = JSON.parse(sanitizedResponse);
|
|
8734
8771
|
log("\u2705 Successfully parsed direct JSON response");
|
|
@@ -8736,22 +8773,48 @@ ${"=".repeat(60)}
|
|
|
8736
8773
|
} catch (parseErr) {
|
|
8737
8774
|
const errMsg = parseErr instanceof Error ? parseErr.message : String(parseErr);
|
|
8738
8775
|
log(`\u{1F50D} Direct JSON parsing failed: ${errMsg}`);
|
|
8739
|
-
|
|
8740
|
-
|
|
8741
|
-
|
|
8776
|
+
let recovered = false;
|
|
8777
|
+
const trailingMatch = errMsg.match(/after JSON at position (\d+)/);
|
|
8778
|
+
if (trailingMatch) {
|
|
8779
|
+
const pos = parseInt(trailingMatch[1], 10);
|
|
8780
|
+
try {
|
|
8781
|
+
reviewData = JSON.parse(sanitizedResponse.substring(0, pos));
|
|
8782
|
+
const trailing = sanitizedResponse.substring(pos).trim();
|
|
8783
|
+
if (trailing && reviewData && typeof reviewData === "object" && typeof reviewData.text === "string") {
|
|
8784
|
+
reviewData.text = reviewData.text + "\n\n" + trailing;
|
|
8785
|
+
log(
|
|
8786
|
+
`\u2705 Recovered JSON and appended ${trailing.length} chars of trailing content to text field`
|
|
8787
|
+
);
|
|
8788
|
+
} else {
|
|
8789
|
+
log(`\u2705 Recovered JSON by trimming trailing content at position ${pos}`);
|
|
8790
|
+
}
|
|
8791
|
+
if (debugInfo) debugInfo.jsonParseSuccess = true;
|
|
8792
|
+
recovered = true;
|
|
8793
|
+
} catch {
|
|
8794
|
+
}
|
|
8795
|
+
}
|
|
8796
|
+
if (!recovered) {
|
|
8797
|
+
if (response.toLowerCase().includes("i cannot") || response.toLowerCase().includes("unable to")) {
|
|
8798
|
+
console.error("\u{1F6AB} AI refused to analyze - returning refusal as output");
|
|
8799
|
+
const trimmed2 = responseForParsing.trim();
|
|
8800
|
+
const out = trimmed2 ? { text: trimmed2 } : {};
|
|
8801
|
+
if (rawOutputBlocks.length > 0) out._rawOutput = rawOutputBlocks.join("\n\n");
|
|
8802
|
+
return {
|
|
8803
|
+
issues: [],
|
|
8804
|
+
output: out,
|
|
8805
|
+
debug: debugInfo
|
|
8806
|
+
};
|
|
8807
|
+
}
|
|
8808
|
+
log("\u{1F527} Treating response as plain text (no JSON extraction)");
|
|
8809
|
+
const trimmed = responseForParsing.trim();
|
|
8810
|
+
const fallbackOut = { text: trimmed };
|
|
8811
|
+
if (rawOutputBlocks.length > 0) fallbackOut._rawOutput = rawOutputBlocks.join("\n\n");
|
|
8742
8812
|
return {
|
|
8743
8813
|
issues: [],
|
|
8744
|
-
output:
|
|
8814
|
+
output: fallbackOut,
|
|
8745
8815
|
debug: debugInfo
|
|
8746
8816
|
};
|
|
8747
8817
|
}
|
|
8748
|
-
log("\u{1F527} Treating response as plain text (no JSON extraction)");
|
|
8749
|
-
const trimmed = response.trim();
|
|
8750
|
-
return {
|
|
8751
|
-
issues: [],
|
|
8752
|
-
output: { text: trimmed },
|
|
8753
|
-
debug: debugInfo
|
|
8754
|
-
};
|
|
8755
8818
|
}
|
|
8756
8819
|
}
|
|
8757
8820
|
const looksLikeTextOutput = reviewData && typeof reviewData === "object" && typeof reviewData.text === "string" && String(reviewData.text).trim().length > 0;
|
|
@@ -8799,6 +8862,9 @@ ${"=".repeat(60)}
|
|
|
8799
8862
|
out.text = fallbackText;
|
|
8800
8863
|
}
|
|
8801
8864
|
}
|
|
8865
|
+
if (rawOutputBlocks.length > 0) {
|
|
8866
|
+
out._rawOutput = rawOutputBlocks.join("\n\n");
|
|
8867
|
+
}
|
|
8802
8868
|
const result2 = {
|
|
8803
8869
|
// Keep issues empty for custom-schema rendering; consumers read from output.*
|
|
8804
8870
|
issues: [],
|
|
@@ -8884,7 +8950,7 @@ ${"=".repeat(60)}
|
|
|
8884
8950
|
* Generate mock response for testing
|
|
8885
8951
|
*/
|
|
8886
8952
|
async generateMockResponse(_prompt, _checkName, _schema) {
|
|
8887
|
-
await new Promise((
|
|
8953
|
+
await new Promise((resolve15) => setTimeout(resolve15, 500));
|
|
8888
8954
|
const name = (_checkName || "").toLowerCase();
|
|
8889
8955
|
if (name.includes("extract-facts")) {
|
|
8890
8956
|
const arr = Array.from({ length: 6 }, (_, i) => ({
|
|
@@ -9245,7 +9311,7 @@ var init_command_executor = __esm({
|
|
|
9245
9311
|
* Execute command with stdin input
|
|
9246
9312
|
*/
|
|
9247
9313
|
executeWithStdin(command, options) {
|
|
9248
|
-
return new Promise((
|
|
9314
|
+
return new Promise((resolve15, reject) => {
|
|
9249
9315
|
const childProcess = (0, import_child_process.exec)(
|
|
9250
9316
|
command,
|
|
9251
9317
|
{
|
|
@@ -9257,7 +9323,7 @@ var init_command_executor = __esm({
|
|
|
9257
9323
|
if (error && error.killed && (error.code === "ETIMEDOUT" || error.signal === "SIGTERM")) {
|
|
9258
9324
|
reject(new Error(`Command timed out after ${options.timeout || 3e4}ms`));
|
|
9259
9325
|
} else {
|
|
9260
|
-
|
|
9326
|
+
resolve15({
|
|
9261
9327
|
stdout: stdout || "",
|
|
9262
9328
|
stderr: stderr || "",
|
|
9263
9329
|
exitCode: error ? error.code || 1 : 0
|
|
@@ -11112,6 +11178,10 @@ var init_config_schema = __esm({
|
|
|
11112
11178
|
type: "number",
|
|
11113
11179
|
description: "Maximum number of checks to run in parallel (default: 3)"
|
|
11114
11180
|
},
|
|
11181
|
+
max_ai_concurrency: {
|
|
11182
|
+
type: "number",
|
|
11183
|
+
description: "Maximum total concurrent AI API calls across all checks (default: unlimited). When set, creates a shared concurrency limiter that gates every LLM request across all ProbeAgent instances in this run."
|
|
11184
|
+
},
|
|
11115
11185
|
fail_fast: {
|
|
11116
11186
|
type: "boolean",
|
|
11117
11187
|
description: "Stop execution when any check fails (default: false)"
|
|
@@ -11158,6 +11228,18 @@ var init_config_schema = __esm({
|
|
|
11158
11228
|
$ref: "#/definitions/WorkspaceConfig",
|
|
11159
11229
|
description: "Workspace isolation configuration for sandboxed execution"
|
|
11160
11230
|
},
|
|
11231
|
+
sandbox: {
|
|
11232
|
+
type: "string",
|
|
11233
|
+
description: "Workspace-level default sandbox name (all checks use this unless overridden)"
|
|
11234
|
+
},
|
|
11235
|
+
sandboxes: {
|
|
11236
|
+
$ref: "#/definitions/Record%3Cstring%2CSandboxConfig%3E",
|
|
11237
|
+
description: "Named sandbox environment definitions"
|
|
11238
|
+
},
|
|
11239
|
+
sandbox_defaults: {
|
|
11240
|
+
$ref: "#/definitions/SandboxDefaults",
|
|
11241
|
+
description: "Workspace-level sandbox defaults (env allowlist, etc.)"
|
|
11242
|
+
},
|
|
11161
11243
|
slack: {
|
|
11162
11244
|
$ref: "#/definitions/SlackConfig",
|
|
11163
11245
|
description: "Slack configuration"
|
|
@@ -11168,7 +11250,7 @@ var init_config_schema = __esm({
|
|
|
11168
11250
|
},
|
|
11169
11251
|
policy: {
|
|
11170
11252
|
$ref: "#/definitions/PolicyConfig",
|
|
11171
|
-
description: "Enterprise policy engine configuration
|
|
11253
|
+
description: "Enterprise policy engine configuration"
|
|
11172
11254
|
}
|
|
11173
11255
|
},
|
|
11174
11256
|
required: ["version"],
|
|
@@ -11471,7 +11553,7 @@ var init_config_schema = __esm({
|
|
|
11471
11553
|
},
|
|
11472
11554
|
ai_bash_config_js: {
|
|
11473
11555
|
type: "string",
|
|
11474
|
-
description: "JavaScript expression to dynamically compute bash configuration for this AI check. Expression has access to: outputs, inputs, pr, files, env, memory
|
|
11556
|
+
description: "JavaScript expression to dynamically compute bash configuration for this AI check. Expression has access to: outputs, inputs, pr, files, env, memory Must return a BashConfig object with allow/deny arrays.\n\nExample: ``` return outputs['build-config']?.bash_config ?? {}; ```"
|
|
11475
11557
|
},
|
|
11476
11558
|
claude_code: {
|
|
11477
11559
|
$ref: "#/definitions/ClaudeCodeConfig",
|
|
@@ -11737,7 +11819,7 @@ var init_config_schema = __esm({
|
|
|
11737
11819
|
description: "Arguments/inputs for the workflow"
|
|
11738
11820
|
},
|
|
11739
11821
|
overrides: {
|
|
11740
|
-
$ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-
|
|
11822
|
+
$ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-13489-27516-src_types_config.ts-0-51381%3E%3E",
|
|
11741
11823
|
description: "Override specific step configurations in the workflow"
|
|
11742
11824
|
},
|
|
11743
11825
|
output_mapping: {
|
|
@@ -11753,7 +11835,7 @@ var init_config_schema = __esm({
|
|
|
11753
11835
|
description: "Config file path - alternative to workflow ID (loads a Visor config file as workflow)"
|
|
11754
11836
|
},
|
|
11755
11837
|
workflow_overrides: {
|
|
11756
|
-
$ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-
|
|
11838
|
+
$ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-13489-27516-src_types_config.ts-0-51381%3E%3E",
|
|
11757
11839
|
description: "Alias for overrides - workflow step overrides (backward compatibility)"
|
|
11758
11840
|
},
|
|
11759
11841
|
ref: {
|
|
@@ -11823,6 +11905,10 @@ var init_config_schema = __esm({
|
|
|
11823
11905
|
type: "boolean",
|
|
11824
11906
|
description: "Keep worktree after workflow completion (default: false)"
|
|
11825
11907
|
},
|
|
11908
|
+
sandbox: {
|
|
11909
|
+
type: "string",
|
|
11910
|
+
description: "Sandbox name to use for this check (overrides workspace-level default)"
|
|
11911
|
+
},
|
|
11826
11912
|
policy: {
|
|
11827
11913
|
$ref: "#/definitions/StepPolicyOverride",
|
|
11828
11914
|
description: "Per-step policy override (enterprise)"
|
|
@@ -11967,6 +12053,14 @@ var init_config_schema = __esm({
|
|
|
11967
12053
|
completion_prompt: {
|
|
11968
12054
|
type: "string",
|
|
11969
12055
|
description: "Completion prompt for post-completion validation/review (runs after attempt_completion)"
|
|
12056
|
+
},
|
|
12057
|
+
enable_scheduler: {
|
|
12058
|
+
type: "boolean",
|
|
12059
|
+
description: "Enable the schedule tool for scheduling workflow executions (requires scheduler configuration)"
|
|
12060
|
+
},
|
|
12061
|
+
enableExecutePlan: {
|
|
12062
|
+
type: "boolean",
|
|
12063
|
+
description: "Enable the execute_plan DSL orchestration tool (replaces analyze_all when enabled)"
|
|
11970
12064
|
}
|
|
11971
12065
|
},
|
|
11972
12066
|
additionalProperties: false,
|
|
@@ -12429,7 +12523,7 @@ var init_config_schema = __esm({
|
|
|
12429
12523
|
description: "Custom output name (defaults to workflow name)"
|
|
12430
12524
|
},
|
|
12431
12525
|
overrides: {
|
|
12432
|
-
$ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-
|
|
12526
|
+
$ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-13489-27516-src_types_config.ts-0-51381%3E%3E",
|
|
12433
12527
|
description: "Step overrides"
|
|
12434
12528
|
},
|
|
12435
12529
|
output_mapping: {
|
|
@@ -12444,13 +12538,13 @@ var init_config_schema = __esm({
|
|
|
12444
12538
|
"^x-": {}
|
|
12445
12539
|
}
|
|
12446
12540
|
},
|
|
12447
|
-
"Record<string,Partial<interface-src_types_config.ts-
|
|
12541
|
+
"Record<string,Partial<interface-src_types_config.ts-13489-27516-src_types_config.ts-0-51381>>": {
|
|
12448
12542
|
type: "object",
|
|
12449
12543
|
additionalProperties: {
|
|
12450
|
-
$ref: "#/definitions/Partial%3Cinterface-src_types_config.ts-
|
|
12544
|
+
$ref: "#/definitions/Partial%3Cinterface-src_types_config.ts-13489-27516-src_types_config.ts-0-51381%3E"
|
|
12451
12545
|
}
|
|
12452
12546
|
},
|
|
12453
|
-
"Partial<interface-src_types_config.ts-
|
|
12547
|
+
"Partial<interface-src_types_config.ts-13489-27516-src_types_config.ts-0-51381>": {
|
|
12454
12548
|
type: "object",
|
|
12455
12549
|
additionalProperties: false
|
|
12456
12550
|
},
|
|
@@ -12564,9 +12658,9 @@ var init_config_schema = __esm({
|
|
|
12564
12658
|
run: {
|
|
12565
12659
|
type: "array",
|
|
12566
12660
|
items: {
|
|
12567
|
-
|
|
12661
|
+
$ref: "#/definitions/OnSuccessRunItem"
|
|
12568
12662
|
},
|
|
12569
|
-
description: "Post-success steps to run"
|
|
12663
|
+
description: "Post-success steps to run - can be step names or rich invocations with arguments"
|
|
12570
12664
|
},
|
|
12571
12665
|
goto: {
|
|
12572
12666
|
type: "string",
|
|
@@ -12598,6 +12692,20 @@ var init_config_schema = __esm({
|
|
|
12598
12692
|
"^x-": {}
|
|
12599
12693
|
}
|
|
12600
12694
|
},
|
|
12695
|
+
OnSuccessRunItem: {
|
|
12696
|
+
anyOf: [
|
|
12697
|
+
{
|
|
12698
|
+
type: "string"
|
|
12699
|
+
},
|
|
12700
|
+
{
|
|
12701
|
+
$ref: "#/definitions/OnInitStepInvocation"
|
|
12702
|
+
},
|
|
12703
|
+
{
|
|
12704
|
+
$ref: "#/definitions/OnInitWorkflowInvocation"
|
|
12705
|
+
}
|
|
12706
|
+
],
|
|
12707
|
+
description: "Success routing run item - can be step name, step with args, or workflow with args"
|
|
12708
|
+
},
|
|
12601
12709
|
OnFinishConfig: {
|
|
12602
12710
|
type: "object",
|
|
12603
12711
|
properties: {
|
|
@@ -12638,6 +12746,40 @@ var init_config_schema = __esm({
|
|
|
12638
12746
|
"^x-": {}
|
|
12639
12747
|
}
|
|
12640
12748
|
},
|
|
12749
|
+
StepPolicyOverride: {
|
|
12750
|
+
type: "object",
|
|
12751
|
+
properties: {
|
|
12752
|
+
require: {
|
|
12753
|
+
anyOf: [
|
|
12754
|
+
{
|
|
12755
|
+
type: "string"
|
|
12756
|
+
},
|
|
12757
|
+
{
|
|
12758
|
+
type: "array",
|
|
12759
|
+
items: {
|
|
12760
|
+
type: "string"
|
|
12761
|
+
}
|
|
12762
|
+
}
|
|
12763
|
+
],
|
|
12764
|
+
description: "Required role(s) - any of these roles suffices"
|
|
12765
|
+
},
|
|
12766
|
+
deny: {
|
|
12767
|
+
type: "array",
|
|
12768
|
+
items: {
|
|
12769
|
+
type: "string"
|
|
12770
|
+
},
|
|
12771
|
+
description: "Explicit deny for roles"
|
|
12772
|
+
},
|
|
12773
|
+
rule: {
|
|
12774
|
+
type: "string",
|
|
12775
|
+
description: "Custom OPA rule path for this step"
|
|
12776
|
+
}
|
|
12777
|
+
},
|
|
12778
|
+
additionalProperties: false,
|
|
12779
|
+
patternProperties: {
|
|
12780
|
+
"^x-": {}
|
|
12781
|
+
}
|
|
12782
|
+
},
|
|
12641
12783
|
OutputConfig: {
|
|
12642
12784
|
type: "object",
|
|
12643
12785
|
properties: {
|
|
@@ -13044,6 +13186,141 @@ var init_config_schema = __esm({
|
|
|
13044
13186
|
"^x-": {}
|
|
13045
13187
|
}
|
|
13046
13188
|
},
|
|
13189
|
+
"Record<string,SandboxConfig>": {
|
|
13190
|
+
type: "object",
|
|
13191
|
+
additionalProperties: {
|
|
13192
|
+
$ref: "#/definitions/SandboxConfig"
|
|
13193
|
+
}
|
|
13194
|
+
},
|
|
13195
|
+
SandboxConfig: {
|
|
13196
|
+
type: "object",
|
|
13197
|
+
properties: {
|
|
13198
|
+
image: {
|
|
13199
|
+
type: "string",
|
|
13200
|
+
description: 'Docker image to use (e.g., "node:20-alpine")'
|
|
13201
|
+
},
|
|
13202
|
+
dockerfile: {
|
|
13203
|
+
type: "string",
|
|
13204
|
+
description: "Path to Dockerfile (relative to config file or absolute)"
|
|
13205
|
+
},
|
|
13206
|
+
dockerfile_inline: {
|
|
13207
|
+
type: "string",
|
|
13208
|
+
description: "Inline Dockerfile content"
|
|
13209
|
+
},
|
|
13210
|
+
compose: {
|
|
13211
|
+
type: "string",
|
|
13212
|
+
description: "Path to docker-compose file"
|
|
13213
|
+
},
|
|
13214
|
+
service: {
|
|
13215
|
+
type: "string",
|
|
13216
|
+
description: "Service name within the compose file"
|
|
13217
|
+
},
|
|
13218
|
+
workdir: {
|
|
13219
|
+
type: "string",
|
|
13220
|
+
description: "Working directory inside container (default: /workspace)"
|
|
13221
|
+
},
|
|
13222
|
+
env_passthrough: {
|
|
13223
|
+
type: "array",
|
|
13224
|
+
items: {
|
|
13225
|
+
type: "string"
|
|
13226
|
+
},
|
|
13227
|
+
description: "Glob patterns for host env vars to forward into sandbox"
|
|
13228
|
+
},
|
|
13229
|
+
network: {
|
|
13230
|
+
type: "boolean",
|
|
13231
|
+
description: "Enable/disable network access (default: true)"
|
|
13232
|
+
},
|
|
13233
|
+
read_only: {
|
|
13234
|
+
type: "boolean",
|
|
13235
|
+
description: "Mount repo as read-only (default: false)"
|
|
13236
|
+
},
|
|
13237
|
+
resources: {
|
|
13238
|
+
$ref: "#/definitions/SandboxResourceConfig",
|
|
13239
|
+
description: "Resource limits"
|
|
13240
|
+
},
|
|
13241
|
+
visor_path: {
|
|
13242
|
+
type: "string",
|
|
13243
|
+
description: "Where visor is mounted inside container (default: /opt/visor)"
|
|
13244
|
+
},
|
|
13245
|
+
cache: {
|
|
13246
|
+
$ref: "#/definitions/SandboxCacheConfig",
|
|
13247
|
+
description: "Cache volume configuration"
|
|
13248
|
+
}
|
|
13249
|
+
},
|
|
13250
|
+
additionalProperties: false,
|
|
13251
|
+
description: "Configuration for a single sandbox environment",
|
|
13252
|
+
patternProperties: {
|
|
13253
|
+
"^x-": {}
|
|
13254
|
+
}
|
|
13255
|
+
},
|
|
13256
|
+
SandboxResourceConfig: {
|
|
13257
|
+
type: "object",
|
|
13258
|
+
properties: {
|
|
13259
|
+
memory: {
|
|
13260
|
+
type: "string",
|
|
13261
|
+
description: 'Memory limit (e.g., "512m", "2g")'
|
|
13262
|
+
},
|
|
13263
|
+
cpu: {
|
|
13264
|
+
type: "number",
|
|
13265
|
+
description: "CPU limit (e.g., 1.0, 0.5)"
|
|
13266
|
+
}
|
|
13267
|
+
},
|
|
13268
|
+
additionalProperties: false,
|
|
13269
|
+
description: "Resource limits for sandbox containers",
|
|
13270
|
+
patternProperties: {
|
|
13271
|
+
"^x-": {}
|
|
13272
|
+
}
|
|
13273
|
+
},
|
|
13274
|
+
SandboxCacheConfig: {
|
|
13275
|
+
type: "object",
|
|
13276
|
+
properties: {
|
|
13277
|
+
prefix: {
|
|
13278
|
+
type: "string",
|
|
13279
|
+
description: "Liquid template for cache scope prefix (default: git branch)"
|
|
13280
|
+
},
|
|
13281
|
+
fallback_prefix: {
|
|
13282
|
+
type: "string",
|
|
13283
|
+
description: "Fallback prefix when current prefix has no cache"
|
|
13284
|
+
},
|
|
13285
|
+
paths: {
|
|
13286
|
+
type: "array",
|
|
13287
|
+
items: {
|
|
13288
|
+
type: "string"
|
|
13289
|
+
},
|
|
13290
|
+
description: "Paths inside the container to cache"
|
|
13291
|
+
},
|
|
13292
|
+
ttl: {
|
|
13293
|
+
type: "string",
|
|
13294
|
+
description: 'Time-to-live for cache volumes (e.g., "7d", "24h")'
|
|
13295
|
+
},
|
|
13296
|
+
max_scopes: {
|
|
13297
|
+
type: "number",
|
|
13298
|
+
description: "Maximum number of cache scopes to keep"
|
|
13299
|
+
}
|
|
13300
|
+
},
|
|
13301
|
+
required: ["paths"],
|
|
13302
|
+
additionalProperties: false,
|
|
13303
|
+
description: "Cache configuration for sandbox volumes",
|
|
13304
|
+
patternProperties: {
|
|
13305
|
+
"^x-": {}
|
|
13306
|
+
}
|
|
13307
|
+
},
|
|
13308
|
+
SandboxDefaults: {
|
|
13309
|
+
type: "object",
|
|
13310
|
+
properties: {
|
|
13311
|
+
env_passthrough: {
|
|
13312
|
+
type: "array",
|
|
13313
|
+
items: {
|
|
13314
|
+
type: "string"
|
|
13315
|
+
},
|
|
13316
|
+
description: "Base env var patterns for all sandboxes (replaces hardcoded defaults when set)"
|
|
13317
|
+
}
|
|
13318
|
+
},
|
|
13319
|
+
additionalProperties: false,
|
|
13320
|
+
patternProperties: {
|
|
13321
|
+
"^x-": {}
|
|
13322
|
+
}
|
|
13323
|
+
},
|
|
13047
13324
|
SlackConfig: {
|
|
13048
13325
|
type: "object",
|
|
13049
13326
|
properties: {
|
|
@@ -13103,7 +13380,16 @@ var init_config_schema = __esm({
|
|
|
13103
13380
|
properties: {
|
|
13104
13381
|
path: {
|
|
13105
13382
|
type: "string",
|
|
13106
|
-
description: "Path to schedules JSON file (
|
|
13383
|
+
description: "Path to schedules JSON file (legacy, triggers auto-migration)"
|
|
13384
|
+
},
|
|
13385
|
+
driver: {
|
|
13386
|
+
type: "string",
|
|
13387
|
+
enum: ["sqlite", "postgresql", "mysql", "mssql"],
|
|
13388
|
+
description: "Database driver (default: 'sqlite')"
|
|
13389
|
+
},
|
|
13390
|
+
connection: {
|
|
13391
|
+
$ref: "#/definitions/SchedulerStorageConnectionConfig",
|
|
13392
|
+
description: "Database connection configuration"
|
|
13107
13393
|
}
|
|
13108
13394
|
},
|
|
13109
13395
|
additionalProperties: false,
|
|
@@ -13112,6 +13398,10 @@ var init_config_schema = __esm({
|
|
|
13112
13398
|
"^x-": {}
|
|
13113
13399
|
}
|
|
13114
13400
|
},
|
|
13401
|
+
ha: {
|
|
13402
|
+
$ref: "#/definitions/SchedulerHAConfig",
|
|
13403
|
+
description: "High-availability configuration for multi-node deployments"
|
|
13404
|
+
},
|
|
13115
13405
|
limits: {
|
|
13116
13406
|
$ref: "#/definitions/SchedulerLimitsConfig",
|
|
13117
13407
|
description: "Limits for dynamic schedules"
|
|
@@ -13139,44 +13429,123 @@ var init_config_schema = __esm({
|
|
|
13139
13429
|
"^x-": {}
|
|
13140
13430
|
}
|
|
13141
13431
|
},
|
|
13142
|
-
|
|
13432
|
+
SchedulerStorageConnectionConfig: {
|
|
13143
13433
|
type: "object",
|
|
13144
13434
|
properties: {
|
|
13145
|
-
|
|
13435
|
+
filename: {
|
|
13146
13436
|
type: "string",
|
|
13147
|
-
|
|
13148
|
-
description: "Policy engine mode: 'local' (WASM), 'remote' (HTTP OPA server), or 'disabled'"
|
|
13437
|
+
description: "SQLite database file path (default: '.visor/schedules.db')"
|
|
13149
13438
|
},
|
|
13150
|
-
|
|
13151
|
-
|
|
13152
|
-
description: "
|
|
13439
|
+
host: {
|
|
13440
|
+
type: "string",
|
|
13441
|
+
description: "Database host (PostgreSQL/MySQL/MSSQL)"
|
|
13153
13442
|
},
|
|
13154
|
-
|
|
13443
|
+
port: {
|
|
13444
|
+
type: "number",
|
|
13445
|
+
description: "Database port (PostgreSQL/MySQL/MSSQL)"
|
|
13446
|
+
},
|
|
13447
|
+
database: {
|
|
13155
13448
|
type: "string",
|
|
13156
|
-
description: "
|
|
13449
|
+
description: "Database name (PostgreSQL/MySQL/MSSQL)"
|
|
13157
13450
|
},
|
|
13158
|
-
|
|
13451
|
+
user: {
|
|
13159
13452
|
type: "string",
|
|
13160
|
-
description: "
|
|
13453
|
+
description: "Database user (PostgreSQL/MySQL/MSSQL)"
|
|
13161
13454
|
},
|
|
13162
|
-
|
|
13455
|
+
password: {
|
|
13163
13456
|
type: "string",
|
|
13164
|
-
|
|
13165
|
-
description: "Default decision when policy evaluation fails (default: 'deny'). Use 'warn' for audit mode: violations are logged but not enforced."
|
|
13457
|
+
description: "Database password (PostgreSQL/MySQL/MSSQL)"
|
|
13166
13458
|
},
|
|
13167
|
-
|
|
13168
|
-
|
|
13169
|
-
|
|
13459
|
+
ssl: {
|
|
13460
|
+
anyOf: [
|
|
13461
|
+
{
|
|
13462
|
+
type: "boolean"
|
|
13463
|
+
},
|
|
13464
|
+
{
|
|
13465
|
+
$ref: "#/definitions/SchedulerSslConfig"
|
|
13466
|
+
}
|
|
13467
|
+
],
|
|
13468
|
+
description: "SSL/TLS configuration (PostgreSQL/MySQL/MSSQL)"
|
|
13170
13469
|
},
|
|
13171
|
-
|
|
13470
|
+
connection_string: {
|
|
13471
|
+
type: "string",
|
|
13472
|
+
description: "Connection string URL (e.g., postgresql://user:pass@host/db)"
|
|
13473
|
+
},
|
|
13474
|
+
pool: {
|
|
13172
13475
|
type: "object",
|
|
13173
|
-
|
|
13174
|
-
|
|
13476
|
+
properties: {
|
|
13477
|
+
min: {
|
|
13478
|
+
type: "number"
|
|
13479
|
+
},
|
|
13480
|
+
max: {
|
|
13481
|
+
type: "number"
|
|
13482
|
+
}
|
|
13175
13483
|
},
|
|
13176
|
-
|
|
13484
|
+
additionalProperties: false,
|
|
13485
|
+
description: "Connection pool configuration",
|
|
13486
|
+
patternProperties: {
|
|
13487
|
+
"^x-": {}
|
|
13488
|
+
}
|
|
13177
13489
|
}
|
|
13178
13490
|
},
|
|
13179
13491
|
additionalProperties: false,
|
|
13492
|
+
description: "Scheduler storage connection configuration",
|
|
13493
|
+
patternProperties: {
|
|
13494
|
+
"^x-": {}
|
|
13495
|
+
}
|
|
13496
|
+
},
|
|
13497
|
+
SchedulerSslConfig: {
|
|
13498
|
+
type: "object",
|
|
13499
|
+
properties: {
|
|
13500
|
+
enabled: {
|
|
13501
|
+
type: "boolean",
|
|
13502
|
+
description: "Enable SSL (default: true when SslConfig object is provided)"
|
|
13503
|
+
},
|
|
13504
|
+
reject_unauthorized: {
|
|
13505
|
+
type: "boolean",
|
|
13506
|
+
description: "Reject unauthorized certificates (default: true)"
|
|
13507
|
+
},
|
|
13508
|
+
ca: {
|
|
13509
|
+
type: "string",
|
|
13510
|
+
description: "Path to CA certificate PEM file"
|
|
13511
|
+
},
|
|
13512
|
+
cert: {
|
|
13513
|
+
type: "string",
|
|
13514
|
+
description: "Path to client certificate PEM file"
|
|
13515
|
+
},
|
|
13516
|
+
key: {
|
|
13517
|
+
type: "string",
|
|
13518
|
+
description: "Path to client key PEM file"
|
|
13519
|
+
}
|
|
13520
|
+
},
|
|
13521
|
+
additionalProperties: false,
|
|
13522
|
+
description: "SSL/TLS configuration for scheduler database connections",
|
|
13523
|
+
patternProperties: {
|
|
13524
|
+
"^x-": {}
|
|
13525
|
+
}
|
|
13526
|
+
},
|
|
13527
|
+
SchedulerHAConfig: {
|
|
13528
|
+
type: "object",
|
|
13529
|
+
properties: {
|
|
13530
|
+
enabled: {
|
|
13531
|
+
type: "boolean",
|
|
13532
|
+
description: "Enable distributed locking for multi-node deployments (default: false)"
|
|
13533
|
+
},
|
|
13534
|
+
node_id: {
|
|
13535
|
+
type: "string",
|
|
13536
|
+
description: "Unique node identifier (default: hostname-pid)"
|
|
13537
|
+
},
|
|
13538
|
+
lock_ttl: {
|
|
13539
|
+
type: "number",
|
|
13540
|
+
description: "Lock time-to-live in seconds (default: 60)"
|
|
13541
|
+
},
|
|
13542
|
+
heartbeat_interval: {
|
|
13543
|
+
type: "number",
|
|
13544
|
+
description: "Heartbeat interval for lock renewal in seconds (default: 15)"
|
|
13545
|
+
}
|
|
13546
|
+
},
|
|
13547
|
+
additionalProperties: false,
|
|
13548
|
+
description: "Scheduler high-availability configuration",
|
|
13180
13549
|
patternProperties: {
|
|
13181
13550
|
"^x-": {}
|
|
13182
13551
|
}
|
|
@@ -13239,45 +13608,6 @@ var init_config_schema = __esm({
|
|
|
13239
13608
|
"^x-": {}
|
|
13240
13609
|
}
|
|
13241
13610
|
},
|
|
13242
|
-
PolicyRoleConfig: {
|
|
13243
|
-
type: "object",
|
|
13244
|
-
properties: {
|
|
13245
|
-
author_association: {
|
|
13246
|
-
type: "array",
|
|
13247
|
-
items: { type: "string" },
|
|
13248
|
-
description: "GitHub author associations that map to this role"
|
|
13249
|
-
},
|
|
13250
|
-
teams: {
|
|
13251
|
-
type: "array",
|
|
13252
|
-
items: { type: "string" },
|
|
13253
|
-
description: "GitHub team slugs"
|
|
13254
|
-
},
|
|
13255
|
-
users: {
|
|
13256
|
-
type: "array",
|
|
13257
|
-
items: { type: "string" },
|
|
13258
|
-
description: "Explicit GitHub usernames"
|
|
13259
|
-
},
|
|
13260
|
-
slack_users: {
|
|
13261
|
-
type: "array",
|
|
13262
|
-
items: { type: "string" },
|
|
13263
|
-
description: "Slack user IDs (e.g., U0123ABC)"
|
|
13264
|
-
},
|
|
13265
|
-
emails: {
|
|
13266
|
-
type: "array",
|
|
13267
|
-
items: { type: "string" },
|
|
13268
|
-
description: "Email addresses for identity matching"
|
|
13269
|
-
},
|
|
13270
|
-
slack_channels: {
|
|
13271
|
-
type: "array",
|
|
13272
|
-
items: { type: "string" },
|
|
13273
|
-
description: "Slack channel IDs \u2014 role only applies when triggered from these channels"
|
|
13274
|
-
}
|
|
13275
|
-
},
|
|
13276
|
-
additionalProperties: false,
|
|
13277
|
-
patternProperties: {
|
|
13278
|
-
"^x-": {}
|
|
13279
|
-
}
|
|
13280
|
-
},
|
|
13281
13611
|
"Record<string,StaticCronJob>": {
|
|
13282
13612
|
type: "object",
|
|
13283
13613
|
additionalProperties: {
|
|
@@ -13343,21 +13673,106 @@ var init_config_schema = __esm({
|
|
|
13343
13673
|
"^x-": {}
|
|
13344
13674
|
}
|
|
13345
13675
|
},
|
|
13346
|
-
|
|
13676
|
+
PolicyConfig: {
|
|
13347
13677
|
type: "object",
|
|
13348
13678
|
properties: {
|
|
13349
|
-
|
|
13350
|
-
|
|
13351
|
-
|
|
13679
|
+
engine: {
|
|
13680
|
+
type: "string",
|
|
13681
|
+
enum: ["local", "remote", "disabled"],
|
|
13682
|
+
description: "Policy engine mode"
|
|
13352
13683
|
},
|
|
13353
|
-
|
|
13354
|
-
|
|
13355
|
-
|
|
13356
|
-
|
|
13684
|
+
rules: {
|
|
13685
|
+
anyOf: [
|
|
13686
|
+
{
|
|
13687
|
+
type: "string"
|
|
13688
|
+
},
|
|
13689
|
+
{
|
|
13690
|
+
type: "array",
|
|
13691
|
+
items: {
|
|
13692
|
+
type: "string"
|
|
13693
|
+
}
|
|
13694
|
+
}
|
|
13695
|
+
],
|
|
13696
|
+
description: "Path to .rego files or .wasm bundle (local mode)"
|
|
13357
13697
|
},
|
|
13358
|
-
|
|
13698
|
+
data: {
|
|
13359
13699
|
type: "string",
|
|
13360
|
-
description: "
|
|
13700
|
+
description: "Path to a JSON file to load as OPA data document"
|
|
13701
|
+
},
|
|
13702
|
+
url: {
|
|
13703
|
+
type: "string",
|
|
13704
|
+
description: "OPA server URL (remote mode)"
|
|
13705
|
+
},
|
|
13706
|
+
fallback: {
|
|
13707
|
+
type: "string",
|
|
13708
|
+
enum: ["allow", "deny", "warn"],
|
|
13709
|
+
description: "Default decision when policy evaluation fails"
|
|
13710
|
+
},
|
|
13711
|
+
timeout: {
|
|
13712
|
+
type: "number",
|
|
13713
|
+
description: "Evaluation timeout in ms (default: 5000)"
|
|
13714
|
+
},
|
|
13715
|
+
roles: {
|
|
13716
|
+
$ref: "#/definitions/Record%3Cstring%2CPolicyRoleConfig%3E",
|
|
13717
|
+
description: "Role definitions: map role names to conditions"
|
|
13718
|
+
}
|
|
13719
|
+
},
|
|
13720
|
+
required: ["engine"],
|
|
13721
|
+
additionalProperties: false,
|
|
13722
|
+
patternProperties: {
|
|
13723
|
+
"^x-": {}
|
|
13724
|
+
}
|
|
13725
|
+
},
|
|
13726
|
+
"Record<string,PolicyRoleConfig>": {
|
|
13727
|
+
type: "object",
|
|
13728
|
+
additionalProperties: {
|
|
13729
|
+
$ref: "#/definitions/PolicyRoleConfig"
|
|
13730
|
+
}
|
|
13731
|
+
},
|
|
13732
|
+
PolicyRoleConfig: {
|
|
13733
|
+
type: "object",
|
|
13734
|
+
properties: {
|
|
13735
|
+
author_association: {
|
|
13736
|
+
type: "array",
|
|
13737
|
+
items: {
|
|
13738
|
+
type: "string"
|
|
13739
|
+
},
|
|
13740
|
+
description: "GitHub author associations that map to this role"
|
|
13741
|
+
},
|
|
13742
|
+
teams: {
|
|
13743
|
+
type: "array",
|
|
13744
|
+
items: {
|
|
13745
|
+
type: "string"
|
|
13746
|
+
},
|
|
13747
|
+
description: "GitHub team slugs (requires GitHub API)"
|
|
13748
|
+
},
|
|
13749
|
+
users: {
|
|
13750
|
+
type: "array",
|
|
13751
|
+
items: {
|
|
13752
|
+
type: "string"
|
|
13753
|
+
},
|
|
13754
|
+
description: "Explicit GitHub usernames"
|
|
13755
|
+
},
|
|
13756
|
+
slack_users: {
|
|
13757
|
+
type: "array",
|
|
13758
|
+
items: {
|
|
13759
|
+
type: "string"
|
|
13760
|
+
},
|
|
13761
|
+
description: 'Slack user IDs (e.g., ["U0123ABC"])'
|
|
13762
|
+
},
|
|
13763
|
+
emails: {
|
|
13764
|
+
type: "array",
|
|
13765
|
+
items: {
|
|
13766
|
+
type: "string"
|
|
13767
|
+
},
|
|
13768
|
+
description: 'Email addresses for identity matching (e.g., ["alice@co.com"])'
|
|
13769
|
+
},
|
|
13770
|
+
slack_channels: {
|
|
13771
|
+
type: "array",
|
|
13772
|
+
items: {
|
|
13773
|
+
type: "string"
|
|
13774
|
+
},
|
|
13775
|
+
description: "Slack channel IDs \u2014 role only applies when triggered from these channels"
|
|
13361
13776
|
}
|
|
13362
13777
|
},
|
|
13363
13778
|
additionalProperties: false,
|
|
@@ -15517,6 +15932,20 @@ var init_workflow_check_provider = __esm({
|
|
|
15517
15932
|
`[WorkflowProvider] Workflow '${workflow.id}' has null/undefined outputs: [${nullOutputs.join(", ")}]. This may indicate value_js expressions are not finding expected data.`
|
|
15518
15933
|
);
|
|
15519
15934
|
}
|
|
15935
|
+
if (!outputs._rawOutput) {
|
|
15936
|
+
const rawParts = [];
|
|
15937
|
+
for (const stepOutput of Object.values(outputsMap)) {
|
|
15938
|
+
if (stepOutput && typeof stepOutput === "object" && typeof stepOutput._rawOutput === "string") {
|
|
15939
|
+
rawParts.push(stepOutput._rawOutput);
|
|
15940
|
+
}
|
|
15941
|
+
}
|
|
15942
|
+
if (rawParts.length > 0) {
|
|
15943
|
+
outputs._rawOutput = rawParts.join("\n\n");
|
|
15944
|
+
logger.debug(
|
|
15945
|
+
`[WorkflowProvider] Propagated _rawOutput from steps (${rawParts.length} blocks, ${outputs._rawOutput.length} chars)`
|
|
15946
|
+
);
|
|
15947
|
+
}
|
|
15948
|
+
}
|
|
15520
15949
|
return outputs;
|
|
15521
15950
|
}
|
|
15522
15951
|
/**
|
|
@@ -15580,17 +16009,17 @@ var init_workflow_check_provider = __esm({
|
|
|
15580
16009
|
* so it can be executed by the state machine as a nested workflow.
|
|
15581
16010
|
*/
|
|
15582
16011
|
async loadWorkflowFromConfigPath(sourcePath, baseDir) {
|
|
15583
|
-
const
|
|
15584
|
-
const
|
|
16012
|
+
const path29 = require("path");
|
|
16013
|
+
const fs25 = require("fs");
|
|
15585
16014
|
const yaml5 = require("js-yaml");
|
|
15586
|
-
const resolved =
|
|
15587
|
-
if (!
|
|
16015
|
+
const resolved = path29.isAbsolute(sourcePath) ? sourcePath : path29.resolve(baseDir, sourcePath);
|
|
16016
|
+
if (!fs25.existsSync(resolved)) {
|
|
15588
16017
|
throw new Error(`Workflow config not found at: ${resolved}`);
|
|
15589
16018
|
}
|
|
15590
|
-
const rawContent =
|
|
16019
|
+
const rawContent = fs25.readFileSync(resolved, "utf8");
|
|
15591
16020
|
const rawData = yaml5.load(rawContent);
|
|
15592
16021
|
if (rawData.imports && Array.isArray(rawData.imports)) {
|
|
15593
|
-
const configDir =
|
|
16022
|
+
const configDir = path29.dirname(resolved);
|
|
15594
16023
|
for (const source of rawData.imports) {
|
|
15595
16024
|
const results = await this.registry.import(source, {
|
|
15596
16025
|
basePath: configDir,
|
|
@@ -15620,8 +16049,8 @@ ${errors}`);
|
|
|
15620
16049
|
if (!steps || Object.keys(steps).length === 0) {
|
|
15621
16050
|
throw new Error(`Config '${resolved}' does not contain any steps to execute as a workflow`);
|
|
15622
16051
|
}
|
|
15623
|
-
const id =
|
|
15624
|
-
const name = loaded.name || `Workflow from ${
|
|
16052
|
+
const id = path29.basename(resolved).replace(/\.(ya?ml)$/i, "");
|
|
16053
|
+
const name = loaded.name || `Workflow from ${path29.basename(resolved)}`;
|
|
15625
16054
|
const workflowDef = {
|
|
15626
16055
|
id,
|
|
15627
16056
|
name,
|
|
@@ -15689,7 +16118,7 @@ function workflowInputsToJsonSchema(inputs) {
|
|
|
15689
16118
|
required: required.length > 0 ? required : void 0
|
|
15690
16119
|
};
|
|
15691
16120
|
}
|
|
15692
|
-
function createWorkflowToolDefinition(workflow, argsOverrides) {
|
|
16121
|
+
function createWorkflowToolDefinition(workflow, argsOverrides, nameOverride) {
|
|
15693
16122
|
const baseSchema = workflowInputsToJsonSchema(workflow.inputs);
|
|
15694
16123
|
let inputSchema = baseSchema;
|
|
15695
16124
|
if (argsOverrides && baseSchema && typeof baseSchema === "object") {
|
|
@@ -15707,7 +16136,7 @@ function createWorkflowToolDefinition(workflow, argsOverrides) {
|
|
|
15707
16136
|
};
|
|
15708
16137
|
}
|
|
15709
16138
|
return {
|
|
15710
|
-
name: workflow.id,
|
|
16139
|
+
name: nameOverride || workflow.id,
|
|
15711
16140
|
description: workflow.description || `Execute the ${workflow.name} workflow`,
|
|
15712
16141
|
inputSchema,
|
|
15713
16142
|
// Workflow tools don't have an exec command - they're executed specially
|
|
@@ -15757,6 +16186,16 @@ async function executeWorkflowAsTool(workflowId, args, context2, argsOverrides)
|
|
|
15757
16186
|
);
|
|
15758
16187
|
logger.debug(`[WorkflowToolExecutor] Workflow '${workflowId}' output preview: ${outputPreview}`);
|
|
15759
16188
|
if (output !== void 0) {
|
|
16189
|
+
if (output && typeof output === "object" && typeof output._rawOutput === "string") {
|
|
16190
|
+
const rawOutput = output._rawOutput;
|
|
16191
|
+
const cleanOutput = { ...output };
|
|
16192
|
+
delete cleanOutput._rawOutput;
|
|
16193
|
+
const jsonStr = JSON.stringify(cleanOutput, null, 2);
|
|
16194
|
+
logger.debug(
|
|
16195
|
+
`[WorkflowToolExecutor] Wrapping _rawOutput (${rawOutput.length} chars) in RAW_OUTPUT delimiters for '${workflowId}'`
|
|
16196
|
+
);
|
|
16197
|
+
return jsonStr + "\n<<<RAW_OUTPUT>>>\n" + rawOutput + "\n<<<END_RAW_OUTPUT>>>";
|
|
16198
|
+
}
|
|
15760
16199
|
return output;
|
|
15761
16200
|
}
|
|
15762
16201
|
if (result.content) {
|
|
@@ -15781,7 +16220,7 @@ function resolveWorkflowToolFromItem(item) {
|
|
|
15781
16220
|
logger.warn(`[WorkflowToolExecutor] Workflow '${item.workflow}' not found in registry`);
|
|
15782
16221
|
return void 0;
|
|
15783
16222
|
}
|
|
15784
|
-
return createWorkflowToolDefinition(workflow, item.args);
|
|
16223
|
+
return createWorkflowToolDefinition(workflow, item.args, item.name);
|
|
15785
16224
|
}
|
|
15786
16225
|
return void 0;
|
|
15787
16226
|
}
|
|
@@ -16240,8 +16679,8 @@ async function createStoreBackend(storageConfig, haConfig) {
|
|
|
16240
16679
|
case "mssql": {
|
|
16241
16680
|
try {
|
|
16242
16681
|
const loaderPath = "../../enterprise/loader";
|
|
16243
|
-
const { loadEnterpriseStoreBackend } = await import(loaderPath);
|
|
16244
|
-
return await
|
|
16682
|
+
const { loadEnterpriseStoreBackend: loadEnterpriseStoreBackend2 } = await import(loaderPath);
|
|
16683
|
+
return await loadEnterpriseStoreBackend2(driver, storageConfig, haConfig);
|
|
16245
16684
|
} catch (err) {
|
|
16246
16685
|
const msg = err instanceof Error ? err.message : String(err);
|
|
16247
16686
|
logger.error(`[StoreFactory] Failed to load enterprise ${driver} backend: ${msg}`);
|
|
@@ -17508,7 +17947,7 @@ var init_mcp_custom_sse_server = __esm({
|
|
|
17508
17947
|
* Returns the actual bound port number
|
|
17509
17948
|
*/
|
|
17510
17949
|
async start() {
|
|
17511
|
-
return new Promise((
|
|
17950
|
+
return new Promise((resolve15, reject) => {
|
|
17512
17951
|
try {
|
|
17513
17952
|
this.server = import_http.default.createServer((req, res) => {
|
|
17514
17953
|
this.handleRequest(req, res).catch((error) => {
|
|
@@ -17542,7 +17981,7 @@ var init_mcp_custom_sse_server = __esm({
|
|
|
17542
17981
|
);
|
|
17543
17982
|
}
|
|
17544
17983
|
this.startKeepalive();
|
|
17545
|
-
|
|
17984
|
+
resolve15(this.port);
|
|
17546
17985
|
});
|
|
17547
17986
|
} catch (error) {
|
|
17548
17987
|
reject(error);
|
|
@@ -17605,7 +18044,7 @@ var init_mcp_custom_sse_server = __esm({
|
|
|
17605
18044
|
logger.debug(
|
|
17606
18045
|
`[CustomToolsSSEServer:${this.sessionId}] Grace period before stop: ${waitMs}ms (activeToolCalls=${this.activeToolCalls})`
|
|
17607
18046
|
);
|
|
17608
|
-
await new Promise((
|
|
18047
|
+
await new Promise((resolve15) => setTimeout(resolve15, waitMs));
|
|
17609
18048
|
}
|
|
17610
18049
|
}
|
|
17611
18050
|
if (this.activeToolCalls > 0) {
|
|
@@ -17614,7 +18053,7 @@ var init_mcp_custom_sse_server = __esm({
|
|
|
17614
18053
|
`[CustomToolsSSEServer:${this.sessionId}] Waiting for ${this.activeToolCalls} active tool call(s) before stop`
|
|
17615
18054
|
);
|
|
17616
18055
|
while (this.activeToolCalls > 0 && Date.now() - startedAt < effectiveDrainTimeoutMs) {
|
|
17617
|
-
await new Promise((
|
|
18056
|
+
await new Promise((resolve15) => setTimeout(resolve15, 250));
|
|
17618
18057
|
}
|
|
17619
18058
|
if (this.activeToolCalls > 0) {
|
|
17620
18059
|
logger.warn(
|
|
@@ -17639,21 +18078,21 @@ var init_mcp_custom_sse_server = __esm({
|
|
|
17639
18078
|
}
|
|
17640
18079
|
this.connections.clear();
|
|
17641
18080
|
if (this.server) {
|
|
17642
|
-
await new Promise((
|
|
18081
|
+
await new Promise((resolve15, reject) => {
|
|
17643
18082
|
const timeout = setTimeout(() => {
|
|
17644
18083
|
if (this.debug) {
|
|
17645
18084
|
logger.debug(
|
|
17646
18085
|
`[CustomToolsSSEServer:${this.sessionId}] Force closing server after timeout`
|
|
17647
18086
|
);
|
|
17648
18087
|
}
|
|
17649
|
-
this.server?.close(() =>
|
|
18088
|
+
this.server?.close(() => resolve15());
|
|
17650
18089
|
}, 5e3);
|
|
17651
18090
|
this.server.close((error) => {
|
|
17652
18091
|
clearTimeout(timeout);
|
|
17653
18092
|
if (error) {
|
|
17654
18093
|
reject(error);
|
|
17655
18094
|
} else {
|
|
17656
|
-
|
|
18095
|
+
resolve15();
|
|
17657
18096
|
}
|
|
17658
18097
|
});
|
|
17659
18098
|
});
|
|
@@ -18067,7 +18506,7 @@ var init_mcp_custom_sse_server = __esm({
|
|
|
18067
18506
|
logger.warn(
|
|
18068
18507
|
`[CustomToolsSSEServer:${this.sessionId}] Tool ${toolName} failed (attempt ${attempt + 1}/${retryCount + 1}): ${errorMsg}. Retrying in ${delay}ms`
|
|
18069
18508
|
);
|
|
18070
|
-
await new Promise((
|
|
18509
|
+
await new Promise((resolve15) => setTimeout(resolve15, delay));
|
|
18071
18510
|
attempt++;
|
|
18072
18511
|
}
|
|
18073
18512
|
}
|
|
@@ -18162,7 +18601,7 @@ var init_ai_check_provider = __esm({
|
|
|
18162
18601
|
init_sandbox();
|
|
18163
18602
|
init_schedule_tool();
|
|
18164
18603
|
init_schedule_tool_handler();
|
|
18165
|
-
AICheckProvider = class extends CheckProvider {
|
|
18604
|
+
AICheckProvider = class _AICheckProvider extends CheckProvider {
|
|
18166
18605
|
aiReviewService;
|
|
18167
18606
|
liquidEngine;
|
|
18168
18607
|
sandbox = null;
|
|
@@ -18332,9 +18771,9 @@ var init_ai_check_provider = __esm({
|
|
|
18332
18771
|
} else {
|
|
18333
18772
|
resolvedPath = import_path6.default.resolve(process.cwd(), str);
|
|
18334
18773
|
}
|
|
18335
|
-
const
|
|
18774
|
+
const fs25 = require("fs").promises;
|
|
18336
18775
|
try {
|
|
18337
|
-
const stat = await
|
|
18776
|
+
const stat = await fs25.stat(resolvedPath);
|
|
18338
18777
|
return stat.isFile();
|
|
18339
18778
|
} catch {
|
|
18340
18779
|
return hasFileExtension && (isRelativePath || isAbsolutePath || hasPathSeparators);
|
|
@@ -18744,6 +19183,9 @@ ${preview}`);
|
|
|
18744
19183
|
if (aiAny2.enableTasks !== void 0) {
|
|
18745
19184
|
aiConfig.enableTasks = aiAny2.enableTasks;
|
|
18746
19185
|
}
|
|
19186
|
+
if (aiAny2.enableExecutePlan !== void 0) {
|
|
19187
|
+
aiConfig.enableExecutePlan = aiAny2.enableExecutePlan;
|
|
19188
|
+
}
|
|
18747
19189
|
if (aiAny2.allowEdit !== void 0) {
|
|
18748
19190
|
aiConfig.allowEdit = aiAny2.allowEdit;
|
|
18749
19191
|
}
|
|
@@ -18889,13 +19331,10 @@ ${preview}`);
|
|
|
18889
19331
|
if (decision.capabilities.allowEdit === false) aiConfig.allowEdit = false;
|
|
18890
19332
|
if (decision.capabilities.allowBash === false) aiConfig.allowBash = false;
|
|
18891
19333
|
if (decision.capabilities.allowedTools) {
|
|
18892
|
-
|
|
18893
|
-
aiConfig.allowedTools
|
|
18894
|
-
|
|
18895
|
-
|
|
18896
|
-
} else {
|
|
18897
|
-
aiConfig.allowedTools = decision.capabilities.allowedTools;
|
|
18898
|
-
}
|
|
19334
|
+
aiConfig.allowedTools = _AICheckProvider.intersectAllowedTools(
|
|
19335
|
+
aiConfig.allowedTools,
|
|
19336
|
+
decision.capabilities.allowedTools
|
|
19337
|
+
);
|
|
18899
19338
|
}
|
|
18900
19339
|
}
|
|
18901
19340
|
} catch (err) {
|
|
@@ -19016,7 +19455,8 @@ ${preview}`);
|
|
|
19016
19455
|
if (cfg.workflow && typeof cfg.workflow === "string") {
|
|
19017
19456
|
workflowEntriesFromMcp.push({
|
|
19018
19457
|
workflow: cfg.workflow,
|
|
19019
|
-
args: cfg.inputs
|
|
19458
|
+
args: cfg.inputs,
|
|
19459
|
+
name: serverName
|
|
19020
19460
|
});
|
|
19021
19461
|
mcpEntriesToRemove.push(serverName);
|
|
19022
19462
|
logger.debug(
|
|
@@ -19157,6 +19597,27 @@ ${preview}`);
|
|
|
19157
19597
|
);
|
|
19158
19598
|
}
|
|
19159
19599
|
}
|
|
19600
|
+
const allowedToolsJsExpr = config.ai_allowed_tools_js;
|
|
19601
|
+
if (allowedToolsJsExpr && _dependencyResults) {
|
|
19602
|
+
try {
|
|
19603
|
+
const dynamicAllowedTools = this.evaluateAllowedToolsJs(
|
|
19604
|
+
allowedToolsJsExpr,
|
|
19605
|
+
prInfo,
|
|
19606
|
+
_dependencyResults,
|
|
19607
|
+
config
|
|
19608
|
+
);
|
|
19609
|
+
if (dynamicAllowedTools !== null) {
|
|
19610
|
+
aiConfig.allowedTools = dynamicAllowedTools;
|
|
19611
|
+
this.logDebug(
|
|
19612
|
+
`[AI Provider] ai_allowed_tools_js evaluated to: ${JSON.stringify(dynamicAllowedTools)}`
|
|
19613
|
+
);
|
|
19614
|
+
}
|
|
19615
|
+
} catch (error) {
|
|
19616
|
+
logger.error(
|
|
19617
|
+
`[AICheckProvider] Failed to evaluate ai_allowed_tools_js: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
19618
|
+
);
|
|
19619
|
+
}
|
|
19620
|
+
}
|
|
19160
19621
|
const templateContext = {
|
|
19161
19622
|
pr: {
|
|
19162
19623
|
number: prInfo.number,
|
|
@@ -19660,6 +20121,65 @@ ${processedPrompt}` : processedPrompt;
|
|
|
19660
20121
|
return {};
|
|
19661
20122
|
}
|
|
19662
20123
|
}
|
|
20124
|
+
/**
|
|
20125
|
+
* Evaluate ai_allowed_tools_js expression to dynamically compute allowed tools list.
|
|
20126
|
+
* Returns a string array of tool names, or null if the expression returns a non-array.
|
|
20127
|
+
*/
|
|
20128
|
+
evaluateAllowedToolsJs(expression, prInfo, dependencyResults, config) {
|
|
20129
|
+
if (!this.sandbox) {
|
|
20130
|
+
this.sandbox = createSecureSandbox();
|
|
20131
|
+
}
|
|
20132
|
+
const outputs = {};
|
|
20133
|
+
for (const [checkId, result] of dependencyResults.entries()) {
|
|
20134
|
+
const summary = result;
|
|
20135
|
+
outputs[checkId] = summary.output !== void 0 ? summary.output : summary;
|
|
20136
|
+
}
|
|
20137
|
+
const jsContext = {
|
|
20138
|
+
outputs,
|
|
20139
|
+
inputs: config.inputs || {},
|
|
20140
|
+
pr: {
|
|
20141
|
+
number: prInfo.number,
|
|
20142
|
+
title: prInfo.title,
|
|
20143
|
+
description: prInfo.body,
|
|
20144
|
+
author: prInfo.author,
|
|
20145
|
+
branch: prInfo.head,
|
|
20146
|
+
base: prInfo.base,
|
|
20147
|
+
authorAssociation: prInfo.authorAssociation
|
|
20148
|
+
},
|
|
20149
|
+
files: prInfo.files?.map((f) => ({
|
|
20150
|
+
filename: f.filename,
|
|
20151
|
+
status: f.status,
|
|
20152
|
+
additions: f.additions,
|
|
20153
|
+
deletions: f.deletions,
|
|
20154
|
+
changes: f.changes
|
|
20155
|
+
})) || [],
|
|
20156
|
+
env: this.buildSafeEnv(),
|
|
20157
|
+
memory: config.__memoryAccessor || {}
|
|
20158
|
+
};
|
|
20159
|
+
try {
|
|
20160
|
+
const result = compileAndRun(this.sandbox, expression, jsContext, {
|
|
20161
|
+
injectLog: true,
|
|
20162
|
+
wrapFunction: true,
|
|
20163
|
+
logPrefix: "[ai_allowed_tools_js]"
|
|
20164
|
+
});
|
|
20165
|
+
if (!Array.isArray(result)) {
|
|
20166
|
+
logger.warn(
|
|
20167
|
+
`[AICheckProvider] ai_allowed_tools_js must return an array, got ${typeof result}`
|
|
20168
|
+
);
|
|
20169
|
+
return null;
|
|
20170
|
+
}
|
|
20171
|
+
const tools = result.filter((item) => typeof item === "string");
|
|
20172
|
+
logger.debug(
|
|
20173
|
+
`[AICheckProvider] ai_allowed_tools_js evaluated to ${tools.length} tools: ${tools.join(", ")}`
|
|
20174
|
+
);
|
|
20175
|
+
return tools;
|
|
20176
|
+
} catch (error) {
|
|
20177
|
+
logger.error(
|
|
20178
|
+
`[AICheckProvider] Failed to evaluate ai_allowed_tools_js: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
20179
|
+
);
|
|
20180
|
+
return null;
|
|
20181
|
+
}
|
|
20182
|
+
}
|
|
19663
20183
|
/**
|
|
19664
20184
|
* Build a safe subset of environment variables for sandbox access.
|
|
19665
20185
|
* Excludes sensitive keys like API keys, secrets, tokens.
|
|
@@ -19721,6 +20241,22 @@ ${processedPrompt}` : processedPrompt;
|
|
|
19721
20241
|
}
|
|
19722
20242
|
return tools;
|
|
19723
20243
|
}
|
|
20244
|
+
/**
|
|
20245
|
+
* Intersect config-level allowedTools with policy-level allowedTools.
|
|
20246
|
+
* When the config list contains glob patterns ("*", "!" exclusions),
|
|
20247
|
+
* it is passed through unchanged — ProbeAgent resolves those patterns.
|
|
20248
|
+
* Literal tool name lists are intersected normally.
|
|
20249
|
+
*/
|
|
20250
|
+
static intersectAllowedTools(configTools, policyTools) {
|
|
20251
|
+
if (!configTools) {
|
|
20252
|
+
return policyTools;
|
|
20253
|
+
}
|
|
20254
|
+
const hasGlobs = configTools.some((t) => t === "*" || t.startsWith("!"));
|
|
20255
|
+
if (hasGlobs) {
|
|
20256
|
+
return configTools;
|
|
20257
|
+
}
|
|
20258
|
+
return configTools.filter((t) => policyTools.includes(t));
|
|
20259
|
+
}
|
|
19724
20260
|
getSupportedConfigKeys() {
|
|
19725
20261
|
return [
|
|
19726
20262
|
"type",
|
|
@@ -19736,6 +20272,7 @@ ${processedPrompt}` : processedPrompt;
|
|
|
19736
20272
|
"ai.mcpServers",
|
|
19737
20273
|
"ai.enableDelegate",
|
|
19738
20274
|
"ai.enableTasks",
|
|
20275
|
+
"ai.enableExecutePlan",
|
|
19739
20276
|
// legacy persona/prompt keys supported in config
|
|
19740
20277
|
"ai_persona",
|
|
19741
20278
|
"ai_prompt_type",
|
|
@@ -19757,6 +20294,7 @@ ${processedPrompt}` : processedPrompt;
|
|
|
19757
20294
|
"ai_custom_tools",
|
|
19758
20295
|
"ai_custom_tools_js",
|
|
19759
20296
|
"ai_bash_config_js",
|
|
20297
|
+
"ai_allowed_tools_js",
|
|
19760
20298
|
"env"
|
|
19761
20299
|
];
|
|
19762
20300
|
}
|
|
@@ -24115,14 +24653,14 @@ var require_util = __commonJS({
|
|
|
24115
24653
|
}
|
|
24116
24654
|
const port = url.port != null ? url.port : url.protocol === "https:" ? 443 : 80;
|
|
24117
24655
|
let origin = url.origin != null ? url.origin : `${url.protocol}//${url.hostname}:${port}`;
|
|
24118
|
-
let
|
|
24656
|
+
let path29 = url.path != null ? url.path : `${url.pathname || ""}${url.search || ""}`;
|
|
24119
24657
|
if (origin.endsWith("/")) {
|
|
24120
24658
|
origin = origin.substring(0, origin.length - 1);
|
|
24121
24659
|
}
|
|
24122
|
-
if (
|
|
24123
|
-
|
|
24660
|
+
if (path29 && !path29.startsWith("/")) {
|
|
24661
|
+
path29 = `/${path29}`;
|
|
24124
24662
|
}
|
|
24125
|
-
url = new URL(origin +
|
|
24663
|
+
url = new URL(origin + path29);
|
|
24126
24664
|
}
|
|
24127
24665
|
return url;
|
|
24128
24666
|
}
|
|
@@ -25736,20 +26274,20 @@ var require_parseParams = __commonJS({
|
|
|
25736
26274
|
var require_basename = __commonJS({
|
|
25737
26275
|
"node_modules/@fastify/busboy/lib/utils/basename.js"(exports2, module2) {
|
|
25738
26276
|
"use strict";
|
|
25739
|
-
module2.exports = function basename4(
|
|
25740
|
-
if (typeof
|
|
26277
|
+
module2.exports = function basename4(path29) {
|
|
26278
|
+
if (typeof path29 !== "string") {
|
|
25741
26279
|
return "";
|
|
25742
26280
|
}
|
|
25743
|
-
for (var i =
|
|
25744
|
-
switch (
|
|
26281
|
+
for (var i = path29.length - 1; i >= 0; --i) {
|
|
26282
|
+
switch (path29.charCodeAt(i)) {
|
|
25745
26283
|
case 47:
|
|
25746
26284
|
// '/'
|
|
25747
26285
|
case 92:
|
|
25748
|
-
|
|
25749
|
-
return
|
|
26286
|
+
path29 = path29.slice(i + 1);
|
|
26287
|
+
return path29 === ".." || path29 === "." ? "" : path29;
|
|
25750
26288
|
}
|
|
25751
26289
|
}
|
|
25752
|
-
return
|
|
26290
|
+
return path29 === ".." || path29 === "." ? "" : path29;
|
|
25753
26291
|
};
|
|
25754
26292
|
}
|
|
25755
26293
|
});
|
|
@@ -26753,11 +27291,11 @@ var require_util2 = __commonJS({
|
|
|
26753
27291
|
var assert = require("assert");
|
|
26754
27292
|
var { isUint8Array } = require("util/types");
|
|
26755
27293
|
var supportedHashes = [];
|
|
26756
|
-
var
|
|
27294
|
+
var crypto4;
|
|
26757
27295
|
try {
|
|
26758
|
-
|
|
27296
|
+
crypto4 = require("crypto");
|
|
26759
27297
|
const possibleRelevantHashes = ["sha256", "sha384", "sha512"];
|
|
26760
|
-
supportedHashes =
|
|
27298
|
+
supportedHashes = crypto4.getHashes().filter((hash) => possibleRelevantHashes.includes(hash));
|
|
26761
27299
|
} catch {
|
|
26762
27300
|
}
|
|
26763
27301
|
function responseURL(response) {
|
|
@@ -27034,7 +27572,7 @@ var require_util2 = __commonJS({
|
|
|
27034
27572
|
}
|
|
27035
27573
|
}
|
|
27036
27574
|
function bytesMatch(bytes, metadataList) {
|
|
27037
|
-
if (
|
|
27575
|
+
if (crypto4 === void 0) {
|
|
27038
27576
|
return true;
|
|
27039
27577
|
}
|
|
27040
27578
|
const parsedMetadata = parseMetadata(metadataList);
|
|
@@ -27049,7 +27587,7 @@ var require_util2 = __commonJS({
|
|
|
27049
27587
|
for (const item of metadata) {
|
|
27050
27588
|
const algorithm = item.algo;
|
|
27051
27589
|
const expectedValue = item.hash;
|
|
27052
|
-
let actualValue =
|
|
27590
|
+
let actualValue = crypto4.createHash(algorithm).update(bytes).digest("base64");
|
|
27053
27591
|
if (actualValue[actualValue.length - 1] === "=") {
|
|
27054
27592
|
if (actualValue[actualValue.length - 2] === "=") {
|
|
27055
27593
|
actualValue = actualValue.slice(0, -2);
|
|
@@ -27142,8 +27680,8 @@ var require_util2 = __commonJS({
|
|
|
27142
27680
|
function createDeferredPromise() {
|
|
27143
27681
|
let res;
|
|
27144
27682
|
let rej;
|
|
27145
|
-
const promise = new Promise((
|
|
27146
|
-
res =
|
|
27683
|
+
const promise = new Promise((resolve15, reject) => {
|
|
27684
|
+
res = resolve15;
|
|
27147
27685
|
rej = reject;
|
|
27148
27686
|
});
|
|
27149
27687
|
return { promise, resolve: res, reject: rej };
|
|
@@ -28396,8 +28934,8 @@ var require_body = __commonJS({
|
|
|
28396
28934
|
var { parseMIMEType, serializeAMimeType } = require_dataURL();
|
|
28397
28935
|
var random;
|
|
28398
28936
|
try {
|
|
28399
|
-
const
|
|
28400
|
-
random = (max) =>
|
|
28937
|
+
const crypto4 = require("crypto");
|
|
28938
|
+
random = (max) => crypto4.randomInt(0, max);
|
|
28401
28939
|
} catch {
|
|
28402
28940
|
random = (max) => Math.floor(Math.random(max));
|
|
28403
28941
|
}
|
|
@@ -28648,8 +29186,8 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
28648
29186
|
});
|
|
28649
29187
|
}
|
|
28650
29188
|
});
|
|
28651
|
-
const busboyResolve = new Promise((
|
|
28652
|
-
busboy.on("finish",
|
|
29189
|
+
const busboyResolve = new Promise((resolve15, reject) => {
|
|
29190
|
+
busboy.on("finish", resolve15);
|
|
28653
29191
|
busboy.on("error", (err) => reject(new TypeError(err)));
|
|
28654
29192
|
});
|
|
28655
29193
|
if (this.body !== null) for await (const chunk of consumeBody(this[kState].body)) busboy.write(chunk);
|
|
@@ -28780,7 +29318,7 @@ var require_request = __commonJS({
|
|
|
28780
29318
|
}
|
|
28781
29319
|
var Request = class _Request {
|
|
28782
29320
|
constructor(origin, {
|
|
28783
|
-
path:
|
|
29321
|
+
path: path29,
|
|
28784
29322
|
method,
|
|
28785
29323
|
body,
|
|
28786
29324
|
headers,
|
|
@@ -28794,11 +29332,11 @@ var require_request = __commonJS({
|
|
|
28794
29332
|
throwOnError,
|
|
28795
29333
|
expectContinue
|
|
28796
29334
|
}, handler) {
|
|
28797
|
-
if (typeof
|
|
29335
|
+
if (typeof path29 !== "string") {
|
|
28798
29336
|
throw new InvalidArgumentError("path must be a string");
|
|
28799
|
-
} else if (
|
|
29337
|
+
} else if (path29[0] !== "/" && !(path29.startsWith("http://") || path29.startsWith("https://")) && method !== "CONNECT") {
|
|
28800
29338
|
throw new InvalidArgumentError("path must be an absolute URL or start with a slash");
|
|
28801
|
-
} else if (invalidPathRegex.exec(
|
|
29339
|
+
} else if (invalidPathRegex.exec(path29) !== null) {
|
|
28802
29340
|
throw new InvalidArgumentError("invalid request path");
|
|
28803
29341
|
}
|
|
28804
29342
|
if (typeof method !== "string") {
|
|
@@ -28861,7 +29399,7 @@ var require_request = __commonJS({
|
|
|
28861
29399
|
this.completed = false;
|
|
28862
29400
|
this.aborted = false;
|
|
28863
29401
|
this.upgrade = upgrade || null;
|
|
28864
|
-
this.path = query ? util.buildURL(
|
|
29402
|
+
this.path = query ? util.buildURL(path29, query) : path29;
|
|
28865
29403
|
this.origin = origin;
|
|
28866
29404
|
this.idempotent = idempotent == null ? method === "HEAD" || method === "GET" : idempotent;
|
|
28867
29405
|
this.blocking = blocking == null ? false : blocking;
|
|
@@ -29183,9 +29721,9 @@ var require_dispatcher_base = __commonJS({
|
|
|
29183
29721
|
}
|
|
29184
29722
|
close(callback) {
|
|
29185
29723
|
if (callback === void 0) {
|
|
29186
|
-
return new Promise((
|
|
29724
|
+
return new Promise((resolve15, reject) => {
|
|
29187
29725
|
this.close((err, data) => {
|
|
29188
|
-
return err ? reject(err) :
|
|
29726
|
+
return err ? reject(err) : resolve15(data);
|
|
29189
29727
|
});
|
|
29190
29728
|
});
|
|
29191
29729
|
}
|
|
@@ -29223,12 +29761,12 @@ var require_dispatcher_base = __commonJS({
|
|
|
29223
29761
|
err = null;
|
|
29224
29762
|
}
|
|
29225
29763
|
if (callback === void 0) {
|
|
29226
|
-
return new Promise((
|
|
29764
|
+
return new Promise((resolve15, reject) => {
|
|
29227
29765
|
this.destroy(err, (err2, data) => {
|
|
29228
29766
|
return err2 ? (
|
|
29229
29767
|
/* istanbul ignore next: should never error */
|
|
29230
29768
|
reject(err2)
|
|
29231
|
-
) :
|
|
29769
|
+
) : resolve15(data);
|
|
29232
29770
|
});
|
|
29233
29771
|
});
|
|
29234
29772
|
}
|
|
@@ -29869,9 +30407,9 @@ var require_RedirectHandler = __commonJS({
|
|
|
29869
30407
|
return this.handler.onHeaders(statusCode, headers, resume, statusText);
|
|
29870
30408
|
}
|
|
29871
30409
|
const { origin, pathname, search } = util.parseURL(new URL(this.location, this.opts.origin && new URL(this.opts.path, this.opts.origin)));
|
|
29872
|
-
const
|
|
30410
|
+
const path29 = search ? `${pathname}${search}` : pathname;
|
|
29873
30411
|
this.opts.headers = cleanRequestHeaders(this.opts.headers, statusCode === 303, this.opts.origin !== origin);
|
|
29874
|
-
this.opts.path =
|
|
30412
|
+
this.opts.path = path29;
|
|
29875
30413
|
this.opts.origin = origin;
|
|
29876
30414
|
this.opts.maxRedirections = 0;
|
|
29877
30415
|
this.opts.query = null;
|
|
@@ -30290,16 +30828,16 @@ var require_client = __commonJS({
|
|
|
30290
30828
|
return this[kNeedDrain] < 2;
|
|
30291
30829
|
}
|
|
30292
30830
|
async [kClose]() {
|
|
30293
|
-
return new Promise((
|
|
30831
|
+
return new Promise((resolve15) => {
|
|
30294
30832
|
if (!this[kSize]) {
|
|
30295
|
-
|
|
30833
|
+
resolve15(null);
|
|
30296
30834
|
} else {
|
|
30297
|
-
this[kClosedResolve] =
|
|
30835
|
+
this[kClosedResolve] = resolve15;
|
|
30298
30836
|
}
|
|
30299
30837
|
});
|
|
30300
30838
|
}
|
|
30301
30839
|
async [kDestroy](err) {
|
|
30302
|
-
return new Promise((
|
|
30840
|
+
return new Promise((resolve15) => {
|
|
30303
30841
|
const requests = this[kQueue].splice(this[kPendingIdx]);
|
|
30304
30842
|
for (let i = 0; i < requests.length; i++) {
|
|
30305
30843
|
const request = requests[i];
|
|
@@ -30310,7 +30848,7 @@ var require_client = __commonJS({
|
|
|
30310
30848
|
this[kClosedResolve]();
|
|
30311
30849
|
this[kClosedResolve] = null;
|
|
30312
30850
|
}
|
|
30313
|
-
|
|
30851
|
+
resolve15();
|
|
30314
30852
|
};
|
|
30315
30853
|
if (this[kHTTP2Session] != null) {
|
|
30316
30854
|
util.destroy(this[kHTTP2Session], err);
|
|
@@ -30890,7 +31428,7 @@ var require_client = __commonJS({
|
|
|
30890
31428
|
});
|
|
30891
31429
|
}
|
|
30892
31430
|
try {
|
|
30893
|
-
const socket = await new Promise((
|
|
31431
|
+
const socket = await new Promise((resolve15, reject) => {
|
|
30894
31432
|
client[kConnector]({
|
|
30895
31433
|
host,
|
|
30896
31434
|
hostname,
|
|
@@ -30902,7 +31440,7 @@ var require_client = __commonJS({
|
|
|
30902
31440
|
if (err) {
|
|
30903
31441
|
reject(err);
|
|
30904
31442
|
} else {
|
|
30905
|
-
|
|
31443
|
+
resolve15(socket2);
|
|
30906
31444
|
}
|
|
30907
31445
|
});
|
|
30908
31446
|
});
|
|
@@ -31113,7 +31651,7 @@ var require_client = __commonJS({
|
|
|
31113
31651
|
writeH2(client, client[kHTTP2Session], request);
|
|
31114
31652
|
return;
|
|
31115
31653
|
}
|
|
31116
|
-
const { body, method, path:
|
|
31654
|
+
const { body, method, path: path29, host, upgrade, headers, blocking, reset } = request;
|
|
31117
31655
|
const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH";
|
|
31118
31656
|
if (body && typeof body.read === "function") {
|
|
31119
31657
|
body.read(0);
|
|
@@ -31163,7 +31701,7 @@ var require_client = __commonJS({
|
|
|
31163
31701
|
if (blocking) {
|
|
31164
31702
|
socket[kBlocking] = true;
|
|
31165
31703
|
}
|
|
31166
|
-
let header = `${method} ${
|
|
31704
|
+
let header = `${method} ${path29} HTTP/1.1\r
|
|
31167
31705
|
`;
|
|
31168
31706
|
if (typeof host === "string") {
|
|
31169
31707
|
header += `host: ${host}\r
|
|
@@ -31226,7 +31764,7 @@ upgrade: ${upgrade}\r
|
|
|
31226
31764
|
return true;
|
|
31227
31765
|
}
|
|
31228
31766
|
function writeH2(client, session, request) {
|
|
31229
|
-
const { body, method, path:
|
|
31767
|
+
const { body, method, path: path29, host, upgrade, expectContinue, signal, headers: reqHeaders } = request;
|
|
31230
31768
|
let headers;
|
|
31231
31769
|
if (typeof reqHeaders === "string") headers = Request[kHTTP2CopyHeaders](reqHeaders.trim());
|
|
31232
31770
|
else headers = reqHeaders;
|
|
@@ -31269,7 +31807,7 @@ upgrade: ${upgrade}\r
|
|
|
31269
31807
|
});
|
|
31270
31808
|
return true;
|
|
31271
31809
|
}
|
|
31272
|
-
headers[HTTP2_HEADER_PATH] =
|
|
31810
|
+
headers[HTTP2_HEADER_PATH] = path29;
|
|
31273
31811
|
headers[HTTP2_HEADER_SCHEME] = "https";
|
|
31274
31812
|
const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH";
|
|
31275
31813
|
if (body && typeof body.read === "function") {
|
|
@@ -31526,12 +32064,12 @@ upgrade: ${upgrade}\r
|
|
|
31526
32064
|
cb();
|
|
31527
32065
|
}
|
|
31528
32066
|
}
|
|
31529
|
-
const waitForDrain = () => new Promise((
|
|
32067
|
+
const waitForDrain = () => new Promise((resolve15, reject) => {
|
|
31530
32068
|
assert(callback === null);
|
|
31531
32069
|
if (socket[kError]) {
|
|
31532
32070
|
reject(socket[kError]);
|
|
31533
32071
|
} else {
|
|
31534
|
-
callback =
|
|
32072
|
+
callback = resolve15;
|
|
31535
32073
|
}
|
|
31536
32074
|
});
|
|
31537
32075
|
if (client[kHTTPConnVersion] === "h2") {
|
|
@@ -31877,8 +32415,8 @@ var require_pool_base = __commonJS({
|
|
|
31877
32415
|
if (this[kQueue].isEmpty()) {
|
|
31878
32416
|
return Promise.all(this[kClients].map((c) => c.close()));
|
|
31879
32417
|
} else {
|
|
31880
|
-
return new Promise((
|
|
31881
|
-
this[kClosedResolve] =
|
|
32418
|
+
return new Promise((resolve15) => {
|
|
32419
|
+
this[kClosedResolve] = resolve15;
|
|
31882
32420
|
});
|
|
31883
32421
|
}
|
|
31884
32422
|
}
|
|
@@ -32456,7 +32994,7 @@ var require_readable = __commonJS({
|
|
|
32456
32994
|
if (this.closed) {
|
|
32457
32995
|
return Promise.resolve(null);
|
|
32458
32996
|
}
|
|
32459
|
-
return new Promise((
|
|
32997
|
+
return new Promise((resolve15, reject) => {
|
|
32460
32998
|
const signalListenerCleanup = signal ? util.addAbortListener(signal, () => {
|
|
32461
32999
|
this.destroy();
|
|
32462
33000
|
}) : noop;
|
|
@@ -32465,7 +33003,7 @@ var require_readable = __commonJS({
|
|
|
32465
33003
|
if (signal && signal.aborted) {
|
|
32466
33004
|
reject(signal.reason || Object.assign(new Error("The operation was aborted"), { name: "AbortError" }));
|
|
32467
33005
|
} else {
|
|
32468
|
-
|
|
33006
|
+
resolve15(null);
|
|
32469
33007
|
}
|
|
32470
33008
|
}).on("error", noop).on("data", function(chunk) {
|
|
32471
33009
|
limit -= chunk.length;
|
|
@@ -32487,11 +33025,11 @@ var require_readable = __commonJS({
|
|
|
32487
33025
|
throw new TypeError("unusable");
|
|
32488
33026
|
}
|
|
32489
33027
|
assert(!stream[kConsume]);
|
|
32490
|
-
return new Promise((
|
|
33028
|
+
return new Promise((resolve15, reject) => {
|
|
32491
33029
|
stream[kConsume] = {
|
|
32492
33030
|
type,
|
|
32493
33031
|
stream,
|
|
32494
|
-
resolve:
|
|
33032
|
+
resolve: resolve15,
|
|
32495
33033
|
reject,
|
|
32496
33034
|
length: 0,
|
|
32497
33035
|
body: []
|
|
@@ -32526,12 +33064,12 @@ var require_readable = __commonJS({
|
|
|
32526
33064
|
}
|
|
32527
33065
|
}
|
|
32528
33066
|
function consumeEnd(consume2) {
|
|
32529
|
-
const { type, body, resolve:
|
|
33067
|
+
const { type, body, resolve: resolve15, stream, length } = consume2;
|
|
32530
33068
|
try {
|
|
32531
33069
|
if (type === "text") {
|
|
32532
|
-
|
|
33070
|
+
resolve15(toUSVString(Buffer.concat(body)));
|
|
32533
33071
|
} else if (type === "json") {
|
|
32534
|
-
|
|
33072
|
+
resolve15(JSON.parse(Buffer.concat(body)));
|
|
32535
33073
|
} else if (type === "arrayBuffer") {
|
|
32536
33074
|
const dst = new Uint8Array(length);
|
|
32537
33075
|
let pos = 0;
|
|
@@ -32539,12 +33077,12 @@ var require_readable = __commonJS({
|
|
|
32539
33077
|
dst.set(buf, pos);
|
|
32540
33078
|
pos += buf.byteLength;
|
|
32541
33079
|
}
|
|
32542
|
-
|
|
33080
|
+
resolve15(dst.buffer);
|
|
32543
33081
|
} else if (type === "blob") {
|
|
32544
33082
|
if (!Blob2) {
|
|
32545
33083
|
Blob2 = require("buffer").Blob;
|
|
32546
33084
|
}
|
|
32547
|
-
|
|
33085
|
+
resolve15(new Blob2(body, { type: stream[kContentType] }));
|
|
32548
33086
|
}
|
|
32549
33087
|
consumeFinish(consume2);
|
|
32550
33088
|
} catch (err) {
|
|
@@ -32801,9 +33339,9 @@ var require_api_request = __commonJS({
|
|
|
32801
33339
|
};
|
|
32802
33340
|
function request(opts, callback) {
|
|
32803
33341
|
if (callback === void 0) {
|
|
32804
|
-
return new Promise((
|
|
33342
|
+
return new Promise((resolve15, reject) => {
|
|
32805
33343
|
request.call(this, opts, (err, data) => {
|
|
32806
|
-
return err ? reject(err) :
|
|
33344
|
+
return err ? reject(err) : resolve15(data);
|
|
32807
33345
|
});
|
|
32808
33346
|
});
|
|
32809
33347
|
}
|
|
@@ -32976,9 +33514,9 @@ var require_api_stream = __commonJS({
|
|
|
32976
33514
|
};
|
|
32977
33515
|
function stream(opts, factory, callback) {
|
|
32978
33516
|
if (callback === void 0) {
|
|
32979
|
-
return new Promise((
|
|
33517
|
+
return new Promise((resolve15, reject) => {
|
|
32980
33518
|
stream.call(this, opts, factory, (err, data) => {
|
|
32981
|
-
return err ? reject(err) :
|
|
33519
|
+
return err ? reject(err) : resolve15(data);
|
|
32982
33520
|
});
|
|
32983
33521
|
});
|
|
32984
33522
|
}
|
|
@@ -33259,9 +33797,9 @@ var require_api_upgrade = __commonJS({
|
|
|
33259
33797
|
};
|
|
33260
33798
|
function upgrade(opts, callback) {
|
|
33261
33799
|
if (callback === void 0) {
|
|
33262
|
-
return new Promise((
|
|
33800
|
+
return new Promise((resolve15, reject) => {
|
|
33263
33801
|
upgrade.call(this, opts, (err, data) => {
|
|
33264
|
-
return err ? reject(err) :
|
|
33802
|
+
return err ? reject(err) : resolve15(data);
|
|
33265
33803
|
});
|
|
33266
33804
|
});
|
|
33267
33805
|
}
|
|
@@ -33350,9 +33888,9 @@ var require_api_connect = __commonJS({
|
|
|
33350
33888
|
};
|
|
33351
33889
|
function connect(opts, callback) {
|
|
33352
33890
|
if (callback === void 0) {
|
|
33353
|
-
return new Promise((
|
|
33891
|
+
return new Promise((resolve15, reject) => {
|
|
33354
33892
|
connect.call(this, opts, (err, data) => {
|
|
33355
|
-
return err ? reject(err) :
|
|
33893
|
+
return err ? reject(err) : resolve15(data);
|
|
33356
33894
|
});
|
|
33357
33895
|
});
|
|
33358
33896
|
}
|
|
@@ -33512,20 +34050,20 @@ var require_mock_utils = __commonJS({
|
|
|
33512
34050
|
}
|
|
33513
34051
|
return true;
|
|
33514
34052
|
}
|
|
33515
|
-
function safeUrl(
|
|
33516
|
-
if (typeof
|
|
33517
|
-
return
|
|
34053
|
+
function safeUrl(path29) {
|
|
34054
|
+
if (typeof path29 !== "string") {
|
|
34055
|
+
return path29;
|
|
33518
34056
|
}
|
|
33519
|
-
const pathSegments =
|
|
34057
|
+
const pathSegments = path29.split("?");
|
|
33520
34058
|
if (pathSegments.length !== 2) {
|
|
33521
|
-
return
|
|
34059
|
+
return path29;
|
|
33522
34060
|
}
|
|
33523
34061
|
const qp = new URLSearchParams(pathSegments.pop());
|
|
33524
34062
|
qp.sort();
|
|
33525
34063
|
return [...pathSegments, qp.toString()].join("?");
|
|
33526
34064
|
}
|
|
33527
|
-
function matchKey(mockDispatch2, { path:
|
|
33528
|
-
const pathMatch = matchValue(mockDispatch2.path,
|
|
34065
|
+
function matchKey(mockDispatch2, { path: path29, method, body, headers }) {
|
|
34066
|
+
const pathMatch = matchValue(mockDispatch2.path, path29);
|
|
33529
34067
|
const methodMatch = matchValue(mockDispatch2.method, method);
|
|
33530
34068
|
const bodyMatch = typeof mockDispatch2.body !== "undefined" ? matchValue(mockDispatch2.body, body) : true;
|
|
33531
34069
|
const headersMatch = matchHeaders(mockDispatch2, headers);
|
|
@@ -33543,7 +34081,7 @@ var require_mock_utils = __commonJS({
|
|
|
33543
34081
|
function getMockDispatch(mockDispatches, key) {
|
|
33544
34082
|
const basePath = key.query ? buildURL(key.path, key.query) : key.path;
|
|
33545
34083
|
const resolvedPath = typeof basePath === "string" ? safeUrl(basePath) : basePath;
|
|
33546
|
-
let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path:
|
|
34084
|
+
let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path: path29 }) => matchValue(safeUrl(path29), resolvedPath));
|
|
33547
34085
|
if (matchedMockDispatches.length === 0) {
|
|
33548
34086
|
throw new MockNotMatchedError(`Mock dispatch not matched for path '${resolvedPath}'`);
|
|
33549
34087
|
}
|
|
@@ -33580,9 +34118,9 @@ var require_mock_utils = __commonJS({
|
|
|
33580
34118
|
}
|
|
33581
34119
|
}
|
|
33582
34120
|
function buildKey(opts) {
|
|
33583
|
-
const { path:
|
|
34121
|
+
const { path: path29, method, body, headers, query } = opts;
|
|
33584
34122
|
return {
|
|
33585
|
-
path:
|
|
34123
|
+
path: path29,
|
|
33586
34124
|
method,
|
|
33587
34125
|
body,
|
|
33588
34126
|
headers,
|
|
@@ -34031,10 +34569,10 @@ var require_pending_interceptors_formatter = __commonJS({
|
|
|
34031
34569
|
}
|
|
34032
34570
|
format(pendingInterceptors) {
|
|
34033
34571
|
const withPrettyHeaders = pendingInterceptors.map(
|
|
34034
|
-
({ method, path:
|
|
34572
|
+
({ method, path: path29, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
|
|
34035
34573
|
Method: method,
|
|
34036
34574
|
Origin: origin,
|
|
34037
|
-
Path:
|
|
34575
|
+
Path: path29,
|
|
34038
34576
|
"Status code": statusCode,
|
|
34039
34577
|
Persistent: persist ? "\u2705" : "\u274C",
|
|
34040
34578
|
Invocations: timesInvoked,
|
|
@@ -36975,7 +37513,7 @@ var require_fetch = __commonJS({
|
|
|
36975
37513
|
async function dispatch({ body }) {
|
|
36976
37514
|
const url = requestCurrentURL(request);
|
|
36977
37515
|
const agent = fetchParams.controller.dispatcher;
|
|
36978
|
-
return new Promise((
|
|
37516
|
+
return new Promise((resolve15, reject) => agent.dispatch(
|
|
36979
37517
|
{
|
|
36980
37518
|
path: url.pathname + url.search,
|
|
36981
37519
|
origin: url.origin,
|
|
@@ -37051,7 +37589,7 @@ var require_fetch = __commonJS({
|
|
|
37051
37589
|
}
|
|
37052
37590
|
}
|
|
37053
37591
|
}
|
|
37054
|
-
|
|
37592
|
+
resolve15({
|
|
37055
37593
|
status,
|
|
37056
37594
|
statusText,
|
|
37057
37595
|
headersList: headers[kHeadersList],
|
|
@@ -37094,7 +37632,7 @@ var require_fetch = __commonJS({
|
|
|
37094
37632
|
const val = headersList[n + 1].toString("latin1");
|
|
37095
37633
|
headers[kHeadersList].append(key, val);
|
|
37096
37634
|
}
|
|
37097
|
-
|
|
37635
|
+
resolve15({
|
|
37098
37636
|
status,
|
|
37099
37637
|
statusText: STATUS_CODES[status],
|
|
37100
37638
|
headersList: headers[kHeadersList],
|
|
@@ -38655,8 +39193,8 @@ var require_util6 = __commonJS({
|
|
|
38655
39193
|
}
|
|
38656
39194
|
}
|
|
38657
39195
|
}
|
|
38658
|
-
function validateCookiePath(
|
|
38659
|
-
for (const char of
|
|
39196
|
+
function validateCookiePath(path29) {
|
|
39197
|
+
for (const char of path29) {
|
|
38660
39198
|
const code = char.charCodeAt(0);
|
|
38661
39199
|
if (code < 33 || char === ";") {
|
|
38662
39200
|
throw new Error("Invalid cookie path");
|
|
@@ -39453,9 +39991,9 @@ var require_connection = __commonJS({
|
|
|
39453
39991
|
channels.open = diagnosticsChannel.channel("undici:websocket:open");
|
|
39454
39992
|
channels.close = diagnosticsChannel.channel("undici:websocket:close");
|
|
39455
39993
|
channels.socketError = diagnosticsChannel.channel("undici:websocket:socket_error");
|
|
39456
|
-
var
|
|
39994
|
+
var crypto4;
|
|
39457
39995
|
try {
|
|
39458
|
-
|
|
39996
|
+
crypto4 = require("crypto");
|
|
39459
39997
|
} catch {
|
|
39460
39998
|
}
|
|
39461
39999
|
function establishWebSocketConnection(url, protocols, ws, onEstablish, options) {
|
|
@@ -39474,7 +40012,7 @@ var require_connection = __commonJS({
|
|
|
39474
40012
|
const headersList = new Headers(options.headers)[kHeadersList];
|
|
39475
40013
|
request.headersList = headersList;
|
|
39476
40014
|
}
|
|
39477
|
-
const keyValue =
|
|
40015
|
+
const keyValue = crypto4.randomBytes(16).toString("base64");
|
|
39478
40016
|
request.headersList.append("sec-websocket-key", keyValue);
|
|
39479
40017
|
request.headersList.append("sec-websocket-version", "13");
|
|
39480
40018
|
for (const protocol of protocols) {
|
|
@@ -39503,7 +40041,7 @@ var require_connection = __commonJS({
|
|
|
39503
40041
|
return;
|
|
39504
40042
|
}
|
|
39505
40043
|
const secWSAccept = response.headersList.get("Sec-WebSocket-Accept");
|
|
39506
|
-
const digest =
|
|
40044
|
+
const digest = crypto4.createHash("sha1").update(keyValue + uid).digest("base64");
|
|
39507
40045
|
if (secWSAccept !== digest) {
|
|
39508
40046
|
failWebsocketConnection(ws, "Incorrect hash received in Sec-WebSocket-Accept header.");
|
|
39509
40047
|
return;
|
|
@@ -39583,9 +40121,9 @@ var require_frame = __commonJS({
|
|
|
39583
40121
|
"node_modules/undici/lib/websocket/frame.js"(exports2, module2) {
|
|
39584
40122
|
"use strict";
|
|
39585
40123
|
var { maxUnsigned16Bit } = require_constants5();
|
|
39586
|
-
var
|
|
40124
|
+
var crypto4;
|
|
39587
40125
|
try {
|
|
39588
|
-
|
|
40126
|
+
crypto4 = require("crypto");
|
|
39589
40127
|
} catch {
|
|
39590
40128
|
}
|
|
39591
40129
|
var WebsocketFrameSend = class {
|
|
@@ -39594,7 +40132,7 @@ var require_frame = __commonJS({
|
|
|
39594
40132
|
*/
|
|
39595
40133
|
constructor(data) {
|
|
39596
40134
|
this.frameData = data;
|
|
39597
|
-
this.maskKey =
|
|
40135
|
+
this.maskKey = crypto4.randomBytes(4);
|
|
39598
40136
|
}
|
|
39599
40137
|
createFrame(opcode) {
|
|
39600
40138
|
const bodyLength = this.frameData?.byteLength ?? 0;
|
|
@@ -40336,11 +40874,11 @@ var require_undici = __commonJS({
|
|
|
40336
40874
|
if (typeof opts.path !== "string") {
|
|
40337
40875
|
throw new InvalidArgumentError("invalid opts.path");
|
|
40338
40876
|
}
|
|
40339
|
-
let
|
|
40877
|
+
let path29 = opts.path;
|
|
40340
40878
|
if (!opts.path.startsWith("/")) {
|
|
40341
|
-
|
|
40879
|
+
path29 = `/${path29}`;
|
|
40342
40880
|
}
|
|
40343
|
-
url = new URL(util.parseOrigin(url).origin +
|
|
40881
|
+
url = new URL(util.parseOrigin(url).origin + path29);
|
|
40344
40882
|
} else {
|
|
40345
40883
|
if (!opts) {
|
|
40346
40884
|
opts = typeof url === "object" ? url : {};
|
|
@@ -40867,7 +41405,7 @@ var init_mcp_check_provider = __esm({
|
|
|
40867
41405
|
logger.warn(
|
|
40868
41406
|
`MCP ${transportName} failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${delay}ms: ${error instanceof Error ? error.message : String(error)}`
|
|
40869
41407
|
);
|
|
40870
|
-
await new Promise((
|
|
41408
|
+
await new Promise((resolve15) => setTimeout(resolve15, delay));
|
|
40871
41409
|
attempt += 1;
|
|
40872
41410
|
} finally {
|
|
40873
41411
|
try {
|
|
@@ -41136,7 +41674,7 @@ async function acquirePromptLock() {
|
|
|
41136
41674
|
activePrompt = true;
|
|
41137
41675
|
return;
|
|
41138
41676
|
}
|
|
41139
|
-
await new Promise((
|
|
41677
|
+
await new Promise((resolve15) => waiters.push(resolve15));
|
|
41140
41678
|
activePrompt = true;
|
|
41141
41679
|
}
|
|
41142
41680
|
function releasePromptLock() {
|
|
@@ -41146,7 +41684,7 @@ function releasePromptLock() {
|
|
|
41146
41684
|
}
|
|
41147
41685
|
async function interactivePrompt(options) {
|
|
41148
41686
|
await acquirePromptLock();
|
|
41149
|
-
return new Promise((
|
|
41687
|
+
return new Promise((resolve15, reject) => {
|
|
41150
41688
|
const dbg = process.env.VISOR_DEBUG === "true";
|
|
41151
41689
|
try {
|
|
41152
41690
|
if (dbg) {
|
|
@@ -41233,12 +41771,12 @@ async function interactivePrompt(options) {
|
|
|
41233
41771
|
};
|
|
41234
41772
|
const finish = (value) => {
|
|
41235
41773
|
cleanup();
|
|
41236
|
-
|
|
41774
|
+
resolve15(value);
|
|
41237
41775
|
};
|
|
41238
41776
|
if (options.timeout && options.timeout > 0) {
|
|
41239
41777
|
timeoutId = setTimeout(() => {
|
|
41240
41778
|
cleanup();
|
|
41241
|
-
if (defaultValue !== void 0) return
|
|
41779
|
+
if (defaultValue !== void 0) return resolve15(defaultValue);
|
|
41242
41780
|
return reject(new Error("Input timeout"));
|
|
41243
41781
|
}, options.timeout);
|
|
41244
41782
|
}
|
|
@@ -41370,7 +41908,7 @@ async function interactivePrompt(options) {
|
|
|
41370
41908
|
});
|
|
41371
41909
|
}
|
|
41372
41910
|
async function simplePrompt(prompt) {
|
|
41373
|
-
return new Promise((
|
|
41911
|
+
return new Promise((resolve15) => {
|
|
41374
41912
|
const rl = readline.createInterface({
|
|
41375
41913
|
input: process.stdin,
|
|
41376
41914
|
output: process.stdout
|
|
@@ -41386,7 +41924,7 @@ async function simplePrompt(prompt) {
|
|
|
41386
41924
|
rl.question(`${prompt}
|
|
41387
41925
|
> `, (answer) => {
|
|
41388
41926
|
rl.close();
|
|
41389
|
-
|
|
41927
|
+
resolve15(answer.trim());
|
|
41390
41928
|
});
|
|
41391
41929
|
});
|
|
41392
41930
|
}
|
|
@@ -41554,7 +42092,7 @@ function isStdinAvailable() {
|
|
|
41554
42092
|
return !process.stdin.isTTY;
|
|
41555
42093
|
}
|
|
41556
42094
|
async function readStdin(timeout, maxSize = 1024 * 1024) {
|
|
41557
|
-
return new Promise((
|
|
42095
|
+
return new Promise((resolve15, reject) => {
|
|
41558
42096
|
let data = "";
|
|
41559
42097
|
let timeoutId;
|
|
41560
42098
|
if (timeout) {
|
|
@@ -41581,7 +42119,7 @@ async function readStdin(timeout, maxSize = 1024 * 1024) {
|
|
|
41581
42119
|
};
|
|
41582
42120
|
const onEnd = () => {
|
|
41583
42121
|
cleanup();
|
|
41584
|
-
|
|
42122
|
+
resolve15(data.trim());
|
|
41585
42123
|
};
|
|
41586
42124
|
const onError = (err) => {
|
|
41587
42125
|
cleanup();
|
|
@@ -44906,23 +45444,23 @@ __export(renderer_schema_exports, {
|
|
|
44906
45444
|
});
|
|
44907
45445
|
async function loadRendererSchema(name) {
|
|
44908
45446
|
try {
|
|
44909
|
-
const
|
|
44910
|
-
const
|
|
45447
|
+
const fs25 = await import("fs/promises");
|
|
45448
|
+
const path29 = await import("path");
|
|
44911
45449
|
const sanitized = String(name).replace(/[^a-zA-Z0-9-]/g, "");
|
|
44912
45450
|
if (!sanitized) return void 0;
|
|
44913
45451
|
const candidates = [
|
|
44914
45452
|
// When bundled with ncc, __dirname is dist/ and output/ is at dist/output/
|
|
44915
|
-
|
|
45453
|
+
path29.join(__dirname, "output", sanitized, "schema.json"),
|
|
44916
45454
|
// When running from source, __dirname is src/state-machine/dispatch/ and output/ is at output/
|
|
44917
|
-
|
|
45455
|
+
path29.join(__dirname, "..", "..", "output", sanitized, "schema.json"),
|
|
44918
45456
|
// When running from a checkout with output/ folder copied to CWD
|
|
44919
|
-
|
|
45457
|
+
path29.join(process.cwd(), "output", sanitized, "schema.json"),
|
|
44920
45458
|
// Fallback: cwd/dist/output/
|
|
44921
|
-
|
|
45459
|
+
path29.join(process.cwd(), "dist", "output", sanitized, "schema.json")
|
|
44922
45460
|
];
|
|
44923
45461
|
for (const p of candidates) {
|
|
44924
45462
|
try {
|
|
44925
|
-
const raw = await
|
|
45463
|
+
const raw = await fs25.readFile(p, "utf-8");
|
|
44926
45464
|
return JSON.parse(raw);
|
|
44927
45465
|
} catch {
|
|
44928
45466
|
}
|
|
@@ -47341,37 +47879,37 @@ function updateStats2(results, state, isForEachIteration = false) {
|
|
|
47341
47879
|
async function renderTemplateContent2(checkId, checkConfig, reviewSummary) {
|
|
47342
47880
|
try {
|
|
47343
47881
|
const { createExtendedLiquid: createExtendedLiquid2 } = await Promise.resolve().then(() => (init_liquid_extensions(), liquid_extensions_exports));
|
|
47344
|
-
const
|
|
47345
|
-
const
|
|
47882
|
+
const fs25 = await import("fs/promises");
|
|
47883
|
+
const path29 = await import("path");
|
|
47346
47884
|
const schemaRaw = checkConfig.schema || "plain";
|
|
47347
|
-
const schema = typeof schemaRaw === "string" ? schemaRaw : "code-review";
|
|
47885
|
+
const schema = typeof schemaRaw === "string" && !schemaRaw.includes("{{") && !schemaRaw.includes("{%") ? schemaRaw : typeof schemaRaw === "object" ? "code-review" : "plain";
|
|
47348
47886
|
let templateContent;
|
|
47349
47887
|
if (checkConfig.template && checkConfig.template.content) {
|
|
47350
47888
|
templateContent = String(checkConfig.template.content);
|
|
47351
47889
|
logger.debug(`[LevelDispatch] Using inline template for ${checkId}`);
|
|
47352
47890
|
} else if (checkConfig.template && checkConfig.template.file) {
|
|
47353
47891
|
const file = String(checkConfig.template.file);
|
|
47354
|
-
const resolved =
|
|
47355
|
-
templateContent = await
|
|
47892
|
+
const resolved = path29.resolve(process.cwd(), file);
|
|
47893
|
+
templateContent = await fs25.readFile(resolved, "utf-8");
|
|
47356
47894
|
logger.debug(`[LevelDispatch] Using template file for ${checkId}: ${resolved}`);
|
|
47357
47895
|
} else if (schema && schema !== "plain") {
|
|
47358
47896
|
const sanitized = String(schema).replace(/[^a-zA-Z0-9-]/g, "");
|
|
47359
47897
|
if (sanitized) {
|
|
47360
47898
|
const candidatePaths = [
|
|
47361
|
-
|
|
47899
|
+
path29.join(__dirname, "output", sanitized, "template.liquid"),
|
|
47362
47900
|
// bundled: dist/output/
|
|
47363
|
-
|
|
47901
|
+
path29.join(__dirname, "..", "..", "output", sanitized, "template.liquid"),
|
|
47364
47902
|
// source (from state-machine/states)
|
|
47365
|
-
|
|
47903
|
+
path29.join(__dirname, "..", "..", "..", "output", sanitized, "template.liquid"),
|
|
47366
47904
|
// source (alternate)
|
|
47367
|
-
|
|
47905
|
+
path29.join(process.cwd(), "output", sanitized, "template.liquid"),
|
|
47368
47906
|
// fallback: cwd/output/
|
|
47369
|
-
|
|
47907
|
+
path29.join(process.cwd(), "dist", "output", sanitized, "template.liquid")
|
|
47370
47908
|
// fallback: cwd/dist/output/
|
|
47371
47909
|
];
|
|
47372
47910
|
for (const p of candidatePaths) {
|
|
47373
47911
|
try {
|
|
47374
|
-
templateContent = await
|
|
47912
|
+
templateContent = await fs25.readFile(p, "utf-8");
|
|
47375
47913
|
if (templateContent) {
|
|
47376
47914
|
logger.debug(`[LevelDispatch] Using schema template for ${checkId}: ${p}`);
|
|
47377
47915
|
break;
|
|
@@ -49172,8 +49710,8 @@ var init_workspace_manager = __esm({
|
|
|
49172
49710
|
);
|
|
49173
49711
|
if (this.cleanupRequested && this.activeOperations === 0) {
|
|
49174
49712
|
logger.debug(`[Workspace] All references released, proceeding with deferred cleanup`);
|
|
49175
|
-
for (const
|
|
49176
|
-
|
|
49713
|
+
for (const resolve15 of this.cleanupResolvers) {
|
|
49714
|
+
resolve15();
|
|
49177
49715
|
}
|
|
49178
49716
|
this.cleanupResolvers = [];
|
|
49179
49717
|
}
|
|
@@ -49298,19 +49836,19 @@ var init_workspace_manager = __esm({
|
|
|
49298
49836
|
);
|
|
49299
49837
|
this.cleanupRequested = true;
|
|
49300
49838
|
await Promise.race([
|
|
49301
|
-
new Promise((
|
|
49839
|
+
new Promise((resolve15) => {
|
|
49302
49840
|
if (this.activeOperations === 0) {
|
|
49303
|
-
|
|
49841
|
+
resolve15();
|
|
49304
49842
|
} else {
|
|
49305
|
-
this.cleanupResolvers.push(
|
|
49843
|
+
this.cleanupResolvers.push(resolve15);
|
|
49306
49844
|
}
|
|
49307
49845
|
}),
|
|
49308
|
-
new Promise((
|
|
49846
|
+
new Promise((resolve15) => {
|
|
49309
49847
|
setTimeout(() => {
|
|
49310
49848
|
logger.warn(
|
|
49311
49849
|
`[Workspace] Cleanup timeout after ${timeout}ms, proceeding anyway (${this.activeOperations} operations still active)`
|
|
49312
49850
|
);
|
|
49313
|
-
|
|
49851
|
+
resolve15();
|
|
49314
49852
|
}, timeout);
|
|
49315
49853
|
})
|
|
49316
49854
|
]);
|
|
@@ -49589,6 +50127,1264 @@ var init_build_engine_context = __esm({
|
|
|
49589
50127
|
}
|
|
49590
50128
|
});
|
|
49591
50129
|
|
|
50130
|
+
// src/policy/default-engine.ts
|
|
50131
|
+
var DefaultPolicyEngine;
|
|
50132
|
+
var init_default_engine = __esm({
|
|
50133
|
+
"src/policy/default-engine.ts"() {
|
|
50134
|
+
"use strict";
|
|
50135
|
+
DefaultPolicyEngine = class {
|
|
50136
|
+
async initialize(_config) {
|
|
50137
|
+
}
|
|
50138
|
+
async evaluateCheckExecution(_checkId, _checkConfig) {
|
|
50139
|
+
return { allowed: true };
|
|
50140
|
+
}
|
|
50141
|
+
async evaluateToolInvocation(_serverName, _methodName, _transport) {
|
|
50142
|
+
return { allowed: true };
|
|
50143
|
+
}
|
|
50144
|
+
async evaluateCapabilities(_checkId, _capabilities) {
|
|
50145
|
+
return { allowed: true };
|
|
50146
|
+
}
|
|
50147
|
+
async shutdown() {
|
|
50148
|
+
}
|
|
50149
|
+
};
|
|
50150
|
+
}
|
|
50151
|
+
});
|
|
50152
|
+
|
|
50153
|
+
// src/enterprise/license/validator.ts
|
|
50154
|
+
var validator_exports = {};
|
|
50155
|
+
__export(validator_exports, {
|
|
50156
|
+
LicenseValidator: () => LicenseValidator
|
|
50157
|
+
});
|
|
50158
|
+
var crypto2, fs18, path22, LicenseValidator;
|
|
50159
|
+
var init_validator = __esm({
|
|
50160
|
+
"src/enterprise/license/validator.ts"() {
|
|
50161
|
+
"use strict";
|
|
50162
|
+
crypto2 = __toESM(require("crypto"));
|
|
50163
|
+
fs18 = __toESM(require("fs"));
|
|
50164
|
+
path22 = __toESM(require("path"));
|
|
50165
|
+
LicenseValidator = class _LicenseValidator {
|
|
50166
|
+
/** Ed25519 public key for license verification (PEM format). */
|
|
50167
|
+
static PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAI/Zd08EFmgIdrDm/HXd0l3/5GBt7R1PrdvhdmEXhJlU=\n-----END PUBLIC KEY-----\n";
|
|
50168
|
+
cache = null;
|
|
50169
|
+
static CACHE_TTL = 5 * 60 * 1e3;
|
|
50170
|
+
// 5 minutes
|
|
50171
|
+
static GRACE_PERIOD = 72 * 3600 * 1e3;
|
|
50172
|
+
// 72 hours after expiry
|
|
50173
|
+
/**
|
|
50174
|
+
* Load and validate license from environment or file.
|
|
50175
|
+
*
|
|
50176
|
+
* Resolution order:
|
|
50177
|
+
* 1. VISOR_LICENSE env var (JWT string)
|
|
50178
|
+
* 2. VISOR_LICENSE_FILE env var (path to file)
|
|
50179
|
+
* 3. .visor-license in project root (cwd)
|
|
50180
|
+
* 4. .visor-license in ~/.config/visor/
|
|
50181
|
+
*/
|
|
50182
|
+
async loadAndValidate() {
|
|
50183
|
+
if (this.cache && Date.now() - this.cache.validatedAt < _LicenseValidator.CACHE_TTL) {
|
|
50184
|
+
return this.cache.payload;
|
|
50185
|
+
}
|
|
50186
|
+
const token = this.resolveToken();
|
|
50187
|
+
if (!token) return null;
|
|
50188
|
+
const payload = this.verifyAndDecode(token);
|
|
50189
|
+
if (!payload) return null;
|
|
50190
|
+
this.cache = { payload, validatedAt: Date.now() };
|
|
50191
|
+
return payload;
|
|
50192
|
+
}
|
|
50193
|
+
/** Check if a specific feature is licensed */
|
|
50194
|
+
hasFeature(feature) {
|
|
50195
|
+
if (!this.cache) return false;
|
|
50196
|
+
return this.cache.payload.features.includes(feature);
|
|
50197
|
+
}
|
|
50198
|
+
/** Check if license is valid (with grace period) */
|
|
50199
|
+
isValid() {
|
|
50200
|
+
if (!this.cache) return false;
|
|
50201
|
+
const now = Date.now();
|
|
50202
|
+
const expiryMs = this.cache.payload.exp * 1e3;
|
|
50203
|
+
return now < expiryMs + _LicenseValidator.GRACE_PERIOD;
|
|
50204
|
+
}
|
|
50205
|
+
/** Check if the license is within its grace period (expired but still valid) */
|
|
50206
|
+
isInGracePeriod() {
|
|
50207
|
+
if (!this.cache) return false;
|
|
50208
|
+
const now = Date.now();
|
|
50209
|
+
const expiryMs = this.cache.payload.exp * 1e3;
|
|
50210
|
+
return now >= expiryMs && now < expiryMs + _LicenseValidator.GRACE_PERIOD;
|
|
50211
|
+
}
|
|
50212
|
+
resolveToken() {
|
|
50213
|
+
if (process.env.VISOR_LICENSE) {
|
|
50214
|
+
return process.env.VISOR_LICENSE.trim();
|
|
50215
|
+
}
|
|
50216
|
+
if (process.env.VISOR_LICENSE_FILE) {
|
|
50217
|
+
const resolved = path22.resolve(process.env.VISOR_LICENSE_FILE);
|
|
50218
|
+
const home2 = process.env.HOME || process.env.USERPROFILE || "";
|
|
50219
|
+
const allowedPrefixes = [path22.normalize(process.cwd())];
|
|
50220
|
+
if (home2) allowedPrefixes.push(path22.normalize(path22.join(home2, ".config", "visor")));
|
|
50221
|
+
let realPath;
|
|
50222
|
+
try {
|
|
50223
|
+
realPath = fs18.realpathSync(resolved);
|
|
50224
|
+
} catch {
|
|
50225
|
+
return null;
|
|
50226
|
+
}
|
|
50227
|
+
const isSafe = allowedPrefixes.some(
|
|
50228
|
+
(prefix) => realPath === prefix || realPath.startsWith(prefix + path22.sep)
|
|
50229
|
+
);
|
|
50230
|
+
if (!isSafe) return null;
|
|
50231
|
+
return this.readFile(realPath);
|
|
50232
|
+
}
|
|
50233
|
+
const cwdPath = path22.join(process.cwd(), ".visor-license");
|
|
50234
|
+
const cwdToken = this.readFile(cwdPath);
|
|
50235
|
+
if (cwdToken) return cwdToken;
|
|
50236
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
50237
|
+
if (home) {
|
|
50238
|
+
const configPath = path22.join(home, ".config", "visor", ".visor-license");
|
|
50239
|
+
const configToken = this.readFile(configPath);
|
|
50240
|
+
if (configToken) return configToken;
|
|
50241
|
+
}
|
|
50242
|
+
return null;
|
|
50243
|
+
}
|
|
50244
|
+
readFile(filePath) {
|
|
50245
|
+
try {
|
|
50246
|
+
return fs18.readFileSync(filePath, "utf-8").trim();
|
|
50247
|
+
} catch {
|
|
50248
|
+
return null;
|
|
50249
|
+
}
|
|
50250
|
+
}
|
|
50251
|
+
verifyAndDecode(token) {
|
|
50252
|
+
try {
|
|
50253
|
+
const parts = token.split(".");
|
|
50254
|
+
if (parts.length !== 3) return null;
|
|
50255
|
+
const [headerB64, payloadB64, signatureB64] = parts;
|
|
50256
|
+
const header = JSON.parse(Buffer.from(headerB64, "base64url").toString());
|
|
50257
|
+
if (header.alg !== "EdDSA") return null;
|
|
50258
|
+
const data = `${headerB64}.${payloadB64}`;
|
|
50259
|
+
const signature = Buffer.from(signatureB64, "base64url");
|
|
50260
|
+
const publicKey = crypto2.createPublicKey(_LicenseValidator.PUBLIC_KEY);
|
|
50261
|
+
if (publicKey.asymmetricKeyType !== "ed25519") {
|
|
50262
|
+
return null;
|
|
50263
|
+
}
|
|
50264
|
+
const isValid = crypto2.verify(null, Buffer.from(data), publicKey, signature);
|
|
50265
|
+
if (!isValid) return null;
|
|
50266
|
+
const payload = JSON.parse(Buffer.from(payloadB64, "base64url").toString());
|
|
50267
|
+
if (!payload.org || !Array.isArray(payload.features) || typeof payload.exp !== "number" || typeof payload.iat !== "number" || !payload.sub) {
|
|
50268
|
+
return null;
|
|
50269
|
+
}
|
|
50270
|
+
const now = Date.now();
|
|
50271
|
+
const expiryMs = payload.exp * 1e3;
|
|
50272
|
+
if (now >= expiryMs + _LicenseValidator.GRACE_PERIOD) {
|
|
50273
|
+
return null;
|
|
50274
|
+
}
|
|
50275
|
+
return payload;
|
|
50276
|
+
} catch {
|
|
50277
|
+
return null;
|
|
50278
|
+
}
|
|
50279
|
+
}
|
|
50280
|
+
};
|
|
50281
|
+
}
|
|
50282
|
+
});
|
|
50283
|
+
|
|
50284
|
+
// src/enterprise/policy/opa-compiler.ts
|
|
50285
|
+
var fs19, path23, os, crypto3, import_child_process5, OpaCompiler;
|
|
50286
|
+
var init_opa_compiler = __esm({
|
|
50287
|
+
"src/enterprise/policy/opa-compiler.ts"() {
|
|
50288
|
+
"use strict";
|
|
50289
|
+
fs19 = __toESM(require("fs"));
|
|
50290
|
+
path23 = __toESM(require("path"));
|
|
50291
|
+
os = __toESM(require("os"));
|
|
50292
|
+
crypto3 = __toESM(require("crypto"));
|
|
50293
|
+
import_child_process5 = require("child_process");
|
|
50294
|
+
OpaCompiler = class _OpaCompiler {
|
|
50295
|
+
static CACHE_DIR = path23.join(os.tmpdir(), "visor-opa-cache");
|
|
50296
|
+
/**
|
|
50297
|
+
* Resolve the input paths to WASM bytes.
|
|
50298
|
+
*
|
|
50299
|
+
* Strategy:
|
|
50300
|
+
* 1. If any path is a .wasm file, read it directly
|
|
50301
|
+
* 2. If a directory contains policy.wasm, read it
|
|
50302
|
+
* 3. Otherwise, collect all .rego files and auto-compile via `opa build`
|
|
50303
|
+
*/
|
|
50304
|
+
async resolveWasmBytes(paths) {
|
|
50305
|
+
const regoFiles = [];
|
|
50306
|
+
for (const p of paths) {
|
|
50307
|
+
const resolved = path23.resolve(p);
|
|
50308
|
+
if (path23.normalize(resolved).includes("..")) {
|
|
50309
|
+
throw new Error(`Policy path contains traversal sequences: ${p}`);
|
|
50310
|
+
}
|
|
50311
|
+
if (resolved.endsWith(".wasm") && fs19.existsSync(resolved)) {
|
|
50312
|
+
return fs19.readFileSync(resolved);
|
|
50313
|
+
}
|
|
50314
|
+
if (!fs19.existsSync(resolved)) continue;
|
|
50315
|
+
const stat = fs19.statSync(resolved);
|
|
50316
|
+
if (stat.isDirectory()) {
|
|
50317
|
+
const wasmCandidate = path23.join(resolved, "policy.wasm");
|
|
50318
|
+
if (fs19.existsSync(wasmCandidate)) {
|
|
50319
|
+
return fs19.readFileSync(wasmCandidate);
|
|
50320
|
+
}
|
|
50321
|
+
const files = fs19.readdirSync(resolved);
|
|
50322
|
+
for (const f of files) {
|
|
50323
|
+
if (f.endsWith(".rego")) {
|
|
50324
|
+
regoFiles.push(path23.join(resolved, f));
|
|
50325
|
+
}
|
|
50326
|
+
}
|
|
50327
|
+
} else if (resolved.endsWith(".rego")) {
|
|
50328
|
+
regoFiles.push(resolved);
|
|
50329
|
+
}
|
|
50330
|
+
}
|
|
50331
|
+
if (regoFiles.length === 0) {
|
|
50332
|
+
throw new Error(
|
|
50333
|
+
`OPA WASM evaluator: no .wasm bundle or .rego files found in: ${paths.join(", ")}`
|
|
50334
|
+
);
|
|
50335
|
+
}
|
|
50336
|
+
return this.compileRego(regoFiles);
|
|
50337
|
+
}
|
|
50338
|
+
/**
|
|
50339
|
+
* Auto-compile .rego files to a WASM bundle using the `opa` CLI.
|
|
50340
|
+
*
|
|
50341
|
+
* Caches the compiled bundle based on a content hash of all input .rego files
|
|
50342
|
+
* so subsequent runs skip compilation if policies haven't changed.
|
|
50343
|
+
*/
|
|
50344
|
+
compileRego(regoFiles) {
|
|
50345
|
+
try {
|
|
50346
|
+
(0, import_child_process5.execFileSync)("opa", ["version"], { stdio: "pipe" });
|
|
50347
|
+
} catch {
|
|
50348
|
+
throw new Error(
|
|
50349
|
+
"OPA CLI (`opa`) not found on PATH. Install it from https://www.openpolicyagent.org/docs/latest/#running-opa\nOr pre-compile your .rego files: opa build -t wasm -e visor -o bundle.tar.gz " + regoFiles.join(" ")
|
|
50350
|
+
);
|
|
50351
|
+
}
|
|
50352
|
+
const hash = crypto3.createHash("sha256");
|
|
50353
|
+
for (const f of regoFiles.sort()) {
|
|
50354
|
+
hash.update(fs19.readFileSync(f));
|
|
50355
|
+
hash.update(f);
|
|
50356
|
+
}
|
|
50357
|
+
const cacheKey = hash.digest("hex").slice(0, 16);
|
|
50358
|
+
const cacheDir = _OpaCompiler.CACHE_DIR;
|
|
50359
|
+
const cachedWasm = path23.join(cacheDir, `${cacheKey}.wasm`);
|
|
50360
|
+
if (fs19.existsSync(cachedWasm)) {
|
|
50361
|
+
return fs19.readFileSync(cachedWasm);
|
|
50362
|
+
}
|
|
50363
|
+
fs19.mkdirSync(cacheDir, { recursive: true });
|
|
50364
|
+
const bundleTar = path23.join(cacheDir, `${cacheKey}-bundle.tar.gz`);
|
|
50365
|
+
try {
|
|
50366
|
+
const args = [
|
|
50367
|
+
"build",
|
|
50368
|
+
"-t",
|
|
50369
|
+
"wasm",
|
|
50370
|
+
"-e",
|
|
50371
|
+
"visor",
|
|
50372
|
+
// entrypoint: the visor package tree
|
|
50373
|
+
"-o",
|
|
50374
|
+
bundleTar,
|
|
50375
|
+
...regoFiles
|
|
50376
|
+
];
|
|
50377
|
+
(0, import_child_process5.execFileSync)("opa", args, {
|
|
50378
|
+
stdio: "pipe",
|
|
50379
|
+
timeout: 3e4
|
|
50380
|
+
});
|
|
50381
|
+
} catch (err) {
|
|
50382
|
+
const stderr = err?.stderr?.toString() || "";
|
|
50383
|
+
throw new Error(
|
|
50384
|
+
`Failed to compile .rego files to WASM:
|
|
50385
|
+
${stderr}
|
|
50386
|
+
Ensure your .rego files are valid and the \`opa\` CLI is installed.`
|
|
50387
|
+
);
|
|
50388
|
+
}
|
|
50389
|
+
try {
|
|
50390
|
+
(0, import_child_process5.execFileSync)("tar", ["-xzf", bundleTar, "-C", cacheDir, "/policy.wasm"], {
|
|
50391
|
+
stdio: "pipe"
|
|
50392
|
+
});
|
|
50393
|
+
const extractedWasm = path23.join(cacheDir, "policy.wasm");
|
|
50394
|
+
if (fs19.existsSync(extractedWasm)) {
|
|
50395
|
+
fs19.renameSync(extractedWasm, cachedWasm);
|
|
50396
|
+
}
|
|
50397
|
+
} catch {
|
|
50398
|
+
try {
|
|
50399
|
+
(0, import_child_process5.execFileSync)("tar", ["-xzf", bundleTar, "-C", cacheDir, "policy.wasm"], {
|
|
50400
|
+
stdio: "pipe"
|
|
50401
|
+
});
|
|
50402
|
+
const extractedWasm = path23.join(cacheDir, "policy.wasm");
|
|
50403
|
+
if (fs19.existsSync(extractedWasm)) {
|
|
50404
|
+
fs19.renameSync(extractedWasm, cachedWasm);
|
|
50405
|
+
}
|
|
50406
|
+
} catch (err2) {
|
|
50407
|
+
throw new Error(`Failed to extract policy.wasm from OPA bundle: ${err2?.message || err2}`);
|
|
50408
|
+
}
|
|
50409
|
+
}
|
|
50410
|
+
try {
|
|
50411
|
+
fs19.unlinkSync(bundleTar);
|
|
50412
|
+
} catch {
|
|
50413
|
+
}
|
|
50414
|
+
if (!fs19.existsSync(cachedWasm)) {
|
|
50415
|
+
throw new Error("OPA build succeeded but policy.wasm was not found in the bundle");
|
|
50416
|
+
}
|
|
50417
|
+
return fs19.readFileSync(cachedWasm);
|
|
50418
|
+
}
|
|
50419
|
+
};
|
|
50420
|
+
}
|
|
50421
|
+
});
|
|
50422
|
+
|
|
50423
|
+
// src/enterprise/policy/opa-wasm-evaluator.ts
|
|
50424
|
+
var fs20, path24, OpaWasmEvaluator;
|
|
50425
|
+
var init_opa_wasm_evaluator = __esm({
|
|
50426
|
+
"src/enterprise/policy/opa-wasm-evaluator.ts"() {
|
|
50427
|
+
"use strict";
|
|
50428
|
+
fs20 = __toESM(require("fs"));
|
|
50429
|
+
path24 = __toESM(require("path"));
|
|
50430
|
+
init_opa_compiler();
|
|
50431
|
+
OpaWasmEvaluator = class {
|
|
50432
|
+
policy = null;
|
|
50433
|
+
dataDocument = {};
|
|
50434
|
+
compiler = new OpaCompiler();
|
|
50435
|
+
async initialize(rulesPath) {
|
|
50436
|
+
const paths = Array.isArray(rulesPath) ? rulesPath : [rulesPath];
|
|
50437
|
+
const wasmBytes = await this.compiler.resolveWasmBytes(paths);
|
|
50438
|
+
try {
|
|
50439
|
+
const { createRequire } = require("module");
|
|
50440
|
+
const runtimeRequire = createRequire(__filename);
|
|
50441
|
+
const opaWasm = runtimeRequire("@open-policy-agent/opa-wasm");
|
|
50442
|
+
const loadPolicy = opaWasm.loadPolicy || opaWasm.default?.loadPolicy;
|
|
50443
|
+
if (!loadPolicy) {
|
|
50444
|
+
throw new Error("loadPolicy not found in @open-policy-agent/opa-wasm");
|
|
50445
|
+
}
|
|
50446
|
+
this.policy = await loadPolicy(wasmBytes);
|
|
50447
|
+
} catch (err) {
|
|
50448
|
+
if (err?.code === "MODULE_NOT_FOUND" || err?.code === "ERR_MODULE_NOT_FOUND") {
|
|
50449
|
+
throw new Error(
|
|
50450
|
+
"OPA WASM evaluator requires @open-policy-agent/opa-wasm. Install it with: npm install @open-policy-agent/opa-wasm"
|
|
50451
|
+
);
|
|
50452
|
+
}
|
|
50453
|
+
throw err;
|
|
50454
|
+
}
|
|
50455
|
+
}
|
|
50456
|
+
/**
|
|
50457
|
+
* Load external data from a JSON file to use as the OPA data document.
|
|
50458
|
+
* The loaded data will be passed to `policy.setData()` during evaluation,
|
|
50459
|
+
* making it available in Rego via `data.<key>`.
|
|
50460
|
+
*/
|
|
50461
|
+
loadData(dataPath) {
|
|
50462
|
+
const resolved = path24.resolve(dataPath);
|
|
50463
|
+
if (path24.normalize(resolved).includes("..")) {
|
|
50464
|
+
throw new Error(`Data path contains traversal sequences: ${dataPath}`);
|
|
50465
|
+
}
|
|
50466
|
+
if (!fs20.existsSync(resolved)) {
|
|
50467
|
+
throw new Error(`OPA data file not found: ${resolved}`);
|
|
50468
|
+
}
|
|
50469
|
+
const stat = fs20.statSync(resolved);
|
|
50470
|
+
if (stat.size > 10 * 1024 * 1024) {
|
|
50471
|
+
throw new Error(`OPA data file exceeds 10MB limit: ${resolved} (${stat.size} bytes)`);
|
|
50472
|
+
}
|
|
50473
|
+
const raw = fs20.readFileSync(resolved, "utf-8");
|
|
50474
|
+
try {
|
|
50475
|
+
const parsed = JSON.parse(raw);
|
|
50476
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
50477
|
+
throw new Error("OPA data file must contain a JSON object (not an array or primitive)");
|
|
50478
|
+
}
|
|
50479
|
+
this.dataDocument = parsed;
|
|
50480
|
+
} catch (err) {
|
|
50481
|
+
if (err.message.startsWith("OPA data file must")) {
|
|
50482
|
+
throw err;
|
|
50483
|
+
}
|
|
50484
|
+
throw new Error(`Failed to parse OPA data file ${resolved}: ${err.message}`);
|
|
50485
|
+
}
|
|
50486
|
+
}
|
|
50487
|
+
async evaluate(input) {
|
|
50488
|
+
if (!this.policy) {
|
|
50489
|
+
throw new Error("OPA WASM evaluator not initialized");
|
|
50490
|
+
}
|
|
50491
|
+
this.policy.setData(this.dataDocument);
|
|
50492
|
+
const resultSet = this.policy.evaluate(input);
|
|
50493
|
+
if (Array.isArray(resultSet) && resultSet.length > 0) {
|
|
50494
|
+
return resultSet[0].result;
|
|
50495
|
+
}
|
|
50496
|
+
return void 0;
|
|
50497
|
+
}
|
|
50498
|
+
async shutdown() {
|
|
50499
|
+
if (this.policy) {
|
|
50500
|
+
if (typeof this.policy.close === "function") {
|
|
50501
|
+
try {
|
|
50502
|
+
this.policy.close();
|
|
50503
|
+
} catch {
|
|
50504
|
+
}
|
|
50505
|
+
} else if (typeof this.policy.free === "function") {
|
|
50506
|
+
try {
|
|
50507
|
+
this.policy.free();
|
|
50508
|
+
} catch {
|
|
50509
|
+
}
|
|
50510
|
+
}
|
|
50511
|
+
}
|
|
50512
|
+
this.policy = null;
|
|
50513
|
+
}
|
|
50514
|
+
};
|
|
50515
|
+
}
|
|
50516
|
+
});
|
|
50517
|
+
|
|
50518
|
+
// src/enterprise/policy/opa-http-evaluator.ts
|
|
50519
|
+
var OpaHttpEvaluator;
|
|
50520
|
+
var init_opa_http_evaluator = __esm({
|
|
50521
|
+
"src/enterprise/policy/opa-http-evaluator.ts"() {
|
|
50522
|
+
"use strict";
|
|
50523
|
+
OpaHttpEvaluator = class {
|
|
50524
|
+
baseUrl;
|
|
50525
|
+
timeout;
|
|
50526
|
+
constructor(baseUrl, timeout = 5e3) {
|
|
50527
|
+
let parsed;
|
|
50528
|
+
try {
|
|
50529
|
+
parsed = new URL(baseUrl);
|
|
50530
|
+
} catch {
|
|
50531
|
+
throw new Error(`OPA HTTP evaluator: invalid URL: ${baseUrl}`);
|
|
50532
|
+
}
|
|
50533
|
+
if (!["http:", "https:"].includes(parsed.protocol)) {
|
|
50534
|
+
throw new Error(
|
|
50535
|
+
`OPA HTTP evaluator: url must use http:// or https:// protocol, got: ${baseUrl}`
|
|
50536
|
+
);
|
|
50537
|
+
}
|
|
50538
|
+
const hostname = parsed.hostname;
|
|
50539
|
+
if (this.isBlockedHostname(hostname)) {
|
|
50540
|
+
throw new Error(
|
|
50541
|
+
`OPA HTTP evaluator: url must not point to internal, loopback, or private network addresses`
|
|
50542
|
+
);
|
|
50543
|
+
}
|
|
50544
|
+
this.baseUrl = baseUrl.replace(/\/+$/, "");
|
|
50545
|
+
this.timeout = timeout;
|
|
50546
|
+
}
|
|
50547
|
+
/**
|
|
50548
|
+
* Check if a hostname is blocked due to SSRF concerns.
|
|
50549
|
+
*
|
|
50550
|
+
* Blocks:
|
|
50551
|
+
* - Loopback addresses (127.x.x.x, localhost, 0.0.0.0, ::1)
|
|
50552
|
+
* - Link-local addresses (169.254.x.x)
|
|
50553
|
+
* - Private networks (10.x.x.x, 172.16-31.x.x, 192.168.x.x)
|
|
50554
|
+
* - IPv6 unique local addresses (fd00::/8)
|
|
50555
|
+
* - Cloud metadata services (*.internal)
|
|
50556
|
+
*/
|
|
50557
|
+
isBlockedHostname(hostname) {
|
|
50558
|
+
if (!hostname) return true;
|
|
50559
|
+
const normalized = hostname.toLowerCase().replace(/^\[|\]$/g, "");
|
|
50560
|
+
if (normalized === "metadata.google.internal" || normalized.endsWith(".internal")) {
|
|
50561
|
+
return true;
|
|
50562
|
+
}
|
|
50563
|
+
if (normalized === "localhost" || normalized === "localhost.localdomain") {
|
|
50564
|
+
return true;
|
|
50565
|
+
}
|
|
50566
|
+
if (normalized === "::1" || normalized === "0:0:0:0:0:0:0:1") {
|
|
50567
|
+
return true;
|
|
50568
|
+
}
|
|
50569
|
+
const ipv4Pattern = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
|
|
50570
|
+
const ipv4Match = normalized.match(ipv4Pattern);
|
|
50571
|
+
if (ipv4Match) {
|
|
50572
|
+
const octets = ipv4Match.slice(1, 5).map(Number);
|
|
50573
|
+
if (octets.some((octet) => octet > 255)) {
|
|
50574
|
+
return false;
|
|
50575
|
+
}
|
|
50576
|
+
const [a, b] = octets;
|
|
50577
|
+
if (a === 127) {
|
|
50578
|
+
return true;
|
|
50579
|
+
}
|
|
50580
|
+
if (a === 0) {
|
|
50581
|
+
return true;
|
|
50582
|
+
}
|
|
50583
|
+
if (a === 169 && b === 254) {
|
|
50584
|
+
return true;
|
|
50585
|
+
}
|
|
50586
|
+
if (a === 10) {
|
|
50587
|
+
return true;
|
|
50588
|
+
}
|
|
50589
|
+
if (a === 172 && b >= 16 && b <= 31) {
|
|
50590
|
+
return true;
|
|
50591
|
+
}
|
|
50592
|
+
if (a === 192 && b === 168) {
|
|
50593
|
+
return true;
|
|
50594
|
+
}
|
|
50595
|
+
}
|
|
50596
|
+
if (normalized.startsWith("fd") || normalized.startsWith("fc")) {
|
|
50597
|
+
return true;
|
|
50598
|
+
}
|
|
50599
|
+
if (normalized.startsWith("fe80:")) {
|
|
50600
|
+
return true;
|
|
50601
|
+
}
|
|
50602
|
+
return false;
|
|
50603
|
+
}
|
|
50604
|
+
/**
|
|
50605
|
+
* Evaluate a policy rule against an input document via OPA REST API.
|
|
50606
|
+
*
|
|
50607
|
+
* @param input - The input document to evaluate
|
|
50608
|
+
* @param rulePath - OPA rule path (e.g., 'visor/check/execute')
|
|
50609
|
+
* @returns The result object from OPA, or undefined on error
|
|
50610
|
+
*/
|
|
50611
|
+
async evaluate(input, rulePath) {
|
|
50612
|
+
const encodedPath = rulePath.split("/").map((s) => encodeURIComponent(s)).join("/");
|
|
50613
|
+
const url = `${this.baseUrl}/v1/data/${encodedPath}`;
|
|
50614
|
+
const controller = new AbortController();
|
|
50615
|
+
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
50616
|
+
try {
|
|
50617
|
+
const response = await fetch(url, {
|
|
50618
|
+
method: "POST",
|
|
50619
|
+
headers: { "Content-Type": "application/json" },
|
|
50620
|
+
body: JSON.stringify({ input }),
|
|
50621
|
+
signal: controller.signal
|
|
50622
|
+
});
|
|
50623
|
+
if (!response.ok) {
|
|
50624
|
+
throw new Error(`OPA HTTP ${response.status}: ${response.statusText}`);
|
|
50625
|
+
}
|
|
50626
|
+
let body;
|
|
50627
|
+
try {
|
|
50628
|
+
body = await response.json();
|
|
50629
|
+
} catch (jsonErr) {
|
|
50630
|
+
throw new Error(
|
|
50631
|
+
`OPA HTTP evaluator: failed to parse JSON response: ${jsonErr instanceof Error ? jsonErr.message : String(jsonErr)}`
|
|
50632
|
+
);
|
|
50633
|
+
}
|
|
50634
|
+
return body?.result;
|
|
50635
|
+
} finally {
|
|
50636
|
+
clearTimeout(timer);
|
|
50637
|
+
}
|
|
50638
|
+
}
|
|
50639
|
+
async shutdown() {
|
|
50640
|
+
}
|
|
50641
|
+
};
|
|
50642
|
+
}
|
|
50643
|
+
});
|
|
50644
|
+
|
|
50645
|
+
// src/enterprise/policy/policy-input-builder.ts
|
|
50646
|
+
var PolicyInputBuilder;
|
|
50647
|
+
var init_policy_input_builder = __esm({
|
|
50648
|
+
"src/enterprise/policy/policy-input-builder.ts"() {
|
|
50649
|
+
"use strict";
|
|
50650
|
+
PolicyInputBuilder = class {
|
|
50651
|
+
roles;
|
|
50652
|
+
actor;
|
|
50653
|
+
repository;
|
|
50654
|
+
pullRequest;
|
|
50655
|
+
constructor(policyConfig, actor, repository, pullRequest) {
|
|
50656
|
+
this.roles = policyConfig.roles || {};
|
|
50657
|
+
this.actor = actor;
|
|
50658
|
+
this.repository = repository;
|
|
50659
|
+
this.pullRequest = pullRequest;
|
|
50660
|
+
}
|
|
50661
|
+
/** Resolve which roles apply to the current actor. */
|
|
50662
|
+
resolveRoles() {
|
|
50663
|
+
const matched = [];
|
|
50664
|
+
for (const [roleName, roleConfig] of Object.entries(this.roles)) {
|
|
50665
|
+
let identityMatch = false;
|
|
50666
|
+
if (roleConfig.author_association && this.actor.authorAssociation && roleConfig.author_association.includes(this.actor.authorAssociation)) {
|
|
50667
|
+
identityMatch = true;
|
|
50668
|
+
}
|
|
50669
|
+
if (!identityMatch && roleConfig.users && this.actor.login && roleConfig.users.includes(this.actor.login)) {
|
|
50670
|
+
identityMatch = true;
|
|
50671
|
+
}
|
|
50672
|
+
if (!identityMatch && roleConfig.slack_users && this.actor.slack?.userId && roleConfig.slack_users.includes(this.actor.slack.userId)) {
|
|
50673
|
+
identityMatch = true;
|
|
50674
|
+
}
|
|
50675
|
+
if (!identityMatch && roleConfig.emails && this.actor.slack?.email) {
|
|
50676
|
+
const actorEmail = this.actor.slack.email.toLowerCase();
|
|
50677
|
+
if (roleConfig.emails.some((e) => e.toLowerCase() === actorEmail)) {
|
|
50678
|
+
identityMatch = true;
|
|
50679
|
+
}
|
|
50680
|
+
}
|
|
50681
|
+
if (!identityMatch) continue;
|
|
50682
|
+
if (roleConfig.slack_channels && roleConfig.slack_channels.length > 0) {
|
|
50683
|
+
if (!this.actor.slack?.channelId || !roleConfig.slack_channels.includes(this.actor.slack.channelId)) {
|
|
50684
|
+
continue;
|
|
50685
|
+
}
|
|
50686
|
+
}
|
|
50687
|
+
matched.push(roleName);
|
|
50688
|
+
}
|
|
50689
|
+
return matched;
|
|
50690
|
+
}
|
|
50691
|
+
buildActor() {
|
|
50692
|
+
return {
|
|
50693
|
+
authorAssociation: this.actor.authorAssociation,
|
|
50694
|
+
login: this.actor.login,
|
|
50695
|
+
roles: this.resolveRoles(),
|
|
50696
|
+
isLocalMode: this.actor.isLocalMode,
|
|
50697
|
+
...this.actor.slack && { slack: this.actor.slack }
|
|
50698
|
+
};
|
|
50699
|
+
}
|
|
50700
|
+
forCheckExecution(check) {
|
|
50701
|
+
return {
|
|
50702
|
+
scope: "check.execute",
|
|
50703
|
+
check: {
|
|
50704
|
+
id: check.id,
|
|
50705
|
+
type: check.type,
|
|
50706
|
+
group: check.group,
|
|
50707
|
+
tags: check.tags,
|
|
50708
|
+
criticality: check.criticality,
|
|
50709
|
+
sandbox: check.sandbox,
|
|
50710
|
+
policy: check.policy
|
|
50711
|
+
},
|
|
50712
|
+
actor: this.buildActor(),
|
|
50713
|
+
repository: this.repository,
|
|
50714
|
+
pullRequest: this.pullRequest
|
|
50715
|
+
};
|
|
50716
|
+
}
|
|
50717
|
+
forToolInvocation(serverName, methodName, transport) {
|
|
50718
|
+
return {
|
|
50719
|
+
scope: "tool.invoke",
|
|
50720
|
+
tool: { serverName, methodName, transport },
|
|
50721
|
+
actor: this.buildActor(),
|
|
50722
|
+
repository: this.repository,
|
|
50723
|
+
pullRequest: this.pullRequest
|
|
50724
|
+
};
|
|
50725
|
+
}
|
|
50726
|
+
forCapabilityResolve(checkId, capabilities) {
|
|
50727
|
+
return {
|
|
50728
|
+
scope: "capability.resolve",
|
|
50729
|
+
check: { id: checkId, type: "ai" },
|
|
50730
|
+
capability: capabilities,
|
|
50731
|
+
actor: this.buildActor(),
|
|
50732
|
+
repository: this.repository,
|
|
50733
|
+
pullRequest: this.pullRequest
|
|
50734
|
+
};
|
|
50735
|
+
}
|
|
50736
|
+
};
|
|
50737
|
+
}
|
|
50738
|
+
});
|
|
50739
|
+
|
|
50740
|
+
// src/enterprise/policy/opa-policy-engine.ts
|
|
50741
|
+
var opa_policy_engine_exports = {};
|
|
50742
|
+
__export(opa_policy_engine_exports, {
|
|
50743
|
+
OpaPolicyEngine: () => OpaPolicyEngine
|
|
50744
|
+
});
|
|
50745
|
+
var OpaPolicyEngine;
|
|
50746
|
+
var init_opa_policy_engine = __esm({
|
|
50747
|
+
"src/enterprise/policy/opa-policy-engine.ts"() {
|
|
50748
|
+
"use strict";
|
|
50749
|
+
init_opa_wasm_evaluator();
|
|
50750
|
+
init_opa_http_evaluator();
|
|
50751
|
+
init_policy_input_builder();
|
|
50752
|
+
OpaPolicyEngine = class {
|
|
50753
|
+
evaluator = null;
|
|
50754
|
+
fallback;
|
|
50755
|
+
timeout;
|
|
50756
|
+
config;
|
|
50757
|
+
inputBuilder = null;
|
|
50758
|
+
logger = null;
|
|
50759
|
+
constructor(config) {
|
|
50760
|
+
this.config = config;
|
|
50761
|
+
this.fallback = config.fallback || "deny";
|
|
50762
|
+
this.timeout = config.timeout || 5e3;
|
|
50763
|
+
}
|
|
50764
|
+
async initialize(config) {
|
|
50765
|
+
try {
|
|
50766
|
+
this.logger = (init_logger(), __toCommonJS(logger_exports)).logger;
|
|
50767
|
+
} catch {
|
|
50768
|
+
}
|
|
50769
|
+
const actor = {
|
|
50770
|
+
authorAssociation: process.env.VISOR_AUTHOR_ASSOCIATION,
|
|
50771
|
+
login: process.env.VISOR_AUTHOR_LOGIN || process.env.GITHUB_ACTOR,
|
|
50772
|
+
isLocalMode: !process.env.GITHUB_ACTIONS
|
|
50773
|
+
};
|
|
50774
|
+
const repo = {
|
|
50775
|
+
owner: process.env.GITHUB_REPOSITORY_OWNER,
|
|
50776
|
+
name: process.env.GITHUB_REPOSITORY?.split("/")[1],
|
|
50777
|
+
branch: process.env.GITHUB_HEAD_REF,
|
|
50778
|
+
baseBranch: process.env.GITHUB_BASE_REF,
|
|
50779
|
+
event: process.env.GITHUB_EVENT_NAME
|
|
50780
|
+
};
|
|
50781
|
+
const prNum = process.env.GITHUB_PR_NUMBER ? parseInt(process.env.GITHUB_PR_NUMBER, 10) : void 0;
|
|
50782
|
+
const pullRequest = {
|
|
50783
|
+
number: prNum !== void 0 && Number.isFinite(prNum) ? prNum : void 0
|
|
50784
|
+
};
|
|
50785
|
+
this.inputBuilder = new PolicyInputBuilder(config, actor, repo, pullRequest);
|
|
50786
|
+
if (config.engine === "local") {
|
|
50787
|
+
if (!config.rules) {
|
|
50788
|
+
throw new Error("OPA local mode requires `policy.rules` path to .wasm or .rego files");
|
|
50789
|
+
}
|
|
50790
|
+
const wasm = new OpaWasmEvaluator();
|
|
50791
|
+
await wasm.initialize(config.rules);
|
|
50792
|
+
if (config.data) {
|
|
50793
|
+
wasm.loadData(config.data);
|
|
50794
|
+
}
|
|
50795
|
+
this.evaluator = wasm;
|
|
50796
|
+
} else if (config.engine === "remote") {
|
|
50797
|
+
if (!config.url) {
|
|
50798
|
+
throw new Error("OPA remote mode requires `policy.url` pointing to OPA server");
|
|
50799
|
+
}
|
|
50800
|
+
this.evaluator = new OpaHttpEvaluator(config.url, this.timeout);
|
|
50801
|
+
} else {
|
|
50802
|
+
this.evaluator = null;
|
|
50803
|
+
}
|
|
50804
|
+
}
|
|
50805
|
+
/**
|
|
50806
|
+
* Update actor/repo/PR context (e.g., after PR info becomes available).
|
|
50807
|
+
* Called by the enterprise loader when engine context is enriched.
|
|
50808
|
+
*/
|
|
50809
|
+
setActorContext(actor, repo, pullRequest) {
|
|
50810
|
+
this.inputBuilder = new PolicyInputBuilder(this.config, actor, repo, pullRequest);
|
|
50811
|
+
}
|
|
50812
|
+
async evaluateCheckExecution(checkId, checkConfig) {
|
|
50813
|
+
if (!this.evaluator || !this.inputBuilder) return { allowed: true };
|
|
50814
|
+
const cfg = checkConfig && typeof checkConfig === "object" ? checkConfig : {};
|
|
50815
|
+
const policyOverride = cfg.policy;
|
|
50816
|
+
const input = this.inputBuilder.forCheckExecution({
|
|
50817
|
+
id: checkId,
|
|
50818
|
+
type: cfg.type || "ai",
|
|
50819
|
+
group: cfg.group,
|
|
50820
|
+
tags: cfg.tags,
|
|
50821
|
+
criticality: cfg.criticality,
|
|
50822
|
+
sandbox: cfg.sandbox,
|
|
50823
|
+
policy: policyOverride
|
|
50824
|
+
});
|
|
50825
|
+
return this.doEvaluate(input, this.resolveRulePath("check.execute", policyOverride?.rule));
|
|
50826
|
+
}
|
|
50827
|
+
async evaluateToolInvocation(serverName, methodName, transport) {
|
|
50828
|
+
if (!this.evaluator || !this.inputBuilder) return { allowed: true };
|
|
50829
|
+
const input = this.inputBuilder.forToolInvocation(serverName, methodName, transport);
|
|
50830
|
+
return this.doEvaluate(input, "visor/tool/invoke");
|
|
50831
|
+
}
|
|
50832
|
+
async evaluateCapabilities(checkId, capabilities) {
|
|
50833
|
+
if (!this.evaluator || !this.inputBuilder) return { allowed: true };
|
|
50834
|
+
const input = this.inputBuilder.forCapabilityResolve(checkId, capabilities);
|
|
50835
|
+
return this.doEvaluate(input, "visor/capability/resolve");
|
|
50836
|
+
}
|
|
50837
|
+
async shutdown() {
|
|
50838
|
+
if (this.evaluator && "shutdown" in this.evaluator) {
|
|
50839
|
+
await this.evaluator.shutdown();
|
|
50840
|
+
}
|
|
50841
|
+
this.evaluator = null;
|
|
50842
|
+
this.inputBuilder = null;
|
|
50843
|
+
}
|
|
50844
|
+
resolveRulePath(defaultScope, override) {
|
|
50845
|
+
if (override) {
|
|
50846
|
+
return override.startsWith("visor/") ? override : `visor/${override}`;
|
|
50847
|
+
}
|
|
50848
|
+
return `visor/${defaultScope.replace(/\./g, "/")}`;
|
|
50849
|
+
}
|
|
50850
|
+
async doEvaluate(input, rulePath) {
|
|
50851
|
+
try {
|
|
50852
|
+
this.logger?.debug(`[PolicyEngine] Evaluating ${rulePath}`, JSON.stringify(input));
|
|
50853
|
+
let timer;
|
|
50854
|
+
const timeoutPromise = new Promise((_resolve, reject) => {
|
|
50855
|
+
timer = setTimeout(() => reject(new Error("policy evaluation timeout")), this.timeout);
|
|
50856
|
+
});
|
|
50857
|
+
try {
|
|
50858
|
+
const result = await Promise.race([this.rawEvaluate(input, rulePath), timeoutPromise]);
|
|
50859
|
+
const decision = this.parseDecision(result);
|
|
50860
|
+
if (!decision.allowed && this.fallback === "warn") {
|
|
50861
|
+
decision.allowed = true;
|
|
50862
|
+
decision.warn = true;
|
|
50863
|
+
decision.reason = `audit: ${decision.reason || "policy denied"}`;
|
|
50864
|
+
}
|
|
50865
|
+
this.logger?.debug(
|
|
50866
|
+
`[PolicyEngine] Decision for ${rulePath}: allowed=${decision.allowed}, warn=${decision.warn || false}, reason=${decision.reason || "none"}`
|
|
50867
|
+
);
|
|
50868
|
+
return decision;
|
|
50869
|
+
} finally {
|
|
50870
|
+
if (timer) clearTimeout(timer);
|
|
50871
|
+
}
|
|
50872
|
+
} catch (err) {
|
|
50873
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
50874
|
+
this.logger?.warn(`[PolicyEngine] Evaluation failed for ${rulePath}: ${msg}`);
|
|
50875
|
+
return {
|
|
50876
|
+
allowed: this.fallback === "allow" || this.fallback === "warn",
|
|
50877
|
+
warn: this.fallback === "warn" ? true : void 0,
|
|
50878
|
+
reason: `policy evaluation failed, fallback=${this.fallback}`
|
|
50879
|
+
};
|
|
50880
|
+
}
|
|
50881
|
+
}
|
|
50882
|
+
async rawEvaluate(input, rulePath) {
|
|
50883
|
+
if (this.evaluator instanceof OpaWasmEvaluator) {
|
|
50884
|
+
const result = await this.evaluator.evaluate(input);
|
|
50885
|
+
return this.navigateWasmResult(result, rulePath);
|
|
50886
|
+
}
|
|
50887
|
+
return this.evaluator.evaluate(input, rulePath);
|
|
50888
|
+
}
|
|
50889
|
+
/**
|
|
50890
|
+
* Navigate nested OPA WASM result tree to reach the specific rule's output.
|
|
50891
|
+
* The WASM entrypoint `-e visor` means the result root IS the visor package,
|
|
50892
|
+
* so we strip the `visor/` prefix and walk the remaining segments.
|
|
50893
|
+
*/
|
|
50894
|
+
navigateWasmResult(result, rulePath) {
|
|
50895
|
+
if (!result || typeof result !== "object") return result;
|
|
50896
|
+
const segments = rulePath.replace(/^visor\//, "").split("/");
|
|
50897
|
+
let current = result;
|
|
50898
|
+
for (const seg of segments) {
|
|
50899
|
+
if (current && typeof current === "object" && seg in current) {
|
|
50900
|
+
current = current[seg];
|
|
50901
|
+
} else {
|
|
50902
|
+
return void 0;
|
|
50903
|
+
}
|
|
50904
|
+
}
|
|
50905
|
+
return current;
|
|
50906
|
+
}
|
|
50907
|
+
parseDecision(result) {
|
|
50908
|
+
if (result === void 0 || result === null) {
|
|
50909
|
+
return {
|
|
50910
|
+
allowed: this.fallback === "allow" || this.fallback === "warn",
|
|
50911
|
+
warn: this.fallback === "warn" ? true : void 0,
|
|
50912
|
+
reason: this.fallback === "warn" ? "audit: no policy result" : "no policy result"
|
|
50913
|
+
};
|
|
50914
|
+
}
|
|
50915
|
+
const allowed = result.allowed !== false;
|
|
50916
|
+
const decision = {
|
|
50917
|
+
allowed,
|
|
50918
|
+
reason: result.reason
|
|
50919
|
+
};
|
|
50920
|
+
if (result.capabilities) {
|
|
50921
|
+
decision.capabilities = result.capabilities;
|
|
50922
|
+
}
|
|
50923
|
+
return decision;
|
|
50924
|
+
}
|
|
50925
|
+
};
|
|
50926
|
+
}
|
|
50927
|
+
});
|
|
50928
|
+
|
|
50929
|
+
// src/enterprise/scheduler/knex-store.ts
|
|
50930
|
+
var knex_store_exports = {};
|
|
50931
|
+
__export(knex_store_exports, {
|
|
50932
|
+
KnexStoreBackend: () => KnexStoreBackend
|
|
50933
|
+
});
|
|
50934
|
+
function toNum(val) {
|
|
50935
|
+
if (val === null || val === void 0) return void 0;
|
|
50936
|
+
return typeof val === "string" ? parseInt(val, 10) : val;
|
|
50937
|
+
}
|
|
50938
|
+
function safeJsonParse2(value) {
|
|
50939
|
+
if (!value) return void 0;
|
|
50940
|
+
try {
|
|
50941
|
+
return JSON.parse(value);
|
|
50942
|
+
} catch {
|
|
50943
|
+
return void 0;
|
|
50944
|
+
}
|
|
50945
|
+
}
|
|
50946
|
+
function fromDbRow2(row) {
|
|
50947
|
+
return {
|
|
50948
|
+
id: row.id,
|
|
50949
|
+
creatorId: row.creator_id,
|
|
50950
|
+
creatorContext: row.creator_context ?? void 0,
|
|
50951
|
+
creatorName: row.creator_name ?? void 0,
|
|
50952
|
+
timezone: row.timezone,
|
|
50953
|
+
schedule: row.schedule_expr,
|
|
50954
|
+
runAt: toNum(row.run_at),
|
|
50955
|
+
isRecurring: row.is_recurring === true || row.is_recurring === 1,
|
|
50956
|
+
originalExpression: row.original_expression,
|
|
50957
|
+
workflow: row.workflow ?? void 0,
|
|
50958
|
+
workflowInputs: safeJsonParse2(row.workflow_inputs),
|
|
50959
|
+
outputContext: safeJsonParse2(row.output_context),
|
|
50960
|
+
status: row.status,
|
|
50961
|
+
createdAt: toNum(row.created_at),
|
|
50962
|
+
lastRunAt: toNum(row.last_run_at),
|
|
50963
|
+
nextRunAt: toNum(row.next_run_at),
|
|
50964
|
+
runCount: row.run_count,
|
|
50965
|
+
failureCount: row.failure_count,
|
|
50966
|
+
lastError: row.last_error ?? void 0,
|
|
50967
|
+
previousResponse: row.previous_response ?? void 0
|
|
50968
|
+
};
|
|
50969
|
+
}
|
|
50970
|
+
function toInsertRow(schedule) {
|
|
50971
|
+
return {
|
|
50972
|
+
id: schedule.id,
|
|
50973
|
+
creator_id: schedule.creatorId,
|
|
50974
|
+
creator_context: schedule.creatorContext ?? null,
|
|
50975
|
+
creator_name: schedule.creatorName ?? null,
|
|
50976
|
+
timezone: schedule.timezone,
|
|
50977
|
+
schedule_expr: schedule.schedule,
|
|
50978
|
+
run_at: schedule.runAt ?? null,
|
|
50979
|
+
is_recurring: schedule.isRecurring,
|
|
50980
|
+
original_expression: schedule.originalExpression,
|
|
50981
|
+
workflow: schedule.workflow ?? null,
|
|
50982
|
+
workflow_inputs: schedule.workflowInputs ? JSON.stringify(schedule.workflowInputs) : null,
|
|
50983
|
+
output_context: schedule.outputContext ? JSON.stringify(schedule.outputContext) : null,
|
|
50984
|
+
status: schedule.status,
|
|
50985
|
+
created_at: schedule.createdAt,
|
|
50986
|
+
last_run_at: schedule.lastRunAt ?? null,
|
|
50987
|
+
next_run_at: schedule.nextRunAt ?? null,
|
|
50988
|
+
run_count: schedule.runCount,
|
|
50989
|
+
failure_count: schedule.failureCount,
|
|
50990
|
+
last_error: schedule.lastError ?? null,
|
|
50991
|
+
previous_response: schedule.previousResponse ?? null
|
|
50992
|
+
};
|
|
50993
|
+
}
|
|
50994
|
+
var fs21, path25, import_uuid2, KnexStoreBackend;
|
|
50995
|
+
var init_knex_store = __esm({
|
|
50996
|
+
"src/enterprise/scheduler/knex-store.ts"() {
|
|
50997
|
+
"use strict";
|
|
50998
|
+
fs21 = __toESM(require("fs"));
|
|
50999
|
+
path25 = __toESM(require("path"));
|
|
51000
|
+
import_uuid2 = require("uuid");
|
|
51001
|
+
init_logger();
|
|
51002
|
+
KnexStoreBackend = class {
|
|
51003
|
+
knex = null;
|
|
51004
|
+
driver;
|
|
51005
|
+
connection;
|
|
51006
|
+
constructor(driver, storageConfig, _haConfig) {
|
|
51007
|
+
this.driver = driver;
|
|
51008
|
+
this.connection = storageConfig.connection || {};
|
|
51009
|
+
}
|
|
51010
|
+
async initialize() {
|
|
51011
|
+
const { createRequire } = require("module");
|
|
51012
|
+
const runtimeRequire = createRequire(__filename);
|
|
51013
|
+
let knexFactory;
|
|
51014
|
+
try {
|
|
51015
|
+
knexFactory = runtimeRequire("knex");
|
|
51016
|
+
} catch (err) {
|
|
51017
|
+
const code = err?.code;
|
|
51018
|
+
if (code === "MODULE_NOT_FOUND" || code === "ERR_MODULE_NOT_FOUND") {
|
|
51019
|
+
throw new Error(
|
|
51020
|
+
"knex is required for PostgreSQL/MySQL/MSSQL schedule storage. Install it with: npm install knex"
|
|
51021
|
+
);
|
|
51022
|
+
}
|
|
51023
|
+
throw err;
|
|
51024
|
+
}
|
|
51025
|
+
const clientMap = {
|
|
51026
|
+
postgresql: "pg",
|
|
51027
|
+
mysql: "mysql2",
|
|
51028
|
+
mssql: "tedious"
|
|
51029
|
+
};
|
|
51030
|
+
const client = clientMap[this.driver];
|
|
51031
|
+
let connection;
|
|
51032
|
+
if (this.connection.connection_string) {
|
|
51033
|
+
connection = this.connection.connection_string;
|
|
51034
|
+
} else if (this.driver === "mssql") {
|
|
51035
|
+
connection = this.buildMssqlConnection();
|
|
51036
|
+
} else {
|
|
51037
|
+
connection = this.buildStandardConnection();
|
|
51038
|
+
}
|
|
51039
|
+
this.knex = knexFactory({
|
|
51040
|
+
client,
|
|
51041
|
+
connection,
|
|
51042
|
+
pool: {
|
|
51043
|
+
min: this.connection.pool?.min ?? 0,
|
|
51044
|
+
max: this.connection.pool?.max ?? 10
|
|
51045
|
+
}
|
|
51046
|
+
});
|
|
51047
|
+
await this.migrateSchema();
|
|
51048
|
+
logger.info(`[KnexStore] Initialized (${this.driver})`);
|
|
51049
|
+
}
|
|
51050
|
+
buildStandardConnection() {
|
|
51051
|
+
return {
|
|
51052
|
+
host: this.connection.host || "localhost",
|
|
51053
|
+
port: this.connection.port,
|
|
51054
|
+
database: this.connection.database || "visor",
|
|
51055
|
+
user: this.connection.user,
|
|
51056
|
+
password: this.connection.password,
|
|
51057
|
+
ssl: this.resolveSslConfig()
|
|
51058
|
+
};
|
|
51059
|
+
}
|
|
51060
|
+
buildMssqlConnection() {
|
|
51061
|
+
const ssl = this.connection.ssl;
|
|
51062
|
+
const sslEnabled = ssl === true || typeof ssl === "object" && ssl.enabled !== false;
|
|
51063
|
+
return {
|
|
51064
|
+
server: this.connection.host || "localhost",
|
|
51065
|
+
port: this.connection.port,
|
|
51066
|
+
database: this.connection.database || "visor",
|
|
51067
|
+
user: this.connection.user,
|
|
51068
|
+
password: this.connection.password,
|
|
51069
|
+
options: {
|
|
51070
|
+
encrypt: sslEnabled,
|
|
51071
|
+
trustServerCertificate: typeof ssl === "object" ? ssl.reject_unauthorized === false : !sslEnabled
|
|
51072
|
+
}
|
|
51073
|
+
};
|
|
51074
|
+
}
|
|
51075
|
+
resolveSslConfig() {
|
|
51076
|
+
const ssl = this.connection.ssl;
|
|
51077
|
+
if (ssl === false || ssl === void 0) return false;
|
|
51078
|
+
if (ssl === true) return { rejectUnauthorized: true };
|
|
51079
|
+
if (ssl.enabled === false) return false;
|
|
51080
|
+
const result = {
|
|
51081
|
+
rejectUnauthorized: ssl.reject_unauthorized !== false
|
|
51082
|
+
};
|
|
51083
|
+
if (ssl.ca) {
|
|
51084
|
+
const caPath = this.validateSslPath(ssl.ca, "CA certificate");
|
|
51085
|
+
result.ca = fs21.readFileSync(caPath, "utf8");
|
|
51086
|
+
}
|
|
51087
|
+
if (ssl.cert) {
|
|
51088
|
+
const certPath = this.validateSslPath(ssl.cert, "client certificate");
|
|
51089
|
+
result.cert = fs21.readFileSync(certPath, "utf8");
|
|
51090
|
+
}
|
|
51091
|
+
if (ssl.key) {
|
|
51092
|
+
const keyPath = this.validateSslPath(ssl.key, "client key");
|
|
51093
|
+
result.key = fs21.readFileSync(keyPath, "utf8");
|
|
51094
|
+
}
|
|
51095
|
+
return result;
|
|
51096
|
+
}
|
|
51097
|
+
validateSslPath(filePath, label) {
|
|
51098
|
+
const resolved = path25.resolve(filePath);
|
|
51099
|
+
if (resolved !== path25.normalize(resolved)) {
|
|
51100
|
+
throw new Error(`SSL ${label} path contains invalid sequences: ${filePath}`);
|
|
51101
|
+
}
|
|
51102
|
+
if (!fs21.existsSync(resolved)) {
|
|
51103
|
+
throw new Error(`SSL ${label} not found: ${filePath}`);
|
|
51104
|
+
}
|
|
51105
|
+
return resolved;
|
|
51106
|
+
}
|
|
51107
|
+
async shutdown() {
|
|
51108
|
+
if (this.knex) {
|
|
51109
|
+
await this.knex.destroy();
|
|
51110
|
+
this.knex = null;
|
|
51111
|
+
}
|
|
51112
|
+
}
|
|
51113
|
+
async migrateSchema() {
|
|
51114
|
+
const knex = this.getKnex();
|
|
51115
|
+
const exists = await knex.schema.hasTable("schedules");
|
|
51116
|
+
if (!exists) {
|
|
51117
|
+
await knex.schema.createTable("schedules", (table) => {
|
|
51118
|
+
table.string("id", 36).primary();
|
|
51119
|
+
table.string("creator_id", 255).notNullable().index();
|
|
51120
|
+
table.string("creator_context", 255);
|
|
51121
|
+
table.string("creator_name", 255);
|
|
51122
|
+
table.string("timezone", 64).notNullable().defaultTo("UTC");
|
|
51123
|
+
table.string("schedule_expr", 255);
|
|
51124
|
+
table.bigInteger("run_at");
|
|
51125
|
+
table.boolean("is_recurring").notNullable();
|
|
51126
|
+
table.text("original_expression");
|
|
51127
|
+
table.string("workflow", 255);
|
|
51128
|
+
table.text("workflow_inputs");
|
|
51129
|
+
table.text("output_context");
|
|
51130
|
+
table.string("status", 20).notNullable().index();
|
|
51131
|
+
table.bigInteger("created_at").notNullable();
|
|
51132
|
+
table.bigInteger("last_run_at");
|
|
51133
|
+
table.bigInteger("next_run_at");
|
|
51134
|
+
table.integer("run_count").notNullable().defaultTo(0);
|
|
51135
|
+
table.integer("failure_count").notNullable().defaultTo(0);
|
|
51136
|
+
table.text("last_error");
|
|
51137
|
+
table.text("previous_response");
|
|
51138
|
+
table.index(["status", "next_run_at"]);
|
|
51139
|
+
});
|
|
51140
|
+
}
|
|
51141
|
+
const locksExist = await knex.schema.hasTable("scheduler_locks");
|
|
51142
|
+
if (!locksExist) {
|
|
51143
|
+
await knex.schema.createTable("scheduler_locks", (table) => {
|
|
51144
|
+
table.string("lock_id", 255).primary();
|
|
51145
|
+
table.string("node_id", 255).notNullable();
|
|
51146
|
+
table.string("lock_token", 36).notNullable();
|
|
51147
|
+
table.bigInteger("acquired_at").notNullable();
|
|
51148
|
+
table.bigInteger("expires_at").notNullable();
|
|
51149
|
+
});
|
|
51150
|
+
}
|
|
51151
|
+
}
|
|
51152
|
+
getKnex() {
|
|
51153
|
+
if (!this.knex) {
|
|
51154
|
+
throw new Error("[KnexStore] Not initialized. Call initialize() first.");
|
|
51155
|
+
}
|
|
51156
|
+
return this.knex;
|
|
51157
|
+
}
|
|
51158
|
+
// --- CRUD ---
|
|
51159
|
+
async create(schedule) {
|
|
51160
|
+
const knex = this.getKnex();
|
|
51161
|
+
const newSchedule = {
|
|
51162
|
+
...schedule,
|
|
51163
|
+
id: (0, import_uuid2.v4)(),
|
|
51164
|
+
createdAt: Date.now(),
|
|
51165
|
+
runCount: 0,
|
|
51166
|
+
failureCount: 0,
|
|
51167
|
+
status: "active"
|
|
51168
|
+
};
|
|
51169
|
+
await knex("schedules").insert(toInsertRow(newSchedule));
|
|
51170
|
+
logger.info(`[KnexStore] Created schedule ${newSchedule.id} for user ${newSchedule.creatorId}`);
|
|
51171
|
+
return newSchedule;
|
|
51172
|
+
}
|
|
51173
|
+
async importSchedule(schedule) {
|
|
51174
|
+
const knex = this.getKnex();
|
|
51175
|
+
const existing = await knex("schedules").where("id", schedule.id).first();
|
|
51176
|
+
if (existing) return;
|
|
51177
|
+
await knex("schedules").insert(toInsertRow(schedule));
|
|
51178
|
+
}
|
|
51179
|
+
async get(id) {
|
|
51180
|
+
const knex = this.getKnex();
|
|
51181
|
+
const row = await knex("schedules").where("id", id).first();
|
|
51182
|
+
return row ? fromDbRow2(row) : void 0;
|
|
51183
|
+
}
|
|
51184
|
+
async update(id, patch) {
|
|
51185
|
+
const knex = this.getKnex();
|
|
51186
|
+
const existing = await knex("schedules").where("id", id).first();
|
|
51187
|
+
if (!existing) return void 0;
|
|
51188
|
+
const current = fromDbRow2(existing);
|
|
51189
|
+
const updated = { ...current, ...patch, id: current.id };
|
|
51190
|
+
const row = toInsertRow(updated);
|
|
51191
|
+
delete row.id;
|
|
51192
|
+
await knex("schedules").where("id", id).update(row);
|
|
51193
|
+
return updated;
|
|
51194
|
+
}
|
|
51195
|
+
async delete(id) {
|
|
51196
|
+
const knex = this.getKnex();
|
|
51197
|
+
const deleted = await knex("schedules").where("id", id).del();
|
|
51198
|
+
if (deleted > 0) {
|
|
51199
|
+
logger.info(`[KnexStore] Deleted schedule ${id}`);
|
|
51200
|
+
return true;
|
|
51201
|
+
}
|
|
51202
|
+
return false;
|
|
51203
|
+
}
|
|
51204
|
+
// --- Queries ---
|
|
51205
|
+
async getByCreator(creatorId) {
|
|
51206
|
+
const knex = this.getKnex();
|
|
51207
|
+
const rows = await knex("schedules").where("creator_id", creatorId);
|
|
51208
|
+
return rows.map((r) => fromDbRow2(r));
|
|
51209
|
+
}
|
|
51210
|
+
async getActiveSchedules() {
|
|
51211
|
+
const knex = this.getKnex();
|
|
51212
|
+
const rows = await knex("schedules").where("status", "active");
|
|
51213
|
+
return rows.map((r) => fromDbRow2(r));
|
|
51214
|
+
}
|
|
51215
|
+
async getDueSchedules(now) {
|
|
51216
|
+
const ts = now ?? Date.now();
|
|
51217
|
+
const knex = this.getKnex();
|
|
51218
|
+
const bFalse = this.driver === "mssql" ? 0 : false;
|
|
51219
|
+
const bTrue = this.driver === "mssql" ? 1 : true;
|
|
51220
|
+
const rows = await knex("schedules").where("status", "active").andWhere(function() {
|
|
51221
|
+
this.where(function() {
|
|
51222
|
+
this.where("is_recurring", bFalse).whereNotNull("run_at").where("run_at", "<=", ts);
|
|
51223
|
+
}).orWhere(function() {
|
|
51224
|
+
this.where("is_recurring", bTrue).whereNotNull("next_run_at").where("next_run_at", "<=", ts);
|
|
51225
|
+
});
|
|
51226
|
+
});
|
|
51227
|
+
return rows.map((r) => fromDbRow2(r));
|
|
51228
|
+
}
|
|
51229
|
+
async findByWorkflow(creatorId, workflowName) {
|
|
51230
|
+
const knex = this.getKnex();
|
|
51231
|
+
const escaped = workflowName.toLowerCase().replace(/[%_\\]/g, "\\$&");
|
|
51232
|
+
const pattern = `%${escaped}%`;
|
|
51233
|
+
const rows = await knex("schedules").where("creator_id", creatorId).where("status", "active").whereRaw("LOWER(workflow) LIKE ? ESCAPE '\\'", [pattern]);
|
|
51234
|
+
return rows.map((r) => fromDbRow2(r));
|
|
51235
|
+
}
|
|
51236
|
+
async getAll() {
|
|
51237
|
+
const knex = this.getKnex();
|
|
51238
|
+
const rows = await knex("schedules");
|
|
51239
|
+
return rows.map((r) => fromDbRow2(r));
|
|
51240
|
+
}
|
|
51241
|
+
async getStats() {
|
|
51242
|
+
const knex = this.getKnex();
|
|
51243
|
+
const boolTrue = this.driver === "mssql" ? "1" : "true";
|
|
51244
|
+
const boolFalse = this.driver === "mssql" ? "0" : "false";
|
|
51245
|
+
const result = await knex("schedules").select(
|
|
51246
|
+
knex.raw("COUNT(*) as total"),
|
|
51247
|
+
knex.raw("SUM(CASE WHEN status = 'active' THEN 1 ELSE 0 END) as active"),
|
|
51248
|
+
knex.raw("SUM(CASE WHEN status = 'paused' THEN 1 ELSE 0 END) as paused"),
|
|
51249
|
+
knex.raw("SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed"),
|
|
51250
|
+
knex.raw("SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed"),
|
|
51251
|
+
knex.raw(`SUM(CASE WHEN is_recurring = ${boolTrue} THEN 1 ELSE 0 END) as recurring`),
|
|
51252
|
+
knex.raw(`SUM(CASE WHEN is_recurring = ${boolFalse} THEN 1 ELSE 0 END) as one_time`)
|
|
51253
|
+
).first();
|
|
51254
|
+
return {
|
|
51255
|
+
total: Number(result.total) || 0,
|
|
51256
|
+
active: Number(result.active) || 0,
|
|
51257
|
+
paused: Number(result.paused) || 0,
|
|
51258
|
+
completed: Number(result.completed) || 0,
|
|
51259
|
+
failed: Number(result.failed) || 0,
|
|
51260
|
+
recurring: Number(result.recurring) || 0,
|
|
51261
|
+
oneTime: Number(result.one_time) || 0
|
|
51262
|
+
};
|
|
51263
|
+
}
|
|
51264
|
+
async validateLimits(creatorId, isRecurring, limits) {
|
|
51265
|
+
const knex = this.getKnex();
|
|
51266
|
+
if (limits.maxGlobal) {
|
|
51267
|
+
const result = await knex("schedules").count("* as cnt").first();
|
|
51268
|
+
if (Number(result?.cnt) >= limits.maxGlobal) {
|
|
51269
|
+
throw new Error(`Global schedule limit reached (${limits.maxGlobal})`);
|
|
51270
|
+
}
|
|
51271
|
+
}
|
|
51272
|
+
if (limits.maxPerUser) {
|
|
51273
|
+
const result = await knex("schedules").where("creator_id", creatorId).count("* as cnt").first();
|
|
51274
|
+
if (Number(result?.cnt) >= limits.maxPerUser) {
|
|
51275
|
+
throw new Error(`You have reached the maximum number of schedules (${limits.maxPerUser})`);
|
|
51276
|
+
}
|
|
51277
|
+
}
|
|
51278
|
+
if (isRecurring && limits.maxRecurringPerUser) {
|
|
51279
|
+
const bTrue = this.driver === "mssql" ? 1 : true;
|
|
51280
|
+
const result = await knex("schedules").where("creator_id", creatorId).where("is_recurring", bTrue).count("* as cnt").first();
|
|
51281
|
+
if (Number(result?.cnt) >= limits.maxRecurringPerUser) {
|
|
51282
|
+
throw new Error(
|
|
51283
|
+
`You have reached the maximum number of recurring schedules (${limits.maxRecurringPerUser})`
|
|
51284
|
+
);
|
|
51285
|
+
}
|
|
51286
|
+
}
|
|
51287
|
+
}
|
|
51288
|
+
// --- HA Distributed Locking (via scheduler_locks table) ---
|
|
51289
|
+
async tryAcquireLock(lockId, nodeId, ttlSeconds) {
|
|
51290
|
+
const knex = this.getKnex();
|
|
51291
|
+
const now = Date.now();
|
|
51292
|
+
const expiresAt = now + ttlSeconds * 1e3;
|
|
51293
|
+
const token = (0, import_uuid2.v4)();
|
|
51294
|
+
const updated = await knex("scheduler_locks").where("lock_id", lockId).where("expires_at", "<", now).update({
|
|
51295
|
+
node_id: nodeId,
|
|
51296
|
+
lock_token: token,
|
|
51297
|
+
acquired_at: now,
|
|
51298
|
+
expires_at: expiresAt
|
|
51299
|
+
});
|
|
51300
|
+
if (updated > 0) return token;
|
|
51301
|
+
try {
|
|
51302
|
+
await knex("scheduler_locks").insert({
|
|
51303
|
+
lock_id: lockId,
|
|
51304
|
+
node_id: nodeId,
|
|
51305
|
+
lock_token: token,
|
|
51306
|
+
acquired_at: now,
|
|
51307
|
+
expires_at: expiresAt
|
|
51308
|
+
});
|
|
51309
|
+
return token;
|
|
51310
|
+
} catch {
|
|
51311
|
+
return null;
|
|
51312
|
+
}
|
|
51313
|
+
}
|
|
51314
|
+
async releaseLock(lockId, lockToken) {
|
|
51315
|
+
const knex = this.getKnex();
|
|
51316
|
+
await knex("scheduler_locks").where("lock_id", lockId).where("lock_token", lockToken).del();
|
|
51317
|
+
}
|
|
51318
|
+
async renewLock(lockId, lockToken, ttlSeconds) {
|
|
51319
|
+
const knex = this.getKnex();
|
|
51320
|
+
const now = Date.now();
|
|
51321
|
+
const expiresAt = now + ttlSeconds * 1e3;
|
|
51322
|
+
const updated = await knex("scheduler_locks").where("lock_id", lockId).where("lock_token", lockToken).update({ acquired_at: now, expires_at: expiresAt });
|
|
51323
|
+
return updated > 0;
|
|
51324
|
+
}
|
|
51325
|
+
async flush() {
|
|
51326
|
+
}
|
|
51327
|
+
};
|
|
51328
|
+
}
|
|
51329
|
+
});
|
|
51330
|
+
|
|
51331
|
+
// src/enterprise/loader.ts
|
|
51332
|
+
var loader_exports = {};
|
|
51333
|
+
__export(loader_exports, {
|
|
51334
|
+
loadEnterprisePolicyEngine: () => loadEnterprisePolicyEngine,
|
|
51335
|
+
loadEnterpriseStoreBackend: () => loadEnterpriseStoreBackend
|
|
51336
|
+
});
|
|
51337
|
+
async function loadEnterprisePolicyEngine(config) {
|
|
51338
|
+
try {
|
|
51339
|
+
const { LicenseValidator: LicenseValidator2 } = await Promise.resolve().then(() => (init_validator(), validator_exports));
|
|
51340
|
+
const validator = new LicenseValidator2();
|
|
51341
|
+
const license = await validator.loadAndValidate();
|
|
51342
|
+
if (!license || !validator.hasFeature("policy")) {
|
|
51343
|
+
return new DefaultPolicyEngine();
|
|
51344
|
+
}
|
|
51345
|
+
if (validator.isInGracePeriod()) {
|
|
51346
|
+
console.warn(
|
|
51347
|
+
"[visor:enterprise] License has expired but is within the 72-hour grace period. Please renew your license."
|
|
51348
|
+
);
|
|
51349
|
+
}
|
|
51350
|
+
const { OpaPolicyEngine: OpaPolicyEngine2 } = await Promise.resolve().then(() => (init_opa_policy_engine(), opa_policy_engine_exports));
|
|
51351
|
+
const engine = new OpaPolicyEngine2(config);
|
|
51352
|
+
await engine.initialize(config);
|
|
51353
|
+
return engine;
|
|
51354
|
+
} catch (err) {
|
|
51355
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
51356
|
+
try {
|
|
51357
|
+
const { logger: logger2 } = (init_logger(), __toCommonJS(logger_exports));
|
|
51358
|
+
logger2.warn(`[PolicyEngine] Enterprise policy init failed, falling back to default: ${msg}`);
|
|
51359
|
+
} catch {
|
|
51360
|
+
}
|
|
51361
|
+
return new DefaultPolicyEngine();
|
|
51362
|
+
}
|
|
51363
|
+
}
|
|
51364
|
+
async function loadEnterpriseStoreBackend(driver, storageConfig, haConfig) {
|
|
51365
|
+
const { LicenseValidator: LicenseValidator2 } = await Promise.resolve().then(() => (init_validator(), validator_exports));
|
|
51366
|
+
const validator = new LicenseValidator2();
|
|
51367
|
+
const license = await validator.loadAndValidate();
|
|
51368
|
+
if (!license || !validator.hasFeature("scheduler-sql")) {
|
|
51369
|
+
throw new Error(
|
|
51370
|
+
`The ${driver} schedule storage driver requires a Visor Enterprise license with the 'scheduler-sql' feature. Please upgrade or use driver: 'sqlite' (default).`
|
|
51371
|
+
);
|
|
51372
|
+
}
|
|
51373
|
+
if (validator.isInGracePeriod()) {
|
|
51374
|
+
console.warn(
|
|
51375
|
+
"[visor:enterprise] License has expired but is within the 72-hour grace period. Please renew your license."
|
|
51376
|
+
);
|
|
51377
|
+
}
|
|
51378
|
+
const { KnexStoreBackend: KnexStoreBackend2 } = await Promise.resolve().then(() => (init_knex_store(), knex_store_exports));
|
|
51379
|
+
return new KnexStoreBackend2(driver, storageConfig, haConfig);
|
|
51380
|
+
}
|
|
51381
|
+
var init_loader = __esm({
|
|
51382
|
+
"src/enterprise/loader.ts"() {
|
|
51383
|
+
"use strict";
|
|
51384
|
+
init_default_engine();
|
|
51385
|
+
}
|
|
51386
|
+
});
|
|
51387
|
+
|
|
49592
51388
|
// src/event-bus/event-bus.ts
|
|
49593
51389
|
var event_bus_exports = {};
|
|
49594
51390
|
__export(event_bus_exports, {
|
|
@@ -50495,8 +52291,8 @@ ${content}
|
|
|
50495
52291
|
* Sleep utility
|
|
50496
52292
|
*/
|
|
50497
52293
|
sleep(ms) {
|
|
50498
|
-
return new Promise((
|
|
50499
|
-
const t = setTimeout(
|
|
52294
|
+
return new Promise((resolve15) => {
|
|
52295
|
+
const t = setTimeout(resolve15, ms);
|
|
50500
52296
|
if (typeof t.unref === "function") {
|
|
50501
52297
|
try {
|
|
50502
52298
|
t.unref();
|
|
@@ -50770,8 +52566,8 @@ ${end}`);
|
|
|
50770
52566
|
async updateGroupedComment(ctx, comments, group, changedIds) {
|
|
50771
52567
|
const existingLock = this.updateLocks.get(group);
|
|
50772
52568
|
let resolveLock;
|
|
50773
|
-
const ourLock = new Promise((
|
|
50774
|
-
resolveLock =
|
|
52569
|
+
const ourLock = new Promise((resolve15) => {
|
|
52570
|
+
resolveLock = resolve15;
|
|
50775
52571
|
});
|
|
50776
52572
|
this.updateLocks.set(group, ourLock);
|
|
50777
52573
|
try {
|
|
@@ -51083,7 +52879,7 @@ ${blocks}
|
|
|
51083
52879
|
* Sleep utility for enforcing delays
|
|
51084
52880
|
*/
|
|
51085
52881
|
sleep(ms) {
|
|
51086
|
-
return new Promise((
|
|
52882
|
+
return new Promise((resolve15) => setTimeout(resolve15, ms));
|
|
51087
52883
|
}
|
|
51088
52884
|
};
|
|
51089
52885
|
}
|
|
@@ -51281,7 +53077,8 @@ var init_client = __esm({
|
|
|
51281
53077
|
user: m.user,
|
|
51282
53078
|
text: m.text,
|
|
51283
53079
|
bot_id: m.bot_id,
|
|
51284
|
-
thread_ts: m.thread_ts
|
|
53080
|
+
thread_ts: m.thread_ts,
|
|
53081
|
+
files: Array.isArray(m.files) ? m.files : void 0
|
|
51285
53082
|
}));
|
|
51286
53083
|
} catch (e) {
|
|
51287
53084
|
console.warn(
|
|
@@ -51304,9 +53101,9 @@ var init_client = __esm({
|
|
|
51304
53101
|
initial_comment
|
|
51305
53102
|
}) => {
|
|
51306
53103
|
try {
|
|
51307
|
-
const getUrlResp = await this.
|
|
53104
|
+
const getUrlResp = await this.apiForm("files.getUploadURLExternal", {
|
|
51308
53105
|
filename,
|
|
51309
|
-
length: content.length
|
|
53106
|
+
length: String(content.length)
|
|
51310
53107
|
});
|
|
51311
53108
|
if (!getUrlResp || getUrlResp.ok !== true || !getUrlResp.upload_url) {
|
|
51312
53109
|
console.warn(
|
|
@@ -51364,6 +53161,19 @@ var init_client = __esm({
|
|
|
51364
53161
|
});
|
|
51365
53162
|
return await res.json();
|
|
51366
53163
|
}
|
|
53164
|
+
/** Send a Slack API request as application/x-www-form-urlencoded (required by some file methods). */
|
|
53165
|
+
async apiForm(method, params) {
|
|
53166
|
+
const body = new URLSearchParams(params);
|
|
53167
|
+
const res = await fetch(`https://slack.com/api/${method}`, {
|
|
53168
|
+
method: "POST",
|
|
53169
|
+
headers: {
|
|
53170
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
53171
|
+
Authorization: `Bearer ${this.token}`
|
|
53172
|
+
},
|
|
53173
|
+
body: body.toString()
|
|
53174
|
+
});
|
|
53175
|
+
return await res.json();
|
|
53176
|
+
}
|
|
51367
53177
|
};
|
|
51368
53178
|
}
|
|
51369
53179
|
});
|
|
@@ -51384,17 +53194,17 @@ function extractMermaidDiagrams(text) {
|
|
|
51384
53194
|
return diagrams;
|
|
51385
53195
|
}
|
|
51386
53196
|
async function renderMermaidToPng(mermaidCode) {
|
|
51387
|
-
const tmpDir =
|
|
51388
|
-
const inputFile =
|
|
53197
|
+
const tmpDir = os2.tmpdir();
|
|
53198
|
+
const inputFile = path27.join(
|
|
51389
53199
|
tmpDir,
|
|
51390
53200
|
`mermaid-${Date.now()}-${Math.random().toString(36).slice(2)}.mmd`
|
|
51391
53201
|
);
|
|
51392
|
-
const outputFile =
|
|
53202
|
+
const outputFile = path27.join(
|
|
51393
53203
|
tmpDir,
|
|
51394
53204
|
`mermaid-${Date.now()}-${Math.random().toString(36).slice(2)}.png`
|
|
51395
53205
|
);
|
|
51396
53206
|
try {
|
|
51397
|
-
|
|
53207
|
+
fs23.writeFileSync(inputFile, mermaidCode, "utf-8");
|
|
51398
53208
|
const chromiumPaths = [
|
|
51399
53209
|
"/usr/bin/chromium",
|
|
51400
53210
|
"/usr/bin/chromium-browser",
|
|
@@ -51403,7 +53213,7 @@ async function renderMermaidToPng(mermaidCode) {
|
|
|
51403
53213
|
];
|
|
51404
53214
|
let chromiumPath;
|
|
51405
53215
|
for (const p of chromiumPaths) {
|
|
51406
|
-
if (
|
|
53216
|
+
if (fs23.existsSync(p)) {
|
|
51407
53217
|
chromiumPath = p;
|
|
51408
53218
|
break;
|
|
51409
53219
|
}
|
|
@@ -51412,8 +53222,8 @@ async function renderMermaidToPng(mermaidCode) {
|
|
|
51412
53222
|
if (chromiumPath) {
|
|
51413
53223
|
env.PUPPETEER_EXECUTABLE_PATH = chromiumPath;
|
|
51414
53224
|
}
|
|
51415
|
-
const result = await new Promise((
|
|
51416
|
-
const proc = (0,
|
|
53225
|
+
const result = await new Promise((resolve15) => {
|
|
53226
|
+
const proc = (0, import_child_process6.spawn)(
|
|
51417
53227
|
"npx",
|
|
51418
53228
|
[
|
|
51419
53229
|
"--yes",
|
|
@@ -51442,32 +53252,32 @@ async function renderMermaidToPng(mermaidCode) {
|
|
|
51442
53252
|
});
|
|
51443
53253
|
proc.on("close", (code) => {
|
|
51444
53254
|
if (code === 0) {
|
|
51445
|
-
|
|
53255
|
+
resolve15({ success: true });
|
|
51446
53256
|
} else {
|
|
51447
|
-
|
|
53257
|
+
resolve15({ success: false, error: stderr || `Exit code ${code}` });
|
|
51448
53258
|
}
|
|
51449
53259
|
});
|
|
51450
53260
|
proc.on("error", (err) => {
|
|
51451
|
-
|
|
53261
|
+
resolve15({ success: false, error: err.message });
|
|
51452
53262
|
});
|
|
51453
53263
|
});
|
|
51454
53264
|
if (!result.success) {
|
|
51455
53265
|
console.warn(`Mermaid rendering failed: ${result.error}`);
|
|
51456
53266
|
return null;
|
|
51457
53267
|
}
|
|
51458
|
-
if (!
|
|
53268
|
+
if (!fs23.existsSync(outputFile)) {
|
|
51459
53269
|
console.warn("Mermaid output file not created");
|
|
51460
53270
|
return null;
|
|
51461
53271
|
}
|
|
51462
|
-
const pngBuffer =
|
|
53272
|
+
const pngBuffer = fs23.readFileSync(outputFile);
|
|
51463
53273
|
return pngBuffer;
|
|
51464
53274
|
} catch (e) {
|
|
51465
53275
|
console.warn(`Mermaid rendering error: ${e instanceof Error ? e.message : String(e)}`);
|
|
51466
53276
|
return null;
|
|
51467
53277
|
} finally {
|
|
51468
53278
|
try {
|
|
51469
|
-
if (
|
|
51470
|
-
if (
|
|
53279
|
+
if (fs23.existsSync(inputFile)) fs23.unlinkSync(inputFile);
|
|
53280
|
+
if (fs23.existsSync(outputFile)) fs23.unlinkSync(outputFile);
|
|
51471
53281
|
} catch {
|
|
51472
53282
|
}
|
|
51473
53283
|
}
|
|
@@ -51528,17 +53338,58 @@ function markdownToSlack(text) {
|
|
|
51528
53338
|
out = lines.join("\n");
|
|
51529
53339
|
return out;
|
|
51530
53340
|
}
|
|
53341
|
+
function extractFileSections(text) {
|
|
53342
|
+
const sections = [];
|
|
53343
|
+
const delimRegex = /^--- ([\w][\w.\-]*\.\w+) ---$/gm;
|
|
53344
|
+
const delimiters = [];
|
|
53345
|
+
let m;
|
|
53346
|
+
while ((m = delimRegex.exec(text)) !== null) {
|
|
53347
|
+
delimiters.push({
|
|
53348
|
+
filename: m[1],
|
|
53349
|
+
start: m.index,
|
|
53350
|
+
end: m.index + m[0].length
|
|
53351
|
+
});
|
|
53352
|
+
}
|
|
53353
|
+
if (delimiters.length === 0) return sections;
|
|
53354
|
+
for (let i = 0; i < delimiters.length; i++) {
|
|
53355
|
+
const open = delimiters[i];
|
|
53356
|
+
const contentStart = open.end < text.length && text[open.end] === "\n" ? open.end + 1 : open.end;
|
|
53357
|
+
const sectionEnd = i + 1 < delimiters.length ? delimiters[i + 1].start : text.length;
|
|
53358
|
+
const content = text.substring(contentStart, sectionEnd).trim();
|
|
53359
|
+
if (content.length > 0) {
|
|
53360
|
+
sections.push({
|
|
53361
|
+
fullMatch: text.substring(open.start, sectionEnd),
|
|
53362
|
+
filename: open.filename,
|
|
53363
|
+
content,
|
|
53364
|
+
startIndex: open.start,
|
|
53365
|
+
endIndex: sectionEnd
|
|
53366
|
+
});
|
|
53367
|
+
}
|
|
53368
|
+
}
|
|
53369
|
+
return sections;
|
|
53370
|
+
}
|
|
53371
|
+
function replaceFileSections(text, sections, replacement = (idx) => `_(See file: ${sections[idx].filename} above)_`) {
|
|
53372
|
+
if (sections.length === 0) return text;
|
|
53373
|
+
const sorted = [...sections].sort((a, b) => b.startIndex - a.startIndex);
|
|
53374
|
+
let result = text;
|
|
53375
|
+
sorted.forEach((section, sortedIndex) => {
|
|
53376
|
+
const originalIndex = sections.length - 1 - sortedIndex;
|
|
53377
|
+
const rep = typeof replacement === "function" ? replacement(originalIndex) : replacement;
|
|
53378
|
+
result = result.slice(0, section.startIndex) + rep + result.slice(section.endIndex);
|
|
53379
|
+
});
|
|
53380
|
+
return result;
|
|
53381
|
+
}
|
|
51531
53382
|
function formatSlackText(text) {
|
|
51532
53383
|
return markdownToSlack(text);
|
|
51533
53384
|
}
|
|
51534
|
-
var
|
|
53385
|
+
var import_child_process6, fs23, path27, os2;
|
|
51535
53386
|
var init_markdown = __esm({
|
|
51536
53387
|
"src/slack/markdown.ts"() {
|
|
51537
53388
|
"use strict";
|
|
51538
|
-
|
|
51539
|
-
|
|
51540
|
-
|
|
51541
|
-
|
|
53389
|
+
import_child_process6 = require("child_process");
|
|
53390
|
+
fs23 = __toESM(require("fs"));
|
|
53391
|
+
path27 = __toESM(require("path"));
|
|
53392
|
+
os2 = __toESM(require("os"));
|
|
51542
53393
|
}
|
|
51543
53394
|
});
|
|
51544
53395
|
|
|
@@ -51956,6 +53807,9 @@ ${message}`;
|
|
|
51956
53807
|
text = String(out);
|
|
51957
53808
|
}
|
|
51958
53809
|
}
|
|
53810
|
+
if (out && typeof out._rawOutput === "string" && out._rawOutput.trim().length > 0) {
|
|
53811
|
+
text = (text || "") + "\n\n" + out._rawOutput.trim();
|
|
53812
|
+
}
|
|
51959
53813
|
if (!text) {
|
|
51960
53814
|
ctx.logger.info(
|
|
51961
53815
|
`[slack-frontend] skip posting AI reply for ${checkId}: no renderable text in check output`
|
|
@@ -52014,6 +53868,39 @@ ${message}`;
|
|
|
52014
53868
|
);
|
|
52015
53869
|
}
|
|
52016
53870
|
}
|
|
53871
|
+
processedText = processedText.replace(/\\n/g, "\n");
|
|
53872
|
+
const fileSections = extractFileSections(processedText);
|
|
53873
|
+
if (fileSections.length > 0) {
|
|
53874
|
+
const uploadedFileIndices = [];
|
|
53875
|
+
for (let i = 0; i < fileSections.length; i++) {
|
|
53876
|
+
const section = fileSections[i];
|
|
53877
|
+
try {
|
|
53878
|
+
const buffer = Buffer.from(section.content, "utf-8");
|
|
53879
|
+
const uploadResult = await slack.files.uploadV2({
|
|
53880
|
+
content: buffer,
|
|
53881
|
+
filename: section.filename,
|
|
53882
|
+
channel,
|
|
53883
|
+
thread_ts: threadTs,
|
|
53884
|
+
title: section.filename
|
|
53885
|
+
});
|
|
53886
|
+
if (uploadResult.ok) {
|
|
53887
|
+
uploadedFileIndices.push(i);
|
|
53888
|
+
ctx.logger.info(`[slack-frontend] uploaded file ${section.filename} to ${channel}`);
|
|
53889
|
+
} else {
|
|
53890
|
+
ctx.logger.warn(`[slack-frontend] upload failed for file ${section.filename}`);
|
|
53891
|
+
}
|
|
53892
|
+
} catch (e) {
|
|
53893
|
+
ctx.logger.warn(
|
|
53894
|
+
`[slack-frontend] failed to upload file ${section.filename}: ${e instanceof Error ? e.message : String(e)}`
|
|
53895
|
+
);
|
|
53896
|
+
}
|
|
53897
|
+
}
|
|
53898
|
+
processedText = replaceFileSections(
|
|
53899
|
+
processedText,
|
|
53900
|
+
fileSections,
|
|
53901
|
+
(idx) => uploadedFileIndices.includes(idx) ? `_(See file: ${fileSections[idx].filename} above)_` : `_(File upload failed: ${fileSections[idx].filename})_`
|
|
53902
|
+
);
|
|
53903
|
+
}
|
|
52017
53904
|
let decoratedText = processedText;
|
|
52018
53905
|
const telemetryEnabled = telemetryCfg === true || telemetryCfg && typeof telemetryCfg === "object" && telemetryCfg.enabled === true;
|
|
52019
53906
|
if (telemetryEnabled) {
|
|
@@ -52478,15 +54365,15 @@ function serializeRunState(state) {
|
|
|
52478
54365
|
])
|
|
52479
54366
|
};
|
|
52480
54367
|
}
|
|
52481
|
-
var
|
|
54368
|
+
var path28, fs24, StateMachineExecutionEngine;
|
|
52482
54369
|
var init_state_machine_execution_engine = __esm({
|
|
52483
54370
|
"src/state-machine-execution-engine.ts"() {
|
|
52484
54371
|
"use strict";
|
|
52485
54372
|
init_runner();
|
|
52486
54373
|
init_logger();
|
|
52487
54374
|
init_sandbox_manager();
|
|
52488
|
-
|
|
52489
|
-
|
|
54375
|
+
path28 = __toESM(require("path"));
|
|
54376
|
+
fs24 = __toESM(require("fs"));
|
|
52490
54377
|
StateMachineExecutionEngine = class _StateMachineExecutionEngine {
|
|
52491
54378
|
workingDirectory;
|
|
52492
54379
|
executionContext;
|
|
@@ -52709,8 +54596,8 @@ var init_state_machine_execution_engine = __esm({
|
|
|
52709
54596
|
logger.debug(
|
|
52710
54597
|
`[PolicyEngine] Loading enterprise policy engine (engine=${configWithTagFilter.policy.engine})`
|
|
52711
54598
|
);
|
|
52712
|
-
const { loadEnterprisePolicyEngine } = await
|
|
52713
|
-
context2.policyEngine = await
|
|
54599
|
+
const { loadEnterprisePolicyEngine: loadEnterprisePolicyEngine2 } = await Promise.resolve().then(() => (init_loader(), loader_exports));
|
|
54600
|
+
context2.policyEngine = await loadEnterprisePolicyEngine2(configWithTagFilter.policy);
|
|
52714
54601
|
logger.debug(
|
|
52715
54602
|
`[PolicyEngine] Initialized: ${context2.policyEngine?.constructor?.name || "unknown"}`
|
|
52716
54603
|
);
|
|
@@ -52862,9 +54749,9 @@ var init_state_machine_execution_engine = __esm({
|
|
|
52862
54749
|
}
|
|
52863
54750
|
const checkId = String(ev?.checkId || "unknown");
|
|
52864
54751
|
const threadKey = ev?.threadKey || (channel && threadTs ? `${channel}:${threadTs}` : "session");
|
|
52865
|
-
const baseDir = process.env.VISOR_SNAPSHOT_DIR ||
|
|
52866
|
-
|
|
52867
|
-
const filePath =
|
|
54752
|
+
const baseDir = process.env.VISOR_SNAPSHOT_DIR || path28.resolve(process.cwd(), ".visor", "snapshots");
|
|
54753
|
+
fs24.mkdirSync(baseDir, { recursive: true });
|
|
54754
|
+
const filePath = path28.join(baseDir, `${threadKey}-${checkId}.json`);
|
|
52868
54755
|
await this.saveSnapshotToFile(filePath);
|
|
52869
54756
|
logger.info(`[Snapshot] Saved run snapshot: ${filePath}`);
|
|
52870
54757
|
try {
|
|
@@ -53005,7 +54892,7 @@ var init_state_machine_execution_engine = __esm({
|
|
|
53005
54892
|
* Does not include secrets. Intended for debugging and future resume support.
|
|
53006
54893
|
*/
|
|
53007
54894
|
async saveSnapshotToFile(filePath) {
|
|
53008
|
-
const
|
|
54895
|
+
const fs25 = await import("fs/promises");
|
|
53009
54896
|
const ctx = this._lastContext;
|
|
53010
54897
|
const runner = this._lastRunner;
|
|
53011
54898
|
if (!ctx || !runner) {
|
|
@@ -53025,14 +54912,14 @@ var init_state_machine_execution_engine = __esm({
|
|
|
53025
54912
|
journal: entries,
|
|
53026
54913
|
requestedChecks: ctx.requestedChecks || []
|
|
53027
54914
|
};
|
|
53028
|
-
await
|
|
54915
|
+
await fs25.writeFile(filePath, JSON.stringify(payload, null, 2), "utf8");
|
|
53029
54916
|
}
|
|
53030
54917
|
/**
|
|
53031
54918
|
* Load a snapshot JSON from file and return it. Resume support can build on this.
|
|
53032
54919
|
*/
|
|
53033
54920
|
async loadSnapshotFromFile(filePath) {
|
|
53034
|
-
const
|
|
53035
|
-
const raw = await
|
|
54921
|
+
const fs25 = await import("fs/promises");
|
|
54922
|
+
const raw = await fs25.readFile(filePath, "utf8");
|
|
53036
54923
|
return JSON.parse(raw);
|
|
53037
54924
|
}
|
|
53038
54925
|
/**
|