@probelabs/visor 0.1.144 → 0.1.145-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/dist/config.d.ts.map +1 -1
- package/dist/docs/architecture.md +28 -0
- package/dist/docs/configuration.md +2 -0
- package/dist/docs/sandbox-engines.md +357 -0
- package/dist/docs/security.md +40 -0
- package/dist/generated/config-schema.d.ts +5 -0
- package/dist/generated/config-schema.d.ts.map +1 -1
- package/dist/generated/config-schema.json +9 -0
- package/dist/index.js +2388 -183
- package/dist/providers/mcp-check-provider.d.ts.map +1 -1
- package/dist/sandbox/bubblewrap-sandbox.d.ts +30 -0
- package/dist/sandbox/bubblewrap-sandbox.d.ts.map +1 -0
- package/dist/sandbox/index.d.ts +3 -1
- package/dist/sandbox/index.d.ts.map +1 -1
- package/dist/sandbox/sandbox-manager.d.ts +3 -2
- package/dist/sandbox/sandbox-manager.d.ts.map +1 -1
- package/dist/sandbox/seatbelt-sandbox.d.ts +36 -0
- package/dist/sandbox/seatbelt-sandbox.d.ts.map +1 -0
- package/dist/sandbox/types.d.ts +3 -1
- package/dist/sandbox/types.d.ts.map +1 -1
- package/dist/sdk/{check-provider-registry-VTNNTMWC.mjs → check-provider-registry-HFPKHYTG.mjs} +3 -3
- package/dist/sdk/{check-provider-registry-WBEOZCGW.mjs → check-provider-registry-TH25S2OB.mjs} +7 -7
- package/dist/sdk/{chunk-D3UC5KUJ.mjs → chunk-3BOOHJI5.mjs} +472 -105
- package/dist/sdk/chunk-3BOOHJI5.mjs.map +1 -0
- package/dist/sdk/{chunk-PXWWPPNF.mjs → chunk-GZMQPC6D.mjs} +459 -92
- package/dist/sdk/chunk-GZMQPC6D.mjs.map +1 -0
- package/dist/sdk/{chunk-CLQTOZKH.mjs → chunk-I42ZCVA5.mjs} +3 -3
- package/dist/sdk/{chunk-PQWAAGUP.mjs → chunk-L3XPYQ6I.mjs} +2 -2
- package/dist/sdk/{chunk-FG6THKK7.mjs → chunk-OM3WYVFI.mjs} +3 -3
- package/dist/sdk/chunk-OM3WYVFI.mjs.map +1 -0
- package/dist/sdk/{chunk-AKCHIYWU.mjs → chunk-YOKAA4IU.mjs} +96 -63
- package/dist/sdk/chunk-YOKAA4IU.mjs.map +1 -0
- package/dist/sdk/{config-KOKJ3PYE.mjs → config-AAB2FL22.mjs} +2 -2
- package/dist/sdk/{failure-condition-evaluator-LWH3NQ2S.mjs → failure-condition-evaluator-O464EJMD.mjs} +3 -3
- package/dist/sdk/{github-frontend-UUASYGNV.mjs → github-frontend-MSX6Q2WL.mjs} +3 -3
- package/dist/sdk/{host-LAF3NFPZ.mjs → host-GA76UESS.mjs} +2 -2
- 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-LEUV6A4K.mjs → routing-RIHVCEIU.mjs} +4 -4
- package/dist/sdk/{schedule-tool-handler-W7IB4MK3.mjs → schedule-tool-handler-BTLEDYAI.mjs} +3 -3
- package/dist/sdk/{schedule-tool-handler-EYDCUGOB.mjs → schedule-tool-handler-NYL2ONJB.mjs} +7 -7
- package/dist/sdk/sdk.d.mts +3 -1
- package/dist/sdk/sdk.d.ts +3 -1
- package/dist/sdk/sdk.js +2006 -348
- package/dist/sdk/sdk.js.map +1 -1
- package/dist/sdk/sdk.mjs +6 -6
- package/dist/sdk/{trace-helpers-NNBQNFWZ.mjs → trace-helpers-QQSTZGDT.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-WW5U6R2P.mjs → workflow-check-provider-3IIKJFM4.mjs} +3 -3
- package/dist/sdk/{workflow-check-provider-FLBIJQ4Z.mjs → workflow-check-provider-LVUUL2PZ.mjs} +7 -7
- package/dist/slack/socket-runner.d.ts.map +1 -1
- package/dist/utils/workspace-manager.d.ts +9 -0
- package/dist/utils/workspace-manager.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/output/traces/run-2026-02-25T17-29-59-894Z.ndjson +0 -138
- package/dist/output/traces/run-2026-02-25T17-30-47-149Z.ndjson +0 -1442
- package/dist/sdk/check-provider-registry-GKLK3I2X.mjs +0 -30
- package/dist/sdk/chunk-AKCHIYWU.mjs.map +0 -1
- package/dist/sdk/chunk-D3UC5KUJ.mjs.map +0 -1
- package/dist/sdk/chunk-FG6THKK7.mjs.map +0 -1
- package/dist/sdk/chunk-N7LW3Q5B.mjs +0 -40284
- package/dist/sdk/chunk-N7LW3Q5B.mjs.map +0 -1
- package/dist/sdk/chunk-PXWWPPNF.mjs.map +0 -1
- package/dist/sdk/schedule-tool-handler-7RGTKO24.mjs +0 -40
- package/dist/sdk/workflow-check-provider-2G2CEXFR.mjs +0 -30
- package/dist/sdk/workflow-check-provider-2G2CEXFR.mjs.map +0 -1
- package/dist/sdk/workflow-check-provider-FLBIJQ4Z.mjs.map +0 -1
- package/dist/sdk/workflow-check-provider-WW5U6R2P.mjs.map +0 -1
- package/dist/traces/run-2026-02-25T17-29-59-894Z.ndjson +0 -138
- package/dist/traces/run-2026-02-25T17-30-47-149Z.ndjson +0 -1442
- /package/dist/sdk/{check-provider-registry-GKLK3I2X.mjs.map → check-provider-registry-HFPKHYTG.mjs.map} +0 -0
- /package/dist/sdk/{check-provider-registry-VTNNTMWC.mjs.map → check-provider-registry-TH25S2OB.mjs.map} +0 -0
- /package/dist/sdk/{chunk-CLQTOZKH.mjs.map → chunk-I42ZCVA5.mjs.map} +0 -0
- /package/dist/sdk/{chunk-PQWAAGUP.mjs.map → chunk-L3XPYQ6I.mjs.map} +0 -0
- /package/dist/sdk/{check-provider-registry-WBEOZCGW.mjs.map → config-AAB2FL22.mjs.map} +0 -0
- /package/dist/sdk/{config-KOKJ3PYE.mjs.map → failure-condition-evaluator-O464EJMD.mjs.map} +0 -0
- /package/dist/sdk/{github-frontend-UUASYGNV.mjs.map → github-frontend-MSX6Q2WL.mjs.map} +0 -0
- /package/dist/sdk/{host-LAF3NFPZ.mjs.map → host-GA76UESS.mjs.map} +0 -0
- /package/dist/sdk/{failure-condition-evaluator-LWH3NQ2S.mjs.map → routing-RIHVCEIU.mjs.map} +0 -0
- /package/dist/sdk/{routing-LEUV6A4K.mjs.map → schedule-tool-handler-BTLEDYAI.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-handler-7RGTKO24.mjs.map → schedule-tool-handler-NYL2ONJB.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-handler-EYDCUGOB.mjs.map → trace-helpers-QQSTZGDT.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-handler-W7IB4MK3.mjs.map → workflow-check-provider-3IIKJFM4.mjs.map} +0 -0
- /package/dist/sdk/{trace-helpers-NNBQNFWZ.mjs.map → workflow-check-provider-LVUUL2PZ.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"
|
|
@@ -760,7 +760,7 @@ var require_package = __commonJS({
|
|
|
760
760
|
"@opentelemetry/sdk-node": "^0.203.0",
|
|
761
761
|
"@opentelemetry/sdk-trace-base": "^1.30.1",
|
|
762
762
|
"@opentelemetry/semantic-conventions": "^1.30.1",
|
|
763
|
-
"@probelabs/probe": "^0.6.0-
|
|
763
|
+
"@probelabs/probe": "^0.6.0-rc260",
|
|
764
764
|
"@types/commander": "^2.12.0",
|
|
765
765
|
"@types/uuid": "^10.0.0",
|
|
766
766
|
acorn: "^8.16.0",
|
|
@@ -864,11 +864,11 @@ function getTracer() {
|
|
|
864
864
|
}
|
|
865
865
|
async function withActiveSpan(name, attrs, fn) {
|
|
866
866
|
const tracer = getTracer();
|
|
867
|
-
return await new Promise((
|
|
867
|
+
return await new Promise((resolve17, reject) => {
|
|
868
868
|
const callback = async (span) => {
|
|
869
869
|
try {
|
|
870
870
|
const res = await fn(span);
|
|
871
|
-
|
|
871
|
+
resolve17(res);
|
|
872
872
|
} catch (err) {
|
|
873
873
|
try {
|
|
874
874
|
if (err instanceof Error) span.recordException(err);
|
|
@@ -945,19 +945,19 @@ function __getOrCreateNdjsonPath() {
|
|
|
945
945
|
try {
|
|
946
946
|
if (process.env.VISOR_TELEMETRY_SINK && process.env.VISOR_TELEMETRY_SINK !== "file")
|
|
947
947
|
return null;
|
|
948
|
-
const
|
|
949
|
-
const
|
|
948
|
+
const path30 = require("path");
|
|
949
|
+
const fs26 = require("fs");
|
|
950
950
|
if (process.env.VISOR_FALLBACK_TRACE_FILE) {
|
|
951
951
|
__ndjsonPath = process.env.VISOR_FALLBACK_TRACE_FILE;
|
|
952
|
-
const dir =
|
|
953
|
-
if (!
|
|
952
|
+
const dir = path30.dirname(__ndjsonPath);
|
|
953
|
+
if (!fs26.existsSync(dir)) fs26.mkdirSync(dir, { recursive: true });
|
|
954
954
|
return __ndjsonPath;
|
|
955
955
|
}
|
|
956
|
-
const outDir = process.env.VISOR_TRACE_DIR ||
|
|
957
|
-
if (!
|
|
956
|
+
const outDir = process.env.VISOR_TRACE_DIR || path30.join(process.cwd(), "output", "traces");
|
|
957
|
+
if (!fs26.existsSync(outDir)) fs26.mkdirSync(outDir, { recursive: true });
|
|
958
958
|
if (!__ndjsonPath) {
|
|
959
959
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
960
|
-
__ndjsonPath =
|
|
960
|
+
__ndjsonPath = path30.join(outDir, `${ts}.ndjson`);
|
|
961
961
|
}
|
|
962
962
|
return __ndjsonPath;
|
|
963
963
|
} catch {
|
|
@@ -966,11 +966,11 @@ function __getOrCreateNdjsonPath() {
|
|
|
966
966
|
}
|
|
967
967
|
function _appendRunMarker() {
|
|
968
968
|
try {
|
|
969
|
-
const
|
|
969
|
+
const fs26 = require("fs");
|
|
970
970
|
const p = __getOrCreateNdjsonPath();
|
|
971
971
|
if (!p) return;
|
|
972
972
|
const line = { name: "visor.run", attributes: { started: true } };
|
|
973
|
-
|
|
973
|
+
fs26.appendFileSync(p, JSON.stringify(line) + "\n", "utf8");
|
|
974
974
|
} catch {
|
|
975
975
|
}
|
|
976
976
|
}
|
|
@@ -3193,7 +3193,7 @@ var init_failure_condition_evaluator = __esm({
|
|
|
3193
3193
|
*/
|
|
3194
3194
|
evaluateExpression(condition, context2) {
|
|
3195
3195
|
try {
|
|
3196
|
-
const
|
|
3196
|
+
const normalize8 = (expr) => {
|
|
3197
3197
|
const trimmed = expr.trim();
|
|
3198
3198
|
if (!/[\n;]/.test(trimmed)) return trimmed;
|
|
3199
3199
|
const parts = trimmed.split(/[\n;]+/).map((s) => s.trim()).filter((s) => s.length > 0 && !s.startsWith("//"));
|
|
@@ -3351,7 +3351,7 @@ var init_failure_condition_evaluator = __esm({
|
|
|
3351
3351
|
try {
|
|
3352
3352
|
exec2 = this.sandbox.compile(`return (${raw});`);
|
|
3353
3353
|
} catch {
|
|
3354
|
-
const normalizedExpr =
|
|
3354
|
+
const normalizedExpr = normalize8(condition);
|
|
3355
3355
|
exec2 = this.sandbox.compile(`return (${normalizedExpr});`);
|
|
3356
3356
|
}
|
|
3357
3357
|
const result = exec2(scope).run();
|
|
@@ -3734,9 +3734,9 @@ function configureLiquidWithExtensions(liquid) {
|
|
|
3734
3734
|
});
|
|
3735
3735
|
liquid.registerFilter("get", (obj, pathExpr) => {
|
|
3736
3736
|
if (obj == null) return void 0;
|
|
3737
|
-
const
|
|
3738
|
-
if (!
|
|
3739
|
-
const parts =
|
|
3737
|
+
const path30 = typeof pathExpr === "string" ? pathExpr : String(pathExpr || "");
|
|
3738
|
+
if (!path30) return obj;
|
|
3739
|
+
const parts = path30.split(".");
|
|
3740
3740
|
let cur = obj;
|
|
3741
3741
|
for (const p of parts) {
|
|
3742
3742
|
if (cur == null) return void 0;
|
|
@@ -3855,9 +3855,9 @@ function configureLiquidWithExtensions(liquid) {
|
|
|
3855
3855
|
}
|
|
3856
3856
|
}
|
|
3857
3857
|
const defaultRole = typeof rolesCfg.default === "string" && rolesCfg.default.trim() ? rolesCfg.default.trim() : void 0;
|
|
3858
|
-
const getNested = (obj,
|
|
3859
|
-
if (!obj || !
|
|
3860
|
-
const parts =
|
|
3858
|
+
const getNested = (obj, path30) => {
|
|
3859
|
+
if (!obj || !path30) return void 0;
|
|
3860
|
+
const parts = path30.split(".");
|
|
3861
3861
|
let cur = obj;
|
|
3862
3862
|
for (const p of parts) {
|
|
3863
3863
|
if (cur == null) return void 0;
|
|
@@ -6409,8 +6409,8 @@ var init_dependency_gating = __esm({
|
|
|
6409
6409
|
async function renderTemplateContent(checkId, checkConfig, reviewSummary) {
|
|
6410
6410
|
try {
|
|
6411
6411
|
const { createExtendedLiquid: createExtendedLiquid2 } = await Promise.resolve().then(() => (init_liquid_extensions(), liquid_extensions_exports));
|
|
6412
|
-
const
|
|
6413
|
-
const
|
|
6412
|
+
const fs26 = await import("fs/promises");
|
|
6413
|
+
const path30 = await import("path");
|
|
6414
6414
|
const schemaRaw = checkConfig.schema || "plain";
|
|
6415
6415
|
const schema = typeof schemaRaw === "string" ? schemaRaw : "code-review";
|
|
6416
6416
|
let templateContent;
|
|
@@ -6418,24 +6418,24 @@ async function renderTemplateContent(checkId, checkConfig, reviewSummary) {
|
|
|
6418
6418
|
templateContent = String(checkConfig.template.content);
|
|
6419
6419
|
} else if (checkConfig.template && checkConfig.template.file) {
|
|
6420
6420
|
const file = String(checkConfig.template.file);
|
|
6421
|
-
const resolved =
|
|
6422
|
-
templateContent = await
|
|
6421
|
+
const resolved = path30.resolve(process.cwd(), file);
|
|
6422
|
+
templateContent = await fs26.readFile(resolved, "utf-8");
|
|
6423
6423
|
} else if (schema && schema !== "plain") {
|
|
6424
6424
|
const sanitized = String(schema).replace(/[^a-zA-Z0-9-]/g, "");
|
|
6425
6425
|
if (sanitized) {
|
|
6426
6426
|
const candidatePaths = [
|
|
6427
|
-
|
|
6427
|
+
path30.join(__dirname, "output", sanitized, "template.liquid"),
|
|
6428
6428
|
// bundled: dist/output/
|
|
6429
|
-
|
|
6429
|
+
path30.join(__dirname, "..", "..", "output", sanitized, "template.liquid"),
|
|
6430
6430
|
// source: output/
|
|
6431
|
-
|
|
6431
|
+
path30.join(process.cwd(), "output", sanitized, "template.liquid"),
|
|
6432
6432
|
// fallback: cwd/output/
|
|
6433
|
-
|
|
6433
|
+
path30.join(process.cwd(), "dist", "output", sanitized, "template.liquid")
|
|
6434
6434
|
// fallback: cwd/dist/output/
|
|
6435
6435
|
];
|
|
6436
6436
|
for (const p of candidatePaths) {
|
|
6437
6437
|
try {
|
|
6438
|
-
templateContent = await
|
|
6438
|
+
templateContent = await fs26.readFile(p, "utf-8");
|
|
6439
6439
|
if (templateContent) break;
|
|
6440
6440
|
} catch {
|
|
6441
6441
|
}
|
|
@@ -6840,7 +6840,7 @@ async function processDiffWithOutline(diffContent) {
|
|
|
6840
6840
|
}
|
|
6841
6841
|
try {
|
|
6842
6842
|
const originalProbePath = process.env.PROBE_PATH;
|
|
6843
|
-
const
|
|
6843
|
+
const fs26 = require("fs");
|
|
6844
6844
|
const possiblePaths = [
|
|
6845
6845
|
// Relative to current working directory (most common in production)
|
|
6846
6846
|
path6.join(process.cwd(), "node_modules/@probelabs/probe/bin/probe-binary"),
|
|
@@ -6851,7 +6851,7 @@ async function processDiffWithOutline(diffContent) {
|
|
|
6851
6851
|
];
|
|
6852
6852
|
let probeBinaryPath;
|
|
6853
6853
|
for (const candidatePath of possiblePaths) {
|
|
6854
|
-
if (
|
|
6854
|
+
if (fs26.existsSync(candidatePath)) {
|
|
6855
6855
|
probeBinaryPath = candidatePath;
|
|
6856
6856
|
break;
|
|
6857
6857
|
}
|
|
@@ -6972,7 +6972,7 @@ async function renderMermaidToPng(mermaidCode) {
|
|
|
6972
6972
|
if (chromiumPath) {
|
|
6973
6973
|
env.PUPPETEER_EXECUTABLE_PATH = chromiumPath;
|
|
6974
6974
|
}
|
|
6975
|
-
const result = await new Promise((
|
|
6975
|
+
const result = await new Promise((resolve17) => {
|
|
6976
6976
|
const proc = (0, import_child_process.spawn)(
|
|
6977
6977
|
"npx",
|
|
6978
6978
|
[
|
|
@@ -7002,13 +7002,13 @@ async function renderMermaidToPng(mermaidCode) {
|
|
|
7002
7002
|
});
|
|
7003
7003
|
proc.on("close", (code) => {
|
|
7004
7004
|
if (code === 0) {
|
|
7005
|
-
|
|
7005
|
+
resolve17({ success: true });
|
|
7006
7006
|
} else {
|
|
7007
|
-
|
|
7007
|
+
resolve17({ success: false, error: stderr || `Exit code ${code}` });
|
|
7008
7008
|
}
|
|
7009
7009
|
});
|
|
7010
7010
|
proc.on("error", (err) => {
|
|
7011
|
-
|
|
7011
|
+
resolve17({ success: false, error: err.message });
|
|
7012
7012
|
});
|
|
7013
7013
|
});
|
|
7014
7014
|
if (!result.success) {
|
|
@@ -8153,8 +8153,8 @@ ${schemaString}`);
|
|
|
8153
8153
|
}
|
|
8154
8154
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
8155
8155
|
try {
|
|
8156
|
-
const
|
|
8157
|
-
const
|
|
8156
|
+
const fs26 = require("fs");
|
|
8157
|
+
const path30 = require("path");
|
|
8158
8158
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8159
8159
|
const provider = this.config.provider || "auto";
|
|
8160
8160
|
const model = this.config.model || "default";
|
|
@@ -8268,20 +8268,20 @@ ${"=".repeat(60)}
|
|
|
8268
8268
|
`;
|
|
8269
8269
|
readableVersion += `${"=".repeat(60)}
|
|
8270
8270
|
`;
|
|
8271
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
8272
|
-
if (!
|
|
8273
|
-
|
|
8271
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path30.join(process.cwd(), "debug-artifacts");
|
|
8272
|
+
if (!fs26.existsSync(debugArtifactsDir)) {
|
|
8273
|
+
fs26.mkdirSync(debugArtifactsDir, { recursive: true });
|
|
8274
8274
|
}
|
|
8275
|
-
const debugFile =
|
|
8275
|
+
const debugFile = path30.join(
|
|
8276
8276
|
debugArtifactsDir,
|
|
8277
8277
|
`prompt-${_checkName || "unknown"}-${timestamp}.json`
|
|
8278
8278
|
);
|
|
8279
|
-
|
|
8280
|
-
const readableFile =
|
|
8279
|
+
fs26.writeFileSync(debugFile, debugJson, "utf-8");
|
|
8280
|
+
const readableFile = path30.join(
|
|
8281
8281
|
debugArtifactsDir,
|
|
8282
8282
|
`prompt-${_checkName || "unknown"}-${timestamp}.txt`
|
|
8283
8283
|
);
|
|
8284
|
-
|
|
8284
|
+
fs26.writeFileSync(readableFile, readableVersion, "utf-8");
|
|
8285
8285
|
log(`
|
|
8286
8286
|
\u{1F4BE} Full debug info saved to:`);
|
|
8287
8287
|
log(` JSON: ${debugFile}`);
|
|
@@ -8314,8 +8314,8 @@ ${"=".repeat(60)}
|
|
|
8314
8314
|
log(`\u{1F4E4} Response length: ${response.length} characters`);
|
|
8315
8315
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
8316
8316
|
try {
|
|
8317
|
-
const
|
|
8318
|
-
const
|
|
8317
|
+
const fs26 = require("fs");
|
|
8318
|
+
const path30 = require("path");
|
|
8319
8319
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8320
8320
|
const agentAny2 = agent;
|
|
8321
8321
|
let fullHistory = [];
|
|
@@ -8326,8 +8326,8 @@ ${"=".repeat(60)}
|
|
|
8326
8326
|
} else if (agentAny2._messages) {
|
|
8327
8327
|
fullHistory = agentAny2._messages;
|
|
8328
8328
|
}
|
|
8329
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
8330
|
-
const sessionBase =
|
|
8329
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path30.join(process.cwd(), "debug-artifacts");
|
|
8330
|
+
const sessionBase = path30.join(
|
|
8331
8331
|
debugArtifactsDir,
|
|
8332
8332
|
`session-${_checkName || "unknown"}-${timestamp}`
|
|
8333
8333
|
);
|
|
@@ -8339,7 +8339,7 @@ ${"=".repeat(60)}
|
|
|
8339
8339
|
schema: effectiveSchema,
|
|
8340
8340
|
totalMessages: fullHistory.length
|
|
8341
8341
|
};
|
|
8342
|
-
|
|
8342
|
+
fs26.writeFileSync(sessionBase + ".json", JSON.stringify(sessionData, null, 2), "utf-8");
|
|
8343
8343
|
let readable = `=============================================================
|
|
8344
8344
|
`;
|
|
8345
8345
|
readable += `COMPLETE AI SESSION HISTORY (AFTER RESPONSE)
|
|
@@ -8366,7 +8366,7 @@ ${"=".repeat(60)}
|
|
|
8366
8366
|
`;
|
|
8367
8367
|
readable += content + "\n";
|
|
8368
8368
|
});
|
|
8369
|
-
|
|
8369
|
+
fs26.writeFileSync(sessionBase + ".summary.txt", readable, "utf-8");
|
|
8370
8370
|
log(`\u{1F4BE} Complete session history saved:`);
|
|
8371
8371
|
log(` - Contains ALL ${fullHistory.length} messages (prompts + responses)`);
|
|
8372
8372
|
} catch (error) {
|
|
@@ -8375,11 +8375,11 @@ ${"=".repeat(60)}
|
|
|
8375
8375
|
}
|
|
8376
8376
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
8377
8377
|
try {
|
|
8378
|
-
const
|
|
8379
|
-
const
|
|
8378
|
+
const fs26 = require("fs");
|
|
8379
|
+
const path30 = require("path");
|
|
8380
8380
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8381
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
8382
|
-
const responseFile =
|
|
8381
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path30.join(process.cwd(), "debug-artifacts");
|
|
8382
|
+
const responseFile = path30.join(
|
|
8383
8383
|
debugArtifactsDir,
|
|
8384
8384
|
`response-${_checkName || "unknown"}-${timestamp}.txt`
|
|
8385
8385
|
);
|
|
@@ -8412,7 +8412,7 @@ ${"=".repeat(60)}
|
|
|
8412
8412
|
`;
|
|
8413
8413
|
responseContent += `${"=".repeat(60)}
|
|
8414
8414
|
`;
|
|
8415
|
-
|
|
8415
|
+
fs26.writeFileSync(responseFile, responseContent, "utf-8");
|
|
8416
8416
|
log(`\u{1F4BE} Response saved to: ${responseFile}`);
|
|
8417
8417
|
} catch (error) {
|
|
8418
8418
|
log(`\u26A0\uFE0F Could not save response file: ${error}`);
|
|
@@ -8428,9 +8428,9 @@ ${"=".repeat(60)}
|
|
|
8428
8428
|
await agentAny._telemetryConfig.shutdown();
|
|
8429
8429
|
log(`\u{1F4CA} OpenTelemetry trace saved to: ${agentAny._traceFilePath}`);
|
|
8430
8430
|
if (process.env.GITHUB_ACTIONS) {
|
|
8431
|
-
const
|
|
8432
|
-
if (
|
|
8433
|
-
const stats =
|
|
8431
|
+
const fs26 = require("fs");
|
|
8432
|
+
if (fs26.existsSync(agentAny._traceFilePath)) {
|
|
8433
|
+
const stats = fs26.statSync(agentAny._traceFilePath);
|
|
8434
8434
|
console.log(
|
|
8435
8435
|
`::notice title=AI Trace Saved::${agentAny._traceFilePath} (${stats.size} bytes)`
|
|
8436
8436
|
);
|
|
@@ -8631,9 +8631,9 @@ ${schemaString}`);
|
|
|
8631
8631
|
const model = this.config.model || "default";
|
|
8632
8632
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
8633
8633
|
try {
|
|
8634
|
-
const
|
|
8635
|
-
const
|
|
8636
|
-
const
|
|
8634
|
+
const fs26 = require("fs");
|
|
8635
|
+
const path30 = require("path");
|
|
8636
|
+
const os3 = require("os");
|
|
8637
8637
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8638
8638
|
const debugData = {
|
|
8639
8639
|
timestamp,
|
|
@@ -8705,19 +8705,19 @@ ${"=".repeat(60)}
|
|
|
8705
8705
|
`;
|
|
8706
8706
|
readableVersion += `${"=".repeat(60)}
|
|
8707
8707
|
`;
|
|
8708
|
-
const tempDir =
|
|
8709
|
-
const promptFile =
|
|
8710
|
-
|
|
8708
|
+
const tempDir = os3.tmpdir();
|
|
8709
|
+
const promptFile = path30.join(tempDir, `visor-prompt-${timestamp}.txt`);
|
|
8710
|
+
fs26.writeFileSync(promptFile, prompt, "utf-8");
|
|
8711
8711
|
log(`
|
|
8712
8712
|
\u{1F4BE} Prompt saved to: ${promptFile}`);
|
|
8713
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
8713
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path30.join(process.cwd(), "debug-artifacts");
|
|
8714
8714
|
try {
|
|
8715
|
-
const base =
|
|
8715
|
+
const base = path30.join(
|
|
8716
8716
|
debugArtifactsDir,
|
|
8717
8717
|
`prompt-${_checkName || "unknown"}-${timestamp}`
|
|
8718
8718
|
);
|
|
8719
|
-
|
|
8720
|
-
|
|
8719
|
+
fs26.writeFileSync(base + ".json", debugJson, "utf-8");
|
|
8720
|
+
fs26.writeFileSync(base + ".summary.txt", readableVersion, "utf-8");
|
|
8721
8721
|
log(`
|
|
8722
8722
|
\u{1F4BE} Full debug info saved to directory: ${debugArtifactsDir}`);
|
|
8723
8723
|
} catch {
|
|
@@ -8762,8 +8762,8 @@ $ ${cliCommand}
|
|
|
8762
8762
|
log(`\u{1F4E4} Response length: ${response.length} characters`);
|
|
8763
8763
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
8764
8764
|
try {
|
|
8765
|
-
const
|
|
8766
|
-
const
|
|
8765
|
+
const fs26 = require("fs");
|
|
8766
|
+
const path30 = require("path");
|
|
8767
8767
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8768
8768
|
const agentAny = agent;
|
|
8769
8769
|
let fullHistory = [];
|
|
@@ -8774,8 +8774,8 @@ $ ${cliCommand}
|
|
|
8774
8774
|
} else if (agentAny._messages) {
|
|
8775
8775
|
fullHistory = agentAny._messages;
|
|
8776
8776
|
}
|
|
8777
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
8778
|
-
const sessionBase =
|
|
8777
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path30.join(process.cwd(), "debug-artifacts");
|
|
8778
|
+
const sessionBase = path30.join(
|
|
8779
8779
|
debugArtifactsDir,
|
|
8780
8780
|
`session-${_checkName || "unknown"}-${timestamp}`
|
|
8781
8781
|
);
|
|
@@ -8787,7 +8787,7 @@ $ ${cliCommand}
|
|
|
8787
8787
|
schema: effectiveSchema,
|
|
8788
8788
|
totalMessages: fullHistory.length
|
|
8789
8789
|
};
|
|
8790
|
-
|
|
8790
|
+
fs26.writeFileSync(sessionBase + ".json", JSON.stringify(sessionData, null, 2), "utf-8");
|
|
8791
8791
|
let readable = `=============================================================
|
|
8792
8792
|
`;
|
|
8793
8793
|
readable += `COMPLETE AI SESSION HISTORY (AFTER RESPONSE)
|
|
@@ -8814,7 +8814,7 @@ ${"=".repeat(60)}
|
|
|
8814
8814
|
`;
|
|
8815
8815
|
readable += content + "\n";
|
|
8816
8816
|
});
|
|
8817
|
-
|
|
8817
|
+
fs26.writeFileSync(sessionBase + ".summary.txt", readable, "utf-8");
|
|
8818
8818
|
log(`\u{1F4BE} Complete session history saved:`);
|
|
8819
8819
|
log(` - Contains ALL ${fullHistory.length} messages (prompts + responses)`);
|
|
8820
8820
|
} catch (error) {
|
|
@@ -8823,11 +8823,11 @@ ${"=".repeat(60)}
|
|
|
8823
8823
|
}
|
|
8824
8824
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
8825
8825
|
try {
|
|
8826
|
-
const
|
|
8827
|
-
const
|
|
8826
|
+
const fs26 = require("fs");
|
|
8827
|
+
const path30 = require("path");
|
|
8828
8828
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8829
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
8830
|
-
const responseFile =
|
|
8829
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path30.join(process.cwd(), "debug-artifacts");
|
|
8830
|
+
const responseFile = path30.join(
|
|
8831
8831
|
debugArtifactsDir,
|
|
8832
8832
|
`response-${_checkName || "unknown"}-${timestamp}.txt`
|
|
8833
8833
|
);
|
|
@@ -8860,7 +8860,7 @@ ${"=".repeat(60)}
|
|
|
8860
8860
|
`;
|
|
8861
8861
|
responseContent += `${"=".repeat(60)}
|
|
8862
8862
|
`;
|
|
8863
|
-
|
|
8863
|
+
fs26.writeFileSync(responseFile, responseContent, "utf-8");
|
|
8864
8864
|
log(`\u{1F4BE} Response saved to: ${responseFile}`);
|
|
8865
8865
|
} catch (error) {
|
|
8866
8866
|
log(`\u26A0\uFE0F Could not save response file: ${error}`);
|
|
@@ -8878,9 +8878,9 @@ ${"=".repeat(60)}
|
|
|
8878
8878
|
await telemetry.shutdown();
|
|
8879
8879
|
log(`\u{1F4CA} OpenTelemetry trace saved to: ${traceFilePath}`);
|
|
8880
8880
|
if (process.env.GITHUB_ACTIONS) {
|
|
8881
|
-
const
|
|
8882
|
-
if (
|
|
8883
|
-
const stats =
|
|
8881
|
+
const fs26 = require("fs");
|
|
8882
|
+
if (fs26.existsSync(traceFilePath)) {
|
|
8883
|
+
const stats = fs26.statSync(traceFilePath);
|
|
8884
8884
|
console.log(
|
|
8885
8885
|
`::notice title=AI Trace Saved::OpenTelemetry trace file size: ${stats.size} bytes`
|
|
8886
8886
|
);
|
|
@@ -8918,8 +8918,8 @@ ${"=".repeat(60)}
|
|
|
8918
8918
|
* Load schema content from schema files or inline definitions
|
|
8919
8919
|
*/
|
|
8920
8920
|
async loadSchemaContent(schema) {
|
|
8921
|
-
const
|
|
8922
|
-
const
|
|
8921
|
+
const fs26 = require("fs").promises;
|
|
8922
|
+
const path30 = require("path");
|
|
8923
8923
|
if (typeof schema === "object" && schema !== null) {
|
|
8924
8924
|
log("\u{1F4CB} Using inline schema object from configuration");
|
|
8925
8925
|
return JSON.stringify(schema);
|
|
@@ -8932,14 +8932,14 @@ ${"=".repeat(60)}
|
|
|
8932
8932
|
}
|
|
8933
8933
|
} catch {
|
|
8934
8934
|
}
|
|
8935
|
-
if ((schema.startsWith("./") || schema.includes(".json")) && !
|
|
8935
|
+
if ((schema.startsWith("./") || schema.includes(".json")) && !path30.isAbsolute(schema)) {
|
|
8936
8936
|
if (schema.includes("..") || schema.includes("\0")) {
|
|
8937
8937
|
throw new Error("Invalid schema path: path traversal not allowed");
|
|
8938
8938
|
}
|
|
8939
8939
|
try {
|
|
8940
|
-
const schemaPath =
|
|
8940
|
+
const schemaPath = path30.resolve(process.cwd(), schema);
|
|
8941
8941
|
log(`\u{1F4CB} Loading custom schema from file: ${schemaPath}`);
|
|
8942
|
-
const schemaContent = await
|
|
8942
|
+
const schemaContent = await fs26.readFile(schemaPath, "utf-8");
|
|
8943
8943
|
return schemaContent.trim();
|
|
8944
8944
|
} catch (error) {
|
|
8945
8945
|
throw new Error(
|
|
@@ -8953,22 +8953,22 @@ ${"=".repeat(60)}
|
|
|
8953
8953
|
}
|
|
8954
8954
|
const candidatePaths = [
|
|
8955
8955
|
// GitHub Action bundle location
|
|
8956
|
-
|
|
8956
|
+
path30.join(__dirname, "output", sanitizedSchemaName, "schema.json"),
|
|
8957
8957
|
// Historical fallback when src/output was inadvertently bundled as output1/
|
|
8958
|
-
|
|
8958
|
+
path30.join(__dirname, "output1", sanitizedSchemaName, "schema.json"),
|
|
8959
8959
|
// Local dev (repo root)
|
|
8960
|
-
|
|
8960
|
+
path30.join(process.cwd(), "output", sanitizedSchemaName, "schema.json")
|
|
8961
8961
|
];
|
|
8962
8962
|
for (const schemaPath of candidatePaths) {
|
|
8963
8963
|
try {
|
|
8964
|
-
const schemaContent = await
|
|
8964
|
+
const schemaContent = await fs26.readFile(schemaPath, "utf-8");
|
|
8965
8965
|
return schemaContent.trim();
|
|
8966
8966
|
} catch {
|
|
8967
8967
|
}
|
|
8968
8968
|
}
|
|
8969
|
-
const distPath =
|
|
8970
|
-
const distAltPath =
|
|
8971
|
-
const cwdPath =
|
|
8969
|
+
const distPath = path30.join(__dirname, "output", sanitizedSchemaName, "schema.json");
|
|
8970
|
+
const distAltPath = path30.join(__dirname, "output1", sanitizedSchemaName, "schema.json");
|
|
8971
|
+
const cwdPath = path30.join(process.cwd(), "output", sanitizedSchemaName, "schema.json");
|
|
8972
8972
|
throw new Error(
|
|
8973
8973
|
`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.`
|
|
8974
8974
|
);
|
|
@@ -9213,7 +9213,7 @@ ${"=".repeat(60)}
|
|
|
9213
9213
|
* Generate mock response for testing
|
|
9214
9214
|
*/
|
|
9215
9215
|
async generateMockResponse(_prompt, _checkName, _schema) {
|
|
9216
|
-
await new Promise((
|
|
9216
|
+
await new Promise((resolve17) => setTimeout(resolve17, 500));
|
|
9217
9217
|
const name = (_checkName || "").toLowerCase();
|
|
9218
9218
|
if (name.includes("extract-facts")) {
|
|
9219
9219
|
const arr = Array.from({ length: 6 }, (_, i) => ({
|
|
@@ -9574,7 +9574,7 @@ var init_command_executor = __esm({
|
|
|
9574
9574
|
* Execute command with stdin input
|
|
9575
9575
|
*/
|
|
9576
9576
|
executeWithStdin(command, options) {
|
|
9577
|
-
return new Promise((
|
|
9577
|
+
return new Promise((resolve17, reject) => {
|
|
9578
9578
|
const childProcess = (0, import_child_process2.exec)(
|
|
9579
9579
|
command,
|
|
9580
9580
|
{
|
|
@@ -9586,7 +9586,7 @@ var init_command_executor = __esm({
|
|
|
9586
9586
|
if (error && error.killed && (error.code === "ETIMEDOUT" || error.signal === "SIGTERM")) {
|
|
9587
9587
|
reject(new Error(`Command timed out after ${options.timeout || 3e4}ms`));
|
|
9588
9588
|
} else {
|
|
9589
|
-
|
|
9589
|
+
resolve17({
|
|
9590
9590
|
stdout: stdout || "",
|
|
9591
9591
|
stderr: stderr || "",
|
|
9592
9592
|
exitCode: error ? error.code || 1 : 0
|
|
@@ -14506,6 +14506,11 @@ var init_config_schema = __esm({
|
|
|
14506
14506
|
SandboxConfig: {
|
|
14507
14507
|
type: "object",
|
|
14508
14508
|
properties: {
|
|
14509
|
+
engine: {
|
|
14510
|
+
type: "string",
|
|
14511
|
+
enum: ["docker", "bubblewrap", "seatbelt"],
|
|
14512
|
+
description: "Sandbox engine type: 'docker' (default), 'bubblewrap' (Linux namespaces), or 'seatbelt' (macOS sandbox-exec)"
|
|
14513
|
+
},
|
|
14509
14514
|
image: {
|
|
14510
14515
|
type: "string",
|
|
14511
14516
|
description: 'Docker image to use (e.g., "node:20-alpine")'
|
|
@@ -15883,92 +15888,120 @@ ${errors}`);
|
|
|
15883
15888
|
message: `Sandbox name '${name}' contains invalid characters. Only letters, numbers, dots, hyphens, underscores allowed.`
|
|
15884
15889
|
});
|
|
15885
15890
|
}
|
|
15886
|
-
|
|
15887
|
-
config.image ? "image" : null,
|
|
15888
|
-
config.dockerfile || config.dockerfile_inline ? "dockerfile" : null,
|
|
15889
|
-
config.compose ? "compose" : null
|
|
15890
|
-
].filter(Boolean);
|
|
15891
|
-
if (modes.length === 0) {
|
|
15892
|
-
errors.push({
|
|
15893
|
-
field: `sandboxes.${name}`,
|
|
15894
|
-
message: `Sandbox '${name}' must specify one of: image, dockerfile, dockerfile_inline, or compose`
|
|
15895
|
-
});
|
|
15896
|
-
} else if (modes.length > 1) {
|
|
15897
|
-
errors.push({
|
|
15898
|
-
field: `sandboxes.${name}`,
|
|
15899
|
-
message: `Sandbox '${name}' has multiple modes (${modes.join(", ")}). Specify exactly one.`
|
|
15900
|
-
});
|
|
15901
|
-
}
|
|
15902
|
-
if (config.compose && !config.service) {
|
|
15891
|
+
if (config.engine && !["docker", "bubblewrap", "seatbelt"].includes(config.engine)) {
|
|
15903
15892
|
errors.push({
|
|
15904
|
-
field: `sandboxes.${name}.
|
|
15905
|
-
message: `Sandbox '${name}'
|
|
15893
|
+
field: `sandboxes.${name}.engine`,
|
|
15894
|
+
message: `Sandbox '${name}' has invalid engine '${config.engine}'. Must be 'docker', 'bubblewrap', or 'seatbelt'.`
|
|
15906
15895
|
});
|
|
15907
15896
|
}
|
|
15908
|
-
|
|
15909
|
-
|
|
15910
|
-
|
|
15911
|
-
|
|
15912
|
-
|
|
15913
|
-
|
|
15914
|
-
|
|
15915
|
-
|
|
15916
|
-
|
|
15917
|
-
|
|
15918
|
-
|
|
15919
|
-
|
|
15920
|
-
|
|
15921
|
-
|
|
15897
|
+
const isNativeEngine = config.engine === "bubblewrap" || config.engine === "seatbelt";
|
|
15898
|
+
if (isNativeEngine) {
|
|
15899
|
+
const dockerOnlyFields = [
|
|
15900
|
+
["image", config.image],
|
|
15901
|
+
["dockerfile", config.dockerfile],
|
|
15902
|
+
["dockerfile_inline", config.dockerfile_inline],
|
|
15903
|
+
["compose", config.compose],
|
|
15904
|
+
["service", config.service],
|
|
15905
|
+
["cache", config.cache],
|
|
15906
|
+
["visor_path", config.visor_path],
|
|
15907
|
+
["resources", config.resources]
|
|
15908
|
+
];
|
|
15909
|
+
for (const [field, value] of dockerOnlyFields) {
|
|
15910
|
+
if (value !== void 0) {
|
|
15911
|
+
errors.push({
|
|
15912
|
+
field: `sandboxes.${name}.${field}`,
|
|
15913
|
+
message: `Sandbox '${name}' uses ${config.engine} engine but has Docker-only field '${field}'. Remove it or switch to engine: docker.`
|
|
15914
|
+
});
|
|
15915
|
+
}
|
|
15916
|
+
}
|
|
15917
|
+
} else {
|
|
15918
|
+
const modes = [
|
|
15919
|
+
config.image ? "image" : null,
|
|
15920
|
+
config.dockerfile || config.dockerfile_inline ? "dockerfile" : null,
|
|
15921
|
+
config.compose ? "compose" : null
|
|
15922
|
+
].filter(Boolean);
|
|
15923
|
+
if (modes.length === 0) {
|
|
15922
15924
|
errors.push({
|
|
15923
|
-
field: `sandboxes.${name}
|
|
15924
|
-
message: `
|
|
15925
|
+
field: `sandboxes.${name}`,
|
|
15926
|
+
message: `Sandbox '${name}' must specify one of: image, dockerfile, dockerfile_inline, or compose`
|
|
15927
|
+
});
|
|
15928
|
+
} else if (modes.length > 1) {
|
|
15929
|
+
errors.push({
|
|
15930
|
+
field: `sandboxes.${name}`,
|
|
15931
|
+
message: `Sandbox '${name}' has multiple modes (${modes.join(", ")}). Specify exactly one.`
|
|
15925
15932
|
});
|
|
15926
15933
|
}
|
|
15927
|
-
if (
|
|
15934
|
+
if (config.compose && !config.service) {
|
|
15928
15935
|
errors.push({
|
|
15929
|
-
field: `sandboxes.${name}.
|
|
15930
|
-
message: `
|
|
15936
|
+
field: `sandboxes.${name}.service`,
|
|
15937
|
+
message: `Sandbox '${name}' uses compose mode but is missing required 'service' field`
|
|
15931
15938
|
});
|
|
15932
15939
|
}
|
|
15933
|
-
|
|
15934
|
-
if (config.visor_path) {
|
|
15935
|
-
if (!config.visor_path.startsWith("/")) {
|
|
15940
|
+
if (config.dockerfile && /\.\./.test(config.dockerfile)) {
|
|
15936
15941
|
errors.push({
|
|
15937
|
-
field: `sandboxes.${name}.
|
|
15938
|
-
message: `
|
|
15942
|
+
field: `sandboxes.${name}.dockerfile`,
|
|
15943
|
+
message: `Dockerfile path '${config.dockerfile}' in sandbox '${name}' must not contain '..' path traversal`
|
|
15939
15944
|
});
|
|
15940
15945
|
}
|
|
15941
|
-
if (/\.\./.test(config.
|
|
15946
|
+
if (config.compose && /\.\./.test(config.compose)) {
|
|
15942
15947
|
errors.push({
|
|
15943
|
-
field: `sandboxes.${name}.
|
|
15944
|
-
message: `
|
|
15948
|
+
field: `sandboxes.${name}.compose`,
|
|
15949
|
+
message: `Compose file path '${config.compose}' in sandbox '${name}' must not contain '..' path traversal`
|
|
15945
15950
|
});
|
|
15946
15951
|
}
|
|
15947
|
-
|
|
15948
|
-
|
|
15949
|
-
for (const p of config.cache.paths) {
|
|
15950
|
-
if (!p.startsWith("/")) {
|
|
15952
|
+
if (config.visor_path) {
|
|
15953
|
+
if (!config.visor_path.startsWith("/")) {
|
|
15951
15954
|
errors.push({
|
|
15952
|
-
field: `sandboxes.${name}.
|
|
15953
|
-
message: `
|
|
15954
|
-
value: p
|
|
15955
|
+
field: `sandboxes.${name}.visor_path`,
|
|
15956
|
+
message: `visor_path '${config.visor_path}' in sandbox '${name}' must be an absolute path (start with /)`
|
|
15955
15957
|
});
|
|
15956
15958
|
}
|
|
15957
|
-
if (/\.\./.test(
|
|
15959
|
+
if (/\.\./.test(config.visor_path)) {
|
|
15960
|
+
errors.push({
|
|
15961
|
+
field: `sandboxes.${name}.visor_path`,
|
|
15962
|
+
message: `visor_path '${config.visor_path}' in sandbox '${name}' must not contain '..' path traversal`
|
|
15963
|
+
});
|
|
15964
|
+
}
|
|
15965
|
+
}
|
|
15966
|
+
if (config.cache?.paths) {
|
|
15967
|
+
for (const p of config.cache.paths) {
|
|
15968
|
+
if (!p.startsWith("/")) {
|
|
15969
|
+
errors.push({
|
|
15970
|
+
field: `sandboxes.${name}.cache.paths`,
|
|
15971
|
+
message: `Cache path '${p}' in sandbox '${name}' must be absolute (start with /)`,
|
|
15972
|
+
value: p
|
|
15973
|
+
});
|
|
15974
|
+
}
|
|
15975
|
+
if (/\.\./.test(p)) {
|
|
15976
|
+
errors.push({
|
|
15977
|
+
field: `sandboxes.${name}.cache.paths`,
|
|
15978
|
+
message: `Cache path '${p}' in sandbox '${name}' must not contain '..' path traversal`,
|
|
15979
|
+
value: p
|
|
15980
|
+
});
|
|
15981
|
+
}
|
|
15982
|
+
}
|
|
15983
|
+
}
|
|
15984
|
+
if (config.resources?.cpu !== void 0) {
|
|
15985
|
+
if (typeof config.resources.cpu !== "number" || config.resources.cpu <= 0) {
|
|
15958
15986
|
errors.push({
|
|
15959
|
-
field: `sandboxes.${name}.
|
|
15960
|
-
message: `
|
|
15961
|
-
value:
|
|
15987
|
+
field: `sandboxes.${name}.resources.cpu`,
|
|
15988
|
+
message: `CPU limit in sandbox '${name}' must be a positive number`,
|
|
15989
|
+
value: config.resources.cpu
|
|
15962
15990
|
});
|
|
15963
15991
|
}
|
|
15964
15992
|
}
|
|
15965
15993
|
}
|
|
15966
|
-
if (config.
|
|
15967
|
-
if (
|
|
15994
|
+
if (config.workdir) {
|
|
15995
|
+
if (!config.workdir.startsWith("/")) {
|
|
15996
|
+
errors.push({
|
|
15997
|
+
field: `sandboxes.${name}.workdir`,
|
|
15998
|
+
message: `Workdir '${config.workdir}' in sandbox '${name}' must be an absolute path (start with /)`
|
|
15999
|
+
});
|
|
16000
|
+
}
|
|
16001
|
+
if (/\.\./.test(config.workdir)) {
|
|
15968
16002
|
errors.push({
|
|
15969
|
-
field: `sandboxes.${name}.
|
|
15970
|
-
message: `
|
|
15971
|
-
value: config.resources.cpu
|
|
16003
|
+
field: `sandboxes.${name}.workdir`,
|
|
16004
|
+
message: `Workdir '${config.workdir}' in sandbox '${name}' must not contain '..' path traversal`
|
|
15972
16005
|
});
|
|
15973
16006
|
}
|
|
15974
16007
|
}
|
|
@@ -17388,17 +17421,17 @@ var init_workflow_check_provider = __esm({
|
|
|
17388
17421
|
* so it can be executed by the state machine as a nested workflow.
|
|
17389
17422
|
*/
|
|
17390
17423
|
async loadWorkflowFromConfigPath(sourcePath, baseDir) {
|
|
17391
|
-
const
|
|
17392
|
-
const
|
|
17424
|
+
const path30 = require("path");
|
|
17425
|
+
const fs26 = require("fs");
|
|
17393
17426
|
const yaml5 = require("js-yaml");
|
|
17394
|
-
const resolved =
|
|
17395
|
-
if (!
|
|
17427
|
+
const resolved = path30.isAbsolute(sourcePath) ? sourcePath : path30.resolve(baseDir, sourcePath);
|
|
17428
|
+
if (!fs26.existsSync(resolved)) {
|
|
17396
17429
|
throw new Error(`Workflow config not found at: ${resolved}`);
|
|
17397
17430
|
}
|
|
17398
|
-
const rawContent =
|
|
17431
|
+
const rawContent = fs26.readFileSync(resolved, "utf8");
|
|
17399
17432
|
const rawData = yaml5.load(rawContent);
|
|
17400
17433
|
if (rawData.imports && Array.isArray(rawData.imports)) {
|
|
17401
|
-
const configDir =
|
|
17434
|
+
const configDir = path30.dirname(resolved);
|
|
17402
17435
|
for (const source of rawData.imports) {
|
|
17403
17436
|
const results = await this.registry.import(source, {
|
|
17404
17437
|
basePath: configDir,
|
|
@@ -17428,8 +17461,8 @@ ${errors}`);
|
|
|
17428
17461
|
if (!steps || Object.keys(steps).length === 0) {
|
|
17429
17462
|
throw new Error(`Config '${resolved}' does not contain any steps to execute as a workflow`);
|
|
17430
17463
|
}
|
|
17431
|
-
const id =
|
|
17432
|
-
const name = loaded.name || `Workflow from ${
|
|
17464
|
+
const id = path30.basename(resolved).replace(/\.(ya?ml)$/i, "");
|
|
17465
|
+
const name = loaded.name || `Workflow from ${path30.basename(resolved)}`;
|
|
17433
17466
|
const workflowDef = {
|
|
17434
17467
|
id,
|
|
17435
17468
|
name,
|
|
@@ -18059,8 +18092,8 @@ async function createStoreBackend(storageConfig, haConfig) {
|
|
|
18059
18092
|
case "mssql": {
|
|
18060
18093
|
try {
|
|
18061
18094
|
const loaderPath = "../../enterprise/loader";
|
|
18062
|
-
const { loadEnterpriseStoreBackend } = await import(loaderPath);
|
|
18063
|
-
return await
|
|
18095
|
+
const { loadEnterpriseStoreBackend: loadEnterpriseStoreBackend2 } = await import(loaderPath);
|
|
18096
|
+
return await loadEnterpriseStoreBackend2(driver, storageConfig, haConfig);
|
|
18064
18097
|
} catch (err) {
|
|
18065
18098
|
const msg = err instanceof Error ? err.message : String(err);
|
|
18066
18099
|
logger.error(`[StoreFactory] Failed to load enterprise ${driver} backend: ${msg}`);
|
|
@@ -19334,7 +19367,7 @@ var init_mcp_custom_sse_server = __esm({
|
|
|
19334
19367
|
* Returns the actual bound port number
|
|
19335
19368
|
*/
|
|
19336
19369
|
async start() {
|
|
19337
|
-
return new Promise((
|
|
19370
|
+
return new Promise((resolve17, reject) => {
|
|
19338
19371
|
try {
|
|
19339
19372
|
this.server = import_http.default.createServer((req, res) => {
|
|
19340
19373
|
this.handleRequest(req, res).catch((error) => {
|
|
@@ -19368,7 +19401,7 @@ var init_mcp_custom_sse_server = __esm({
|
|
|
19368
19401
|
);
|
|
19369
19402
|
}
|
|
19370
19403
|
this.startKeepalive();
|
|
19371
|
-
|
|
19404
|
+
resolve17(this.port);
|
|
19372
19405
|
});
|
|
19373
19406
|
} catch (error) {
|
|
19374
19407
|
reject(error);
|
|
@@ -19431,7 +19464,7 @@ var init_mcp_custom_sse_server = __esm({
|
|
|
19431
19464
|
logger.debug(
|
|
19432
19465
|
`[CustomToolsSSEServer:${this.sessionId}] Grace period before stop: ${waitMs}ms (activeToolCalls=${this.activeToolCalls})`
|
|
19433
19466
|
);
|
|
19434
|
-
await new Promise((
|
|
19467
|
+
await new Promise((resolve17) => setTimeout(resolve17, waitMs));
|
|
19435
19468
|
}
|
|
19436
19469
|
}
|
|
19437
19470
|
if (this.activeToolCalls > 0) {
|
|
@@ -19440,7 +19473,7 @@ var init_mcp_custom_sse_server = __esm({
|
|
|
19440
19473
|
`[CustomToolsSSEServer:${this.sessionId}] Waiting for ${this.activeToolCalls} active tool call(s) before stop`
|
|
19441
19474
|
);
|
|
19442
19475
|
while (this.activeToolCalls > 0 && Date.now() - startedAt < effectiveDrainTimeoutMs) {
|
|
19443
|
-
await new Promise((
|
|
19476
|
+
await new Promise((resolve17) => setTimeout(resolve17, 250));
|
|
19444
19477
|
}
|
|
19445
19478
|
if (this.activeToolCalls > 0) {
|
|
19446
19479
|
logger.warn(
|
|
@@ -19465,21 +19498,21 @@ var init_mcp_custom_sse_server = __esm({
|
|
|
19465
19498
|
}
|
|
19466
19499
|
this.connections.clear();
|
|
19467
19500
|
if (this.server) {
|
|
19468
|
-
await new Promise((
|
|
19501
|
+
await new Promise((resolve17, reject) => {
|
|
19469
19502
|
const timeout = setTimeout(() => {
|
|
19470
19503
|
if (this.debug) {
|
|
19471
19504
|
logger.debug(
|
|
19472
19505
|
`[CustomToolsSSEServer:${this.sessionId}] Force closing server after timeout`
|
|
19473
19506
|
);
|
|
19474
19507
|
}
|
|
19475
|
-
this.server?.close(() =>
|
|
19508
|
+
this.server?.close(() => resolve17());
|
|
19476
19509
|
}, 5e3);
|
|
19477
19510
|
this.server.close((error) => {
|
|
19478
19511
|
clearTimeout(timeout);
|
|
19479
19512
|
if (error) {
|
|
19480
19513
|
reject(error);
|
|
19481
19514
|
} else {
|
|
19482
|
-
|
|
19515
|
+
resolve17();
|
|
19483
19516
|
}
|
|
19484
19517
|
});
|
|
19485
19518
|
});
|
|
@@ -19905,7 +19938,7 @@ var init_mcp_custom_sse_server = __esm({
|
|
|
19905
19938
|
logger.warn(
|
|
19906
19939
|
`[CustomToolsSSEServer:${this.sessionId}] Tool ${toolName} failed (attempt ${attempt + 1}/${retryCount + 1}): ${errorMsg}. Retrying in ${delay}ms`
|
|
19907
19940
|
);
|
|
19908
|
-
await new Promise((
|
|
19941
|
+
await new Promise((resolve17) => setTimeout(resolve17, delay));
|
|
19909
19942
|
attempt++;
|
|
19910
19943
|
}
|
|
19911
19944
|
}
|
|
@@ -20208,10 +20241,10 @@ var init_ai_check_provider = __esm({
|
|
|
20208
20241
|
} else {
|
|
20209
20242
|
resolvedPath = import_path7.default.resolve(process.cwd(), str);
|
|
20210
20243
|
}
|
|
20211
|
-
const
|
|
20244
|
+
const fs26 = require("fs").promises;
|
|
20212
20245
|
try {
|
|
20213
|
-
const
|
|
20214
|
-
return
|
|
20246
|
+
const stat2 = await fs26.stat(resolvedPath);
|
|
20247
|
+
return stat2.isFile();
|
|
20215
20248
|
} catch {
|
|
20216
20249
|
return hasFileExtension && (isRelativePath || isAbsolutePath || hasPathSeparators);
|
|
20217
20250
|
}
|
|
@@ -23459,8 +23492,8 @@ var init_claude_code_check_provider = __esm({
|
|
|
23459
23492
|
resolvedPath = import_path8.default.resolve(process.cwd(), str);
|
|
23460
23493
|
}
|
|
23461
23494
|
try {
|
|
23462
|
-
const
|
|
23463
|
-
return
|
|
23495
|
+
const stat2 = await import_promises5.default.stat(resolvedPath);
|
|
23496
|
+
return stat2.isFile();
|
|
23464
23497
|
} catch {
|
|
23465
23498
|
return hasFileExtension && (isRelativePath || isAbsolutePath || hasPathSeparators);
|
|
23466
23499
|
}
|
|
@@ -26062,14 +26095,14 @@ var require_util = __commonJS({
|
|
|
26062
26095
|
}
|
|
26063
26096
|
const port = url.port != null ? url.port : url.protocol === "https:" ? 443 : 80;
|
|
26064
26097
|
let origin = url.origin != null ? url.origin : `${url.protocol}//${url.hostname}:${port}`;
|
|
26065
|
-
let
|
|
26098
|
+
let path30 = url.path != null ? url.path : `${url.pathname || ""}${url.search || ""}`;
|
|
26066
26099
|
if (origin.endsWith("/")) {
|
|
26067
26100
|
origin = origin.substring(0, origin.length - 1);
|
|
26068
26101
|
}
|
|
26069
|
-
if (
|
|
26070
|
-
|
|
26102
|
+
if (path30 && !path30.startsWith("/")) {
|
|
26103
|
+
path30 = `/${path30}`;
|
|
26071
26104
|
}
|
|
26072
|
-
url = new URL(origin +
|
|
26105
|
+
url = new URL(origin + path30);
|
|
26073
26106
|
}
|
|
26074
26107
|
return url;
|
|
26075
26108
|
}
|
|
@@ -27683,20 +27716,20 @@ var require_parseParams = __commonJS({
|
|
|
27683
27716
|
var require_basename = __commonJS({
|
|
27684
27717
|
"node_modules/@fastify/busboy/lib/utils/basename.js"(exports2, module2) {
|
|
27685
27718
|
"use strict";
|
|
27686
|
-
module2.exports = function basename4(
|
|
27687
|
-
if (typeof
|
|
27719
|
+
module2.exports = function basename4(path30) {
|
|
27720
|
+
if (typeof path30 !== "string") {
|
|
27688
27721
|
return "";
|
|
27689
27722
|
}
|
|
27690
|
-
for (var i =
|
|
27691
|
-
switch (
|
|
27723
|
+
for (var i = path30.length - 1; i >= 0; --i) {
|
|
27724
|
+
switch (path30.charCodeAt(i)) {
|
|
27692
27725
|
case 47:
|
|
27693
27726
|
// '/'
|
|
27694
27727
|
case 92:
|
|
27695
|
-
|
|
27696
|
-
return
|
|
27728
|
+
path30 = path30.slice(i + 1);
|
|
27729
|
+
return path30 === ".." || path30 === "." ? "" : path30;
|
|
27697
27730
|
}
|
|
27698
27731
|
}
|
|
27699
|
-
return
|
|
27732
|
+
return path30 === ".." || path30 === "." ? "" : path30;
|
|
27700
27733
|
};
|
|
27701
27734
|
}
|
|
27702
27735
|
});
|
|
@@ -28700,11 +28733,11 @@ var require_util2 = __commonJS({
|
|
|
28700
28733
|
var assert = require("assert");
|
|
28701
28734
|
var { isUint8Array } = require("util/types");
|
|
28702
28735
|
var supportedHashes = [];
|
|
28703
|
-
var
|
|
28736
|
+
var crypto4;
|
|
28704
28737
|
try {
|
|
28705
|
-
|
|
28738
|
+
crypto4 = require("crypto");
|
|
28706
28739
|
const possibleRelevantHashes = ["sha256", "sha384", "sha512"];
|
|
28707
|
-
supportedHashes =
|
|
28740
|
+
supportedHashes = crypto4.getHashes().filter((hash) => possibleRelevantHashes.includes(hash));
|
|
28708
28741
|
} catch {
|
|
28709
28742
|
}
|
|
28710
28743
|
function responseURL(response) {
|
|
@@ -28981,7 +29014,7 @@ var require_util2 = __commonJS({
|
|
|
28981
29014
|
}
|
|
28982
29015
|
}
|
|
28983
29016
|
function bytesMatch(bytes, metadataList) {
|
|
28984
|
-
if (
|
|
29017
|
+
if (crypto4 === void 0) {
|
|
28985
29018
|
return true;
|
|
28986
29019
|
}
|
|
28987
29020
|
const parsedMetadata = parseMetadata(metadataList);
|
|
@@ -28996,7 +29029,7 @@ var require_util2 = __commonJS({
|
|
|
28996
29029
|
for (const item of metadata) {
|
|
28997
29030
|
const algorithm = item.algo;
|
|
28998
29031
|
const expectedValue = item.hash;
|
|
28999
|
-
let actualValue =
|
|
29032
|
+
let actualValue = crypto4.createHash(algorithm).update(bytes).digest("base64");
|
|
29000
29033
|
if (actualValue[actualValue.length - 1] === "=") {
|
|
29001
29034
|
if (actualValue[actualValue.length - 2] === "=") {
|
|
29002
29035
|
actualValue = actualValue.slice(0, -2);
|
|
@@ -29089,8 +29122,8 @@ var require_util2 = __commonJS({
|
|
|
29089
29122
|
function createDeferredPromise() {
|
|
29090
29123
|
let res;
|
|
29091
29124
|
let rej;
|
|
29092
|
-
const promise = new Promise((
|
|
29093
|
-
res =
|
|
29125
|
+
const promise = new Promise((resolve17, reject) => {
|
|
29126
|
+
res = resolve17;
|
|
29094
29127
|
rej = reject;
|
|
29095
29128
|
});
|
|
29096
29129
|
return { promise, resolve: res, reject: rej };
|
|
@@ -30343,8 +30376,8 @@ var require_body = __commonJS({
|
|
|
30343
30376
|
var { parseMIMEType, serializeAMimeType } = require_dataURL();
|
|
30344
30377
|
var random;
|
|
30345
30378
|
try {
|
|
30346
|
-
const
|
|
30347
|
-
random = (max) =>
|
|
30379
|
+
const crypto4 = require("crypto");
|
|
30380
|
+
random = (max) => crypto4.randomInt(0, max);
|
|
30348
30381
|
} catch {
|
|
30349
30382
|
random = (max) => Math.floor(Math.random(max));
|
|
30350
30383
|
}
|
|
@@ -30595,8 +30628,8 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
30595
30628
|
});
|
|
30596
30629
|
}
|
|
30597
30630
|
});
|
|
30598
|
-
const busboyResolve = new Promise((
|
|
30599
|
-
busboy.on("finish",
|
|
30631
|
+
const busboyResolve = new Promise((resolve17, reject) => {
|
|
30632
|
+
busboy.on("finish", resolve17);
|
|
30600
30633
|
busboy.on("error", (err) => reject(new TypeError(err)));
|
|
30601
30634
|
});
|
|
30602
30635
|
if (this.body !== null) for await (const chunk of consumeBody(this[kState].body)) busboy.write(chunk);
|
|
@@ -30727,7 +30760,7 @@ var require_request = __commonJS({
|
|
|
30727
30760
|
}
|
|
30728
30761
|
var Request = class _Request {
|
|
30729
30762
|
constructor(origin, {
|
|
30730
|
-
path:
|
|
30763
|
+
path: path30,
|
|
30731
30764
|
method,
|
|
30732
30765
|
body,
|
|
30733
30766
|
headers,
|
|
@@ -30741,11 +30774,11 @@ var require_request = __commonJS({
|
|
|
30741
30774
|
throwOnError,
|
|
30742
30775
|
expectContinue
|
|
30743
30776
|
}, handler) {
|
|
30744
|
-
if (typeof
|
|
30777
|
+
if (typeof path30 !== "string") {
|
|
30745
30778
|
throw new InvalidArgumentError("path must be a string");
|
|
30746
|
-
} else if (
|
|
30779
|
+
} else if (path30[0] !== "/" && !(path30.startsWith("http://") || path30.startsWith("https://")) && method !== "CONNECT") {
|
|
30747
30780
|
throw new InvalidArgumentError("path must be an absolute URL or start with a slash");
|
|
30748
|
-
} else if (invalidPathRegex.exec(
|
|
30781
|
+
} else if (invalidPathRegex.exec(path30) !== null) {
|
|
30749
30782
|
throw new InvalidArgumentError("invalid request path");
|
|
30750
30783
|
}
|
|
30751
30784
|
if (typeof method !== "string") {
|
|
@@ -30808,7 +30841,7 @@ var require_request = __commonJS({
|
|
|
30808
30841
|
this.completed = false;
|
|
30809
30842
|
this.aborted = false;
|
|
30810
30843
|
this.upgrade = upgrade || null;
|
|
30811
|
-
this.path = query ? util.buildURL(
|
|
30844
|
+
this.path = query ? util.buildURL(path30, query) : path30;
|
|
30812
30845
|
this.origin = origin;
|
|
30813
30846
|
this.idempotent = idempotent == null ? method === "HEAD" || method === "GET" : idempotent;
|
|
30814
30847
|
this.blocking = blocking == null ? false : blocking;
|
|
@@ -31130,9 +31163,9 @@ var require_dispatcher_base = __commonJS({
|
|
|
31130
31163
|
}
|
|
31131
31164
|
close(callback) {
|
|
31132
31165
|
if (callback === void 0) {
|
|
31133
|
-
return new Promise((
|
|
31166
|
+
return new Promise((resolve17, reject) => {
|
|
31134
31167
|
this.close((err, data) => {
|
|
31135
|
-
return err ? reject(err) :
|
|
31168
|
+
return err ? reject(err) : resolve17(data);
|
|
31136
31169
|
});
|
|
31137
31170
|
});
|
|
31138
31171
|
}
|
|
@@ -31170,12 +31203,12 @@ var require_dispatcher_base = __commonJS({
|
|
|
31170
31203
|
err = null;
|
|
31171
31204
|
}
|
|
31172
31205
|
if (callback === void 0) {
|
|
31173
|
-
return new Promise((
|
|
31206
|
+
return new Promise((resolve17, reject) => {
|
|
31174
31207
|
this.destroy(err, (err2, data) => {
|
|
31175
31208
|
return err2 ? (
|
|
31176
31209
|
/* istanbul ignore next: should never error */
|
|
31177
31210
|
reject(err2)
|
|
31178
|
-
) :
|
|
31211
|
+
) : resolve17(data);
|
|
31179
31212
|
});
|
|
31180
31213
|
});
|
|
31181
31214
|
}
|
|
@@ -31816,9 +31849,9 @@ var require_RedirectHandler = __commonJS({
|
|
|
31816
31849
|
return this.handler.onHeaders(statusCode, headers, resume, statusText);
|
|
31817
31850
|
}
|
|
31818
31851
|
const { origin, pathname, search } = util.parseURL(new URL(this.location, this.opts.origin && new URL(this.opts.path, this.opts.origin)));
|
|
31819
|
-
const
|
|
31852
|
+
const path30 = search ? `${pathname}${search}` : pathname;
|
|
31820
31853
|
this.opts.headers = cleanRequestHeaders(this.opts.headers, statusCode === 303, this.opts.origin !== origin);
|
|
31821
|
-
this.opts.path =
|
|
31854
|
+
this.opts.path = path30;
|
|
31822
31855
|
this.opts.origin = origin;
|
|
31823
31856
|
this.opts.maxRedirections = 0;
|
|
31824
31857
|
this.opts.query = null;
|
|
@@ -32237,16 +32270,16 @@ var require_client = __commonJS({
|
|
|
32237
32270
|
return this[kNeedDrain] < 2;
|
|
32238
32271
|
}
|
|
32239
32272
|
async [kClose]() {
|
|
32240
|
-
return new Promise((
|
|
32273
|
+
return new Promise((resolve17) => {
|
|
32241
32274
|
if (!this[kSize]) {
|
|
32242
|
-
|
|
32275
|
+
resolve17(null);
|
|
32243
32276
|
} else {
|
|
32244
|
-
this[kClosedResolve] =
|
|
32277
|
+
this[kClosedResolve] = resolve17;
|
|
32245
32278
|
}
|
|
32246
32279
|
});
|
|
32247
32280
|
}
|
|
32248
32281
|
async [kDestroy](err) {
|
|
32249
|
-
return new Promise((
|
|
32282
|
+
return new Promise((resolve17) => {
|
|
32250
32283
|
const requests = this[kQueue].splice(this[kPendingIdx]);
|
|
32251
32284
|
for (let i = 0; i < requests.length; i++) {
|
|
32252
32285
|
const request = requests[i];
|
|
@@ -32257,7 +32290,7 @@ var require_client = __commonJS({
|
|
|
32257
32290
|
this[kClosedResolve]();
|
|
32258
32291
|
this[kClosedResolve] = null;
|
|
32259
32292
|
}
|
|
32260
|
-
|
|
32293
|
+
resolve17();
|
|
32261
32294
|
};
|
|
32262
32295
|
if (this[kHTTP2Session] != null) {
|
|
32263
32296
|
util.destroy(this[kHTTP2Session], err);
|
|
@@ -32837,7 +32870,7 @@ var require_client = __commonJS({
|
|
|
32837
32870
|
});
|
|
32838
32871
|
}
|
|
32839
32872
|
try {
|
|
32840
|
-
const socket = await new Promise((
|
|
32873
|
+
const socket = await new Promise((resolve17, reject) => {
|
|
32841
32874
|
client[kConnector]({
|
|
32842
32875
|
host,
|
|
32843
32876
|
hostname,
|
|
@@ -32849,7 +32882,7 @@ var require_client = __commonJS({
|
|
|
32849
32882
|
if (err) {
|
|
32850
32883
|
reject(err);
|
|
32851
32884
|
} else {
|
|
32852
|
-
|
|
32885
|
+
resolve17(socket2);
|
|
32853
32886
|
}
|
|
32854
32887
|
});
|
|
32855
32888
|
});
|
|
@@ -33060,7 +33093,7 @@ var require_client = __commonJS({
|
|
|
33060
33093
|
writeH2(client, client[kHTTP2Session], request);
|
|
33061
33094
|
return;
|
|
33062
33095
|
}
|
|
33063
|
-
const { body, method, path:
|
|
33096
|
+
const { body, method, path: path30, host, upgrade, headers, blocking, reset } = request;
|
|
33064
33097
|
const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH";
|
|
33065
33098
|
if (body && typeof body.read === "function") {
|
|
33066
33099
|
body.read(0);
|
|
@@ -33110,7 +33143,7 @@ var require_client = __commonJS({
|
|
|
33110
33143
|
if (blocking) {
|
|
33111
33144
|
socket[kBlocking] = true;
|
|
33112
33145
|
}
|
|
33113
|
-
let header = `${method} ${
|
|
33146
|
+
let header = `${method} ${path30} HTTP/1.1\r
|
|
33114
33147
|
`;
|
|
33115
33148
|
if (typeof host === "string") {
|
|
33116
33149
|
header += `host: ${host}\r
|
|
@@ -33173,7 +33206,7 @@ upgrade: ${upgrade}\r
|
|
|
33173
33206
|
return true;
|
|
33174
33207
|
}
|
|
33175
33208
|
function writeH2(client, session, request) {
|
|
33176
|
-
const { body, method, path:
|
|
33209
|
+
const { body, method, path: path30, host, upgrade, expectContinue, signal, headers: reqHeaders } = request;
|
|
33177
33210
|
let headers;
|
|
33178
33211
|
if (typeof reqHeaders === "string") headers = Request[kHTTP2CopyHeaders](reqHeaders.trim());
|
|
33179
33212
|
else headers = reqHeaders;
|
|
@@ -33216,7 +33249,7 @@ upgrade: ${upgrade}\r
|
|
|
33216
33249
|
});
|
|
33217
33250
|
return true;
|
|
33218
33251
|
}
|
|
33219
|
-
headers[HTTP2_HEADER_PATH] =
|
|
33252
|
+
headers[HTTP2_HEADER_PATH] = path30;
|
|
33220
33253
|
headers[HTTP2_HEADER_SCHEME] = "https";
|
|
33221
33254
|
const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH";
|
|
33222
33255
|
if (body && typeof body.read === "function") {
|
|
@@ -33473,12 +33506,12 @@ upgrade: ${upgrade}\r
|
|
|
33473
33506
|
cb();
|
|
33474
33507
|
}
|
|
33475
33508
|
}
|
|
33476
|
-
const waitForDrain = () => new Promise((
|
|
33509
|
+
const waitForDrain = () => new Promise((resolve17, reject) => {
|
|
33477
33510
|
assert(callback === null);
|
|
33478
33511
|
if (socket[kError]) {
|
|
33479
33512
|
reject(socket[kError]);
|
|
33480
33513
|
} else {
|
|
33481
|
-
callback =
|
|
33514
|
+
callback = resolve17;
|
|
33482
33515
|
}
|
|
33483
33516
|
});
|
|
33484
33517
|
if (client[kHTTPConnVersion] === "h2") {
|
|
@@ -33824,8 +33857,8 @@ var require_pool_base = __commonJS({
|
|
|
33824
33857
|
if (this[kQueue].isEmpty()) {
|
|
33825
33858
|
return Promise.all(this[kClients].map((c) => c.close()));
|
|
33826
33859
|
} else {
|
|
33827
|
-
return new Promise((
|
|
33828
|
-
this[kClosedResolve] =
|
|
33860
|
+
return new Promise((resolve17) => {
|
|
33861
|
+
this[kClosedResolve] = resolve17;
|
|
33829
33862
|
});
|
|
33830
33863
|
}
|
|
33831
33864
|
}
|
|
@@ -34403,7 +34436,7 @@ var require_readable = __commonJS({
|
|
|
34403
34436
|
if (this.closed) {
|
|
34404
34437
|
return Promise.resolve(null);
|
|
34405
34438
|
}
|
|
34406
|
-
return new Promise((
|
|
34439
|
+
return new Promise((resolve17, reject) => {
|
|
34407
34440
|
const signalListenerCleanup = signal ? util.addAbortListener(signal, () => {
|
|
34408
34441
|
this.destroy();
|
|
34409
34442
|
}) : noop;
|
|
@@ -34412,7 +34445,7 @@ var require_readable = __commonJS({
|
|
|
34412
34445
|
if (signal && signal.aborted) {
|
|
34413
34446
|
reject(signal.reason || Object.assign(new Error("The operation was aborted"), { name: "AbortError" }));
|
|
34414
34447
|
} else {
|
|
34415
|
-
|
|
34448
|
+
resolve17(null);
|
|
34416
34449
|
}
|
|
34417
34450
|
}).on("error", noop).on("data", function(chunk) {
|
|
34418
34451
|
limit -= chunk.length;
|
|
@@ -34434,11 +34467,11 @@ var require_readable = __commonJS({
|
|
|
34434
34467
|
throw new TypeError("unusable");
|
|
34435
34468
|
}
|
|
34436
34469
|
assert(!stream[kConsume]);
|
|
34437
|
-
return new Promise((
|
|
34470
|
+
return new Promise((resolve17, reject) => {
|
|
34438
34471
|
stream[kConsume] = {
|
|
34439
34472
|
type,
|
|
34440
34473
|
stream,
|
|
34441
|
-
resolve:
|
|
34474
|
+
resolve: resolve17,
|
|
34442
34475
|
reject,
|
|
34443
34476
|
length: 0,
|
|
34444
34477
|
body: []
|
|
@@ -34473,12 +34506,12 @@ var require_readable = __commonJS({
|
|
|
34473
34506
|
}
|
|
34474
34507
|
}
|
|
34475
34508
|
function consumeEnd(consume2) {
|
|
34476
|
-
const { type, body, resolve:
|
|
34509
|
+
const { type, body, resolve: resolve17, stream, length } = consume2;
|
|
34477
34510
|
try {
|
|
34478
34511
|
if (type === "text") {
|
|
34479
|
-
|
|
34512
|
+
resolve17(toUSVString(Buffer.concat(body)));
|
|
34480
34513
|
} else if (type === "json") {
|
|
34481
|
-
|
|
34514
|
+
resolve17(JSON.parse(Buffer.concat(body)));
|
|
34482
34515
|
} else if (type === "arrayBuffer") {
|
|
34483
34516
|
const dst = new Uint8Array(length);
|
|
34484
34517
|
let pos = 0;
|
|
@@ -34486,12 +34519,12 @@ var require_readable = __commonJS({
|
|
|
34486
34519
|
dst.set(buf, pos);
|
|
34487
34520
|
pos += buf.byteLength;
|
|
34488
34521
|
}
|
|
34489
|
-
|
|
34522
|
+
resolve17(dst.buffer);
|
|
34490
34523
|
} else if (type === "blob") {
|
|
34491
34524
|
if (!Blob2) {
|
|
34492
34525
|
Blob2 = require("buffer").Blob;
|
|
34493
34526
|
}
|
|
34494
|
-
|
|
34527
|
+
resolve17(new Blob2(body, { type: stream[kContentType] }));
|
|
34495
34528
|
}
|
|
34496
34529
|
consumeFinish(consume2);
|
|
34497
34530
|
} catch (err) {
|
|
@@ -34748,9 +34781,9 @@ var require_api_request = __commonJS({
|
|
|
34748
34781
|
};
|
|
34749
34782
|
function request(opts, callback) {
|
|
34750
34783
|
if (callback === void 0) {
|
|
34751
|
-
return new Promise((
|
|
34784
|
+
return new Promise((resolve17, reject) => {
|
|
34752
34785
|
request.call(this, opts, (err, data) => {
|
|
34753
|
-
return err ? reject(err) :
|
|
34786
|
+
return err ? reject(err) : resolve17(data);
|
|
34754
34787
|
});
|
|
34755
34788
|
});
|
|
34756
34789
|
}
|
|
@@ -34923,9 +34956,9 @@ var require_api_stream = __commonJS({
|
|
|
34923
34956
|
};
|
|
34924
34957
|
function stream(opts, factory, callback) {
|
|
34925
34958
|
if (callback === void 0) {
|
|
34926
|
-
return new Promise((
|
|
34959
|
+
return new Promise((resolve17, reject) => {
|
|
34927
34960
|
stream.call(this, opts, factory, (err, data) => {
|
|
34928
|
-
return err ? reject(err) :
|
|
34961
|
+
return err ? reject(err) : resolve17(data);
|
|
34929
34962
|
});
|
|
34930
34963
|
});
|
|
34931
34964
|
}
|
|
@@ -35206,9 +35239,9 @@ var require_api_upgrade = __commonJS({
|
|
|
35206
35239
|
};
|
|
35207
35240
|
function upgrade(opts, callback) {
|
|
35208
35241
|
if (callback === void 0) {
|
|
35209
|
-
return new Promise((
|
|
35242
|
+
return new Promise((resolve17, reject) => {
|
|
35210
35243
|
upgrade.call(this, opts, (err, data) => {
|
|
35211
|
-
return err ? reject(err) :
|
|
35244
|
+
return err ? reject(err) : resolve17(data);
|
|
35212
35245
|
});
|
|
35213
35246
|
});
|
|
35214
35247
|
}
|
|
@@ -35297,9 +35330,9 @@ var require_api_connect = __commonJS({
|
|
|
35297
35330
|
};
|
|
35298
35331
|
function connect(opts, callback) {
|
|
35299
35332
|
if (callback === void 0) {
|
|
35300
|
-
return new Promise((
|
|
35333
|
+
return new Promise((resolve17, reject) => {
|
|
35301
35334
|
connect.call(this, opts, (err, data) => {
|
|
35302
|
-
return err ? reject(err) :
|
|
35335
|
+
return err ? reject(err) : resolve17(data);
|
|
35303
35336
|
});
|
|
35304
35337
|
});
|
|
35305
35338
|
}
|
|
@@ -35459,20 +35492,20 @@ var require_mock_utils = __commonJS({
|
|
|
35459
35492
|
}
|
|
35460
35493
|
return true;
|
|
35461
35494
|
}
|
|
35462
|
-
function safeUrl(
|
|
35463
|
-
if (typeof
|
|
35464
|
-
return
|
|
35495
|
+
function safeUrl(path30) {
|
|
35496
|
+
if (typeof path30 !== "string") {
|
|
35497
|
+
return path30;
|
|
35465
35498
|
}
|
|
35466
|
-
const pathSegments =
|
|
35499
|
+
const pathSegments = path30.split("?");
|
|
35467
35500
|
if (pathSegments.length !== 2) {
|
|
35468
|
-
return
|
|
35501
|
+
return path30;
|
|
35469
35502
|
}
|
|
35470
35503
|
const qp = new URLSearchParams(pathSegments.pop());
|
|
35471
35504
|
qp.sort();
|
|
35472
35505
|
return [...pathSegments, qp.toString()].join("?");
|
|
35473
35506
|
}
|
|
35474
|
-
function matchKey(mockDispatch2, { path:
|
|
35475
|
-
const pathMatch = matchValue(mockDispatch2.path,
|
|
35507
|
+
function matchKey(mockDispatch2, { path: path30, method, body, headers }) {
|
|
35508
|
+
const pathMatch = matchValue(mockDispatch2.path, path30);
|
|
35476
35509
|
const methodMatch = matchValue(mockDispatch2.method, method);
|
|
35477
35510
|
const bodyMatch = typeof mockDispatch2.body !== "undefined" ? matchValue(mockDispatch2.body, body) : true;
|
|
35478
35511
|
const headersMatch = matchHeaders(mockDispatch2, headers);
|
|
@@ -35490,7 +35523,7 @@ var require_mock_utils = __commonJS({
|
|
|
35490
35523
|
function getMockDispatch(mockDispatches, key) {
|
|
35491
35524
|
const basePath = key.query ? buildURL(key.path, key.query) : key.path;
|
|
35492
35525
|
const resolvedPath = typeof basePath === "string" ? safeUrl(basePath) : basePath;
|
|
35493
|
-
let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path:
|
|
35526
|
+
let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path: path30 }) => matchValue(safeUrl(path30), resolvedPath));
|
|
35494
35527
|
if (matchedMockDispatches.length === 0) {
|
|
35495
35528
|
throw new MockNotMatchedError(`Mock dispatch not matched for path '${resolvedPath}'`);
|
|
35496
35529
|
}
|
|
@@ -35527,9 +35560,9 @@ var require_mock_utils = __commonJS({
|
|
|
35527
35560
|
}
|
|
35528
35561
|
}
|
|
35529
35562
|
function buildKey(opts) {
|
|
35530
|
-
const { path:
|
|
35563
|
+
const { path: path30, method, body, headers, query } = opts;
|
|
35531
35564
|
return {
|
|
35532
|
-
path:
|
|
35565
|
+
path: path30,
|
|
35533
35566
|
method,
|
|
35534
35567
|
body,
|
|
35535
35568
|
headers,
|
|
@@ -35823,7 +35856,7 @@ var require_mock_interceptor = __commonJS({
|
|
|
35823
35856
|
var require_mock_client = __commonJS({
|
|
35824
35857
|
"node_modules/undici/lib/mock/mock-client.js"(exports2, module2) {
|
|
35825
35858
|
"use strict";
|
|
35826
|
-
var { promisify:
|
|
35859
|
+
var { promisify: promisify7 } = require("util");
|
|
35827
35860
|
var Client2 = require_client();
|
|
35828
35861
|
var { buildMockDispatch } = require_mock_utils();
|
|
35829
35862
|
var {
|
|
@@ -35863,7 +35896,7 @@ var require_mock_client = __commonJS({
|
|
|
35863
35896
|
return new MockInterceptor(opts, this[kDispatches]);
|
|
35864
35897
|
}
|
|
35865
35898
|
async [kClose]() {
|
|
35866
|
-
await
|
|
35899
|
+
await promisify7(this[kOriginalClose])();
|
|
35867
35900
|
this[kConnected] = 0;
|
|
35868
35901
|
this[kMockAgent][Symbols.kClients].delete(this[kOrigin]);
|
|
35869
35902
|
}
|
|
@@ -35876,7 +35909,7 @@ var require_mock_client = __commonJS({
|
|
|
35876
35909
|
var require_mock_pool = __commonJS({
|
|
35877
35910
|
"node_modules/undici/lib/mock/mock-pool.js"(exports2, module2) {
|
|
35878
35911
|
"use strict";
|
|
35879
|
-
var { promisify:
|
|
35912
|
+
var { promisify: promisify7 } = require("util");
|
|
35880
35913
|
var Pool = require_pool();
|
|
35881
35914
|
var { buildMockDispatch } = require_mock_utils();
|
|
35882
35915
|
var {
|
|
@@ -35916,7 +35949,7 @@ var require_mock_pool = __commonJS({
|
|
|
35916
35949
|
return new MockInterceptor(opts, this[kDispatches]);
|
|
35917
35950
|
}
|
|
35918
35951
|
async [kClose]() {
|
|
35919
|
-
await
|
|
35952
|
+
await promisify7(this[kOriginalClose])();
|
|
35920
35953
|
this[kConnected] = 0;
|
|
35921
35954
|
this[kMockAgent][Symbols.kClients].delete(this[kOrigin]);
|
|
35922
35955
|
}
|
|
@@ -35978,10 +36011,10 @@ var require_pending_interceptors_formatter = __commonJS({
|
|
|
35978
36011
|
}
|
|
35979
36012
|
format(pendingInterceptors) {
|
|
35980
36013
|
const withPrettyHeaders = pendingInterceptors.map(
|
|
35981
|
-
({ method, path:
|
|
36014
|
+
({ method, path: path30, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
|
|
35982
36015
|
Method: method,
|
|
35983
36016
|
Origin: origin,
|
|
35984
|
-
Path:
|
|
36017
|
+
Path: path30,
|
|
35985
36018
|
"Status code": statusCode,
|
|
35986
36019
|
Persistent: persist ? "\u2705" : "\u274C",
|
|
35987
36020
|
Invocations: timesInvoked,
|
|
@@ -38922,7 +38955,7 @@ var require_fetch = __commonJS({
|
|
|
38922
38955
|
async function dispatch({ body }) {
|
|
38923
38956
|
const url = requestCurrentURL(request);
|
|
38924
38957
|
const agent = fetchParams.controller.dispatcher;
|
|
38925
|
-
return new Promise((
|
|
38958
|
+
return new Promise((resolve17, reject) => agent.dispatch(
|
|
38926
38959
|
{
|
|
38927
38960
|
path: url.pathname + url.search,
|
|
38928
38961
|
origin: url.origin,
|
|
@@ -38998,7 +39031,7 @@ var require_fetch = __commonJS({
|
|
|
38998
39031
|
}
|
|
38999
39032
|
}
|
|
39000
39033
|
}
|
|
39001
|
-
|
|
39034
|
+
resolve17({
|
|
39002
39035
|
status,
|
|
39003
39036
|
statusText,
|
|
39004
39037
|
headersList: headers[kHeadersList],
|
|
@@ -39041,7 +39074,7 @@ var require_fetch = __commonJS({
|
|
|
39041
39074
|
const val = headersList[n + 1].toString("latin1");
|
|
39042
39075
|
headers[kHeadersList].append(key, val);
|
|
39043
39076
|
}
|
|
39044
|
-
|
|
39077
|
+
resolve17({
|
|
39045
39078
|
status,
|
|
39046
39079
|
statusText: STATUS_CODES[status],
|
|
39047
39080
|
headersList: headers[kHeadersList],
|
|
@@ -40602,8 +40635,8 @@ var require_util6 = __commonJS({
|
|
|
40602
40635
|
}
|
|
40603
40636
|
}
|
|
40604
40637
|
}
|
|
40605
|
-
function validateCookiePath(
|
|
40606
|
-
for (const char of
|
|
40638
|
+
function validateCookiePath(path30) {
|
|
40639
|
+
for (const char of path30) {
|
|
40607
40640
|
const code = char.charCodeAt(0);
|
|
40608
40641
|
if (code < 33 || char === ";") {
|
|
40609
40642
|
throw new Error("Invalid cookie path");
|
|
@@ -41400,9 +41433,9 @@ var require_connection = __commonJS({
|
|
|
41400
41433
|
channels.open = diagnosticsChannel.channel("undici:websocket:open");
|
|
41401
41434
|
channels.close = diagnosticsChannel.channel("undici:websocket:close");
|
|
41402
41435
|
channels.socketError = diagnosticsChannel.channel("undici:websocket:socket_error");
|
|
41403
|
-
var
|
|
41436
|
+
var crypto4;
|
|
41404
41437
|
try {
|
|
41405
|
-
|
|
41438
|
+
crypto4 = require("crypto");
|
|
41406
41439
|
} catch {
|
|
41407
41440
|
}
|
|
41408
41441
|
function establishWebSocketConnection(url, protocols, ws, onEstablish, options) {
|
|
@@ -41421,7 +41454,7 @@ var require_connection = __commonJS({
|
|
|
41421
41454
|
const headersList = new Headers(options.headers)[kHeadersList];
|
|
41422
41455
|
request.headersList = headersList;
|
|
41423
41456
|
}
|
|
41424
|
-
const keyValue =
|
|
41457
|
+
const keyValue = crypto4.randomBytes(16).toString("base64");
|
|
41425
41458
|
request.headersList.append("sec-websocket-key", keyValue);
|
|
41426
41459
|
request.headersList.append("sec-websocket-version", "13");
|
|
41427
41460
|
for (const protocol of protocols) {
|
|
@@ -41450,7 +41483,7 @@ var require_connection = __commonJS({
|
|
|
41450
41483
|
return;
|
|
41451
41484
|
}
|
|
41452
41485
|
const secWSAccept = response.headersList.get("Sec-WebSocket-Accept");
|
|
41453
|
-
const digest =
|
|
41486
|
+
const digest = crypto4.createHash("sha1").update(keyValue + uid).digest("base64");
|
|
41454
41487
|
if (secWSAccept !== digest) {
|
|
41455
41488
|
failWebsocketConnection(ws, "Incorrect hash received in Sec-WebSocket-Accept header.");
|
|
41456
41489
|
return;
|
|
@@ -41530,9 +41563,9 @@ var require_frame = __commonJS({
|
|
|
41530
41563
|
"node_modules/undici/lib/websocket/frame.js"(exports2, module2) {
|
|
41531
41564
|
"use strict";
|
|
41532
41565
|
var { maxUnsigned16Bit } = require_constants5();
|
|
41533
|
-
var
|
|
41566
|
+
var crypto4;
|
|
41534
41567
|
try {
|
|
41535
|
-
|
|
41568
|
+
crypto4 = require("crypto");
|
|
41536
41569
|
} catch {
|
|
41537
41570
|
}
|
|
41538
41571
|
var WebsocketFrameSend = class {
|
|
@@ -41541,7 +41574,7 @@ var require_frame = __commonJS({
|
|
|
41541
41574
|
*/
|
|
41542
41575
|
constructor(data) {
|
|
41543
41576
|
this.frameData = data;
|
|
41544
|
-
this.maskKey =
|
|
41577
|
+
this.maskKey = crypto4.randomBytes(4);
|
|
41545
41578
|
}
|
|
41546
41579
|
createFrame(opcode) {
|
|
41547
41580
|
const bodyLength = this.frameData?.byteLength ?? 0;
|
|
@@ -42283,11 +42316,11 @@ var require_undici = __commonJS({
|
|
|
42283
42316
|
if (typeof opts.path !== "string") {
|
|
42284
42317
|
throw new InvalidArgumentError("invalid opts.path");
|
|
42285
42318
|
}
|
|
42286
|
-
let
|
|
42319
|
+
let path30 = opts.path;
|
|
42287
42320
|
if (!opts.path.startsWith("/")) {
|
|
42288
|
-
|
|
42321
|
+
path30 = `/${path30}`;
|
|
42289
42322
|
}
|
|
42290
|
-
url = new URL(util.parseOrigin(url).origin +
|
|
42323
|
+
url = new URL(util.parseOrigin(url).origin + path30);
|
|
42291
42324
|
} else {
|
|
42292
42325
|
if (!opts) {
|
|
42293
42326
|
opts = typeof url === "object" ? url : {};
|
|
@@ -42582,7 +42615,8 @@ var init_mcp_check_provider = __esm({
|
|
|
42582
42615
|
fileCount: prInfo.files.length,
|
|
42583
42616
|
outputs: this.buildOutputContext(dependencyResults),
|
|
42584
42617
|
args: sessionInfo?.args || {},
|
|
42585
|
-
env: this.getSafeEnvironmentVariables()
|
|
42618
|
+
env: this.getSafeEnvironmentVariables(),
|
|
42619
|
+
inputs: config.workflowInputs || sessionInfo?.workflowInputs || {}
|
|
42586
42620
|
};
|
|
42587
42621
|
let methodArgs = cfg.methodArgs || {};
|
|
42588
42622
|
if (cfg.argsTransform) {
|
|
@@ -42835,7 +42869,7 @@ var init_mcp_check_provider = __esm({
|
|
|
42835
42869
|
logger.warn(
|
|
42836
42870
|
`MCP ${transportName} failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${delay}ms: ${error instanceof Error ? error.message : String(error)}`
|
|
42837
42871
|
);
|
|
42838
|
-
await new Promise((
|
|
42872
|
+
await new Promise((resolve17) => setTimeout(resolve17, delay));
|
|
42839
42873
|
attempt += 1;
|
|
42840
42874
|
} finally {
|
|
42841
42875
|
try {
|
|
@@ -43117,7 +43151,7 @@ async function acquirePromptLock() {
|
|
|
43117
43151
|
activePrompt = true;
|
|
43118
43152
|
return;
|
|
43119
43153
|
}
|
|
43120
|
-
await new Promise((
|
|
43154
|
+
await new Promise((resolve17) => waiters.push(resolve17));
|
|
43121
43155
|
activePrompt = true;
|
|
43122
43156
|
}
|
|
43123
43157
|
function releasePromptLock() {
|
|
@@ -43127,7 +43161,7 @@ function releasePromptLock() {
|
|
|
43127
43161
|
}
|
|
43128
43162
|
async function interactivePrompt(options) {
|
|
43129
43163
|
await acquirePromptLock();
|
|
43130
|
-
return new Promise((
|
|
43164
|
+
return new Promise((resolve17, reject) => {
|
|
43131
43165
|
const dbg = process.env.VISOR_DEBUG === "true";
|
|
43132
43166
|
try {
|
|
43133
43167
|
if (dbg) {
|
|
@@ -43214,12 +43248,12 @@ async function interactivePrompt(options) {
|
|
|
43214
43248
|
};
|
|
43215
43249
|
const finish = (value) => {
|
|
43216
43250
|
cleanup();
|
|
43217
|
-
|
|
43251
|
+
resolve17(value);
|
|
43218
43252
|
};
|
|
43219
43253
|
if (options.timeout && options.timeout > 0) {
|
|
43220
43254
|
timeoutId = setTimeout(() => {
|
|
43221
43255
|
cleanup();
|
|
43222
|
-
if (defaultValue !== void 0) return
|
|
43256
|
+
if (defaultValue !== void 0) return resolve17(defaultValue);
|
|
43223
43257
|
return reject(new Error("Input timeout"));
|
|
43224
43258
|
}, options.timeout);
|
|
43225
43259
|
}
|
|
@@ -43351,7 +43385,7 @@ async function interactivePrompt(options) {
|
|
|
43351
43385
|
});
|
|
43352
43386
|
}
|
|
43353
43387
|
async function simplePrompt(prompt) {
|
|
43354
|
-
return new Promise((
|
|
43388
|
+
return new Promise((resolve17) => {
|
|
43355
43389
|
const rl = readline.createInterface({
|
|
43356
43390
|
input: process.stdin,
|
|
43357
43391
|
output: process.stdout
|
|
@@ -43367,7 +43401,7 @@ async function simplePrompt(prompt) {
|
|
|
43367
43401
|
rl.question(`${prompt}
|
|
43368
43402
|
> `, (answer) => {
|
|
43369
43403
|
rl.close();
|
|
43370
|
-
|
|
43404
|
+
resolve17(answer.trim());
|
|
43371
43405
|
});
|
|
43372
43406
|
});
|
|
43373
43407
|
}
|
|
@@ -43535,7 +43569,7 @@ function isStdinAvailable() {
|
|
|
43535
43569
|
return !process.stdin.isTTY;
|
|
43536
43570
|
}
|
|
43537
43571
|
async function readStdin(timeout, maxSize = 1024 * 1024) {
|
|
43538
|
-
return new Promise((
|
|
43572
|
+
return new Promise((resolve17, reject) => {
|
|
43539
43573
|
let data = "";
|
|
43540
43574
|
let timeoutId;
|
|
43541
43575
|
if (timeout) {
|
|
@@ -43562,7 +43596,7 @@ async function readStdin(timeout, maxSize = 1024 * 1024) {
|
|
|
43562
43596
|
};
|
|
43563
43597
|
const onEnd = () => {
|
|
43564
43598
|
cleanup();
|
|
43565
|
-
|
|
43599
|
+
resolve17(data.trim());
|
|
43566
43600
|
};
|
|
43567
43601
|
const onError = (err) => {
|
|
43568
43602
|
cleanup();
|
|
@@ -47594,23 +47628,23 @@ __export(renderer_schema_exports, {
|
|
|
47594
47628
|
});
|
|
47595
47629
|
async function loadRendererSchema(name) {
|
|
47596
47630
|
try {
|
|
47597
|
-
const
|
|
47598
|
-
const
|
|
47631
|
+
const fs26 = await import("fs/promises");
|
|
47632
|
+
const path30 = await import("path");
|
|
47599
47633
|
const sanitized = String(name).replace(/[^a-zA-Z0-9-]/g, "");
|
|
47600
47634
|
if (!sanitized) return void 0;
|
|
47601
47635
|
const candidates = [
|
|
47602
47636
|
// When bundled with ncc, __dirname is dist/ and output/ is at dist/output/
|
|
47603
|
-
|
|
47637
|
+
path30.join(__dirname, "output", sanitized, "schema.json"),
|
|
47604
47638
|
// When running from source, __dirname is src/state-machine/dispatch/ and output/ is at output/
|
|
47605
|
-
|
|
47639
|
+
path30.join(__dirname, "..", "..", "output", sanitized, "schema.json"),
|
|
47606
47640
|
// When running from a checkout with output/ folder copied to CWD
|
|
47607
|
-
|
|
47641
|
+
path30.join(process.cwd(), "output", sanitized, "schema.json"),
|
|
47608
47642
|
// Fallback: cwd/dist/output/
|
|
47609
|
-
|
|
47643
|
+
path30.join(process.cwd(), "dist", "output", sanitized, "schema.json")
|
|
47610
47644
|
];
|
|
47611
47645
|
for (const p of candidates) {
|
|
47612
47646
|
try {
|
|
47613
|
-
const raw = await
|
|
47647
|
+
const raw = await fs26.readFile(p, "utf-8");
|
|
47614
47648
|
return JSON.parse(raw);
|
|
47615
47649
|
} catch {
|
|
47616
47650
|
}
|
|
@@ -50029,8 +50063,8 @@ function updateStats2(results, state, isForEachIteration = false) {
|
|
|
50029
50063
|
async function renderTemplateContent2(checkId, checkConfig, reviewSummary) {
|
|
50030
50064
|
try {
|
|
50031
50065
|
const { createExtendedLiquid: createExtendedLiquid2 } = await Promise.resolve().then(() => (init_liquid_extensions(), liquid_extensions_exports));
|
|
50032
|
-
const
|
|
50033
|
-
const
|
|
50066
|
+
const fs26 = await import("fs/promises");
|
|
50067
|
+
const path30 = await import("path");
|
|
50034
50068
|
const schemaRaw = checkConfig.schema || "plain";
|
|
50035
50069
|
const schema = typeof schemaRaw === "string" && !schemaRaw.includes("{{") && !schemaRaw.includes("{%") ? schemaRaw : typeof schemaRaw === "object" ? "code-review" : "plain";
|
|
50036
50070
|
let templateContent;
|
|
@@ -50039,27 +50073,27 @@ async function renderTemplateContent2(checkId, checkConfig, reviewSummary) {
|
|
|
50039
50073
|
logger.debug(`[LevelDispatch] Using inline template for ${checkId}`);
|
|
50040
50074
|
} else if (checkConfig.template && checkConfig.template.file) {
|
|
50041
50075
|
const file = String(checkConfig.template.file);
|
|
50042
|
-
const resolved =
|
|
50043
|
-
templateContent = await
|
|
50076
|
+
const resolved = path30.resolve(process.cwd(), file);
|
|
50077
|
+
templateContent = await fs26.readFile(resolved, "utf-8");
|
|
50044
50078
|
logger.debug(`[LevelDispatch] Using template file for ${checkId}: ${resolved}`);
|
|
50045
50079
|
} else if (schema && schema !== "plain") {
|
|
50046
50080
|
const sanitized = String(schema).replace(/[^a-zA-Z0-9-]/g, "");
|
|
50047
50081
|
if (sanitized) {
|
|
50048
50082
|
const candidatePaths = [
|
|
50049
|
-
|
|
50083
|
+
path30.join(__dirname, "output", sanitized, "template.liquid"),
|
|
50050
50084
|
// bundled: dist/output/
|
|
50051
|
-
|
|
50085
|
+
path30.join(__dirname, "..", "..", "output", sanitized, "template.liquid"),
|
|
50052
50086
|
// source (from state-machine/states)
|
|
50053
|
-
|
|
50087
|
+
path30.join(__dirname, "..", "..", "..", "output", sanitized, "template.liquid"),
|
|
50054
50088
|
// source (alternate)
|
|
50055
|
-
|
|
50089
|
+
path30.join(process.cwd(), "output", sanitized, "template.liquid"),
|
|
50056
50090
|
// fallback: cwd/output/
|
|
50057
|
-
|
|
50091
|
+
path30.join(process.cwd(), "dist", "output", sanitized, "template.liquid")
|
|
50058
50092
|
// fallback: cwd/dist/output/
|
|
50059
50093
|
];
|
|
50060
50094
|
for (const p of candidatePaths) {
|
|
50061
50095
|
try {
|
|
50062
|
-
templateContent = await
|
|
50096
|
+
templateContent = await fs26.readFile(p, "utf-8");
|
|
50063
50097
|
if (templateContent) {
|
|
50064
50098
|
logger.debug(`[LevelDispatch] Using schema template for ${checkId}: ${p}`);
|
|
50065
50099
|
break;
|
|
@@ -50488,8 +50522,8 @@ var init_runner = __esm({
|
|
|
50488
50522
|
stats.sort((a, b) => (b.errorMessage ? 1 : 0) - (a.errorMessage ? 1 : 0));
|
|
50489
50523
|
const results = this.aggregateResultsFromJournal();
|
|
50490
50524
|
let totalDuration = 0;
|
|
50491
|
-
for (const
|
|
50492
|
-
totalDuration = Math.max(totalDuration,
|
|
50525
|
+
for (const stat2 of stats) {
|
|
50526
|
+
totalDuration = Math.max(totalDuration, stat2.totalDuration);
|
|
50493
50527
|
}
|
|
50494
50528
|
try {
|
|
50495
50529
|
for (const s of stats) {
|
|
@@ -51155,13 +51189,275 @@ var init_cache_volume_manager = __esm({
|
|
|
51155
51189
|
}
|
|
51156
51190
|
});
|
|
51157
51191
|
|
|
51192
|
+
// src/sandbox/bubblewrap-sandbox.ts
|
|
51193
|
+
var bubblewrap_sandbox_exports = {};
|
|
51194
|
+
__export(bubblewrap_sandbox_exports, {
|
|
51195
|
+
BubblewrapSandbox: () => BubblewrapSandbox
|
|
51196
|
+
});
|
|
51197
|
+
var import_util5, import_child_process6, import_fs6, import_path10, execFileAsync4, EXEC_MAX_BUFFER4, BubblewrapSandbox;
|
|
51198
|
+
var init_bubblewrap_sandbox = __esm({
|
|
51199
|
+
"src/sandbox/bubblewrap-sandbox.ts"() {
|
|
51200
|
+
"use strict";
|
|
51201
|
+
import_util5 = require("util");
|
|
51202
|
+
import_child_process6 = require("child_process");
|
|
51203
|
+
import_fs6 = require("fs");
|
|
51204
|
+
import_path10 = require("path");
|
|
51205
|
+
init_logger();
|
|
51206
|
+
init_sandbox_telemetry();
|
|
51207
|
+
execFileAsync4 = (0, import_util5.promisify)(import_child_process6.execFile);
|
|
51208
|
+
EXEC_MAX_BUFFER4 = 50 * 1024 * 1024;
|
|
51209
|
+
BubblewrapSandbox = class {
|
|
51210
|
+
name;
|
|
51211
|
+
config;
|
|
51212
|
+
repoPath;
|
|
51213
|
+
constructor(name, config, repoPath) {
|
|
51214
|
+
this.name = name;
|
|
51215
|
+
this.config = config;
|
|
51216
|
+
this.repoPath = (0, import_path10.resolve)(repoPath);
|
|
51217
|
+
}
|
|
51218
|
+
/**
|
|
51219
|
+
* Check if bwrap binary is available on the system.
|
|
51220
|
+
*/
|
|
51221
|
+
static async isAvailable() {
|
|
51222
|
+
try {
|
|
51223
|
+
await execFileAsync4("which", ["bwrap"], { timeout: 5e3 });
|
|
51224
|
+
return true;
|
|
51225
|
+
} catch {
|
|
51226
|
+
return false;
|
|
51227
|
+
}
|
|
51228
|
+
}
|
|
51229
|
+
/**
|
|
51230
|
+
* Execute a command inside a bubblewrap sandbox.
|
|
51231
|
+
* Each exec creates a fresh namespace — no persistent container.
|
|
51232
|
+
*/
|
|
51233
|
+
async exec(options) {
|
|
51234
|
+
const args = this.buildArgs(options);
|
|
51235
|
+
args.push("--", "/bin/sh", "-c", options.command);
|
|
51236
|
+
logger.debug(
|
|
51237
|
+
`[BubblewrapSandbox] Executing in sandbox '${this.name}': ${options.command.slice(0, 100)}`
|
|
51238
|
+
);
|
|
51239
|
+
try {
|
|
51240
|
+
const { stdout, stderr } = await execFileAsync4("bwrap", args, {
|
|
51241
|
+
maxBuffer: options.maxBuffer || EXEC_MAX_BUFFER4,
|
|
51242
|
+
timeout: options.timeoutMs || 6e5
|
|
51243
|
+
});
|
|
51244
|
+
addEvent2("visor.sandbox.bwrap.exec", {
|
|
51245
|
+
"visor.sandbox.name": this.name,
|
|
51246
|
+
"visor.sandbox.exit_code": 0
|
|
51247
|
+
});
|
|
51248
|
+
return { stdout, stderr, exitCode: 0 };
|
|
51249
|
+
} catch (err) {
|
|
51250
|
+
const execErr = err;
|
|
51251
|
+
const exitCode = typeof execErr.code === "number" ? execErr.code : 1;
|
|
51252
|
+
addEvent2("visor.sandbox.bwrap.exec", {
|
|
51253
|
+
"visor.sandbox.name": this.name,
|
|
51254
|
+
"visor.sandbox.exit_code": exitCode
|
|
51255
|
+
});
|
|
51256
|
+
return {
|
|
51257
|
+
stdout: execErr.stdout || "",
|
|
51258
|
+
stderr: execErr.stderr || "",
|
|
51259
|
+
exitCode
|
|
51260
|
+
};
|
|
51261
|
+
}
|
|
51262
|
+
}
|
|
51263
|
+
/**
|
|
51264
|
+
* No-op: bubblewrap processes are ephemeral (no persistent container to stop).
|
|
51265
|
+
*/
|
|
51266
|
+
async stop() {
|
|
51267
|
+
}
|
|
51268
|
+
/**
|
|
51269
|
+
* Build the bwrap command-line arguments.
|
|
51270
|
+
*/
|
|
51271
|
+
buildArgs(options) {
|
|
51272
|
+
const workdir = this.config.workdir || "/workspace";
|
|
51273
|
+
const args = [];
|
|
51274
|
+
args.push("--ro-bind", "/usr", "/usr");
|
|
51275
|
+
args.push("--ro-bind", "/bin", "/bin");
|
|
51276
|
+
if ((0, import_fs6.existsSync)("/lib")) {
|
|
51277
|
+
args.push("--ro-bind", "/lib", "/lib");
|
|
51278
|
+
}
|
|
51279
|
+
if ((0, import_fs6.existsSync)("/lib64")) {
|
|
51280
|
+
args.push("--ro-bind", "/lib64", "/lib64");
|
|
51281
|
+
}
|
|
51282
|
+
if ((0, import_fs6.existsSync)("/etc/resolv.conf")) {
|
|
51283
|
+
args.push("--ro-bind", "/etc/resolv.conf", "/etc/resolv.conf");
|
|
51284
|
+
}
|
|
51285
|
+
if ((0, import_fs6.existsSync)("/etc/ssl")) {
|
|
51286
|
+
args.push("--ro-bind", "/etc/ssl", "/etc/ssl");
|
|
51287
|
+
}
|
|
51288
|
+
args.push("--dev", "/dev");
|
|
51289
|
+
args.push("--proc", "/proc");
|
|
51290
|
+
args.push("--tmpfs", "/tmp");
|
|
51291
|
+
args.push("--tmpfs", "/root");
|
|
51292
|
+
if (this.config.read_only) {
|
|
51293
|
+
args.push("--ro-bind", this.repoPath, workdir);
|
|
51294
|
+
} else {
|
|
51295
|
+
args.push("--bind", this.repoPath, workdir);
|
|
51296
|
+
}
|
|
51297
|
+
args.push("--chdir", workdir);
|
|
51298
|
+
args.push("--unshare-pid");
|
|
51299
|
+
args.push("--new-session");
|
|
51300
|
+
args.push("--die-with-parent");
|
|
51301
|
+
if (this.config.network === false) {
|
|
51302
|
+
args.push("--unshare-net");
|
|
51303
|
+
}
|
|
51304
|
+
args.push("--clearenv");
|
|
51305
|
+
for (const [key, value] of Object.entries(options.env)) {
|
|
51306
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key)) {
|
|
51307
|
+
throw new Error(`Invalid environment variable name: '${key}'`);
|
|
51308
|
+
}
|
|
51309
|
+
args.push("--setenv", key, value);
|
|
51310
|
+
}
|
|
51311
|
+
return args;
|
|
51312
|
+
}
|
|
51313
|
+
};
|
|
51314
|
+
}
|
|
51315
|
+
});
|
|
51316
|
+
|
|
51317
|
+
// src/sandbox/seatbelt-sandbox.ts
|
|
51318
|
+
var seatbelt_sandbox_exports = {};
|
|
51319
|
+
__export(seatbelt_sandbox_exports, {
|
|
51320
|
+
SeatbeltSandbox: () => SeatbeltSandbox
|
|
51321
|
+
});
|
|
51322
|
+
var import_util6, import_child_process7, import_path11, import_fs7, execFileAsync5, EXEC_MAX_BUFFER5, SeatbeltSandbox;
|
|
51323
|
+
var init_seatbelt_sandbox = __esm({
|
|
51324
|
+
"src/sandbox/seatbelt-sandbox.ts"() {
|
|
51325
|
+
"use strict";
|
|
51326
|
+
import_util6 = require("util");
|
|
51327
|
+
import_child_process7 = require("child_process");
|
|
51328
|
+
import_path11 = require("path");
|
|
51329
|
+
import_fs7 = require("fs");
|
|
51330
|
+
init_logger();
|
|
51331
|
+
init_sandbox_telemetry();
|
|
51332
|
+
execFileAsync5 = (0, import_util6.promisify)(import_child_process7.execFile);
|
|
51333
|
+
EXEC_MAX_BUFFER5 = 50 * 1024 * 1024;
|
|
51334
|
+
SeatbeltSandbox = class {
|
|
51335
|
+
name;
|
|
51336
|
+
config;
|
|
51337
|
+
repoPath;
|
|
51338
|
+
constructor(name, config, repoPath) {
|
|
51339
|
+
this.name = name;
|
|
51340
|
+
this.config = config;
|
|
51341
|
+
this.repoPath = (0, import_fs7.realpathSync)((0, import_path11.resolve)(repoPath));
|
|
51342
|
+
}
|
|
51343
|
+
/**
|
|
51344
|
+
* Check if sandbox-exec binary is available on the system.
|
|
51345
|
+
*/
|
|
51346
|
+
static async isAvailable() {
|
|
51347
|
+
try {
|
|
51348
|
+
await execFileAsync5("which", ["sandbox-exec"], { timeout: 5e3 });
|
|
51349
|
+
return true;
|
|
51350
|
+
} catch {
|
|
51351
|
+
return false;
|
|
51352
|
+
}
|
|
51353
|
+
}
|
|
51354
|
+
/**
|
|
51355
|
+
* Execute a command inside a macOS seatbelt sandbox.
|
|
51356
|
+
* Each exec creates a fresh sandbox process — no persistent container.
|
|
51357
|
+
*/
|
|
51358
|
+
async exec(options) {
|
|
51359
|
+
const profile = this.buildProfile();
|
|
51360
|
+
for (const key of Object.keys(options.env)) {
|
|
51361
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(key)) {
|
|
51362
|
+
throw new Error(`Invalid environment variable name: '${key}'`);
|
|
51363
|
+
}
|
|
51364
|
+
}
|
|
51365
|
+
const args = ["-p", profile];
|
|
51366
|
+
args.push("/usr/bin/env", "-i");
|
|
51367
|
+
for (const [key, value] of Object.entries(options.env)) {
|
|
51368
|
+
args.push(`${key}=${value}`);
|
|
51369
|
+
}
|
|
51370
|
+
args.push("/bin/sh", "-c", options.command);
|
|
51371
|
+
logger.debug(
|
|
51372
|
+
`[SeatbeltSandbox] Executing in sandbox '${this.name}': ${options.command.slice(0, 100)}`
|
|
51373
|
+
);
|
|
51374
|
+
try {
|
|
51375
|
+
const { stdout, stderr } = await execFileAsync5("sandbox-exec", args, {
|
|
51376
|
+
maxBuffer: options.maxBuffer || EXEC_MAX_BUFFER5,
|
|
51377
|
+
timeout: options.timeoutMs || 6e5,
|
|
51378
|
+
cwd: this.repoPath
|
|
51379
|
+
});
|
|
51380
|
+
addEvent2("visor.sandbox.seatbelt.exec", {
|
|
51381
|
+
"visor.sandbox.name": this.name,
|
|
51382
|
+
"visor.sandbox.exit_code": 0
|
|
51383
|
+
});
|
|
51384
|
+
return { stdout, stderr, exitCode: 0 };
|
|
51385
|
+
} catch (err) {
|
|
51386
|
+
const execErr = err;
|
|
51387
|
+
const exitCode = typeof execErr.code === "number" ? execErr.code : 1;
|
|
51388
|
+
addEvent2("visor.sandbox.seatbelt.exec", {
|
|
51389
|
+
"visor.sandbox.name": this.name,
|
|
51390
|
+
"visor.sandbox.exit_code": exitCode
|
|
51391
|
+
});
|
|
51392
|
+
return {
|
|
51393
|
+
stdout: execErr.stdout || "",
|
|
51394
|
+
stderr: execErr.stderr || "",
|
|
51395
|
+
exitCode
|
|
51396
|
+
};
|
|
51397
|
+
}
|
|
51398
|
+
}
|
|
51399
|
+
/**
|
|
51400
|
+
* No-op: sandbox-exec processes are ephemeral (no persistent container to stop).
|
|
51401
|
+
*/
|
|
51402
|
+
async stop() {
|
|
51403
|
+
}
|
|
51404
|
+
/**
|
|
51405
|
+
* Escape a path for use inside an SBPL profile string.
|
|
51406
|
+
* Escapes backslashes and double quotes.
|
|
51407
|
+
*/
|
|
51408
|
+
escapePath(p) {
|
|
51409
|
+
return p.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
51410
|
+
}
|
|
51411
|
+
/**
|
|
51412
|
+
* Build the SBPL (Seatbelt Profile Language) profile string.
|
|
51413
|
+
*/
|
|
51414
|
+
buildProfile() {
|
|
51415
|
+
const repoPath = this.escapePath(this.repoPath);
|
|
51416
|
+
const lines = [];
|
|
51417
|
+
lines.push("(version 1)");
|
|
51418
|
+
lines.push("(deny default)");
|
|
51419
|
+
lines.push("(allow process-exec)");
|
|
51420
|
+
lines.push("(allow process-fork)");
|
|
51421
|
+
lines.push("(allow file-read*");
|
|
51422
|
+
lines.push(' (literal "/")');
|
|
51423
|
+
lines.push(' (subpath "/usr")');
|
|
51424
|
+
lines.push(' (subpath "/bin")');
|
|
51425
|
+
lines.push(' (subpath "/sbin")');
|
|
51426
|
+
lines.push(' (subpath "/Library")');
|
|
51427
|
+
lines.push(' (subpath "/System")');
|
|
51428
|
+
lines.push(' (subpath "/private")');
|
|
51429
|
+
lines.push(' (subpath "/var")');
|
|
51430
|
+
lines.push(' (subpath "/etc")');
|
|
51431
|
+
lines.push(' (subpath "/dev")');
|
|
51432
|
+
lines.push(' (subpath "/tmp"))');
|
|
51433
|
+
lines.push("(allow file-write*");
|
|
51434
|
+
lines.push(' (subpath "/tmp")');
|
|
51435
|
+
lines.push(' (subpath "/private/tmp")');
|
|
51436
|
+
lines.push(' (subpath "/dev"))');
|
|
51437
|
+
lines.push('(allow file-write* (regex #"/private/var/folders/.*/T/xcrun_db"))');
|
|
51438
|
+
lines.push(`(allow file-read* (subpath "${repoPath}"))`);
|
|
51439
|
+
if (!this.config.read_only) {
|
|
51440
|
+
lines.push(`(allow file-write* (subpath "${repoPath}"))`);
|
|
51441
|
+
}
|
|
51442
|
+
if (this.config.network !== false) {
|
|
51443
|
+
lines.push("(allow network*)");
|
|
51444
|
+
}
|
|
51445
|
+
lines.push("(allow sysctl-read)");
|
|
51446
|
+
lines.push("(allow mach-lookup)");
|
|
51447
|
+
lines.push("(allow signal)");
|
|
51448
|
+
return lines.join("\n");
|
|
51449
|
+
}
|
|
51450
|
+
};
|
|
51451
|
+
}
|
|
51452
|
+
});
|
|
51453
|
+
|
|
51158
51454
|
// src/sandbox/sandbox-manager.ts
|
|
51159
|
-
var
|
|
51455
|
+
var import_path12, import_fs8, SandboxManager;
|
|
51160
51456
|
var init_sandbox_manager = __esm({
|
|
51161
51457
|
"src/sandbox/sandbox-manager.ts"() {
|
|
51162
51458
|
"use strict";
|
|
51163
|
-
|
|
51164
|
-
|
|
51459
|
+
import_path12 = require("path");
|
|
51460
|
+
import_fs8 = require("fs");
|
|
51165
51461
|
init_docker_image_sandbox();
|
|
51166
51462
|
init_docker_compose_sandbox();
|
|
51167
51463
|
init_cache_volume_manager();
|
|
@@ -51180,10 +51476,10 @@ var init_sandbox_manager = __esm({
|
|
|
51180
51476
|
}
|
|
51181
51477
|
constructor(sandboxDefs, repoPath, gitBranch) {
|
|
51182
51478
|
this.sandboxDefs = sandboxDefs;
|
|
51183
|
-
this.repoPath = (0,
|
|
51479
|
+
this.repoPath = (0, import_path12.resolve)(repoPath);
|
|
51184
51480
|
this.gitBranch = gitBranch;
|
|
51185
51481
|
this.cacheManager = new CacheVolumeManager();
|
|
51186
|
-
this.visorDistPath = (0,
|
|
51482
|
+
this.visorDistPath = (0, import_fs8.existsSync)((0, import_path12.join)(__dirname, "index.js")) ? __dirname : (0, import_path12.resolve)((0, import_path12.dirname)(__dirname));
|
|
51187
51483
|
}
|
|
51188
51484
|
/**
|
|
51189
51485
|
* Resolve which sandbox a check should use.
|
|
@@ -51213,6 +51509,18 @@ var init_sandbox_manager = __esm({
|
|
|
51213
51509
|
throw new Error(`Sandbox '${name}' is not defined`);
|
|
51214
51510
|
}
|
|
51215
51511
|
const mode = config.compose ? "compose" : "image";
|
|
51512
|
+
if (config.engine === "bubblewrap") {
|
|
51513
|
+
const { BubblewrapSandbox: BubblewrapSandbox2 } = (init_bubblewrap_sandbox(), __toCommonJS(bubblewrap_sandbox_exports));
|
|
51514
|
+
const instance = new BubblewrapSandbox2(name, config, this.repoPath);
|
|
51515
|
+
this.instances.set(name, instance);
|
|
51516
|
+
return instance;
|
|
51517
|
+
}
|
|
51518
|
+
if (config.engine === "seatbelt") {
|
|
51519
|
+
const { SeatbeltSandbox: SeatbeltSandbox2 } = (init_seatbelt_sandbox(), __toCommonJS(seatbelt_sandbox_exports));
|
|
51520
|
+
const instance = new SeatbeltSandbox2(name, config, this.repoPath);
|
|
51521
|
+
this.instances.set(name, instance);
|
|
51522
|
+
return instance;
|
|
51523
|
+
}
|
|
51216
51524
|
return withActiveSpan2(
|
|
51217
51525
|
"visor.sandbox.start",
|
|
51218
51526
|
{
|
|
@@ -51860,8 +52168,8 @@ var init_workspace_manager = __esm({
|
|
|
51860
52168
|
);
|
|
51861
52169
|
if (this.cleanupRequested && this.activeOperations === 0) {
|
|
51862
52170
|
logger.debug(`[Workspace] All references released, proceeding with deferred cleanup`);
|
|
51863
|
-
for (const
|
|
51864
|
-
|
|
52171
|
+
for (const resolve17 of this.cleanupResolvers) {
|
|
52172
|
+
resolve17();
|
|
51865
52173
|
}
|
|
51866
52174
|
this.cleanupResolvers = [];
|
|
51867
52175
|
}
|
|
@@ -51911,13 +52219,34 @@ var init_workspace_manager = __esm({
|
|
|
51911
52219
|
const mainProjectPath = path23.join(this.workspacePath, mainProjectName);
|
|
51912
52220
|
const isGitRepo = await this.isGitRepository(this.originalPath);
|
|
51913
52221
|
if (isGitRepo) {
|
|
51914
|
-
await this.
|
|
52222
|
+
const exists = await this.pathExists(mainProjectPath);
|
|
52223
|
+
if (exists) {
|
|
52224
|
+
logger.info(`[Workspace] Reusing existing main project worktree: ${mainProjectPath}`);
|
|
52225
|
+
const isValid = await this.isGitRepository(mainProjectPath);
|
|
52226
|
+
if (!isValid) {
|
|
52227
|
+
logger.warn(`[Workspace] Existing path is not a valid git dir, recreating`);
|
|
52228
|
+
await fsp2.rm(mainProjectPath, { recursive: true, force: true });
|
|
52229
|
+
try {
|
|
52230
|
+
await commandExecutor.execute(
|
|
52231
|
+
`git -C ${shellEscape(this.originalPath)} worktree prune`,
|
|
52232
|
+
{ timeout: 1e4 }
|
|
52233
|
+
);
|
|
52234
|
+
} catch {
|
|
52235
|
+
}
|
|
52236
|
+
await this.createMainProjectWorktree(mainProjectPath);
|
|
52237
|
+
}
|
|
52238
|
+
} else {
|
|
52239
|
+
await this.createMainProjectWorktree(mainProjectPath);
|
|
52240
|
+
}
|
|
51915
52241
|
} else {
|
|
51916
52242
|
logger.debug(`Original path is not a git repo, creating symlink`);
|
|
51917
|
-
|
|
51918
|
-
|
|
51919
|
-
|
|
51920
|
-
|
|
52243
|
+
const exists = await this.pathExists(mainProjectPath);
|
|
52244
|
+
if (!exists) {
|
|
52245
|
+
try {
|
|
52246
|
+
await fsp2.symlink(this.originalPath, mainProjectPath);
|
|
52247
|
+
} catch (error) {
|
|
52248
|
+
throw new Error(`Failed to create symlink for main project: ${error}`);
|
|
52249
|
+
}
|
|
51921
52250
|
}
|
|
51922
52251
|
}
|
|
51923
52252
|
this.registerCleanupHandlers();
|
|
@@ -51977,6 +52306,15 @@ var init_workspace_manager = __esm({
|
|
|
51977
52306
|
* @param timeout Maximum time to wait for active operations (default: 60s)
|
|
51978
52307
|
*/
|
|
51979
52308
|
async cleanup(timeout = 6e4) {
|
|
52309
|
+
if (!this.config.cleanupOnExit) {
|
|
52310
|
+
logger.debug(`[Workspace] Skipping cleanup (cleanupOnExit=false): ${this.workspacePath}`);
|
|
52311
|
+
_WorkspaceManager.instances.delete(this.sessionId);
|
|
52312
|
+
this.initialized = false;
|
|
52313
|
+
this.mainProjectInfo = null;
|
|
52314
|
+
this.projects.clear();
|
|
52315
|
+
this.usedNames.clear();
|
|
52316
|
+
return;
|
|
52317
|
+
}
|
|
51980
52318
|
logger.info(
|
|
51981
52319
|
`Cleaning up workspace: ${this.workspacePath} (active operations: ${this.activeOperations})`
|
|
51982
52320
|
);
|
|
@@ -51986,19 +52324,19 @@ var init_workspace_manager = __esm({
|
|
|
51986
52324
|
);
|
|
51987
52325
|
this.cleanupRequested = true;
|
|
51988
52326
|
await Promise.race([
|
|
51989
|
-
new Promise((
|
|
52327
|
+
new Promise((resolve17) => {
|
|
51990
52328
|
if (this.activeOperations === 0) {
|
|
51991
|
-
|
|
52329
|
+
resolve17();
|
|
51992
52330
|
} else {
|
|
51993
|
-
this.cleanupResolvers.push(
|
|
52331
|
+
this.cleanupResolvers.push(resolve17);
|
|
51994
52332
|
}
|
|
51995
52333
|
}),
|
|
51996
|
-
new Promise((
|
|
52334
|
+
new Promise((resolve17) => {
|
|
51997
52335
|
setTimeout(() => {
|
|
51998
52336
|
logger.warn(
|
|
51999
52337
|
`[Workspace] Cleanup timeout after ${timeout}ms, proceeding anyway (${this.activeOperations} operations still active)`
|
|
52000
52338
|
);
|
|
52001
|
-
|
|
52339
|
+
resolve17();
|
|
52002
52340
|
}, timeout);
|
|
52003
52341
|
})
|
|
52004
52342
|
]);
|
|
@@ -52028,6 +52366,68 @@ var init_workspace_manager = __esm({
|
|
|
52028
52366
|
logger.warn(`Failed to cleanup workspace: ${error}`);
|
|
52029
52367
|
}
|
|
52030
52368
|
}
|
|
52369
|
+
/**
|
|
52370
|
+
* Check if a path exists (file or directory).
|
|
52371
|
+
*/
|
|
52372
|
+
async pathExists(p) {
|
|
52373
|
+
try {
|
|
52374
|
+
await fsp2.access(p);
|
|
52375
|
+
return true;
|
|
52376
|
+
} catch {
|
|
52377
|
+
return false;
|
|
52378
|
+
}
|
|
52379
|
+
}
|
|
52380
|
+
/**
|
|
52381
|
+
* Clean up stale workspace directories older than maxAge.
|
|
52382
|
+
* Call periodically (e.g. at socket-runner startup) to prevent disk bloat.
|
|
52383
|
+
*/
|
|
52384
|
+
static async cleanupStale(basePath = process.env.VISOR_WORKSPACE_PATH || "/tmp/visor-workspaces", maxAgeMs = 24 * 60 * 60 * 1e3) {
|
|
52385
|
+
let cleaned = 0;
|
|
52386
|
+
try {
|
|
52387
|
+
const entries = await fsp2.readdir(basePath, { withFileTypes: true });
|
|
52388
|
+
const now = Date.now();
|
|
52389
|
+
for (const entry of entries) {
|
|
52390
|
+
if (!entry.isDirectory()) continue;
|
|
52391
|
+
const dirPath = path23.join(basePath, entry.name);
|
|
52392
|
+
try {
|
|
52393
|
+
const stat2 = await fsp2.stat(dirPath);
|
|
52394
|
+
if (now - stat2.mtimeMs > maxAgeMs) {
|
|
52395
|
+
try {
|
|
52396
|
+
const subdirs = await fsp2.readdir(dirPath, { withFileTypes: true });
|
|
52397
|
+
for (const sub of subdirs) {
|
|
52398
|
+
if (!sub.isDirectory()) continue;
|
|
52399
|
+
const subPath = path23.join(dirPath, sub.name);
|
|
52400
|
+
const gitFilePath = path23.join(subPath, ".git");
|
|
52401
|
+
try {
|
|
52402
|
+
const gitContent = await fsp2.readFile(gitFilePath, "utf-8");
|
|
52403
|
+
const match = gitContent.match(/gitdir:\s*(.+)/);
|
|
52404
|
+
if (match) {
|
|
52405
|
+
const worktreeGitDir = match[1].trim();
|
|
52406
|
+
const repoGitDir = worktreeGitDir.replace(/\/\.git\/worktrees\/.*$/, "");
|
|
52407
|
+
await commandExecutor.execute(
|
|
52408
|
+
`git -C ${shellEscape(repoGitDir)} worktree remove ${shellEscape(subPath)} --force`,
|
|
52409
|
+
{ timeout: 1e4 }
|
|
52410
|
+
);
|
|
52411
|
+
}
|
|
52412
|
+
} catch {
|
|
52413
|
+
}
|
|
52414
|
+
}
|
|
52415
|
+
} catch {
|
|
52416
|
+
}
|
|
52417
|
+
await fsp2.rm(dirPath, { recursive: true, force: true });
|
|
52418
|
+
cleaned++;
|
|
52419
|
+
}
|
|
52420
|
+
} catch {
|
|
52421
|
+
}
|
|
52422
|
+
}
|
|
52423
|
+
if (cleaned > 0) {
|
|
52424
|
+
logger.info(`[Workspace] Cleaned up ${cleaned} stale workspace(s) from ${basePath}`);
|
|
52425
|
+
}
|
|
52426
|
+
} catch (error) {
|
|
52427
|
+
logger.debug(`[Workspace] Stale cleanup error: ${error}`);
|
|
52428
|
+
}
|
|
52429
|
+
return cleaned;
|
|
52430
|
+
}
|
|
52031
52431
|
/**
|
|
52032
52432
|
* Create worktree for the main project
|
|
52033
52433
|
*
|
|
@@ -52277,6 +52677,1264 @@ var init_build_engine_context = __esm({
|
|
|
52277
52677
|
}
|
|
52278
52678
|
});
|
|
52279
52679
|
|
|
52680
|
+
// src/policy/default-engine.ts
|
|
52681
|
+
var DefaultPolicyEngine;
|
|
52682
|
+
var init_default_engine = __esm({
|
|
52683
|
+
"src/policy/default-engine.ts"() {
|
|
52684
|
+
"use strict";
|
|
52685
|
+
DefaultPolicyEngine = class {
|
|
52686
|
+
async initialize(_config) {
|
|
52687
|
+
}
|
|
52688
|
+
async evaluateCheckExecution(_checkId, _checkConfig) {
|
|
52689
|
+
return { allowed: true };
|
|
52690
|
+
}
|
|
52691
|
+
async evaluateToolInvocation(_serverName, _methodName, _transport) {
|
|
52692
|
+
return { allowed: true };
|
|
52693
|
+
}
|
|
52694
|
+
async evaluateCapabilities(_checkId, _capabilities) {
|
|
52695
|
+
return { allowed: true };
|
|
52696
|
+
}
|
|
52697
|
+
async shutdown() {
|
|
52698
|
+
}
|
|
52699
|
+
};
|
|
52700
|
+
}
|
|
52701
|
+
});
|
|
52702
|
+
|
|
52703
|
+
// src/enterprise/license/validator.ts
|
|
52704
|
+
var validator_exports = {};
|
|
52705
|
+
__export(validator_exports, {
|
|
52706
|
+
LicenseValidator: () => LicenseValidator
|
|
52707
|
+
});
|
|
52708
|
+
var crypto2, fs20, path24, LicenseValidator;
|
|
52709
|
+
var init_validator = __esm({
|
|
52710
|
+
"src/enterprise/license/validator.ts"() {
|
|
52711
|
+
"use strict";
|
|
52712
|
+
crypto2 = __toESM(require("crypto"));
|
|
52713
|
+
fs20 = __toESM(require("fs"));
|
|
52714
|
+
path24 = __toESM(require("path"));
|
|
52715
|
+
LicenseValidator = class _LicenseValidator {
|
|
52716
|
+
/** Ed25519 public key for license verification (PEM format). */
|
|
52717
|
+
static PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAI/Zd08EFmgIdrDm/HXd0l3/5GBt7R1PrdvhdmEXhJlU=\n-----END PUBLIC KEY-----\n";
|
|
52718
|
+
cache = null;
|
|
52719
|
+
static CACHE_TTL = 5 * 60 * 1e3;
|
|
52720
|
+
// 5 minutes
|
|
52721
|
+
static GRACE_PERIOD = 72 * 3600 * 1e3;
|
|
52722
|
+
// 72 hours after expiry
|
|
52723
|
+
/**
|
|
52724
|
+
* Load and validate license from environment or file.
|
|
52725
|
+
*
|
|
52726
|
+
* Resolution order:
|
|
52727
|
+
* 1. VISOR_LICENSE env var (JWT string)
|
|
52728
|
+
* 2. VISOR_LICENSE_FILE env var (path to file)
|
|
52729
|
+
* 3. .visor-license in project root (cwd)
|
|
52730
|
+
* 4. .visor-license in ~/.config/visor/
|
|
52731
|
+
*/
|
|
52732
|
+
async loadAndValidate() {
|
|
52733
|
+
if (this.cache && Date.now() - this.cache.validatedAt < _LicenseValidator.CACHE_TTL) {
|
|
52734
|
+
return this.cache.payload;
|
|
52735
|
+
}
|
|
52736
|
+
const token = this.resolveToken();
|
|
52737
|
+
if (!token) return null;
|
|
52738
|
+
const payload = this.verifyAndDecode(token);
|
|
52739
|
+
if (!payload) return null;
|
|
52740
|
+
this.cache = { payload, validatedAt: Date.now() };
|
|
52741
|
+
return payload;
|
|
52742
|
+
}
|
|
52743
|
+
/** Check if a specific feature is licensed */
|
|
52744
|
+
hasFeature(feature) {
|
|
52745
|
+
if (!this.cache) return false;
|
|
52746
|
+
return this.cache.payload.features.includes(feature);
|
|
52747
|
+
}
|
|
52748
|
+
/** Check if license is valid (with grace period) */
|
|
52749
|
+
isValid() {
|
|
52750
|
+
if (!this.cache) return false;
|
|
52751
|
+
const now = Date.now();
|
|
52752
|
+
const expiryMs = this.cache.payload.exp * 1e3;
|
|
52753
|
+
return now < expiryMs + _LicenseValidator.GRACE_PERIOD;
|
|
52754
|
+
}
|
|
52755
|
+
/** Check if the license is within its grace period (expired but still valid) */
|
|
52756
|
+
isInGracePeriod() {
|
|
52757
|
+
if (!this.cache) return false;
|
|
52758
|
+
const now = Date.now();
|
|
52759
|
+
const expiryMs = this.cache.payload.exp * 1e3;
|
|
52760
|
+
return now >= expiryMs && now < expiryMs + _LicenseValidator.GRACE_PERIOD;
|
|
52761
|
+
}
|
|
52762
|
+
resolveToken() {
|
|
52763
|
+
if (process.env.VISOR_LICENSE) {
|
|
52764
|
+
return process.env.VISOR_LICENSE.trim();
|
|
52765
|
+
}
|
|
52766
|
+
if (process.env.VISOR_LICENSE_FILE) {
|
|
52767
|
+
const resolved = path24.resolve(process.env.VISOR_LICENSE_FILE);
|
|
52768
|
+
const home2 = process.env.HOME || process.env.USERPROFILE || "";
|
|
52769
|
+
const allowedPrefixes = [path24.normalize(process.cwd())];
|
|
52770
|
+
if (home2) allowedPrefixes.push(path24.normalize(path24.join(home2, ".config", "visor")));
|
|
52771
|
+
let realPath;
|
|
52772
|
+
try {
|
|
52773
|
+
realPath = fs20.realpathSync(resolved);
|
|
52774
|
+
} catch {
|
|
52775
|
+
return null;
|
|
52776
|
+
}
|
|
52777
|
+
const isSafe = allowedPrefixes.some(
|
|
52778
|
+
(prefix) => realPath === prefix || realPath.startsWith(prefix + path24.sep)
|
|
52779
|
+
);
|
|
52780
|
+
if (!isSafe) return null;
|
|
52781
|
+
return this.readFile(realPath);
|
|
52782
|
+
}
|
|
52783
|
+
const cwdPath = path24.join(process.cwd(), ".visor-license");
|
|
52784
|
+
const cwdToken = this.readFile(cwdPath);
|
|
52785
|
+
if (cwdToken) return cwdToken;
|
|
52786
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
52787
|
+
if (home) {
|
|
52788
|
+
const configPath = path24.join(home, ".config", "visor", ".visor-license");
|
|
52789
|
+
const configToken = this.readFile(configPath);
|
|
52790
|
+
if (configToken) return configToken;
|
|
52791
|
+
}
|
|
52792
|
+
return null;
|
|
52793
|
+
}
|
|
52794
|
+
readFile(filePath) {
|
|
52795
|
+
try {
|
|
52796
|
+
return fs20.readFileSync(filePath, "utf-8").trim();
|
|
52797
|
+
} catch {
|
|
52798
|
+
return null;
|
|
52799
|
+
}
|
|
52800
|
+
}
|
|
52801
|
+
verifyAndDecode(token) {
|
|
52802
|
+
try {
|
|
52803
|
+
const parts = token.split(".");
|
|
52804
|
+
if (parts.length !== 3) return null;
|
|
52805
|
+
const [headerB64, payloadB64, signatureB64] = parts;
|
|
52806
|
+
const header = JSON.parse(Buffer.from(headerB64, "base64url").toString());
|
|
52807
|
+
if (header.alg !== "EdDSA") return null;
|
|
52808
|
+
const data = `${headerB64}.${payloadB64}`;
|
|
52809
|
+
const signature = Buffer.from(signatureB64, "base64url");
|
|
52810
|
+
const publicKey = crypto2.createPublicKey(_LicenseValidator.PUBLIC_KEY);
|
|
52811
|
+
if (publicKey.asymmetricKeyType !== "ed25519") {
|
|
52812
|
+
return null;
|
|
52813
|
+
}
|
|
52814
|
+
const isValid = crypto2.verify(null, Buffer.from(data), publicKey, signature);
|
|
52815
|
+
if (!isValid) return null;
|
|
52816
|
+
const payload = JSON.parse(Buffer.from(payloadB64, "base64url").toString());
|
|
52817
|
+
if (!payload.org || !Array.isArray(payload.features) || typeof payload.exp !== "number" || typeof payload.iat !== "number" || !payload.sub) {
|
|
52818
|
+
return null;
|
|
52819
|
+
}
|
|
52820
|
+
const now = Date.now();
|
|
52821
|
+
const expiryMs = payload.exp * 1e3;
|
|
52822
|
+
if (now >= expiryMs + _LicenseValidator.GRACE_PERIOD) {
|
|
52823
|
+
return null;
|
|
52824
|
+
}
|
|
52825
|
+
return payload;
|
|
52826
|
+
} catch {
|
|
52827
|
+
return null;
|
|
52828
|
+
}
|
|
52829
|
+
}
|
|
52830
|
+
};
|
|
52831
|
+
}
|
|
52832
|
+
});
|
|
52833
|
+
|
|
52834
|
+
// src/enterprise/policy/opa-compiler.ts
|
|
52835
|
+
var fs21, path25, os2, crypto3, import_child_process8, OpaCompiler;
|
|
52836
|
+
var init_opa_compiler = __esm({
|
|
52837
|
+
"src/enterprise/policy/opa-compiler.ts"() {
|
|
52838
|
+
"use strict";
|
|
52839
|
+
fs21 = __toESM(require("fs"));
|
|
52840
|
+
path25 = __toESM(require("path"));
|
|
52841
|
+
os2 = __toESM(require("os"));
|
|
52842
|
+
crypto3 = __toESM(require("crypto"));
|
|
52843
|
+
import_child_process8 = require("child_process");
|
|
52844
|
+
OpaCompiler = class _OpaCompiler {
|
|
52845
|
+
static CACHE_DIR = path25.join(os2.tmpdir(), "visor-opa-cache");
|
|
52846
|
+
/**
|
|
52847
|
+
* Resolve the input paths to WASM bytes.
|
|
52848
|
+
*
|
|
52849
|
+
* Strategy:
|
|
52850
|
+
* 1. If any path is a .wasm file, read it directly
|
|
52851
|
+
* 2. If a directory contains policy.wasm, read it
|
|
52852
|
+
* 3. Otherwise, collect all .rego files and auto-compile via `opa build`
|
|
52853
|
+
*/
|
|
52854
|
+
async resolveWasmBytes(paths) {
|
|
52855
|
+
const regoFiles = [];
|
|
52856
|
+
for (const p of paths) {
|
|
52857
|
+
const resolved = path25.resolve(p);
|
|
52858
|
+
if (path25.normalize(resolved).includes("..")) {
|
|
52859
|
+
throw new Error(`Policy path contains traversal sequences: ${p}`);
|
|
52860
|
+
}
|
|
52861
|
+
if (resolved.endsWith(".wasm") && fs21.existsSync(resolved)) {
|
|
52862
|
+
return fs21.readFileSync(resolved);
|
|
52863
|
+
}
|
|
52864
|
+
if (!fs21.existsSync(resolved)) continue;
|
|
52865
|
+
const stat2 = fs21.statSync(resolved);
|
|
52866
|
+
if (stat2.isDirectory()) {
|
|
52867
|
+
const wasmCandidate = path25.join(resolved, "policy.wasm");
|
|
52868
|
+
if (fs21.existsSync(wasmCandidate)) {
|
|
52869
|
+
return fs21.readFileSync(wasmCandidate);
|
|
52870
|
+
}
|
|
52871
|
+
const files = fs21.readdirSync(resolved);
|
|
52872
|
+
for (const f of files) {
|
|
52873
|
+
if (f.endsWith(".rego")) {
|
|
52874
|
+
regoFiles.push(path25.join(resolved, f));
|
|
52875
|
+
}
|
|
52876
|
+
}
|
|
52877
|
+
} else if (resolved.endsWith(".rego")) {
|
|
52878
|
+
regoFiles.push(resolved);
|
|
52879
|
+
}
|
|
52880
|
+
}
|
|
52881
|
+
if (regoFiles.length === 0) {
|
|
52882
|
+
throw new Error(
|
|
52883
|
+
`OPA WASM evaluator: no .wasm bundle or .rego files found in: ${paths.join(", ")}`
|
|
52884
|
+
);
|
|
52885
|
+
}
|
|
52886
|
+
return this.compileRego(regoFiles);
|
|
52887
|
+
}
|
|
52888
|
+
/**
|
|
52889
|
+
* Auto-compile .rego files to a WASM bundle using the `opa` CLI.
|
|
52890
|
+
*
|
|
52891
|
+
* Caches the compiled bundle based on a content hash of all input .rego files
|
|
52892
|
+
* so subsequent runs skip compilation if policies haven't changed.
|
|
52893
|
+
*/
|
|
52894
|
+
compileRego(regoFiles) {
|
|
52895
|
+
try {
|
|
52896
|
+
(0, import_child_process8.execFileSync)("opa", ["version"], { stdio: "pipe" });
|
|
52897
|
+
} catch {
|
|
52898
|
+
throw new Error(
|
|
52899
|
+
"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(" ")
|
|
52900
|
+
);
|
|
52901
|
+
}
|
|
52902
|
+
const hash = crypto3.createHash("sha256");
|
|
52903
|
+
for (const f of regoFiles.sort()) {
|
|
52904
|
+
hash.update(fs21.readFileSync(f));
|
|
52905
|
+
hash.update(f);
|
|
52906
|
+
}
|
|
52907
|
+
const cacheKey = hash.digest("hex").slice(0, 16);
|
|
52908
|
+
const cacheDir = _OpaCompiler.CACHE_DIR;
|
|
52909
|
+
const cachedWasm = path25.join(cacheDir, `${cacheKey}.wasm`);
|
|
52910
|
+
if (fs21.existsSync(cachedWasm)) {
|
|
52911
|
+
return fs21.readFileSync(cachedWasm);
|
|
52912
|
+
}
|
|
52913
|
+
fs21.mkdirSync(cacheDir, { recursive: true });
|
|
52914
|
+
const bundleTar = path25.join(cacheDir, `${cacheKey}-bundle.tar.gz`);
|
|
52915
|
+
try {
|
|
52916
|
+
const args = [
|
|
52917
|
+
"build",
|
|
52918
|
+
"-t",
|
|
52919
|
+
"wasm",
|
|
52920
|
+
"-e",
|
|
52921
|
+
"visor",
|
|
52922
|
+
// entrypoint: the visor package tree
|
|
52923
|
+
"-o",
|
|
52924
|
+
bundleTar,
|
|
52925
|
+
...regoFiles
|
|
52926
|
+
];
|
|
52927
|
+
(0, import_child_process8.execFileSync)("opa", args, {
|
|
52928
|
+
stdio: "pipe",
|
|
52929
|
+
timeout: 3e4
|
|
52930
|
+
});
|
|
52931
|
+
} catch (err) {
|
|
52932
|
+
const stderr = err?.stderr?.toString() || "";
|
|
52933
|
+
throw new Error(
|
|
52934
|
+
`Failed to compile .rego files to WASM:
|
|
52935
|
+
${stderr}
|
|
52936
|
+
Ensure your .rego files are valid and the \`opa\` CLI is installed.`
|
|
52937
|
+
);
|
|
52938
|
+
}
|
|
52939
|
+
try {
|
|
52940
|
+
(0, import_child_process8.execFileSync)("tar", ["-xzf", bundleTar, "-C", cacheDir, "/policy.wasm"], {
|
|
52941
|
+
stdio: "pipe"
|
|
52942
|
+
});
|
|
52943
|
+
const extractedWasm = path25.join(cacheDir, "policy.wasm");
|
|
52944
|
+
if (fs21.existsSync(extractedWasm)) {
|
|
52945
|
+
fs21.renameSync(extractedWasm, cachedWasm);
|
|
52946
|
+
}
|
|
52947
|
+
} catch {
|
|
52948
|
+
try {
|
|
52949
|
+
(0, import_child_process8.execFileSync)("tar", ["-xzf", bundleTar, "-C", cacheDir, "policy.wasm"], {
|
|
52950
|
+
stdio: "pipe"
|
|
52951
|
+
});
|
|
52952
|
+
const extractedWasm = path25.join(cacheDir, "policy.wasm");
|
|
52953
|
+
if (fs21.existsSync(extractedWasm)) {
|
|
52954
|
+
fs21.renameSync(extractedWasm, cachedWasm);
|
|
52955
|
+
}
|
|
52956
|
+
} catch (err2) {
|
|
52957
|
+
throw new Error(`Failed to extract policy.wasm from OPA bundle: ${err2?.message || err2}`);
|
|
52958
|
+
}
|
|
52959
|
+
}
|
|
52960
|
+
try {
|
|
52961
|
+
fs21.unlinkSync(bundleTar);
|
|
52962
|
+
} catch {
|
|
52963
|
+
}
|
|
52964
|
+
if (!fs21.existsSync(cachedWasm)) {
|
|
52965
|
+
throw new Error("OPA build succeeded but policy.wasm was not found in the bundle");
|
|
52966
|
+
}
|
|
52967
|
+
return fs21.readFileSync(cachedWasm);
|
|
52968
|
+
}
|
|
52969
|
+
};
|
|
52970
|
+
}
|
|
52971
|
+
});
|
|
52972
|
+
|
|
52973
|
+
// src/enterprise/policy/opa-wasm-evaluator.ts
|
|
52974
|
+
var fs22, path26, OpaWasmEvaluator;
|
|
52975
|
+
var init_opa_wasm_evaluator = __esm({
|
|
52976
|
+
"src/enterprise/policy/opa-wasm-evaluator.ts"() {
|
|
52977
|
+
"use strict";
|
|
52978
|
+
fs22 = __toESM(require("fs"));
|
|
52979
|
+
path26 = __toESM(require("path"));
|
|
52980
|
+
init_opa_compiler();
|
|
52981
|
+
OpaWasmEvaluator = class {
|
|
52982
|
+
policy = null;
|
|
52983
|
+
dataDocument = {};
|
|
52984
|
+
compiler = new OpaCompiler();
|
|
52985
|
+
async initialize(rulesPath) {
|
|
52986
|
+
const paths = Array.isArray(rulesPath) ? rulesPath : [rulesPath];
|
|
52987
|
+
const wasmBytes = await this.compiler.resolveWasmBytes(paths);
|
|
52988
|
+
try {
|
|
52989
|
+
const { createRequire } = require("module");
|
|
52990
|
+
const runtimeRequire = createRequire(__filename);
|
|
52991
|
+
const opaWasm = runtimeRequire("@open-policy-agent/opa-wasm");
|
|
52992
|
+
const loadPolicy = opaWasm.loadPolicy || opaWasm.default?.loadPolicy;
|
|
52993
|
+
if (!loadPolicy) {
|
|
52994
|
+
throw new Error("loadPolicy not found in @open-policy-agent/opa-wasm");
|
|
52995
|
+
}
|
|
52996
|
+
this.policy = await loadPolicy(wasmBytes);
|
|
52997
|
+
} catch (err) {
|
|
52998
|
+
if (err?.code === "MODULE_NOT_FOUND" || err?.code === "ERR_MODULE_NOT_FOUND") {
|
|
52999
|
+
throw new Error(
|
|
53000
|
+
"OPA WASM evaluator requires @open-policy-agent/opa-wasm. Install it with: npm install @open-policy-agent/opa-wasm"
|
|
53001
|
+
);
|
|
53002
|
+
}
|
|
53003
|
+
throw err;
|
|
53004
|
+
}
|
|
53005
|
+
}
|
|
53006
|
+
/**
|
|
53007
|
+
* Load external data from a JSON file to use as the OPA data document.
|
|
53008
|
+
* The loaded data will be passed to `policy.setData()` during evaluation,
|
|
53009
|
+
* making it available in Rego via `data.<key>`.
|
|
53010
|
+
*/
|
|
53011
|
+
loadData(dataPath) {
|
|
53012
|
+
const resolved = path26.resolve(dataPath);
|
|
53013
|
+
if (path26.normalize(resolved).includes("..")) {
|
|
53014
|
+
throw new Error(`Data path contains traversal sequences: ${dataPath}`);
|
|
53015
|
+
}
|
|
53016
|
+
if (!fs22.existsSync(resolved)) {
|
|
53017
|
+
throw new Error(`OPA data file not found: ${resolved}`);
|
|
53018
|
+
}
|
|
53019
|
+
const stat2 = fs22.statSync(resolved);
|
|
53020
|
+
if (stat2.size > 10 * 1024 * 1024) {
|
|
53021
|
+
throw new Error(`OPA data file exceeds 10MB limit: ${resolved} (${stat2.size} bytes)`);
|
|
53022
|
+
}
|
|
53023
|
+
const raw = fs22.readFileSync(resolved, "utf-8");
|
|
53024
|
+
try {
|
|
53025
|
+
const parsed = JSON.parse(raw);
|
|
53026
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
53027
|
+
throw new Error("OPA data file must contain a JSON object (not an array or primitive)");
|
|
53028
|
+
}
|
|
53029
|
+
this.dataDocument = parsed;
|
|
53030
|
+
} catch (err) {
|
|
53031
|
+
if (err.message.startsWith("OPA data file must")) {
|
|
53032
|
+
throw err;
|
|
53033
|
+
}
|
|
53034
|
+
throw new Error(`Failed to parse OPA data file ${resolved}: ${err.message}`);
|
|
53035
|
+
}
|
|
53036
|
+
}
|
|
53037
|
+
async evaluate(input) {
|
|
53038
|
+
if (!this.policy) {
|
|
53039
|
+
throw new Error("OPA WASM evaluator not initialized");
|
|
53040
|
+
}
|
|
53041
|
+
this.policy.setData(this.dataDocument);
|
|
53042
|
+
const resultSet = this.policy.evaluate(input);
|
|
53043
|
+
if (Array.isArray(resultSet) && resultSet.length > 0) {
|
|
53044
|
+
return resultSet[0].result;
|
|
53045
|
+
}
|
|
53046
|
+
return void 0;
|
|
53047
|
+
}
|
|
53048
|
+
async shutdown() {
|
|
53049
|
+
if (this.policy) {
|
|
53050
|
+
if (typeof this.policy.close === "function") {
|
|
53051
|
+
try {
|
|
53052
|
+
this.policy.close();
|
|
53053
|
+
} catch {
|
|
53054
|
+
}
|
|
53055
|
+
} else if (typeof this.policy.free === "function") {
|
|
53056
|
+
try {
|
|
53057
|
+
this.policy.free();
|
|
53058
|
+
} catch {
|
|
53059
|
+
}
|
|
53060
|
+
}
|
|
53061
|
+
}
|
|
53062
|
+
this.policy = null;
|
|
53063
|
+
}
|
|
53064
|
+
};
|
|
53065
|
+
}
|
|
53066
|
+
});
|
|
53067
|
+
|
|
53068
|
+
// src/enterprise/policy/opa-http-evaluator.ts
|
|
53069
|
+
var OpaHttpEvaluator;
|
|
53070
|
+
var init_opa_http_evaluator = __esm({
|
|
53071
|
+
"src/enterprise/policy/opa-http-evaluator.ts"() {
|
|
53072
|
+
"use strict";
|
|
53073
|
+
OpaHttpEvaluator = class {
|
|
53074
|
+
baseUrl;
|
|
53075
|
+
timeout;
|
|
53076
|
+
constructor(baseUrl, timeout = 5e3) {
|
|
53077
|
+
let parsed;
|
|
53078
|
+
try {
|
|
53079
|
+
parsed = new URL(baseUrl);
|
|
53080
|
+
} catch {
|
|
53081
|
+
throw new Error(`OPA HTTP evaluator: invalid URL: ${baseUrl}`);
|
|
53082
|
+
}
|
|
53083
|
+
if (!["http:", "https:"].includes(parsed.protocol)) {
|
|
53084
|
+
throw new Error(
|
|
53085
|
+
`OPA HTTP evaluator: url must use http:// or https:// protocol, got: ${baseUrl}`
|
|
53086
|
+
);
|
|
53087
|
+
}
|
|
53088
|
+
const hostname = parsed.hostname;
|
|
53089
|
+
if (this.isBlockedHostname(hostname)) {
|
|
53090
|
+
throw new Error(
|
|
53091
|
+
`OPA HTTP evaluator: url must not point to internal, loopback, or private network addresses`
|
|
53092
|
+
);
|
|
53093
|
+
}
|
|
53094
|
+
this.baseUrl = baseUrl.replace(/\/+$/, "");
|
|
53095
|
+
this.timeout = timeout;
|
|
53096
|
+
}
|
|
53097
|
+
/**
|
|
53098
|
+
* Check if a hostname is blocked due to SSRF concerns.
|
|
53099
|
+
*
|
|
53100
|
+
* Blocks:
|
|
53101
|
+
* - Loopback addresses (127.x.x.x, localhost, 0.0.0.0, ::1)
|
|
53102
|
+
* - Link-local addresses (169.254.x.x)
|
|
53103
|
+
* - Private networks (10.x.x.x, 172.16-31.x.x, 192.168.x.x)
|
|
53104
|
+
* - IPv6 unique local addresses (fd00::/8)
|
|
53105
|
+
* - Cloud metadata services (*.internal)
|
|
53106
|
+
*/
|
|
53107
|
+
isBlockedHostname(hostname) {
|
|
53108
|
+
if (!hostname) return true;
|
|
53109
|
+
const normalized = hostname.toLowerCase().replace(/^\[|\]$/g, "");
|
|
53110
|
+
if (normalized === "metadata.google.internal" || normalized.endsWith(".internal")) {
|
|
53111
|
+
return true;
|
|
53112
|
+
}
|
|
53113
|
+
if (normalized === "localhost" || normalized === "localhost.localdomain") {
|
|
53114
|
+
return true;
|
|
53115
|
+
}
|
|
53116
|
+
if (normalized === "::1" || normalized === "0:0:0:0:0:0:0:1") {
|
|
53117
|
+
return true;
|
|
53118
|
+
}
|
|
53119
|
+
const ipv4Pattern = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
|
|
53120
|
+
const ipv4Match = normalized.match(ipv4Pattern);
|
|
53121
|
+
if (ipv4Match) {
|
|
53122
|
+
const octets = ipv4Match.slice(1, 5).map(Number);
|
|
53123
|
+
if (octets.some((octet) => octet > 255)) {
|
|
53124
|
+
return false;
|
|
53125
|
+
}
|
|
53126
|
+
const [a, b] = octets;
|
|
53127
|
+
if (a === 127) {
|
|
53128
|
+
return true;
|
|
53129
|
+
}
|
|
53130
|
+
if (a === 0) {
|
|
53131
|
+
return true;
|
|
53132
|
+
}
|
|
53133
|
+
if (a === 169 && b === 254) {
|
|
53134
|
+
return true;
|
|
53135
|
+
}
|
|
53136
|
+
if (a === 10) {
|
|
53137
|
+
return true;
|
|
53138
|
+
}
|
|
53139
|
+
if (a === 172 && b >= 16 && b <= 31) {
|
|
53140
|
+
return true;
|
|
53141
|
+
}
|
|
53142
|
+
if (a === 192 && b === 168) {
|
|
53143
|
+
return true;
|
|
53144
|
+
}
|
|
53145
|
+
}
|
|
53146
|
+
if (normalized.startsWith("fd") || normalized.startsWith("fc")) {
|
|
53147
|
+
return true;
|
|
53148
|
+
}
|
|
53149
|
+
if (normalized.startsWith("fe80:")) {
|
|
53150
|
+
return true;
|
|
53151
|
+
}
|
|
53152
|
+
return false;
|
|
53153
|
+
}
|
|
53154
|
+
/**
|
|
53155
|
+
* Evaluate a policy rule against an input document via OPA REST API.
|
|
53156
|
+
*
|
|
53157
|
+
* @param input - The input document to evaluate
|
|
53158
|
+
* @param rulePath - OPA rule path (e.g., 'visor/check/execute')
|
|
53159
|
+
* @returns The result object from OPA, or undefined on error
|
|
53160
|
+
*/
|
|
53161
|
+
async evaluate(input, rulePath) {
|
|
53162
|
+
const encodedPath = rulePath.split("/").map((s) => encodeURIComponent(s)).join("/");
|
|
53163
|
+
const url = `${this.baseUrl}/v1/data/${encodedPath}`;
|
|
53164
|
+
const controller = new AbortController();
|
|
53165
|
+
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
53166
|
+
try {
|
|
53167
|
+
const response = await fetch(url, {
|
|
53168
|
+
method: "POST",
|
|
53169
|
+
headers: { "Content-Type": "application/json" },
|
|
53170
|
+
body: JSON.stringify({ input }),
|
|
53171
|
+
signal: controller.signal
|
|
53172
|
+
});
|
|
53173
|
+
if (!response.ok) {
|
|
53174
|
+
throw new Error(`OPA HTTP ${response.status}: ${response.statusText}`);
|
|
53175
|
+
}
|
|
53176
|
+
let body;
|
|
53177
|
+
try {
|
|
53178
|
+
body = await response.json();
|
|
53179
|
+
} catch (jsonErr) {
|
|
53180
|
+
throw new Error(
|
|
53181
|
+
`OPA HTTP evaluator: failed to parse JSON response: ${jsonErr instanceof Error ? jsonErr.message : String(jsonErr)}`
|
|
53182
|
+
);
|
|
53183
|
+
}
|
|
53184
|
+
return body?.result;
|
|
53185
|
+
} finally {
|
|
53186
|
+
clearTimeout(timer);
|
|
53187
|
+
}
|
|
53188
|
+
}
|
|
53189
|
+
async shutdown() {
|
|
53190
|
+
}
|
|
53191
|
+
};
|
|
53192
|
+
}
|
|
53193
|
+
});
|
|
53194
|
+
|
|
53195
|
+
// src/enterprise/policy/policy-input-builder.ts
|
|
53196
|
+
var PolicyInputBuilder;
|
|
53197
|
+
var init_policy_input_builder = __esm({
|
|
53198
|
+
"src/enterprise/policy/policy-input-builder.ts"() {
|
|
53199
|
+
"use strict";
|
|
53200
|
+
PolicyInputBuilder = class {
|
|
53201
|
+
roles;
|
|
53202
|
+
actor;
|
|
53203
|
+
repository;
|
|
53204
|
+
pullRequest;
|
|
53205
|
+
constructor(policyConfig, actor, repository, pullRequest) {
|
|
53206
|
+
this.roles = policyConfig.roles || {};
|
|
53207
|
+
this.actor = actor;
|
|
53208
|
+
this.repository = repository;
|
|
53209
|
+
this.pullRequest = pullRequest;
|
|
53210
|
+
}
|
|
53211
|
+
/** Resolve which roles apply to the current actor. */
|
|
53212
|
+
resolveRoles() {
|
|
53213
|
+
const matched = [];
|
|
53214
|
+
for (const [roleName, roleConfig] of Object.entries(this.roles)) {
|
|
53215
|
+
let identityMatch = false;
|
|
53216
|
+
if (roleConfig.author_association && this.actor.authorAssociation && roleConfig.author_association.includes(this.actor.authorAssociation)) {
|
|
53217
|
+
identityMatch = true;
|
|
53218
|
+
}
|
|
53219
|
+
if (!identityMatch && roleConfig.users && this.actor.login && roleConfig.users.includes(this.actor.login)) {
|
|
53220
|
+
identityMatch = true;
|
|
53221
|
+
}
|
|
53222
|
+
if (!identityMatch && roleConfig.slack_users && this.actor.slack?.userId && roleConfig.slack_users.includes(this.actor.slack.userId)) {
|
|
53223
|
+
identityMatch = true;
|
|
53224
|
+
}
|
|
53225
|
+
if (!identityMatch && roleConfig.emails && this.actor.slack?.email) {
|
|
53226
|
+
const actorEmail = this.actor.slack.email.toLowerCase();
|
|
53227
|
+
if (roleConfig.emails.some((e) => e.toLowerCase() === actorEmail)) {
|
|
53228
|
+
identityMatch = true;
|
|
53229
|
+
}
|
|
53230
|
+
}
|
|
53231
|
+
if (!identityMatch) continue;
|
|
53232
|
+
if (roleConfig.slack_channels && roleConfig.slack_channels.length > 0) {
|
|
53233
|
+
if (!this.actor.slack?.channelId || !roleConfig.slack_channels.includes(this.actor.slack.channelId)) {
|
|
53234
|
+
continue;
|
|
53235
|
+
}
|
|
53236
|
+
}
|
|
53237
|
+
matched.push(roleName);
|
|
53238
|
+
}
|
|
53239
|
+
return matched;
|
|
53240
|
+
}
|
|
53241
|
+
buildActor() {
|
|
53242
|
+
return {
|
|
53243
|
+
authorAssociation: this.actor.authorAssociation,
|
|
53244
|
+
login: this.actor.login,
|
|
53245
|
+
roles: this.resolveRoles(),
|
|
53246
|
+
isLocalMode: this.actor.isLocalMode,
|
|
53247
|
+
...this.actor.slack && { slack: this.actor.slack }
|
|
53248
|
+
};
|
|
53249
|
+
}
|
|
53250
|
+
forCheckExecution(check) {
|
|
53251
|
+
return {
|
|
53252
|
+
scope: "check.execute",
|
|
53253
|
+
check: {
|
|
53254
|
+
id: check.id,
|
|
53255
|
+
type: check.type,
|
|
53256
|
+
group: check.group,
|
|
53257
|
+
tags: check.tags,
|
|
53258
|
+
criticality: check.criticality,
|
|
53259
|
+
sandbox: check.sandbox,
|
|
53260
|
+
policy: check.policy
|
|
53261
|
+
},
|
|
53262
|
+
actor: this.buildActor(),
|
|
53263
|
+
repository: this.repository,
|
|
53264
|
+
pullRequest: this.pullRequest
|
|
53265
|
+
};
|
|
53266
|
+
}
|
|
53267
|
+
forToolInvocation(serverName, methodName, transport) {
|
|
53268
|
+
return {
|
|
53269
|
+
scope: "tool.invoke",
|
|
53270
|
+
tool: { serverName, methodName, transport },
|
|
53271
|
+
actor: this.buildActor(),
|
|
53272
|
+
repository: this.repository,
|
|
53273
|
+
pullRequest: this.pullRequest
|
|
53274
|
+
};
|
|
53275
|
+
}
|
|
53276
|
+
forCapabilityResolve(checkId, capabilities) {
|
|
53277
|
+
return {
|
|
53278
|
+
scope: "capability.resolve",
|
|
53279
|
+
check: { id: checkId, type: "ai" },
|
|
53280
|
+
capability: capabilities,
|
|
53281
|
+
actor: this.buildActor(),
|
|
53282
|
+
repository: this.repository,
|
|
53283
|
+
pullRequest: this.pullRequest
|
|
53284
|
+
};
|
|
53285
|
+
}
|
|
53286
|
+
};
|
|
53287
|
+
}
|
|
53288
|
+
});
|
|
53289
|
+
|
|
53290
|
+
// src/enterprise/policy/opa-policy-engine.ts
|
|
53291
|
+
var opa_policy_engine_exports = {};
|
|
53292
|
+
__export(opa_policy_engine_exports, {
|
|
53293
|
+
OpaPolicyEngine: () => OpaPolicyEngine
|
|
53294
|
+
});
|
|
53295
|
+
var OpaPolicyEngine;
|
|
53296
|
+
var init_opa_policy_engine = __esm({
|
|
53297
|
+
"src/enterprise/policy/opa-policy-engine.ts"() {
|
|
53298
|
+
"use strict";
|
|
53299
|
+
init_opa_wasm_evaluator();
|
|
53300
|
+
init_opa_http_evaluator();
|
|
53301
|
+
init_policy_input_builder();
|
|
53302
|
+
OpaPolicyEngine = class {
|
|
53303
|
+
evaluator = null;
|
|
53304
|
+
fallback;
|
|
53305
|
+
timeout;
|
|
53306
|
+
config;
|
|
53307
|
+
inputBuilder = null;
|
|
53308
|
+
logger = null;
|
|
53309
|
+
constructor(config) {
|
|
53310
|
+
this.config = config;
|
|
53311
|
+
this.fallback = config.fallback || "deny";
|
|
53312
|
+
this.timeout = config.timeout || 5e3;
|
|
53313
|
+
}
|
|
53314
|
+
async initialize(config) {
|
|
53315
|
+
try {
|
|
53316
|
+
this.logger = (init_logger(), __toCommonJS(logger_exports)).logger;
|
|
53317
|
+
} catch {
|
|
53318
|
+
}
|
|
53319
|
+
const actor = {
|
|
53320
|
+
authorAssociation: process.env.VISOR_AUTHOR_ASSOCIATION,
|
|
53321
|
+
login: process.env.VISOR_AUTHOR_LOGIN || process.env.GITHUB_ACTOR,
|
|
53322
|
+
isLocalMode: !process.env.GITHUB_ACTIONS
|
|
53323
|
+
};
|
|
53324
|
+
const repo = {
|
|
53325
|
+
owner: process.env.GITHUB_REPOSITORY_OWNER,
|
|
53326
|
+
name: process.env.GITHUB_REPOSITORY?.split("/")[1],
|
|
53327
|
+
branch: process.env.GITHUB_HEAD_REF,
|
|
53328
|
+
baseBranch: process.env.GITHUB_BASE_REF,
|
|
53329
|
+
event: process.env.GITHUB_EVENT_NAME
|
|
53330
|
+
};
|
|
53331
|
+
const prNum = process.env.GITHUB_PR_NUMBER ? parseInt(process.env.GITHUB_PR_NUMBER, 10) : void 0;
|
|
53332
|
+
const pullRequest = {
|
|
53333
|
+
number: prNum !== void 0 && Number.isFinite(prNum) ? prNum : void 0
|
|
53334
|
+
};
|
|
53335
|
+
this.inputBuilder = new PolicyInputBuilder(config, actor, repo, pullRequest);
|
|
53336
|
+
if (config.engine === "local") {
|
|
53337
|
+
if (!config.rules) {
|
|
53338
|
+
throw new Error("OPA local mode requires `policy.rules` path to .wasm or .rego files");
|
|
53339
|
+
}
|
|
53340
|
+
const wasm = new OpaWasmEvaluator();
|
|
53341
|
+
await wasm.initialize(config.rules);
|
|
53342
|
+
if (config.data) {
|
|
53343
|
+
wasm.loadData(config.data);
|
|
53344
|
+
}
|
|
53345
|
+
this.evaluator = wasm;
|
|
53346
|
+
} else if (config.engine === "remote") {
|
|
53347
|
+
if (!config.url) {
|
|
53348
|
+
throw new Error("OPA remote mode requires `policy.url` pointing to OPA server");
|
|
53349
|
+
}
|
|
53350
|
+
this.evaluator = new OpaHttpEvaluator(config.url, this.timeout);
|
|
53351
|
+
} else {
|
|
53352
|
+
this.evaluator = null;
|
|
53353
|
+
}
|
|
53354
|
+
}
|
|
53355
|
+
/**
|
|
53356
|
+
* Update actor/repo/PR context (e.g., after PR info becomes available).
|
|
53357
|
+
* Called by the enterprise loader when engine context is enriched.
|
|
53358
|
+
*/
|
|
53359
|
+
setActorContext(actor, repo, pullRequest) {
|
|
53360
|
+
this.inputBuilder = new PolicyInputBuilder(this.config, actor, repo, pullRequest);
|
|
53361
|
+
}
|
|
53362
|
+
async evaluateCheckExecution(checkId, checkConfig) {
|
|
53363
|
+
if (!this.evaluator || !this.inputBuilder) return { allowed: true };
|
|
53364
|
+
const cfg = checkConfig && typeof checkConfig === "object" ? checkConfig : {};
|
|
53365
|
+
const policyOverride = cfg.policy;
|
|
53366
|
+
const input = this.inputBuilder.forCheckExecution({
|
|
53367
|
+
id: checkId,
|
|
53368
|
+
type: cfg.type || "ai",
|
|
53369
|
+
group: cfg.group,
|
|
53370
|
+
tags: cfg.tags,
|
|
53371
|
+
criticality: cfg.criticality,
|
|
53372
|
+
sandbox: cfg.sandbox,
|
|
53373
|
+
policy: policyOverride
|
|
53374
|
+
});
|
|
53375
|
+
return this.doEvaluate(input, this.resolveRulePath("check.execute", policyOverride?.rule));
|
|
53376
|
+
}
|
|
53377
|
+
async evaluateToolInvocation(serverName, methodName, transport) {
|
|
53378
|
+
if (!this.evaluator || !this.inputBuilder) return { allowed: true };
|
|
53379
|
+
const input = this.inputBuilder.forToolInvocation(serverName, methodName, transport);
|
|
53380
|
+
return this.doEvaluate(input, "visor/tool/invoke");
|
|
53381
|
+
}
|
|
53382
|
+
async evaluateCapabilities(checkId, capabilities) {
|
|
53383
|
+
if (!this.evaluator || !this.inputBuilder) return { allowed: true };
|
|
53384
|
+
const input = this.inputBuilder.forCapabilityResolve(checkId, capabilities);
|
|
53385
|
+
return this.doEvaluate(input, "visor/capability/resolve");
|
|
53386
|
+
}
|
|
53387
|
+
async shutdown() {
|
|
53388
|
+
if (this.evaluator && "shutdown" in this.evaluator) {
|
|
53389
|
+
await this.evaluator.shutdown();
|
|
53390
|
+
}
|
|
53391
|
+
this.evaluator = null;
|
|
53392
|
+
this.inputBuilder = null;
|
|
53393
|
+
}
|
|
53394
|
+
resolveRulePath(defaultScope, override) {
|
|
53395
|
+
if (override) {
|
|
53396
|
+
return override.startsWith("visor/") ? override : `visor/${override}`;
|
|
53397
|
+
}
|
|
53398
|
+
return `visor/${defaultScope.replace(/\./g, "/")}`;
|
|
53399
|
+
}
|
|
53400
|
+
async doEvaluate(input, rulePath) {
|
|
53401
|
+
try {
|
|
53402
|
+
this.logger?.debug(`[PolicyEngine] Evaluating ${rulePath}`, JSON.stringify(input));
|
|
53403
|
+
let timer;
|
|
53404
|
+
const timeoutPromise = new Promise((_resolve, reject) => {
|
|
53405
|
+
timer = setTimeout(() => reject(new Error("policy evaluation timeout")), this.timeout);
|
|
53406
|
+
});
|
|
53407
|
+
try {
|
|
53408
|
+
const result = await Promise.race([this.rawEvaluate(input, rulePath), timeoutPromise]);
|
|
53409
|
+
const decision = this.parseDecision(result);
|
|
53410
|
+
if (!decision.allowed && this.fallback === "warn") {
|
|
53411
|
+
decision.allowed = true;
|
|
53412
|
+
decision.warn = true;
|
|
53413
|
+
decision.reason = `audit: ${decision.reason || "policy denied"}`;
|
|
53414
|
+
}
|
|
53415
|
+
this.logger?.debug(
|
|
53416
|
+
`[PolicyEngine] Decision for ${rulePath}: allowed=${decision.allowed}, warn=${decision.warn || false}, reason=${decision.reason || "none"}`
|
|
53417
|
+
);
|
|
53418
|
+
return decision;
|
|
53419
|
+
} finally {
|
|
53420
|
+
if (timer) clearTimeout(timer);
|
|
53421
|
+
}
|
|
53422
|
+
} catch (err) {
|
|
53423
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
53424
|
+
this.logger?.warn(`[PolicyEngine] Evaluation failed for ${rulePath}: ${msg}`);
|
|
53425
|
+
return {
|
|
53426
|
+
allowed: this.fallback === "allow" || this.fallback === "warn",
|
|
53427
|
+
warn: this.fallback === "warn" ? true : void 0,
|
|
53428
|
+
reason: `policy evaluation failed, fallback=${this.fallback}`
|
|
53429
|
+
};
|
|
53430
|
+
}
|
|
53431
|
+
}
|
|
53432
|
+
async rawEvaluate(input, rulePath) {
|
|
53433
|
+
if (this.evaluator instanceof OpaWasmEvaluator) {
|
|
53434
|
+
const result = await this.evaluator.evaluate(input);
|
|
53435
|
+
return this.navigateWasmResult(result, rulePath);
|
|
53436
|
+
}
|
|
53437
|
+
return this.evaluator.evaluate(input, rulePath);
|
|
53438
|
+
}
|
|
53439
|
+
/**
|
|
53440
|
+
* Navigate nested OPA WASM result tree to reach the specific rule's output.
|
|
53441
|
+
* The WASM entrypoint `-e visor` means the result root IS the visor package,
|
|
53442
|
+
* so we strip the `visor/` prefix and walk the remaining segments.
|
|
53443
|
+
*/
|
|
53444
|
+
navigateWasmResult(result, rulePath) {
|
|
53445
|
+
if (!result || typeof result !== "object") return result;
|
|
53446
|
+
const segments = rulePath.replace(/^visor\//, "").split("/");
|
|
53447
|
+
let current = result;
|
|
53448
|
+
for (const seg of segments) {
|
|
53449
|
+
if (current && typeof current === "object" && seg in current) {
|
|
53450
|
+
current = current[seg];
|
|
53451
|
+
} else {
|
|
53452
|
+
return void 0;
|
|
53453
|
+
}
|
|
53454
|
+
}
|
|
53455
|
+
return current;
|
|
53456
|
+
}
|
|
53457
|
+
parseDecision(result) {
|
|
53458
|
+
if (result === void 0 || result === null) {
|
|
53459
|
+
return {
|
|
53460
|
+
allowed: this.fallback === "allow" || this.fallback === "warn",
|
|
53461
|
+
warn: this.fallback === "warn" ? true : void 0,
|
|
53462
|
+
reason: this.fallback === "warn" ? "audit: no policy result" : "no policy result"
|
|
53463
|
+
};
|
|
53464
|
+
}
|
|
53465
|
+
const allowed = result.allowed !== false;
|
|
53466
|
+
const decision = {
|
|
53467
|
+
allowed,
|
|
53468
|
+
reason: result.reason
|
|
53469
|
+
};
|
|
53470
|
+
if (result.capabilities) {
|
|
53471
|
+
decision.capabilities = result.capabilities;
|
|
53472
|
+
}
|
|
53473
|
+
return decision;
|
|
53474
|
+
}
|
|
53475
|
+
};
|
|
53476
|
+
}
|
|
53477
|
+
});
|
|
53478
|
+
|
|
53479
|
+
// src/enterprise/scheduler/knex-store.ts
|
|
53480
|
+
var knex_store_exports = {};
|
|
53481
|
+
__export(knex_store_exports, {
|
|
53482
|
+
KnexStoreBackend: () => KnexStoreBackend
|
|
53483
|
+
});
|
|
53484
|
+
function toNum(val) {
|
|
53485
|
+
if (val === null || val === void 0) return void 0;
|
|
53486
|
+
return typeof val === "string" ? parseInt(val, 10) : val;
|
|
53487
|
+
}
|
|
53488
|
+
function safeJsonParse2(value) {
|
|
53489
|
+
if (!value) return void 0;
|
|
53490
|
+
try {
|
|
53491
|
+
return JSON.parse(value);
|
|
53492
|
+
} catch {
|
|
53493
|
+
return void 0;
|
|
53494
|
+
}
|
|
53495
|
+
}
|
|
53496
|
+
function fromDbRow2(row) {
|
|
53497
|
+
return {
|
|
53498
|
+
id: row.id,
|
|
53499
|
+
creatorId: row.creator_id,
|
|
53500
|
+
creatorContext: row.creator_context ?? void 0,
|
|
53501
|
+
creatorName: row.creator_name ?? void 0,
|
|
53502
|
+
timezone: row.timezone,
|
|
53503
|
+
schedule: row.schedule_expr,
|
|
53504
|
+
runAt: toNum(row.run_at),
|
|
53505
|
+
isRecurring: row.is_recurring === true || row.is_recurring === 1,
|
|
53506
|
+
originalExpression: row.original_expression,
|
|
53507
|
+
workflow: row.workflow ?? void 0,
|
|
53508
|
+
workflowInputs: safeJsonParse2(row.workflow_inputs),
|
|
53509
|
+
outputContext: safeJsonParse2(row.output_context),
|
|
53510
|
+
status: row.status,
|
|
53511
|
+
createdAt: toNum(row.created_at),
|
|
53512
|
+
lastRunAt: toNum(row.last_run_at),
|
|
53513
|
+
nextRunAt: toNum(row.next_run_at),
|
|
53514
|
+
runCount: row.run_count,
|
|
53515
|
+
failureCount: row.failure_count,
|
|
53516
|
+
lastError: row.last_error ?? void 0,
|
|
53517
|
+
previousResponse: row.previous_response ?? void 0
|
|
53518
|
+
};
|
|
53519
|
+
}
|
|
53520
|
+
function toInsertRow(schedule) {
|
|
53521
|
+
return {
|
|
53522
|
+
id: schedule.id,
|
|
53523
|
+
creator_id: schedule.creatorId,
|
|
53524
|
+
creator_context: schedule.creatorContext ?? null,
|
|
53525
|
+
creator_name: schedule.creatorName ?? null,
|
|
53526
|
+
timezone: schedule.timezone,
|
|
53527
|
+
schedule_expr: schedule.schedule,
|
|
53528
|
+
run_at: schedule.runAt ?? null,
|
|
53529
|
+
is_recurring: schedule.isRecurring,
|
|
53530
|
+
original_expression: schedule.originalExpression,
|
|
53531
|
+
workflow: schedule.workflow ?? null,
|
|
53532
|
+
workflow_inputs: schedule.workflowInputs ? JSON.stringify(schedule.workflowInputs) : null,
|
|
53533
|
+
output_context: schedule.outputContext ? JSON.stringify(schedule.outputContext) : null,
|
|
53534
|
+
status: schedule.status,
|
|
53535
|
+
created_at: schedule.createdAt,
|
|
53536
|
+
last_run_at: schedule.lastRunAt ?? null,
|
|
53537
|
+
next_run_at: schedule.nextRunAt ?? null,
|
|
53538
|
+
run_count: schedule.runCount,
|
|
53539
|
+
failure_count: schedule.failureCount,
|
|
53540
|
+
last_error: schedule.lastError ?? null,
|
|
53541
|
+
previous_response: schedule.previousResponse ?? null
|
|
53542
|
+
};
|
|
53543
|
+
}
|
|
53544
|
+
var fs23, path27, import_uuid2, KnexStoreBackend;
|
|
53545
|
+
var init_knex_store = __esm({
|
|
53546
|
+
"src/enterprise/scheduler/knex-store.ts"() {
|
|
53547
|
+
"use strict";
|
|
53548
|
+
fs23 = __toESM(require("fs"));
|
|
53549
|
+
path27 = __toESM(require("path"));
|
|
53550
|
+
import_uuid2 = require("uuid");
|
|
53551
|
+
init_logger();
|
|
53552
|
+
KnexStoreBackend = class {
|
|
53553
|
+
knex = null;
|
|
53554
|
+
driver;
|
|
53555
|
+
connection;
|
|
53556
|
+
constructor(driver, storageConfig, _haConfig) {
|
|
53557
|
+
this.driver = driver;
|
|
53558
|
+
this.connection = storageConfig.connection || {};
|
|
53559
|
+
}
|
|
53560
|
+
async initialize() {
|
|
53561
|
+
const { createRequire } = require("module");
|
|
53562
|
+
const runtimeRequire = createRequire(__filename);
|
|
53563
|
+
let knexFactory;
|
|
53564
|
+
try {
|
|
53565
|
+
knexFactory = runtimeRequire("knex");
|
|
53566
|
+
} catch (err) {
|
|
53567
|
+
const code = err?.code;
|
|
53568
|
+
if (code === "MODULE_NOT_FOUND" || code === "ERR_MODULE_NOT_FOUND") {
|
|
53569
|
+
throw new Error(
|
|
53570
|
+
"knex is required for PostgreSQL/MySQL/MSSQL schedule storage. Install it with: npm install knex"
|
|
53571
|
+
);
|
|
53572
|
+
}
|
|
53573
|
+
throw err;
|
|
53574
|
+
}
|
|
53575
|
+
const clientMap = {
|
|
53576
|
+
postgresql: "pg",
|
|
53577
|
+
mysql: "mysql2",
|
|
53578
|
+
mssql: "tedious"
|
|
53579
|
+
};
|
|
53580
|
+
const client = clientMap[this.driver];
|
|
53581
|
+
let connection;
|
|
53582
|
+
if (this.connection.connection_string) {
|
|
53583
|
+
connection = this.connection.connection_string;
|
|
53584
|
+
} else if (this.driver === "mssql") {
|
|
53585
|
+
connection = this.buildMssqlConnection();
|
|
53586
|
+
} else {
|
|
53587
|
+
connection = this.buildStandardConnection();
|
|
53588
|
+
}
|
|
53589
|
+
this.knex = knexFactory({
|
|
53590
|
+
client,
|
|
53591
|
+
connection,
|
|
53592
|
+
pool: {
|
|
53593
|
+
min: this.connection.pool?.min ?? 0,
|
|
53594
|
+
max: this.connection.pool?.max ?? 10
|
|
53595
|
+
}
|
|
53596
|
+
});
|
|
53597
|
+
await this.migrateSchema();
|
|
53598
|
+
logger.info(`[KnexStore] Initialized (${this.driver})`);
|
|
53599
|
+
}
|
|
53600
|
+
buildStandardConnection() {
|
|
53601
|
+
return {
|
|
53602
|
+
host: this.connection.host || "localhost",
|
|
53603
|
+
port: this.connection.port,
|
|
53604
|
+
database: this.connection.database || "visor",
|
|
53605
|
+
user: this.connection.user,
|
|
53606
|
+
password: this.connection.password,
|
|
53607
|
+
ssl: this.resolveSslConfig()
|
|
53608
|
+
};
|
|
53609
|
+
}
|
|
53610
|
+
buildMssqlConnection() {
|
|
53611
|
+
const ssl = this.connection.ssl;
|
|
53612
|
+
const sslEnabled = ssl === true || typeof ssl === "object" && ssl.enabled !== false;
|
|
53613
|
+
return {
|
|
53614
|
+
server: this.connection.host || "localhost",
|
|
53615
|
+
port: this.connection.port,
|
|
53616
|
+
database: this.connection.database || "visor",
|
|
53617
|
+
user: this.connection.user,
|
|
53618
|
+
password: this.connection.password,
|
|
53619
|
+
options: {
|
|
53620
|
+
encrypt: sslEnabled,
|
|
53621
|
+
trustServerCertificate: typeof ssl === "object" ? ssl.reject_unauthorized === false : !sslEnabled
|
|
53622
|
+
}
|
|
53623
|
+
};
|
|
53624
|
+
}
|
|
53625
|
+
resolveSslConfig() {
|
|
53626
|
+
const ssl = this.connection.ssl;
|
|
53627
|
+
if (ssl === false || ssl === void 0) return false;
|
|
53628
|
+
if (ssl === true) return { rejectUnauthorized: true };
|
|
53629
|
+
if (ssl.enabled === false) return false;
|
|
53630
|
+
const result = {
|
|
53631
|
+
rejectUnauthorized: ssl.reject_unauthorized !== false
|
|
53632
|
+
};
|
|
53633
|
+
if (ssl.ca) {
|
|
53634
|
+
const caPath = this.validateSslPath(ssl.ca, "CA certificate");
|
|
53635
|
+
result.ca = fs23.readFileSync(caPath, "utf8");
|
|
53636
|
+
}
|
|
53637
|
+
if (ssl.cert) {
|
|
53638
|
+
const certPath = this.validateSslPath(ssl.cert, "client certificate");
|
|
53639
|
+
result.cert = fs23.readFileSync(certPath, "utf8");
|
|
53640
|
+
}
|
|
53641
|
+
if (ssl.key) {
|
|
53642
|
+
const keyPath = this.validateSslPath(ssl.key, "client key");
|
|
53643
|
+
result.key = fs23.readFileSync(keyPath, "utf8");
|
|
53644
|
+
}
|
|
53645
|
+
return result;
|
|
53646
|
+
}
|
|
53647
|
+
validateSslPath(filePath, label) {
|
|
53648
|
+
const resolved = path27.resolve(filePath);
|
|
53649
|
+
if (resolved !== path27.normalize(resolved)) {
|
|
53650
|
+
throw new Error(`SSL ${label} path contains invalid sequences: ${filePath}`);
|
|
53651
|
+
}
|
|
53652
|
+
if (!fs23.existsSync(resolved)) {
|
|
53653
|
+
throw new Error(`SSL ${label} not found: ${filePath}`);
|
|
53654
|
+
}
|
|
53655
|
+
return resolved;
|
|
53656
|
+
}
|
|
53657
|
+
async shutdown() {
|
|
53658
|
+
if (this.knex) {
|
|
53659
|
+
await this.knex.destroy();
|
|
53660
|
+
this.knex = null;
|
|
53661
|
+
}
|
|
53662
|
+
}
|
|
53663
|
+
async migrateSchema() {
|
|
53664
|
+
const knex = this.getKnex();
|
|
53665
|
+
const exists = await knex.schema.hasTable("schedules");
|
|
53666
|
+
if (!exists) {
|
|
53667
|
+
await knex.schema.createTable("schedules", (table) => {
|
|
53668
|
+
table.string("id", 36).primary();
|
|
53669
|
+
table.string("creator_id", 255).notNullable().index();
|
|
53670
|
+
table.string("creator_context", 255);
|
|
53671
|
+
table.string("creator_name", 255);
|
|
53672
|
+
table.string("timezone", 64).notNullable().defaultTo("UTC");
|
|
53673
|
+
table.string("schedule_expr", 255);
|
|
53674
|
+
table.bigInteger("run_at");
|
|
53675
|
+
table.boolean("is_recurring").notNullable();
|
|
53676
|
+
table.text("original_expression");
|
|
53677
|
+
table.string("workflow", 255);
|
|
53678
|
+
table.text("workflow_inputs");
|
|
53679
|
+
table.text("output_context");
|
|
53680
|
+
table.string("status", 20).notNullable().index();
|
|
53681
|
+
table.bigInteger("created_at").notNullable();
|
|
53682
|
+
table.bigInteger("last_run_at");
|
|
53683
|
+
table.bigInteger("next_run_at");
|
|
53684
|
+
table.integer("run_count").notNullable().defaultTo(0);
|
|
53685
|
+
table.integer("failure_count").notNullable().defaultTo(0);
|
|
53686
|
+
table.text("last_error");
|
|
53687
|
+
table.text("previous_response");
|
|
53688
|
+
table.index(["status", "next_run_at"]);
|
|
53689
|
+
});
|
|
53690
|
+
}
|
|
53691
|
+
const locksExist = await knex.schema.hasTable("scheduler_locks");
|
|
53692
|
+
if (!locksExist) {
|
|
53693
|
+
await knex.schema.createTable("scheduler_locks", (table) => {
|
|
53694
|
+
table.string("lock_id", 255).primary();
|
|
53695
|
+
table.string("node_id", 255).notNullable();
|
|
53696
|
+
table.string("lock_token", 36).notNullable();
|
|
53697
|
+
table.bigInteger("acquired_at").notNullable();
|
|
53698
|
+
table.bigInteger("expires_at").notNullable();
|
|
53699
|
+
});
|
|
53700
|
+
}
|
|
53701
|
+
}
|
|
53702
|
+
getKnex() {
|
|
53703
|
+
if (!this.knex) {
|
|
53704
|
+
throw new Error("[KnexStore] Not initialized. Call initialize() first.");
|
|
53705
|
+
}
|
|
53706
|
+
return this.knex;
|
|
53707
|
+
}
|
|
53708
|
+
// --- CRUD ---
|
|
53709
|
+
async create(schedule) {
|
|
53710
|
+
const knex = this.getKnex();
|
|
53711
|
+
const newSchedule = {
|
|
53712
|
+
...schedule,
|
|
53713
|
+
id: (0, import_uuid2.v4)(),
|
|
53714
|
+
createdAt: Date.now(),
|
|
53715
|
+
runCount: 0,
|
|
53716
|
+
failureCount: 0,
|
|
53717
|
+
status: "active"
|
|
53718
|
+
};
|
|
53719
|
+
await knex("schedules").insert(toInsertRow(newSchedule));
|
|
53720
|
+
logger.info(`[KnexStore] Created schedule ${newSchedule.id} for user ${newSchedule.creatorId}`);
|
|
53721
|
+
return newSchedule;
|
|
53722
|
+
}
|
|
53723
|
+
async importSchedule(schedule) {
|
|
53724
|
+
const knex = this.getKnex();
|
|
53725
|
+
const existing = await knex("schedules").where("id", schedule.id).first();
|
|
53726
|
+
if (existing) return;
|
|
53727
|
+
await knex("schedules").insert(toInsertRow(schedule));
|
|
53728
|
+
}
|
|
53729
|
+
async get(id) {
|
|
53730
|
+
const knex = this.getKnex();
|
|
53731
|
+
const row = await knex("schedules").where("id", id).first();
|
|
53732
|
+
return row ? fromDbRow2(row) : void 0;
|
|
53733
|
+
}
|
|
53734
|
+
async update(id, patch) {
|
|
53735
|
+
const knex = this.getKnex();
|
|
53736
|
+
const existing = await knex("schedules").where("id", id).first();
|
|
53737
|
+
if (!existing) return void 0;
|
|
53738
|
+
const current = fromDbRow2(existing);
|
|
53739
|
+
const updated = { ...current, ...patch, id: current.id };
|
|
53740
|
+
const row = toInsertRow(updated);
|
|
53741
|
+
delete row.id;
|
|
53742
|
+
await knex("schedules").where("id", id).update(row);
|
|
53743
|
+
return updated;
|
|
53744
|
+
}
|
|
53745
|
+
async delete(id) {
|
|
53746
|
+
const knex = this.getKnex();
|
|
53747
|
+
const deleted = await knex("schedules").where("id", id).del();
|
|
53748
|
+
if (deleted > 0) {
|
|
53749
|
+
logger.info(`[KnexStore] Deleted schedule ${id}`);
|
|
53750
|
+
return true;
|
|
53751
|
+
}
|
|
53752
|
+
return false;
|
|
53753
|
+
}
|
|
53754
|
+
// --- Queries ---
|
|
53755
|
+
async getByCreator(creatorId) {
|
|
53756
|
+
const knex = this.getKnex();
|
|
53757
|
+
const rows = await knex("schedules").where("creator_id", creatorId);
|
|
53758
|
+
return rows.map((r) => fromDbRow2(r));
|
|
53759
|
+
}
|
|
53760
|
+
async getActiveSchedules() {
|
|
53761
|
+
const knex = this.getKnex();
|
|
53762
|
+
const rows = await knex("schedules").where("status", "active");
|
|
53763
|
+
return rows.map((r) => fromDbRow2(r));
|
|
53764
|
+
}
|
|
53765
|
+
async getDueSchedules(now) {
|
|
53766
|
+
const ts = now ?? Date.now();
|
|
53767
|
+
const knex = this.getKnex();
|
|
53768
|
+
const bFalse = this.driver === "mssql" ? 0 : false;
|
|
53769
|
+
const bTrue = this.driver === "mssql" ? 1 : true;
|
|
53770
|
+
const rows = await knex("schedules").where("status", "active").andWhere(function() {
|
|
53771
|
+
this.where(function() {
|
|
53772
|
+
this.where("is_recurring", bFalse).whereNotNull("run_at").where("run_at", "<=", ts);
|
|
53773
|
+
}).orWhere(function() {
|
|
53774
|
+
this.where("is_recurring", bTrue).whereNotNull("next_run_at").where("next_run_at", "<=", ts);
|
|
53775
|
+
});
|
|
53776
|
+
});
|
|
53777
|
+
return rows.map((r) => fromDbRow2(r));
|
|
53778
|
+
}
|
|
53779
|
+
async findByWorkflow(creatorId, workflowName) {
|
|
53780
|
+
const knex = this.getKnex();
|
|
53781
|
+
const escaped = workflowName.toLowerCase().replace(/[%_\\]/g, "\\$&");
|
|
53782
|
+
const pattern = `%${escaped}%`;
|
|
53783
|
+
const rows = await knex("schedules").where("creator_id", creatorId).where("status", "active").whereRaw("LOWER(workflow) LIKE ? ESCAPE '\\'", [pattern]);
|
|
53784
|
+
return rows.map((r) => fromDbRow2(r));
|
|
53785
|
+
}
|
|
53786
|
+
async getAll() {
|
|
53787
|
+
const knex = this.getKnex();
|
|
53788
|
+
const rows = await knex("schedules");
|
|
53789
|
+
return rows.map((r) => fromDbRow2(r));
|
|
53790
|
+
}
|
|
53791
|
+
async getStats() {
|
|
53792
|
+
const knex = this.getKnex();
|
|
53793
|
+
const boolTrue = this.driver === "mssql" ? "1" : "true";
|
|
53794
|
+
const boolFalse = this.driver === "mssql" ? "0" : "false";
|
|
53795
|
+
const result = await knex("schedules").select(
|
|
53796
|
+
knex.raw("COUNT(*) as total"),
|
|
53797
|
+
knex.raw("SUM(CASE WHEN status = 'active' THEN 1 ELSE 0 END) as active"),
|
|
53798
|
+
knex.raw("SUM(CASE WHEN status = 'paused' THEN 1 ELSE 0 END) as paused"),
|
|
53799
|
+
knex.raw("SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed"),
|
|
53800
|
+
knex.raw("SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed"),
|
|
53801
|
+
knex.raw(`SUM(CASE WHEN is_recurring = ${boolTrue} THEN 1 ELSE 0 END) as recurring`),
|
|
53802
|
+
knex.raw(`SUM(CASE WHEN is_recurring = ${boolFalse} THEN 1 ELSE 0 END) as one_time`)
|
|
53803
|
+
).first();
|
|
53804
|
+
return {
|
|
53805
|
+
total: Number(result.total) || 0,
|
|
53806
|
+
active: Number(result.active) || 0,
|
|
53807
|
+
paused: Number(result.paused) || 0,
|
|
53808
|
+
completed: Number(result.completed) || 0,
|
|
53809
|
+
failed: Number(result.failed) || 0,
|
|
53810
|
+
recurring: Number(result.recurring) || 0,
|
|
53811
|
+
oneTime: Number(result.one_time) || 0
|
|
53812
|
+
};
|
|
53813
|
+
}
|
|
53814
|
+
async validateLimits(creatorId, isRecurring, limits) {
|
|
53815
|
+
const knex = this.getKnex();
|
|
53816
|
+
if (limits.maxGlobal) {
|
|
53817
|
+
const result = await knex("schedules").count("* as cnt").first();
|
|
53818
|
+
if (Number(result?.cnt) >= limits.maxGlobal) {
|
|
53819
|
+
throw new Error(`Global schedule limit reached (${limits.maxGlobal})`);
|
|
53820
|
+
}
|
|
53821
|
+
}
|
|
53822
|
+
if (limits.maxPerUser) {
|
|
53823
|
+
const result = await knex("schedules").where("creator_id", creatorId).count("* as cnt").first();
|
|
53824
|
+
if (Number(result?.cnt) >= limits.maxPerUser) {
|
|
53825
|
+
throw new Error(`You have reached the maximum number of schedules (${limits.maxPerUser})`);
|
|
53826
|
+
}
|
|
53827
|
+
}
|
|
53828
|
+
if (isRecurring && limits.maxRecurringPerUser) {
|
|
53829
|
+
const bTrue = this.driver === "mssql" ? 1 : true;
|
|
53830
|
+
const result = await knex("schedules").where("creator_id", creatorId).where("is_recurring", bTrue).count("* as cnt").first();
|
|
53831
|
+
if (Number(result?.cnt) >= limits.maxRecurringPerUser) {
|
|
53832
|
+
throw new Error(
|
|
53833
|
+
`You have reached the maximum number of recurring schedules (${limits.maxRecurringPerUser})`
|
|
53834
|
+
);
|
|
53835
|
+
}
|
|
53836
|
+
}
|
|
53837
|
+
}
|
|
53838
|
+
// --- HA Distributed Locking (via scheduler_locks table) ---
|
|
53839
|
+
async tryAcquireLock(lockId, nodeId, ttlSeconds) {
|
|
53840
|
+
const knex = this.getKnex();
|
|
53841
|
+
const now = Date.now();
|
|
53842
|
+
const expiresAt = now + ttlSeconds * 1e3;
|
|
53843
|
+
const token = (0, import_uuid2.v4)();
|
|
53844
|
+
const updated = await knex("scheduler_locks").where("lock_id", lockId).where("expires_at", "<", now).update({
|
|
53845
|
+
node_id: nodeId,
|
|
53846
|
+
lock_token: token,
|
|
53847
|
+
acquired_at: now,
|
|
53848
|
+
expires_at: expiresAt
|
|
53849
|
+
});
|
|
53850
|
+
if (updated > 0) return token;
|
|
53851
|
+
try {
|
|
53852
|
+
await knex("scheduler_locks").insert({
|
|
53853
|
+
lock_id: lockId,
|
|
53854
|
+
node_id: nodeId,
|
|
53855
|
+
lock_token: token,
|
|
53856
|
+
acquired_at: now,
|
|
53857
|
+
expires_at: expiresAt
|
|
53858
|
+
});
|
|
53859
|
+
return token;
|
|
53860
|
+
} catch {
|
|
53861
|
+
return null;
|
|
53862
|
+
}
|
|
53863
|
+
}
|
|
53864
|
+
async releaseLock(lockId, lockToken) {
|
|
53865
|
+
const knex = this.getKnex();
|
|
53866
|
+
await knex("scheduler_locks").where("lock_id", lockId).where("lock_token", lockToken).del();
|
|
53867
|
+
}
|
|
53868
|
+
async renewLock(lockId, lockToken, ttlSeconds) {
|
|
53869
|
+
const knex = this.getKnex();
|
|
53870
|
+
const now = Date.now();
|
|
53871
|
+
const expiresAt = now + ttlSeconds * 1e3;
|
|
53872
|
+
const updated = await knex("scheduler_locks").where("lock_id", lockId).where("lock_token", lockToken).update({ acquired_at: now, expires_at: expiresAt });
|
|
53873
|
+
return updated > 0;
|
|
53874
|
+
}
|
|
53875
|
+
async flush() {
|
|
53876
|
+
}
|
|
53877
|
+
};
|
|
53878
|
+
}
|
|
53879
|
+
});
|
|
53880
|
+
|
|
53881
|
+
// src/enterprise/loader.ts
|
|
53882
|
+
var loader_exports = {};
|
|
53883
|
+
__export(loader_exports, {
|
|
53884
|
+
loadEnterprisePolicyEngine: () => loadEnterprisePolicyEngine,
|
|
53885
|
+
loadEnterpriseStoreBackend: () => loadEnterpriseStoreBackend
|
|
53886
|
+
});
|
|
53887
|
+
async function loadEnterprisePolicyEngine(config) {
|
|
53888
|
+
try {
|
|
53889
|
+
const { LicenseValidator: LicenseValidator2 } = await Promise.resolve().then(() => (init_validator(), validator_exports));
|
|
53890
|
+
const validator = new LicenseValidator2();
|
|
53891
|
+
const license = await validator.loadAndValidate();
|
|
53892
|
+
if (!license || !validator.hasFeature("policy")) {
|
|
53893
|
+
return new DefaultPolicyEngine();
|
|
53894
|
+
}
|
|
53895
|
+
if (validator.isInGracePeriod()) {
|
|
53896
|
+
console.warn(
|
|
53897
|
+
"[visor:enterprise] License has expired but is within the 72-hour grace period. Please renew your license."
|
|
53898
|
+
);
|
|
53899
|
+
}
|
|
53900
|
+
const { OpaPolicyEngine: OpaPolicyEngine2 } = await Promise.resolve().then(() => (init_opa_policy_engine(), opa_policy_engine_exports));
|
|
53901
|
+
const engine = new OpaPolicyEngine2(config);
|
|
53902
|
+
await engine.initialize(config);
|
|
53903
|
+
return engine;
|
|
53904
|
+
} catch (err) {
|
|
53905
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
53906
|
+
try {
|
|
53907
|
+
const { logger: logger2 } = (init_logger(), __toCommonJS(logger_exports));
|
|
53908
|
+
logger2.warn(`[PolicyEngine] Enterprise policy init failed, falling back to default: ${msg}`);
|
|
53909
|
+
} catch {
|
|
53910
|
+
}
|
|
53911
|
+
return new DefaultPolicyEngine();
|
|
53912
|
+
}
|
|
53913
|
+
}
|
|
53914
|
+
async function loadEnterpriseStoreBackend(driver, storageConfig, haConfig) {
|
|
53915
|
+
const { LicenseValidator: LicenseValidator2 } = await Promise.resolve().then(() => (init_validator(), validator_exports));
|
|
53916
|
+
const validator = new LicenseValidator2();
|
|
53917
|
+
const license = await validator.loadAndValidate();
|
|
53918
|
+
if (!license || !validator.hasFeature("scheduler-sql")) {
|
|
53919
|
+
throw new Error(
|
|
53920
|
+
`The ${driver} schedule storage driver requires a Visor Enterprise license with the 'scheduler-sql' feature. Please upgrade or use driver: 'sqlite' (default).`
|
|
53921
|
+
);
|
|
53922
|
+
}
|
|
53923
|
+
if (validator.isInGracePeriod()) {
|
|
53924
|
+
console.warn(
|
|
53925
|
+
"[visor:enterprise] License has expired but is within the 72-hour grace period. Please renew your license."
|
|
53926
|
+
);
|
|
53927
|
+
}
|
|
53928
|
+
const { KnexStoreBackend: KnexStoreBackend2 } = await Promise.resolve().then(() => (init_knex_store(), knex_store_exports));
|
|
53929
|
+
return new KnexStoreBackend2(driver, storageConfig, haConfig);
|
|
53930
|
+
}
|
|
53931
|
+
var init_loader = __esm({
|
|
53932
|
+
"src/enterprise/loader.ts"() {
|
|
53933
|
+
"use strict";
|
|
53934
|
+
init_default_engine();
|
|
53935
|
+
}
|
|
53936
|
+
});
|
|
53937
|
+
|
|
52280
53938
|
// src/event-bus/event-bus.ts
|
|
52281
53939
|
var event_bus_exports = {};
|
|
52282
53940
|
__export(event_bus_exports, {
|
|
@@ -52322,12 +53980,12 @@ var ndjson_sink_exports = {};
|
|
|
52322
53980
|
__export(ndjson_sink_exports, {
|
|
52323
53981
|
NdjsonSink: () => NdjsonSink
|
|
52324
53982
|
});
|
|
52325
|
-
var
|
|
53983
|
+
var import_fs9, import_path13, NdjsonSink;
|
|
52326
53984
|
var init_ndjson_sink = __esm({
|
|
52327
53985
|
"src/frontends/ndjson-sink.ts"() {
|
|
52328
53986
|
"use strict";
|
|
52329
|
-
|
|
52330
|
-
|
|
53987
|
+
import_fs9 = __toESM(require("fs"));
|
|
53988
|
+
import_path13 = __toESM(require("path"));
|
|
52331
53989
|
NdjsonSink = class {
|
|
52332
53990
|
name = "ndjson-sink";
|
|
52333
53991
|
cfg;
|
|
@@ -52348,7 +54006,7 @@ var init_ndjson_sink = __esm({
|
|
|
52348
54006
|
payload: envelope && envelope.payload || envelope,
|
|
52349
54007
|
safe: true
|
|
52350
54008
|
});
|
|
52351
|
-
await
|
|
54009
|
+
await import_fs9.default.promises.appendFile(this.filePath, line + "\n");
|
|
52352
54010
|
} catch (err) {
|
|
52353
54011
|
ctx.logger.error("[ndjson-sink] Failed to write event:", err);
|
|
52354
54012
|
}
|
|
@@ -52359,8 +54017,8 @@ var init_ndjson_sink = __esm({
|
|
|
52359
54017
|
this.unsub = void 0;
|
|
52360
54018
|
}
|
|
52361
54019
|
resolveFile(p) {
|
|
52362
|
-
if (
|
|
52363
|
-
return
|
|
54020
|
+
if (import_path13.default.isAbsolute(p)) return p;
|
|
54021
|
+
return import_path13.default.join(process.cwd(), p);
|
|
52364
54022
|
}
|
|
52365
54023
|
};
|
|
52366
54024
|
}
|
|
@@ -53183,8 +54841,8 @@ ${content}
|
|
|
53183
54841
|
* Sleep utility
|
|
53184
54842
|
*/
|
|
53185
54843
|
sleep(ms) {
|
|
53186
|
-
return new Promise((
|
|
53187
|
-
const t = setTimeout(
|
|
54844
|
+
return new Promise((resolve17) => {
|
|
54845
|
+
const t = setTimeout(resolve17, ms);
|
|
53188
54846
|
if (typeof t.unref === "function") {
|
|
53189
54847
|
try {
|
|
53190
54848
|
t.unref();
|
|
@@ -53458,8 +55116,8 @@ ${end}`);
|
|
|
53458
55116
|
async updateGroupedComment(ctx, comments, group, changedIds) {
|
|
53459
55117
|
const existingLock = this.updateLocks.get(group);
|
|
53460
55118
|
let resolveLock;
|
|
53461
|
-
const ourLock = new Promise((
|
|
53462
|
-
resolveLock =
|
|
55119
|
+
const ourLock = new Promise((resolve17) => {
|
|
55120
|
+
resolveLock = resolve17;
|
|
53463
55121
|
});
|
|
53464
55122
|
this.updateLocks.set(group, ourLock);
|
|
53465
55123
|
try {
|
|
@@ -53771,7 +55429,7 @@ ${blocks}
|
|
|
53771
55429
|
* Sleep utility for enforcing delays
|
|
53772
55430
|
*/
|
|
53773
55431
|
sleep(ms) {
|
|
53774
|
-
return new Promise((
|
|
55432
|
+
return new Promise((resolve17) => setTimeout(resolve17, ms));
|
|
53775
55433
|
}
|
|
53776
55434
|
};
|
|
53777
55435
|
}
|
|
@@ -55042,15 +56700,15 @@ function serializeRunState(state) {
|
|
|
55042
56700
|
])
|
|
55043
56701
|
};
|
|
55044
56702
|
}
|
|
55045
|
-
var
|
|
56703
|
+
var path29, fs25, StateMachineExecutionEngine;
|
|
55046
56704
|
var init_state_machine_execution_engine = __esm({
|
|
55047
56705
|
"src/state-machine-execution-engine.ts"() {
|
|
55048
56706
|
"use strict";
|
|
55049
56707
|
init_runner();
|
|
55050
56708
|
init_logger();
|
|
55051
56709
|
init_sandbox_manager();
|
|
55052
|
-
|
|
55053
|
-
|
|
56710
|
+
path29 = __toESM(require("path"));
|
|
56711
|
+
fs25 = __toESM(require("fs"));
|
|
55054
56712
|
StateMachineExecutionEngine = class _StateMachineExecutionEngine {
|
|
55055
56713
|
workingDirectory;
|
|
55056
56714
|
executionContext;
|
|
@@ -55282,8 +56940,8 @@ var init_state_machine_execution_engine = __esm({
|
|
|
55282
56940
|
logger.debug(
|
|
55283
56941
|
`[PolicyEngine] Loading enterprise policy engine (engine=${configWithTagFilter.policy.engine})`
|
|
55284
56942
|
);
|
|
55285
|
-
const { loadEnterprisePolicyEngine } = await
|
|
55286
|
-
context2.policyEngine = await
|
|
56943
|
+
const { loadEnterprisePolicyEngine: loadEnterprisePolicyEngine2 } = await Promise.resolve().then(() => (init_loader(), loader_exports));
|
|
56944
|
+
context2.policyEngine = await loadEnterprisePolicyEngine2(configWithTagFilter.policy);
|
|
55287
56945
|
logger.debug(
|
|
55288
56946
|
`[PolicyEngine] Initialized: ${context2.policyEngine?.constructor?.name || "unknown"}`
|
|
55289
56947
|
);
|
|
@@ -55435,9 +57093,9 @@ var init_state_machine_execution_engine = __esm({
|
|
|
55435
57093
|
}
|
|
55436
57094
|
const checkId = String(ev?.checkId || "unknown");
|
|
55437
57095
|
const threadKey = ev?.threadKey || (channel && threadTs ? `${channel}:${threadTs}` : "session");
|
|
55438
|
-
const baseDir = process.env.VISOR_SNAPSHOT_DIR ||
|
|
55439
|
-
|
|
55440
|
-
const filePath =
|
|
57096
|
+
const baseDir = process.env.VISOR_SNAPSHOT_DIR || path29.resolve(process.cwd(), ".visor", "snapshots");
|
|
57097
|
+
fs25.mkdirSync(baseDir, { recursive: true });
|
|
57098
|
+
const filePath = path29.join(baseDir, `${threadKey}-${checkId}.json`);
|
|
55441
57099
|
await this.saveSnapshotToFile(filePath);
|
|
55442
57100
|
logger.info(`[Snapshot] Saved run snapshot: ${filePath}`);
|
|
55443
57101
|
try {
|
|
@@ -55578,7 +57236,7 @@ var init_state_machine_execution_engine = __esm({
|
|
|
55578
57236
|
* Does not include secrets. Intended for debugging and future resume support.
|
|
55579
57237
|
*/
|
|
55580
57238
|
async saveSnapshotToFile(filePath) {
|
|
55581
|
-
const
|
|
57239
|
+
const fs26 = await import("fs/promises");
|
|
55582
57240
|
const ctx = this._lastContext;
|
|
55583
57241
|
const runner = this._lastRunner;
|
|
55584
57242
|
if (!ctx || !runner) {
|
|
@@ -55598,14 +57256,14 @@ var init_state_machine_execution_engine = __esm({
|
|
|
55598
57256
|
journal: entries,
|
|
55599
57257
|
requestedChecks: ctx.requestedChecks || []
|
|
55600
57258
|
};
|
|
55601
|
-
await
|
|
57259
|
+
await fs26.writeFile(filePath, JSON.stringify(payload, null, 2), "utf8");
|
|
55602
57260
|
}
|
|
55603
57261
|
/**
|
|
55604
57262
|
* Load a snapshot JSON from file and return it. Resume support can build on this.
|
|
55605
57263
|
*/
|
|
55606
57264
|
async loadSnapshotFromFile(filePath) {
|
|
55607
|
-
const
|
|
55608
|
-
const raw = await
|
|
57265
|
+
const fs26 = await import("fs/promises");
|
|
57266
|
+
const raw = await fs26.readFile(filePath, "utf8");
|
|
55609
57267
|
return JSON.parse(raw);
|
|
55610
57268
|
}
|
|
55611
57269
|
/**
|