@probelabs/visor 0.1.149 → 0.1.150-ee
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -1
- package/dist/909.index.js +27117 -0
- package/dist/ai-review-service.d.ts.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/docs/assistant-workflows.md +805 -0
- package/dist/docs/event-triggers.md +25 -0
- package/dist/docs/scheduler.md +156 -0
- package/dist/docs/slack-integration.md +48 -0
- package/dist/enterprise/scheduler/knex-store.d.ts +7 -1
- package/dist/enterprise/scheduler/knex-store.d.ts.map +1 -1
- package/dist/examples/code-talk-as-tool.yaml +76 -0
- package/dist/examples/code-talk-workflow.yaml +68 -0
- package/dist/examples/intent-router-workflow.yaml +66 -0
- package/dist/examples/slack-message-triggers.yaml +270 -0
- package/dist/generated/config-schema.d.ts +102 -7
- package/dist/generated/config-schema.d.ts.map +1 -1
- package/dist/generated/config-schema.json +116 -7
- package/dist/git-repository-analyzer.d.ts +8 -0
- package/dist/git-repository-analyzer.d.ts.map +1 -1
- package/dist/index.js +5868 -4756
- package/dist/scheduler/message-trigger.d.ts +47 -0
- package/dist/scheduler/message-trigger.d.ts.map +1 -0
- package/dist/scheduler/schedule-store.d.ts +31 -1
- package/dist/scheduler/schedule-store.d.ts.map +1 -1
- package/dist/scheduler/schedule-tool.d.ts +17 -1
- package/dist/scheduler/schedule-tool.d.ts.map +1 -1
- package/dist/scheduler/store/sqlite-store.d.ts +7 -1
- package/dist/scheduler/store/sqlite-store.d.ts.map +1 -1
- package/dist/scheduler/store/types.d.ts +45 -0
- package/dist/scheduler/store/types.d.ts.map +1 -1
- package/dist/sdk/{check-provider-registry-LVLC4EPF.mjs → check-provider-registry-RRYBG6AU.mjs} +6 -6
- package/dist/sdk/{check-provider-registry-X4OZJWPK.mjs → check-provider-registry-VHM53BTI.mjs} +6 -6
- package/dist/sdk/{chunk-V6GI4U2M.mjs → chunk-AADKUA6L.mjs} +585 -29
- package/dist/sdk/{chunk-6EXCUX7Y.mjs.map → chunk-AADKUA6L.mjs.map} +1 -1
- package/dist/sdk/{chunk-YYZAN5NK.mjs → chunk-FYK2DJK6.mjs} +106 -9
- package/dist/sdk/chunk-FYK2DJK6.mjs.map +1 -0
- package/dist/sdk/{chunk-RJLJUTSU.mjs → chunk-FZEQ744M.mjs} +2 -2
- package/dist/sdk/{chunk-Q6EPAJ6Z.mjs → chunk-GLROSEYJ.mjs} +3 -3
- package/dist/sdk/{chunk-6EXCUX7Y.mjs → chunk-O4H5KZ7Y.mjs} +586 -30
- package/dist/sdk/{chunk-GFNXX64M.mjs.map → chunk-O4H5KZ7Y.mjs.map} +1 -1
- package/dist/sdk/{chunk-62TNF5PJ.mjs → chunk-PETLPNRA.mjs} +2 -2
- package/dist/sdk/{chunk-62TNF5PJ.mjs.map → chunk-PETLPNRA.mjs.map} +1 -1
- package/dist/sdk/{config-KQH254CA.mjs → config-MTEIGCOQ.mjs} +2 -2
- package/dist/sdk/{failure-condition-evaluator-IVCTD4BZ.mjs → failure-condition-evaluator-XV2ZMFFY.mjs} +3 -3
- package/dist/sdk/{git-repository-analyzer-QFMW6WIS.mjs → git-repository-analyzer-TWNJUN42.mjs} +34 -3
- package/dist/sdk/git-repository-analyzer-TWNJUN42.mjs.map +1 -0
- package/dist/sdk/{github-frontend-DFT5G32K.mjs → github-frontend-A2R7D4N6.mjs} +3 -3
- package/dist/sdk/{host-7YKRMOUJ.mjs → host-BRZQLVCH.mjs} +2 -2
- package/dist/sdk/{host-H7IX4GBK.mjs → host-MR7L57QI.mjs} +2 -2
- package/dist/sdk/knex-store-CRORFJE6.mjs +527 -0
- package/dist/sdk/knex-store-CRORFJE6.mjs.map +1 -0
- package/dist/sdk/loader-NJCF7DUS.mjs +89 -0
- package/dist/sdk/loader-NJCF7DUS.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-LU5PAREW.mjs → routing-V3MYZAOH.mjs} +4 -4
- package/dist/sdk/{schedule-tool-EOMZFICZ.mjs → schedule-tool-4QDBJV74.mjs} +6 -6
- package/dist/sdk/{schedule-tool-NX75VKGA.mjs → schedule-tool-NYLNJUEW.mjs} +6 -6
- package/dist/sdk/{schedule-tool-handler-3FJHDIPG.mjs → schedule-tool-handler-27XEVLSY.mjs} +6 -6
- package/dist/sdk/{schedule-tool-handler-KKN7XJYT.mjs → schedule-tool-handler-EFQIYD3W.mjs} +6 -6
- package/dist/sdk/sdk.d.mts +38 -1
- package/dist/sdk/sdk.d.ts +38 -1
- package/dist/sdk/sdk.js +2338 -280
- package/dist/sdk/sdk.js.map +1 -1
- package/dist/sdk/sdk.mjs +5 -5
- package/dist/sdk/{trace-helpers-EJUIOP6L.mjs → trace-helpers-6NSZBC35.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-AGZ5JY2I.mjs → workflow-check-provider-3MVUCEJR.mjs} +6 -6
- package/dist/sdk/{workflow-check-provider-PTNUWM5W.mjs → workflow-check-provider-XLH7N4FV.mjs} +6 -6
- package/dist/slack/socket-runner.d.ts +14 -0
- package/dist/slack/socket-runner.d.ts.map +1 -1
- package/dist/test-runner/core/flow-stage.d.ts.map +1 -1
- package/dist/test-runner/fixture-loader.d.ts +1 -1
- package/dist/test-runner/fixture-loader.d.ts.map +1 -1
- package/dist/test-runner/index.d.ts.map +1 -1
- package/dist/test-runner/validator.d.ts.map +1 -1
- package/dist/types/config.d.ts +38 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/utils/workspace-manager.d.ts +4 -0
- package/dist/utils/workspace-manager.d.ts.map +1 -1
- package/dist/utils/worktree-manager.d.ts +15 -1
- package/dist/utils/worktree-manager.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/output/traces/run-2026-03-03T07-19-07-543Z.ndjson +0 -138
- package/dist/output/traces/run-2026-03-03T07-19-50-933Z.ndjson +0 -2121
- package/dist/sdk/check-provider-registry-IYSUDKPB.mjs +0 -29
- package/dist/sdk/chunk-BR7DYA3S.mjs +0 -739
- package/dist/sdk/chunk-CISJ6DJW.mjs +0 -1502
- package/dist/sdk/chunk-GFNXX64M.mjs +0 -43159
- package/dist/sdk/chunk-Q6EPAJ6Z.mjs.map +0 -1
- package/dist/sdk/chunk-RJLJUTSU.mjs.map +0 -1
- package/dist/sdk/chunk-V6GI4U2M.mjs.map +0 -1
- package/dist/sdk/chunk-VLUGLWLA.mjs +0 -443
- package/dist/sdk/chunk-VLUGLWLA.mjs.map +0 -1
- package/dist/sdk/chunk-YYZAN5NK.mjs.map +0 -1
- package/dist/sdk/failure-condition-evaluator-LZ2AG5PY.mjs +0 -17
- package/dist/sdk/git-repository-analyzer-QFMW6WIS.mjs.map +0 -1
- package/dist/sdk/github-frontend-S523EEJB.mjs +0 -1368
- package/dist/sdk/github-frontend-S523EEJB.mjs.map +0 -1
- package/dist/sdk/routing-ZMBKWMVI.mjs +0 -25
- package/dist/sdk/schedule-tool-CDVUSZEG.mjs +0 -35
- package/dist/sdk/schedule-tool-handler-KKN7XJYT.mjs.map +0 -1
- package/dist/sdk/schedule-tool-handler-QNZG55DX.mjs +0 -39
- package/dist/sdk/schedule-tool-handler-QNZG55DX.mjs.map +0 -1
- package/dist/sdk/trace-helpers-6ROJR7N3.mjs +0 -25
- package/dist/sdk/trace-helpers-6ROJR7N3.mjs.map +0 -1
- package/dist/sdk/trace-helpers-EJUIOP6L.mjs.map +0 -1
- package/dist/sdk/workflow-check-provider-6ERNNCNA.mjs +0 -29
- package/dist/sdk/workflow-check-provider-6ERNNCNA.mjs.map +0 -1
- package/dist/sdk/workflow-check-provider-AGZ5JY2I.mjs.map +0 -1
- package/dist/sdk/workflow-check-provider-PTNUWM5W.mjs.map +0 -1
- package/dist/traces/run-2026-03-03T07-19-07-543Z.ndjson +0 -138
- package/dist/traces/run-2026-03-03T07-19-50-933Z.ndjson +0 -2121
- /package/dist/sdk/{check-provider-registry-IYSUDKPB.mjs.map → check-provider-registry-RRYBG6AU.mjs.map} +0 -0
- /package/dist/sdk/{check-provider-registry-LVLC4EPF.mjs.map → check-provider-registry-VHM53BTI.mjs.map} +0 -0
- /package/dist/sdk/{chunk-BR7DYA3S.mjs.map → chunk-FZEQ744M.mjs.map} +0 -0
- /package/dist/sdk/{chunk-CISJ6DJW.mjs.map → chunk-GLROSEYJ.mjs.map} +0 -0
- /package/dist/sdk/{check-provider-registry-X4OZJWPK.mjs.map → config-MTEIGCOQ.mjs.map} +0 -0
- /package/dist/sdk/{config-KQH254CA.mjs.map → failure-condition-evaluator-XV2ZMFFY.mjs.map} +0 -0
- /package/dist/sdk/{github-frontend-DFT5G32K.mjs.map → github-frontend-A2R7D4N6.mjs.map} +0 -0
- /package/dist/sdk/{host-7YKRMOUJ.mjs.map → host-BRZQLVCH.mjs.map} +0 -0
- /package/dist/sdk/{host-H7IX4GBK.mjs.map → host-MR7L57QI.mjs.map} +0 -0
- /package/dist/sdk/{failure-condition-evaluator-IVCTD4BZ.mjs.map → routing-V3MYZAOH.mjs.map} +0 -0
- /package/dist/sdk/{failure-condition-evaluator-LZ2AG5PY.mjs.map → schedule-tool-4QDBJV74.mjs.map} +0 -0
- /package/dist/sdk/{routing-LU5PAREW.mjs.map → schedule-tool-NYLNJUEW.mjs.map} +0 -0
- /package/dist/sdk/{routing-ZMBKWMVI.mjs.map → schedule-tool-handler-27XEVLSY.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-CDVUSZEG.mjs.map → schedule-tool-handler-EFQIYD3W.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-EOMZFICZ.mjs.map → trace-helpers-6NSZBC35.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-NX75VKGA.mjs.map → workflow-check-provider-3MVUCEJR.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-handler-3FJHDIPG.mjs.map → workflow-check-provider-XLH7N4FV.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-rc266",
|
|
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((resolve18, reject) => {
|
|
868
868
|
const callback = async (span) => {
|
|
869
869
|
try {
|
|
870
870
|
const res = await fn(span);
|
|
871
|
-
|
|
871
|
+
resolve18(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 path31 = require("path");
|
|
949
|
+
const fs27 = 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 = path31.dirname(__ndjsonPath);
|
|
953
|
+
if (!fs27.existsSync(dir)) fs27.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 || path31.join(process.cwd(), "output", "traces");
|
|
957
|
+
if (!fs27.existsSync(outDir)) fs27.mkdirSync(outDir, { recursive: true });
|
|
958
958
|
if (!__ndjsonPath) {
|
|
959
959
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
960
|
-
__ndjsonPath =
|
|
960
|
+
__ndjsonPath = path31.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 fs27 = require("fs");
|
|
970
970
|
const p = __getOrCreateNdjsonPath();
|
|
971
971
|
if (!p) return;
|
|
972
972
|
const line = { name: "visor.run", attributes: { started: true } };
|
|
973
|
-
|
|
973
|
+
fs27.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 path31 = typeof pathExpr === "string" ? pathExpr : String(pathExpr || "");
|
|
3738
|
+
if (!path31) return obj;
|
|
3739
|
+
const parts = path31.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, path31) => {
|
|
3859
|
+
if (!obj || !path31) return void 0;
|
|
3860
|
+
const parts = path31.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 fs27 = await import("fs/promises");
|
|
6413
|
+
const path31 = 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 = path31.resolve(process.cwd(), file);
|
|
6422
|
+
templateContent = await fs27.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
|
+
path31.join(__dirname, "output", sanitized, "template.liquid"),
|
|
6428
6428
|
// bundled: dist/output/
|
|
6429
|
-
|
|
6429
|
+
path31.join(__dirname, "..", "..", "output", sanitized, "template.liquid"),
|
|
6430
6430
|
// source: output/
|
|
6431
|
-
|
|
6431
|
+
path31.join(process.cwd(), "output", sanitized, "template.liquid"),
|
|
6432
6432
|
// fallback: cwd/output/
|
|
6433
|
-
|
|
6433
|
+
path31.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 fs27.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 fs27 = 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 (fs27.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((resolve18) => {
|
|
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
|
+
resolve18({ success: true });
|
|
7006
7006
|
} else {
|
|
7007
|
-
|
|
7007
|
+
resolve18({ success: false, error: stderr || `Exit code ${code}` });
|
|
7008
7008
|
}
|
|
7009
7009
|
});
|
|
7010
7010
|
proc.on("error", (err) => {
|
|
7011
|
-
|
|
7011
|
+
resolve18({ success: false, error: err.message });
|
|
7012
7012
|
});
|
|
7013
7013
|
});
|
|
7014
7014
|
if (!result.success) {
|
|
@@ -7192,6 +7192,23 @@ function createProbeTracerAdapter(fallbackTracer) {
|
|
|
7192
7192
|
}
|
|
7193
7193
|
}
|
|
7194
7194
|
},
|
|
7195
|
+
recordToolResult: (toolName, result, success, durationMs, metadata) => {
|
|
7196
|
+
const resultStr = typeof result === "string" ? result : JSON.stringify(result || "");
|
|
7197
|
+
emitEvent("tool.result", {
|
|
7198
|
+
"tool.name": toolName,
|
|
7199
|
+
"tool.result": resultStr.substring(0, 1e4),
|
|
7200
|
+
"tool.result.length": resultStr.length,
|
|
7201
|
+
"tool.duration_ms": durationMs,
|
|
7202
|
+
"tool.success": success,
|
|
7203
|
+
...metadata || {}
|
|
7204
|
+
});
|
|
7205
|
+
if (fallback && typeof fallback.recordToolResult === "function") {
|
|
7206
|
+
try {
|
|
7207
|
+
fallback.recordToolResult(toolName, result, success, durationMs, metadata);
|
|
7208
|
+
} catch {
|
|
7209
|
+
}
|
|
7210
|
+
}
|
|
7211
|
+
},
|
|
7195
7212
|
recordDelegationEvent: (phase, attrs) => {
|
|
7196
7213
|
emitEvent(`delegation.${phase}`, attrs);
|
|
7197
7214
|
if (fallback && typeof fallback.recordDelegationEvent === "function") {
|
|
@@ -8153,8 +8170,8 @@ ${schemaString}`);
|
|
|
8153
8170
|
}
|
|
8154
8171
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
8155
8172
|
try {
|
|
8156
|
-
const
|
|
8157
|
-
const
|
|
8173
|
+
const fs27 = require("fs");
|
|
8174
|
+
const path31 = require("path");
|
|
8158
8175
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8159
8176
|
const provider = this.config.provider || "auto";
|
|
8160
8177
|
const model = this.config.model || "default";
|
|
@@ -8268,20 +8285,20 @@ ${"=".repeat(60)}
|
|
|
8268
8285
|
`;
|
|
8269
8286
|
readableVersion += `${"=".repeat(60)}
|
|
8270
8287
|
`;
|
|
8271
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
8272
|
-
if (!
|
|
8273
|
-
|
|
8288
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path31.join(process.cwd(), "debug-artifacts");
|
|
8289
|
+
if (!fs27.existsSync(debugArtifactsDir)) {
|
|
8290
|
+
fs27.mkdirSync(debugArtifactsDir, { recursive: true });
|
|
8274
8291
|
}
|
|
8275
|
-
const debugFile =
|
|
8292
|
+
const debugFile = path31.join(
|
|
8276
8293
|
debugArtifactsDir,
|
|
8277
8294
|
`prompt-${_checkName || "unknown"}-${timestamp}.json`
|
|
8278
8295
|
);
|
|
8279
|
-
|
|
8280
|
-
const readableFile =
|
|
8296
|
+
fs27.writeFileSync(debugFile, debugJson, "utf-8");
|
|
8297
|
+
const readableFile = path31.join(
|
|
8281
8298
|
debugArtifactsDir,
|
|
8282
8299
|
`prompt-${_checkName || "unknown"}-${timestamp}.txt`
|
|
8283
8300
|
);
|
|
8284
|
-
|
|
8301
|
+
fs27.writeFileSync(readableFile, readableVersion, "utf-8");
|
|
8285
8302
|
log(`
|
|
8286
8303
|
\u{1F4BE} Full debug info saved to:`);
|
|
8287
8304
|
log(` JSON: ${debugFile}`);
|
|
@@ -8314,8 +8331,8 @@ ${"=".repeat(60)}
|
|
|
8314
8331
|
log(`\u{1F4E4} Response length: ${response.length} characters`);
|
|
8315
8332
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
8316
8333
|
try {
|
|
8317
|
-
const
|
|
8318
|
-
const
|
|
8334
|
+
const fs27 = require("fs");
|
|
8335
|
+
const path31 = require("path");
|
|
8319
8336
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8320
8337
|
const agentAny2 = agent;
|
|
8321
8338
|
let fullHistory = [];
|
|
@@ -8326,8 +8343,8 @@ ${"=".repeat(60)}
|
|
|
8326
8343
|
} else if (agentAny2._messages) {
|
|
8327
8344
|
fullHistory = agentAny2._messages;
|
|
8328
8345
|
}
|
|
8329
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
8330
|
-
const sessionBase =
|
|
8346
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path31.join(process.cwd(), "debug-artifacts");
|
|
8347
|
+
const sessionBase = path31.join(
|
|
8331
8348
|
debugArtifactsDir,
|
|
8332
8349
|
`session-${_checkName || "unknown"}-${timestamp}`
|
|
8333
8350
|
);
|
|
@@ -8339,7 +8356,7 @@ ${"=".repeat(60)}
|
|
|
8339
8356
|
schema: effectiveSchema,
|
|
8340
8357
|
totalMessages: fullHistory.length
|
|
8341
8358
|
};
|
|
8342
|
-
|
|
8359
|
+
fs27.writeFileSync(sessionBase + ".json", JSON.stringify(sessionData, null, 2), "utf-8");
|
|
8343
8360
|
let readable = `=============================================================
|
|
8344
8361
|
`;
|
|
8345
8362
|
readable += `COMPLETE AI SESSION HISTORY (AFTER RESPONSE)
|
|
@@ -8366,7 +8383,7 @@ ${"=".repeat(60)}
|
|
|
8366
8383
|
`;
|
|
8367
8384
|
readable += content + "\n";
|
|
8368
8385
|
});
|
|
8369
|
-
|
|
8386
|
+
fs27.writeFileSync(sessionBase + ".summary.txt", readable, "utf-8");
|
|
8370
8387
|
log(`\u{1F4BE} Complete session history saved:`);
|
|
8371
8388
|
log(` - Contains ALL ${fullHistory.length} messages (prompts + responses)`);
|
|
8372
8389
|
} catch (error) {
|
|
@@ -8375,11 +8392,11 @@ ${"=".repeat(60)}
|
|
|
8375
8392
|
}
|
|
8376
8393
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
8377
8394
|
try {
|
|
8378
|
-
const
|
|
8379
|
-
const
|
|
8395
|
+
const fs27 = require("fs");
|
|
8396
|
+
const path31 = require("path");
|
|
8380
8397
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8381
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
8382
|
-
const responseFile =
|
|
8398
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path31.join(process.cwd(), "debug-artifacts");
|
|
8399
|
+
const responseFile = path31.join(
|
|
8383
8400
|
debugArtifactsDir,
|
|
8384
8401
|
`response-${_checkName || "unknown"}-${timestamp}.txt`
|
|
8385
8402
|
);
|
|
@@ -8412,7 +8429,7 @@ ${"=".repeat(60)}
|
|
|
8412
8429
|
`;
|
|
8413
8430
|
responseContent += `${"=".repeat(60)}
|
|
8414
8431
|
`;
|
|
8415
|
-
|
|
8432
|
+
fs27.writeFileSync(responseFile, responseContent, "utf-8");
|
|
8416
8433
|
log(`\u{1F4BE} Response saved to: ${responseFile}`);
|
|
8417
8434
|
} catch (error) {
|
|
8418
8435
|
log(`\u26A0\uFE0F Could not save response file: ${error}`);
|
|
@@ -8428,9 +8445,9 @@ ${"=".repeat(60)}
|
|
|
8428
8445
|
await agentAny._telemetryConfig.shutdown();
|
|
8429
8446
|
log(`\u{1F4CA} OpenTelemetry trace saved to: ${agentAny._traceFilePath}`);
|
|
8430
8447
|
if (process.env.GITHUB_ACTIONS) {
|
|
8431
|
-
const
|
|
8432
|
-
if (
|
|
8433
|
-
const stats =
|
|
8448
|
+
const fs27 = require("fs");
|
|
8449
|
+
if (fs27.existsSync(agentAny._traceFilePath)) {
|
|
8450
|
+
const stats = fs27.statSync(agentAny._traceFilePath);
|
|
8434
8451
|
console.log(
|
|
8435
8452
|
`::notice title=AI Trace Saved::${agentAny._traceFilePath} (${stats.size} bytes)`
|
|
8436
8453
|
);
|
|
@@ -8637,9 +8654,9 @@ ${schemaString}`);
|
|
|
8637
8654
|
const model = this.config.model || "default";
|
|
8638
8655
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
8639
8656
|
try {
|
|
8640
|
-
const
|
|
8641
|
-
const
|
|
8642
|
-
const
|
|
8657
|
+
const fs27 = require("fs");
|
|
8658
|
+
const path31 = require("path");
|
|
8659
|
+
const os3 = require("os");
|
|
8643
8660
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8644
8661
|
const debugData = {
|
|
8645
8662
|
timestamp,
|
|
@@ -8711,19 +8728,19 @@ ${"=".repeat(60)}
|
|
|
8711
8728
|
`;
|
|
8712
8729
|
readableVersion += `${"=".repeat(60)}
|
|
8713
8730
|
`;
|
|
8714
|
-
const tempDir =
|
|
8715
|
-
const promptFile =
|
|
8716
|
-
|
|
8731
|
+
const tempDir = os3.tmpdir();
|
|
8732
|
+
const promptFile = path31.join(tempDir, `visor-prompt-${timestamp}.txt`);
|
|
8733
|
+
fs27.writeFileSync(promptFile, prompt, "utf-8");
|
|
8717
8734
|
log(`
|
|
8718
8735
|
\u{1F4BE} Prompt saved to: ${promptFile}`);
|
|
8719
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
8736
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path31.join(process.cwd(), "debug-artifacts");
|
|
8720
8737
|
try {
|
|
8721
|
-
const base =
|
|
8738
|
+
const base = path31.join(
|
|
8722
8739
|
debugArtifactsDir,
|
|
8723
8740
|
`prompt-${_checkName || "unknown"}-${timestamp}`
|
|
8724
8741
|
);
|
|
8725
|
-
|
|
8726
|
-
|
|
8742
|
+
fs27.writeFileSync(base + ".json", debugJson, "utf-8");
|
|
8743
|
+
fs27.writeFileSync(base + ".summary.txt", readableVersion, "utf-8");
|
|
8727
8744
|
log(`
|
|
8728
8745
|
\u{1F4BE} Full debug info saved to directory: ${debugArtifactsDir}`);
|
|
8729
8746
|
} catch {
|
|
@@ -8768,8 +8785,8 @@ $ ${cliCommand}
|
|
|
8768
8785
|
log(`\u{1F4E4} Response length: ${response.length} characters`);
|
|
8769
8786
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
8770
8787
|
try {
|
|
8771
|
-
const
|
|
8772
|
-
const
|
|
8788
|
+
const fs27 = require("fs");
|
|
8789
|
+
const path31 = require("path");
|
|
8773
8790
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8774
8791
|
const agentAny = agent;
|
|
8775
8792
|
let fullHistory = [];
|
|
@@ -8780,8 +8797,8 @@ $ ${cliCommand}
|
|
|
8780
8797
|
} else if (agentAny._messages) {
|
|
8781
8798
|
fullHistory = agentAny._messages;
|
|
8782
8799
|
}
|
|
8783
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
8784
|
-
const sessionBase =
|
|
8800
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path31.join(process.cwd(), "debug-artifacts");
|
|
8801
|
+
const sessionBase = path31.join(
|
|
8785
8802
|
debugArtifactsDir,
|
|
8786
8803
|
`session-${_checkName || "unknown"}-${timestamp}`
|
|
8787
8804
|
);
|
|
@@ -8793,7 +8810,7 @@ $ ${cliCommand}
|
|
|
8793
8810
|
schema: effectiveSchema,
|
|
8794
8811
|
totalMessages: fullHistory.length
|
|
8795
8812
|
};
|
|
8796
|
-
|
|
8813
|
+
fs27.writeFileSync(sessionBase + ".json", JSON.stringify(sessionData, null, 2), "utf-8");
|
|
8797
8814
|
let readable = `=============================================================
|
|
8798
8815
|
`;
|
|
8799
8816
|
readable += `COMPLETE AI SESSION HISTORY (AFTER RESPONSE)
|
|
@@ -8820,7 +8837,7 @@ ${"=".repeat(60)}
|
|
|
8820
8837
|
`;
|
|
8821
8838
|
readable += content + "\n";
|
|
8822
8839
|
});
|
|
8823
|
-
|
|
8840
|
+
fs27.writeFileSync(sessionBase + ".summary.txt", readable, "utf-8");
|
|
8824
8841
|
log(`\u{1F4BE} Complete session history saved:`);
|
|
8825
8842
|
log(` - Contains ALL ${fullHistory.length} messages (prompts + responses)`);
|
|
8826
8843
|
} catch (error) {
|
|
@@ -8829,11 +8846,11 @@ ${"=".repeat(60)}
|
|
|
8829
8846
|
}
|
|
8830
8847
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
8831
8848
|
try {
|
|
8832
|
-
const
|
|
8833
|
-
const
|
|
8849
|
+
const fs27 = require("fs");
|
|
8850
|
+
const path31 = require("path");
|
|
8834
8851
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8835
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
8836
|
-
const responseFile =
|
|
8852
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path31.join(process.cwd(), "debug-artifacts");
|
|
8853
|
+
const responseFile = path31.join(
|
|
8837
8854
|
debugArtifactsDir,
|
|
8838
8855
|
`response-${_checkName || "unknown"}-${timestamp}.txt`
|
|
8839
8856
|
);
|
|
@@ -8866,7 +8883,7 @@ ${"=".repeat(60)}
|
|
|
8866
8883
|
`;
|
|
8867
8884
|
responseContent += `${"=".repeat(60)}
|
|
8868
8885
|
`;
|
|
8869
|
-
|
|
8886
|
+
fs27.writeFileSync(responseFile, responseContent, "utf-8");
|
|
8870
8887
|
log(`\u{1F4BE} Response saved to: ${responseFile}`);
|
|
8871
8888
|
} catch (error) {
|
|
8872
8889
|
log(`\u26A0\uFE0F Could not save response file: ${error}`);
|
|
@@ -8884,9 +8901,9 @@ ${"=".repeat(60)}
|
|
|
8884
8901
|
await telemetry.shutdown();
|
|
8885
8902
|
log(`\u{1F4CA} OpenTelemetry trace saved to: ${traceFilePath}`);
|
|
8886
8903
|
if (process.env.GITHUB_ACTIONS) {
|
|
8887
|
-
const
|
|
8888
|
-
if (
|
|
8889
|
-
const stats =
|
|
8904
|
+
const fs27 = require("fs");
|
|
8905
|
+
if (fs27.existsSync(traceFilePath)) {
|
|
8906
|
+
const stats = fs27.statSync(traceFilePath);
|
|
8890
8907
|
console.log(
|
|
8891
8908
|
`::notice title=AI Trace Saved::OpenTelemetry trace file size: ${stats.size} bytes`
|
|
8892
8909
|
);
|
|
@@ -8924,8 +8941,8 @@ ${"=".repeat(60)}
|
|
|
8924
8941
|
* Load schema content from schema files or inline definitions
|
|
8925
8942
|
*/
|
|
8926
8943
|
async loadSchemaContent(schema) {
|
|
8927
|
-
const
|
|
8928
|
-
const
|
|
8944
|
+
const fs27 = require("fs").promises;
|
|
8945
|
+
const path31 = require("path");
|
|
8929
8946
|
if (typeof schema === "object" && schema !== null) {
|
|
8930
8947
|
log("\u{1F4CB} Using inline schema object from configuration");
|
|
8931
8948
|
return JSON.stringify(schema);
|
|
@@ -8938,14 +8955,14 @@ ${"=".repeat(60)}
|
|
|
8938
8955
|
}
|
|
8939
8956
|
} catch {
|
|
8940
8957
|
}
|
|
8941
|
-
if ((schema.startsWith("./") || schema.includes(".json")) && !
|
|
8958
|
+
if ((schema.startsWith("./") || schema.includes(".json")) && !path31.isAbsolute(schema)) {
|
|
8942
8959
|
if (schema.includes("..") || schema.includes("\0")) {
|
|
8943
8960
|
throw new Error("Invalid schema path: path traversal not allowed");
|
|
8944
8961
|
}
|
|
8945
8962
|
try {
|
|
8946
|
-
const schemaPath =
|
|
8963
|
+
const schemaPath = path31.resolve(process.cwd(), schema);
|
|
8947
8964
|
log(`\u{1F4CB} Loading custom schema from file: ${schemaPath}`);
|
|
8948
|
-
const schemaContent = await
|
|
8965
|
+
const schemaContent = await fs27.readFile(schemaPath, "utf-8");
|
|
8949
8966
|
return schemaContent.trim();
|
|
8950
8967
|
} catch (error) {
|
|
8951
8968
|
throw new Error(
|
|
@@ -8959,22 +8976,22 @@ ${"=".repeat(60)}
|
|
|
8959
8976
|
}
|
|
8960
8977
|
const candidatePaths = [
|
|
8961
8978
|
// GitHub Action bundle location
|
|
8962
|
-
|
|
8979
|
+
path31.join(__dirname, "output", sanitizedSchemaName, "schema.json"),
|
|
8963
8980
|
// Historical fallback when src/output was inadvertently bundled as output1/
|
|
8964
|
-
|
|
8981
|
+
path31.join(__dirname, "output1", sanitizedSchemaName, "schema.json"),
|
|
8965
8982
|
// Local dev (repo root)
|
|
8966
|
-
|
|
8983
|
+
path31.join(process.cwd(), "output", sanitizedSchemaName, "schema.json")
|
|
8967
8984
|
];
|
|
8968
8985
|
for (const schemaPath of candidatePaths) {
|
|
8969
8986
|
try {
|
|
8970
|
-
const schemaContent = await
|
|
8987
|
+
const schemaContent = await fs27.readFile(schemaPath, "utf-8");
|
|
8971
8988
|
return schemaContent.trim();
|
|
8972
8989
|
} catch {
|
|
8973
8990
|
}
|
|
8974
8991
|
}
|
|
8975
|
-
const distPath =
|
|
8976
|
-
const distAltPath =
|
|
8977
|
-
const cwdPath =
|
|
8992
|
+
const distPath = path31.join(__dirname, "output", sanitizedSchemaName, "schema.json");
|
|
8993
|
+
const distAltPath = path31.join(__dirname, "output1", sanitizedSchemaName, "schema.json");
|
|
8994
|
+
const cwdPath = path31.join(process.cwd(), "output", sanitizedSchemaName, "schema.json");
|
|
8978
8995
|
throw new Error(
|
|
8979
8996
|
`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.`
|
|
8980
8997
|
);
|
|
@@ -9219,7 +9236,7 @@ ${"=".repeat(60)}
|
|
|
9219
9236
|
* Generate mock response for testing
|
|
9220
9237
|
*/
|
|
9221
9238
|
async generateMockResponse(_prompt, _checkName, _schema) {
|
|
9222
|
-
await new Promise((
|
|
9239
|
+
await new Promise((resolve18) => setTimeout(resolve18, 500));
|
|
9223
9240
|
const name = (_checkName || "").toLowerCase();
|
|
9224
9241
|
if (name.includes("extract-facts")) {
|
|
9225
9242
|
const arr = Array.from({ length: 6 }, (_, i) => ({
|
|
@@ -9580,7 +9597,7 @@ var init_command_executor = __esm({
|
|
|
9580
9597
|
* Execute command with stdin input
|
|
9581
9598
|
*/
|
|
9582
9599
|
executeWithStdin(command, options) {
|
|
9583
|
-
return new Promise((
|
|
9600
|
+
return new Promise((resolve18, reject) => {
|
|
9584
9601
|
const childProcess = (0, import_child_process2.exec)(
|
|
9585
9602
|
command,
|
|
9586
9603
|
{
|
|
@@ -9592,7 +9609,7 @@ var init_command_executor = __esm({
|
|
|
9592
9609
|
if (error && error.killed && (error.code === "ETIMEDOUT" || error.signal === "SIGTERM")) {
|
|
9593
9610
|
reject(new Error(`Command timed out after ${options.timeout || 3e4}ms`));
|
|
9594
9611
|
} else {
|
|
9595
|
-
|
|
9612
|
+
resolve18({
|
|
9596
9613
|
stdout: stdout || "",
|
|
9597
9614
|
stderr: stderr || "",
|
|
9598
9615
|
exitCode: error ? error.code || 1 : 0
|
|
@@ -13146,7 +13163,7 @@ var init_config_schema = __esm({
|
|
|
13146
13163
|
description: "Arguments/inputs for the workflow"
|
|
13147
13164
|
},
|
|
13148
13165
|
overrides: {
|
|
13149
|
-
$ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-
|
|
13166
|
+
$ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-13509-28103-src_types_config.ts-0-55255%3E%3E",
|
|
13150
13167
|
description: "Override specific step configurations in the workflow"
|
|
13151
13168
|
},
|
|
13152
13169
|
output_mapping: {
|
|
@@ -13162,7 +13179,7 @@ var init_config_schema = __esm({
|
|
|
13162
13179
|
description: "Config file path - alternative to workflow ID (loads a Visor config file as workflow)"
|
|
13163
13180
|
},
|
|
13164
13181
|
workflow_overrides: {
|
|
13165
|
-
$ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-
|
|
13182
|
+
$ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-13509-28103-src_types_config.ts-0-55255%3E%3E",
|
|
13166
13183
|
description: "Alias for overrides - workflow step overrides (backward compatibility)"
|
|
13167
13184
|
},
|
|
13168
13185
|
ref: {
|
|
@@ -13344,7 +13361,8 @@ var init_config_schema = __esm({
|
|
|
13344
13361
|
"issue_comment",
|
|
13345
13362
|
"manual",
|
|
13346
13363
|
"schedule",
|
|
13347
|
-
"webhook_received"
|
|
13364
|
+
"webhook_received",
|
|
13365
|
+
"slack_message"
|
|
13348
13366
|
],
|
|
13349
13367
|
description: "Valid event triggers for checks"
|
|
13350
13368
|
},
|
|
@@ -13850,7 +13868,7 @@ var init_config_schema = __esm({
|
|
|
13850
13868
|
description: "Custom output name (defaults to workflow name)"
|
|
13851
13869
|
},
|
|
13852
13870
|
overrides: {
|
|
13853
|
-
$ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-
|
|
13871
|
+
$ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-13509-28103-src_types_config.ts-0-55255%3E%3E",
|
|
13854
13872
|
description: "Step overrides"
|
|
13855
13873
|
},
|
|
13856
13874
|
output_mapping: {
|
|
@@ -13865,13 +13883,13 @@ var init_config_schema = __esm({
|
|
|
13865
13883
|
"^x-": {}
|
|
13866
13884
|
}
|
|
13867
13885
|
},
|
|
13868
|
-
"Record<string,Partial<interface-src_types_config.ts-
|
|
13886
|
+
"Record<string,Partial<interface-src_types_config.ts-13509-28103-src_types_config.ts-0-55255>>": {
|
|
13869
13887
|
type: "object",
|
|
13870
13888
|
additionalProperties: {
|
|
13871
|
-
$ref: "#/definitions/Partial%3Cinterface-src_types_config.ts-
|
|
13889
|
+
$ref: "#/definitions/Partial%3Cinterface-src_types_config.ts-13509-28103-src_types_config.ts-0-55255%3E"
|
|
13872
13890
|
}
|
|
13873
13891
|
},
|
|
13874
|
-
"Partial<interface-src_types_config.ts-
|
|
13892
|
+
"Partial<interface-src_types_config.ts-13509-28103-src_types_config.ts-0-55255>": {
|
|
13875
13893
|
type: "object",
|
|
13876
13894
|
additionalProperties: false
|
|
13877
13895
|
},
|
|
@@ -14753,6 +14771,10 @@ var init_config_schema = __esm({
|
|
|
14753
14771
|
cron: {
|
|
14754
14772
|
$ref: "#/definitions/Record%3Cstring%2CStaticCronJob%3E",
|
|
14755
14773
|
description: "Static cron jobs defined in configuration (always executed)"
|
|
14774
|
+
},
|
|
14775
|
+
on_message: {
|
|
14776
|
+
$ref: "#/definitions/Record%3Cstring%2CSlackMessageTrigger%3E",
|
|
14777
|
+
description: "Slack message triggers for reactive workflow execution"
|
|
14756
14778
|
}
|
|
14757
14779
|
},
|
|
14758
14780
|
additionalProperties: false,
|
|
@@ -15005,6 +15027,97 @@ var init_config_schema = __esm({
|
|
|
15005
15027
|
"^x-": {}
|
|
15006
15028
|
}
|
|
15007
15029
|
},
|
|
15030
|
+
"Record<string,SlackMessageTrigger>": {
|
|
15031
|
+
type: "object",
|
|
15032
|
+
additionalProperties: {
|
|
15033
|
+
$ref: "#/definitions/SlackMessageTrigger"
|
|
15034
|
+
}
|
|
15035
|
+
},
|
|
15036
|
+
SlackMessageTrigger: {
|
|
15037
|
+
type: "object",
|
|
15038
|
+
properties: {
|
|
15039
|
+
channels: {
|
|
15040
|
+
type: "array",
|
|
15041
|
+
items: {
|
|
15042
|
+
type: "string"
|
|
15043
|
+
},
|
|
15044
|
+
description: 'Channel IDs to monitor (supports wildcard suffix, e.g., "CENG*")'
|
|
15045
|
+
},
|
|
15046
|
+
from: {
|
|
15047
|
+
type: "array",
|
|
15048
|
+
items: {
|
|
15049
|
+
type: "string"
|
|
15050
|
+
},
|
|
15051
|
+
description: "Only trigger on messages from these user IDs"
|
|
15052
|
+
},
|
|
15053
|
+
from_bots: {
|
|
15054
|
+
type: "boolean",
|
|
15055
|
+
description: "Allow bot messages to trigger (default: false)"
|
|
15056
|
+
},
|
|
15057
|
+
contains: {
|
|
15058
|
+
type: "array",
|
|
15059
|
+
items: {
|
|
15060
|
+
type: "string"
|
|
15061
|
+
},
|
|
15062
|
+
description: "Keyword match - any keyword triggers (case-insensitive OR)"
|
|
15063
|
+
},
|
|
15064
|
+
match: {
|
|
15065
|
+
type: "string",
|
|
15066
|
+
description: "Regex pattern to match against message text"
|
|
15067
|
+
},
|
|
15068
|
+
threads: {
|
|
15069
|
+
type: "string",
|
|
15070
|
+
enum: ["root_only", "thread_only", "any"],
|
|
15071
|
+
description: "Thread scope filter (default: 'any')"
|
|
15072
|
+
},
|
|
15073
|
+
workflow: {
|
|
15074
|
+
type: "string",
|
|
15075
|
+
description: "Workflow/check ID to execute"
|
|
15076
|
+
},
|
|
15077
|
+
inputs: {
|
|
15078
|
+
$ref: "#/definitions/Record%3Cstring%2Cunknown%3E",
|
|
15079
|
+
description: "Optional workflow inputs"
|
|
15080
|
+
},
|
|
15081
|
+
output: {
|
|
15082
|
+
type: "object",
|
|
15083
|
+
properties: {
|
|
15084
|
+
type: {
|
|
15085
|
+
type: "string",
|
|
15086
|
+
enum: ["slack", "github", "webhook", "none"],
|
|
15087
|
+
description: "Output type: slack, github, webhook, or none"
|
|
15088
|
+
},
|
|
15089
|
+
target: {
|
|
15090
|
+
type: "string",
|
|
15091
|
+
description: "Target (channel name, repo, URL)"
|
|
15092
|
+
},
|
|
15093
|
+
thread_id: {
|
|
15094
|
+
type: "string",
|
|
15095
|
+
description: "Thread ID for threaded outputs"
|
|
15096
|
+
}
|
|
15097
|
+
},
|
|
15098
|
+
required: ["type"],
|
|
15099
|
+
additionalProperties: false,
|
|
15100
|
+
description: "Output destination configuration",
|
|
15101
|
+
patternProperties: {
|
|
15102
|
+
"^x-": {}
|
|
15103
|
+
}
|
|
15104
|
+
},
|
|
15105
|
+
description: {
|
|
15106
|
+
type: "string",
|
|
15107
|
+
description: "Description for logging/display"
|
|
15108
|
+
},
|
|
15109
|
+
enabled: {
|
|
15110
|
+
type: "boolean",
|
|
15111
|
+
description: "Enable/disable this trigger (default: true)"
|
|
15112
|
+
}
|
|
15113
|
+
},
|
|
15114
|
+
required: ["workflow"],
|
|
15115
|
+
additionalProperties: false,
|
|
15116
|
+
description: "Slack message trigger for reactive workflow execution Triggers workflows based on Slack messages matching configured filters",
|
|
15117
|
+
patternProperties: {
|
|
15118
|
+
"^x-": {}
|
|
15119
|
+
}
|
|
15120
|
+
},
|
|
15008
15121
|
PolicyConfig: {
|
|
15009
15122
|
type: "object",
|
|
15010
15123
|
properties: {
|
|
@@ -15146,7 +15259,8 @@ var init_config = __esm({
|
|
|
15146
15259
|
"issue_comment",
|
|
15147
15260
|
"manual",
|
|
15148
15261
|
"schedule",
|
|
15149
|
-
"webhook_received"
|
|
15262
|
+
"webhook_received",
|
|
15263
|
+
"slack_message"
|
|
15150
15264
|
];
|
|
15151
15265
|
ConfigManager = class {
|
|
15152
15266
|
validCheckTypes = [
|
|
@@ -17432,17 +17546,17 @@ var init_workflow_check_provider = __esm({
|
|
|
17432
17546
|
* so it can be executed by the state machine as a nested workflow.
|
|
17433
17547
|
*/
|
|
17434
17548
|
async loadWorkflowFromConfigPath(sourcePath, baseDir) {
|
|
17435
|
-
const
|
|
17436
|
-
const
|
|
17549
|
+
const path31 = require("path");
|
|
17550
|
+
const fs27 = require("fs");
|
|
17437
17551
|
const yaml5 = require("js-yaml");
|
|
17438
|
-
const resolved =
|
|
17439
|
-
if (!
|
|
17552
|
+
const resolved = path31.isAbsolute(sourcePath) ? sourcePath : path31.resolve(baseDir, sourcePath);
|
|
17553
|
+
if (!fs27.existsSync(resolved)) {
|
|
17440
17554
|
throw new Error(`Workflow config not found at: ${resolved}`);
|
|
17441
17555
|
}
|
|
17442
|
-
const rawContent =
|
|
17556
|
+
const rawContent = fs27.readFileSync(resolved, "utf8");
|
|
17443
17557
|
const rawData = yaml5.load(rawContent);
|
|
17444
17558
|
if (rawData.imports && Array.isArray(rawData.imports)) {
|
|
17445
|
-
const configDir =
|
|
17559
|
+
const configDir = path31.dirname(resolved);
|
|
17446
17560
|
for (const source of rawData.imports) {
|
|
17447
17561
|
const results = await this.registry.import(source, {
|
|
17448
17562
|
basePath: configDir,
|
|
@@ -17472,8 +17586,8 @@ ${errors}`);
|
|
|
17472
17586
|
if (!steps || Object.keys(steps).length === 0) {
|
|
17473
17587
|
throw new Error(`Config '${resolved}' does not contain any steps to execute as a workflow`);
|
|
17474
17588
|
}
|
|
17475
|
-
const id =
|
|
17476
|
-
const name = loaded.name || `Workflow from ${
|
|
17589
|
+
const id = path31.basename(resolved).replace(/\.(ya?ml)$/i, "");
|
|
17590
|
+
const name = loaded.name || `Workflow from ${path31.basename(resolved)}`;
|
|
17477
17591
|
const workflowDef = {
|
|
17478
17592
|
id,
|
|
17479
17593
|
name,
|
|
@@ -17713,6 +17827,48 @@ function fromDbRow(row) {
|
|
|
17713
17827
|
previousResponse: row.previous_response ?? void 0
|
|
17714
17828
|
};
|
|
17715
17829
|
}
|
|
17830
|
+
function toTriggerRow(trigger) {
|
|
17831
|
+
return {
|
|
17832
|
+
id: trigger.id,
|
|
17833
|
+
creator_id: trigger.creatorId,
|
|
17834
|
+
creator_context: trigger.creatorContext ?? null,
|
|
17835
|
+
creator_name: trigger.creatorName ?? null,
|
|
17836
|
+
description: trigger.description ?? null,
|
|
17837
|
+
channels: trigger.channels ? JSON.stringify(trigger.channels) : null,
|
|
17838
|
+
from_users: trigger.fromUsers ? JSON.stringify(trigger.fromUsers) : null,
|
|
17839
|
+
from_bots: trigger.fromBots ? 1 : 0,
|
|
17840
|
+
contains: trigger.contains ? JSON.stringify(trigger.contains) : null,
|
|
17841
|
+
match_pattern: trigger.matchPattern ?? null,
|
|
17842
|
+
threads: trigger.threads,
|
|
17843
|
+
workflow: trigger.workflow,
|
|
17844
|
+
inputs: trigger.inputs ? JSON.stringify(trigger.inputs) : null,
|
|
17845
|
+
output_context: trigger.outputContext ? JSON.stringify(trigger.outputContext) : null,
|
|
17846
|
+
status: trigger.status,
|
|
17847
|
+
enabled: trigger.enabled ? 1 : 0,
|
|
17848
|
+
created_at: trigger.createdAt
|
|
17849
|
+
};
|
|
17850
|
+
}
|
|
17851
|
+
function fromTriggerRow(row) {
|
|
17852
|
+
return {
|
|
17853
|
+
id: row.id,
|
|
17854
|
+
creatorId: row.creator_id,
|
|
17855
|
+
creatorContext: row.creator_context ?? void 0,
|
|
17856
|
+
creatorName: row.creator_name ?? void 0,
|
|
17857
|
+
description: row.description ?? void 0,
|
|
17858
|
+
channels: safeJsonParse(row.channels),
|
|
17859
|
+
fromUsers: safeJsonParse(row.from_users),
|
|
17860
|
+
fromBots: row.from_bots === 1,
|
|
17861
|
+
contains: safeJsonParse(row.contains),
|
|
17862
|
+
matchPattern: row.match_pattern ?? void 0,
|
|
17863
|
+
threads: row.threads,
|
|
17864
|
+
workflow: row.workflow,
|
|
17865
|
+
inputs: safeJsonParse(row.inputs),
|
|
17866
|
+
outputContext: safeJsonParse(row.output_context),
|
|
17867
|
+
status: row.status,
|
|
17868
|
+
enabled: row.enabled === 1,
|
|
17869
|
+
createdAt: row.created_at
|
|
17870
|
+
};
|
|
17871
|
+
}
|
|
17716
17872
|
var import_path5, import_fs4, import_uuid, SqliteStoreBackend;
|
|
17717
17873
|
var init_sqlite_store = __esm({
|
|
17718
17874
|
"src/scheduler/store/sqlite-store.ts"() {
|
|
@@ -17805,6 +17961,32 @@ var init_sqlite_store = __esm({
|
|
|
17805
17961
|
acquired_at BIGINT NOT NULL,
|
|
17806
17962
|
expires_at BIGINT NOT NULL
|
|
17807
17963
|
);
|
|
17964
|
+
|
|
17965
|
+
CREATE TABLE IF NOT EXISTS message_triggers (
|
|
17966
|
+
id VARCHAR(36) PRIMARY KEY,
|
|
17967
|
+
creator_id VARCHAR(255) NOT NULL,
|
|
17968
|
+
creator_context VARCHAR(255),
|
|
17969
|
+
creator_name VARCHAR(255),
|
|
17970
|
+
description TEXT,
|
|
17971
|
+
channels TEXT,
|
|
17972
|
+
from_users TEXT,
|
|
17973
|
+
from_bots BOOLEAN NOT NULL DEFAULT 0,
|
|
17974
|
+
contains TEXT,
|
|
17975
|
+
match_pattern TEXT,
|
|
17976
|
+
threads VARCHAR(20) NOT NULL DEFAULT 'any',
|
|
17977
|
+
workflow VARCHAR(255) NOT NULL,
|
|
17978
|
+
inputs TEXT,
|
|
17979
|
+
output_context TEXT,
|
|
17980
|
+
status VARCHAR(20) NOT NULL DEFAULT 'active',
|
|
17981
|
+
enabled BOOLEAN NOT NULL DEFAULT 1,
|
|
17982
|
+
created_at BIGINT NOT NULL
|
|
17983
|
+
);
|
|
17984
|
+
|
|
17985
|
+
CREATE INDEX IF NOT EXISTS idx_message_triggers_creator
|
|
17986
|
+
ON message_triggers(creator_id);
|
|
17987
|
+
|
|
17988
|
+
CREATE INDEX IF NOT EXISTS idx_message_triggers_status
|
|
17989
|
+
ON message_triggers(status);
|
|
17808
17990
|
`);
|
|
17809
17991
|
}
|
|
17810
17992
|
// --- Helpers ---
|
|
@@ -18086,6 +18268,114 @@ var init_sqlite_store = __esm({
|
|
|
18086
18268
|
}
|
|
18087
18269
|
async flush() {
|
|
18088
18270
|
}
|
|
18271
|
+
// --- Message Triggers ---
|
|
18272
|
+
async createTrigger(trigger) {
|
|
18273
|
+
const db = this.getDb();
|
|
18274
|
+
const newTrigger = {
|
|
18275
|
+
...trigger,
|
|
18276
|
+
id: (0, import_uuid.v4)(),
|
|
18277
|
+
createdAt: Date.now()
|
|
18278
|
+
};
|
|
18279
|
+
const row = toTriggerRow(newTrigger);
|
|
18280
|
+
db.prepare(
|
|
18281
|
+
`INSERT INTO message_triggers (
|
|
18282
|
+
id, creator_id, creator_context, creator_name, description,
|
|
18283
|
+
channels, from_users, from_bots, contains, match_pattern,
|
|
18284
|
+
threads, workflow, inputs, output_context,
|
|
18285
|
+
status, enabled, created_at
|
|
18286
|
+
) VALUES (
|
|
18287
|
+
?, ?, ?, ?, ?,
|
|
18288
|
+
?, ?, ?, ?, ?,
|
|
18289
|
+
?, ?, ?, ?,
|
|
18290
|
+
?, ?, ?
|
|
18291
|
+
)`
|
|
18292
|
+
).run(
|
|
18293
|
+
row.id,
|
|
18294
|
+
row.creator_id,
|
|
18295
|
+
row.creator_context,
|
|
18296
|
+
row.creator_name,
|
|
18297
|
+
row.description,
|
|
18298
|
+
row.channels,
|
|
18299
|
+
row.from_users,
|
|
18300
|
+
row.from_bots,
|
|
18301
|
+
row.contains,
|
|
18302
|
+
row.match_pattern,
|
|
18303
|
+
row.threads,
|
|
18304
|
+
row.workflow,
|
|
18305
|
+
row.inputs,
|
|
18306
|
+
row.output_context,
|
|
18307
|
+
row.status,
|
|
18308
|
+
row.enabled,
|
|
18309
|
+
row.created_at
|
|
18310
|
+
);
|
|
18311
|
+
logger.info(
|
|
18312
|
+
`[SqliteStore] Created message trigger ${newTrigger.id} for user ${newTrigger.creatorId}`
|
|
18313
|
+
);
|
|
18314
|
+
return newTrigger;
|
|
18315
|
+
}
|
|
18316
|
+
async getTrigger(id) {
|
|
18317
|
+
const db = this.getDb();
|
|
18318
|
+
const row = db.prepare("SELECT * FROM message_triggers WHERE id = ?").get(id);
|
|
18319
|
+
return row ? fromTriggerRow(row) : void 0;
|
|
18320
|
+
}
|
|
18321
|
+
async updateTrigger(id, patch) {
|
|
18322
|
+
const db = this.getDb();
|
|
18323
|
+
const existing = db.prepare("SELECT * FROM message_triggers WHERE id = ?").get(id);
|
|
18324
|
+
if (!existing) return void 0;
|
|
18325
|
+
const current = fromTriggerRow(existing);
|
|
18326
|
+
const updated = {
|
|
18327
|
+
...current,
|
|
18328
|
+
...patch,
|
|
18329
|
+
id: current.id,
|
|
18330
|
+
createdAt: current.createdAt
|
|
18331
|
+
};
|
|
18332
|
+
const row = toTriggerRow(updated);
|
|
18333
|
+
db.prepare(
|
|
18334
|
+
`UPDATE message_triggers SET
|
|
18335
|
+
creator_id = ?, creator_context = ?, creator_name = ?, description = ?,
|
|
18336
|
+
channels = ?, from_users = ?, from_bots = ?, contains = ?, match_pattern = ?,
|
|
18337
|
+
threads = ?, workflow = ?, inputs = ?, output_context = ?,
|
|
18338
|
+
status = ?, enabled = ?
|
|
18339
|
+
WHERE id = ?`
|
|
18340
|
+
).run(
|
|
18341
|
+
row.creator_id,
|
|
18342
|
+
row.creator_context,
|
|
18343
|
+
row.creator_name,
|
|
18344
|
+
row.description,
|
|
18345
|
+
row.channels,
|
|
18346
|
+
row.from_users,
|
|
18347
|
+
row.from_bots,
|
|
18348
|
+
row.contains,
|
|
18349
|
+
row.match_pattern,
|
|
18350
|
+
row.threads,
|
|
18351
|
+
row.workflow,
|
|
18352
|
+
row.inputs,
|
|
18353
|
+
row.output_context,
|
|
18354
|
+
row.status,
|
|
18355
|
+
row.enabled,
|
|
18356
|
+
row.id
|
|
18357
|
+
);
|
|
18358
|
+
return updated;
|
|
18359
|
+
}
|
|
18360
|
+
async deleteTrigger(id) {
|
|
18361
|
+
const db = this.getDb();
|
|
18362
|
+
const result = db.prepare("DELETE FROM message_triggers WHERE id = ?").run(id);
|
|
18363
|
+
if (result.changes > 0) {
|
|
18364
|
+
logger.info(`[SqliteStore] Deleted message trigger ${id}`);
|
|
18365
|
+
return true;
|
|
18366
|
+
}
|
|
18367
|
+
return false;
|
|
18368
|
+
}
|
|
18369
|
+
async getTriggersByCreator(creatorId) {
|
|
18370
|
+
const db = this.getDb();
|
|
18371
|
+
const rows = db.prepare("SELECT * FROM message_triggers WHERE creator_id = ?").all(creatorId);
|
|
18372
|
+
return rows.map(fromTriggerRow);
|
|
18373
|
+
}
|
|
18374
|
+
async getActiveTriggers() {
|
|
18375
|
+
const db = this.getDb();
|
|
18376
|
+
const rows = db.prepare("SELECT * FROM message_triggers WHERE status = 'active' AND enabled = 1").all();
|
|
18377
|
+
return rows.map(fromTriggerRow);
|
|
18378
|
+
}
|
|
18089
18379
|
};
|
|
18090
18380
|
}
|
|
18091
18381
|
});
|
|
@@ -18103,8 +18393,8 @@ async function createStoreBackend(storageConfig, haConfig) {
|
|
|
18103
18393
|
case "mssql": {
|
|
18104
18394
|
try {
|
|
18105
18395
|
const loaderPath = "../../enterprise/loader";
|
|
18106
|
-
const { loadEnterpriseStoreBackend } = await import(loaderPath);
|
|
18107
|
-
return await
|
|
18396
|
+
const { loadEnterpriseStoreBackend: loadEnterpriseStoreBackend2 } = await import(loaderPath);
|
|
18397
|
+
return await loadEnterpriseStoreBackend2(driver, storageConfig, haConfig);
|
|
18108
18398
|
} catch (err) {
|
|
18109
18399
|
const msg = err instanceof Error ? err.message : String(err);
|
|
18110
18400
|
logger.error(`[StoreFactory] Failed to load enterprise ${driver} backend: ${msg}`);
|
|
@@ -18205,11 +18495,19 @@ var init_schedule_store = __esm({
|
|
|
18205
18495
|
init_json_migrator();
|
|
18206
18496
|
ScheduleStore = class _ScheduleStore {
|
|
18207
18497
|
static instance;
|
|
18498
|
+
static onTriggersChanged;
|
|
18208
18499
|
backend = null;
|
|
18209
18500
|
initialized = false;
|
|
18210
18501
|
limits;
|
|
18211
18502
|
config;
|
|
18212
18503
|
externalBackend = null;
|
|
18504
|
+
/**
|
|
18505
|
+
* Register a callback to be invoked when message triggers change (create/update/delete).
|
|
18506
|
+
* Used by SlackSocketRunner to rebuild its evaluator.
|
|
18507
|
+
*/
|
|
18508
|
+
static setTriggersChangedCallback(cb) {
|
|
18509
|
+
_ScheduleStore.onTriggersChanged = cb;
|
|
18510
|
+
}
|
|
18213
18511
|
constructor(config, limits, backend) {
|
|
18214
18512
|
this.config = config || {};
|
|
18215
18513
|
this.limits = {
|
|
@@ -18371,6 +18669,53 @@ var init_schedule_store = __esm({
|
|
|
18371
18669
|
}
|
|
18372
18670
|
return this.backend;
|
|
18373
18671
|
}
|
|
18672
|
+
// --- Message Trigger Methods ---
|
|
18673
|
+
/**
|
|
18674
|
+
* Create a new message trigger
|
|
18675
|
+
*/
|
|
18676
|
+
async createTriggerAsync(trigger) {
|
|
18677
|
+
const result = await this.getBackend().createTrigger(trigger);
|
|
18678
|
+
_ScheduleStore.onTriggersChanged?.();
|
|
18679
|
+
return result;
|
|
18680
|
+
}
|
|
18681
|
+
/**
|
|
18682
|
+
* Get a trigger by ID
|
|
18683
|
+
*/
|
|
18684
|
+
async getTriggerAsync(id) {
|
|
18685
|
+
return this.getBackend().getTrigger(id);
|
|
18686
|
+
}
|
|
18687
|
+
/**
|
|
18688
|
+
* Update a trigger
|
|
18689
|
+
*/
|
|
18690
|
+
async updateTriggerAsync(id, patch) {
|
|
18691
|
+
const result = await this.getBackend().updateTrigger(id, patch);
|
|
18692
|
+
if (result) {
|
|
18693
|
+
_ScheduleStore.onTriggersChanged?.();
|
|
18694
|
+
}
|
|
18695
|
+
return result;
|
|
18696
|
+
}
|
|
18697
|
+
/**
|
|
18698
|
+
* Delete a trigger
|
|
18699
|
+
*/
|
|
18700
|
+
async deleteTriggerAsync(id) {
|
|
18701
|
+
const result = await this.getBackend().deleteTrigger(id);
|
|
18702
|
+
if (result) {
|
|
18703
|
+
_ScheduleStore.onTriggersChanged?.();
|
|
18704
|
+
}
|
|
18705
|
+
return result;
|
|
18706
|
+
}
|
|
18707
|
+
/**
|
|
18708
|
+
* Get all triggers for a specific creator
|
|
18709
|
+
*/
|
|
18710
|
+
async getTriggersByCreatorAsync(creatorId) {
|
|
18711
|
+
return this.getBackend().getTriggersByCreator(creatorId);
|
|
18712
|
+
}
|
|
18713
|
+
/**
|
|
18714
|
+
* Get all active triggers
|
|
18715
|
+
*/
|
|
18716
|
+
async getActiveTriggersAsync() {
|
|
18717
|
+
return this.getBackend().getActiveTriggers();
|
|
18718
|
+
}
|
|
18374
18719
|
/**
|
|
18375
18720
|
* Shut down the backend cleanly
|
|
18376
18721
|
*/
|
|
@@ -19611,11 +19956,19 @@ async function handleScheduleAction(args, context2) {
|
|
|
19611
19956
|
return handlePauseResume(args, context2, store, "paused");
|
|
19612
19957
|
case "resume":
|
|
19613
19958
|
return handlePauseResume(args, context2, store, "active");
|
|
19959
|
+
case "create_trigger":
|
|
19960
|
+
return handleCreateTrigger(args, context2, store);
|
|
19961
|
+
case "list_triggers":
|
|
19962
|
+
return handleListTriggers(context2, store);
|
|
19963
|
+
case "delete_trigger":
|
|
19964
|
+
return handleDeleteTrigger(args, context2, store);
|
|
19965
|
+
case "update_trigger":
|
|
19966
|
+
return handleUpdateTrigger(args, context2, store);
|
|
19614
19967
|
default:
|
|
19615
19968
|
return {
|
|
19616
19969
|
success: false,
|
|
19617
19970
|
message: `Unknown action: ${args.action}`,
|
|
19618
|
-
error: `Supported actions: create, list, cancel, pause, resume`
|
|
19971
|
+
error: `Supported actions: create, list, cancel, pause, resume, create_trigger, list_triggers, delete_trigger, update_trigger`
|
|
19619
19972
|
};
|
|
19620
19973
|
}
|
|
19621
19974
|
}
|
|
@@ -19858,6 +20211,172 @@ async function handlePauseResume(args, context2, store, newStatus) {
|
|
|
19858
20211
|
schedule: updated
|
|
19859
20212
|
};
|
|
19860
20213
|
}
|
|
20214
|
+
function formatTrigger(trigger) {
|
|
20215
|
+
const channels = trigger.channels?.length ? trigger.channels.join(", ") : "all";
|
|
20216
|
+
const filters = [];
|
|
20217
|
+
if (trigger.contains?.length) filters.push(`contains: ${trigger.contains.join(", ")}`);
|
|
20218
|
+
if (trigger.matchPattern) filters.push(`match: /${trigger.matchPattern}/`);
|
|
20219
|
+
if (trigger.fromBots) filters.push("bots: yes");
|
|
20220
|
+
const filterStr = filters.length ? ` [${filters.join("; ")}]` : "";
|
|
20221
|
+
const status = trigger.enabled ? "" : " (disabled)";
|
|
20222
|
+
return `\`${trigger.id.substring(0, 8)}\` - channels: ${channels} \u2192 workflow: "${trigger.workflow}"${filterStr}${status}`;
|
|
20223
|
+
}
|
|
20224
|
+
async function handleCreateTrigger(args, context2, store) {
|
|
20225
|
+
if (!args.workflow) {
|
|
20226
|
+
return {
|
|
20227
|
+
success: false,
|
|
20228
|
+
message: "Missing workflow",
|
|
20229
|
+
error: "Please specify the workflow to run when the trigger fires."
|
|
20230
|
+
};
|
|
20231
|
+
}
|
|
20232
|
+
if (context2.availableWorkflows && !context2.availableWorkflows.includes(args.workflow)) {
|
|
20233
|
+
return {
|
|
20234
|
+
success: false,
|
|
20235
|
+
message: `Workflow "${args.workflow}" not found`,
|
|
20236
|
+
error: `Available workflows: ${context2.availableWorkflows.slice(0, 5).join(", ")}${context2.availableWorkflows.length > 5 ? "..." : ""}`
|
|
20237
|
+
};
|
|
20238
|
+
}
|
|
20239
|
+
if ((!args.trigger_channels || args.trigger_channels.length === 0) && (!args.trigger_contains || args.trigger_contains.length === 0) && !args.trigger_match) {
|
|
20240
|
+
return {
|
|
20241
|
+
success: false,
|
|
20242
|
+
message: "Missing trigger filters",
|
|
20243
|
+
error: "Please specify at least one filter: trigger_channels, trigger_contains, or trigger_match."
|
|
20244
|
+
};
|
|
20245
|
+
}
|
|
20246
|
+
const permissionCheck = checkSchedulePermissions(context2, args.workflow);
|
|
20247
|
+
if (!permissionCheck.allowed) {
|
|
20248
|
+
return {
|
|
20249
|
+
success: false,
|
|
20250
|
+
message: "Permission denied",
|
|
20251
|
+
error: permissionCheck.reason || "You do not have permission to create triggers for this workflow"
|
|
20252
|
+
};
|
|
20253
|
+
}
|
|
20254
|
+
try {
|
|
20255
|
+
const trigger = await store.createTriggerAsync({
|
|
20256
|
+
creatorId: context2.userId,
|
|
20257
|
+
creatorContext: context2.contextType,
|
|
20258
|
+
creatorName: context2.userName,
|
|
20259
|
+
description: args.trigger_description,
|
|
20260
|
+
channels: args.trigger_channels,
|
|
20261
|
+
fromBots: args.trigger_from_bots ?? false,
|
|
20262
|
+
contains: args.trigger_contains,
|
|
20263
|
+
matchPattern: args.trigger_match,
|
|
20264
|
+
threads: args.trigger_threads ?? "any",
|
|
20265
|
+
workflow: args.workflow,
|
|
20266
|
+
inputs: args.workflow_inputs,
|
|
20267
|
+
status: "active",
|
|
20268
|
+
enabled: true
|
|
20269
|
+
});
|
|
20270
|
+
logger.info(
|
|
20271
|
+
`[ScheduleTool] Created message trigger ${trigger.id} for user ${context2.userId}: workflow="${args.workflow}"`
|
|
20272
|
+
);
|
|
20273
|
+
return {
|
|
20274
|
+
success: true,
|
|
20275
|
+
message: `**Message trigger created!**
|
|
20276
|
+
|
|
20277
|
+
**Workflow**: ${trigger.workflow}
|
|
20278
|
+
**Channels**: ${trigger.channels?.join(", ") || "all"}
|
|
20279
|
+
${trigger.contains?.length ? `**Contains**: ${trigger.contains.join(", ")}
|
|
20280
|
+
` : ""}${trigger.matchPattern ? `**Pattern**: /${trigger.matchPattern}/
|
|
20281
|
+
` : ""}${trigger.description ? `**Description**: ${trigger.description}
|
|
20282
|
+
` : ""}
|
|
20283
|
+
ID: \`${trigger.id.substring(0, 8)}\``
|
|
20284
|
+
};
|
|
20285
|
+
} catch (error) {
|
|
20286
|
+
const errorMsg = error instanceof Error ? error.message : "Unknown error";
|
|
20287
|
+
logger.warn(`[ScheduleTool] Failed to create trigger: ${errorMsg}`);
|
|
20288
|
+
return {
|
|
20289
|
+
success: false,
|
|
20290
|
+
message: `Failed to create trigger: ${errorMsg}`,
|
|
20291
|
+
error: errorMsg
|
|
20292
|
+
};
|
|
20293
|
+
}
|
|
20294
|
+
}
|
|
20295
|
+
async function handleListTriggers(context2, store) {
|
|
20296
|
+
const triggers = await store.getTriggersByCreatorAsync(context2.userId);
|
|
20297
|
+
const active = triggers.filter((t) => t.status !== "deleted");
|
|
20298
|
+
if (active.length === 0) {
|
|
20299
|
+
return {
|
|
20300
|
+
success: true,
|
|
20301
|
+
message: `You don't have any message triggers.
|
|
20302
|
+
|
|
20303
|
+
To create one: "watch #channel for messages containing 'error' and run %my-workflow"`
|
|
20304
|
+
};
|
|
20305
|
+
}
|
|
20306
|
+
const lines = active.map((t, i) => `${i + 1}. ${formatTrigger(t)}`);
|
|
20307
|
+
return {
|
|
20308
|
+
success: true,
|
|
20309
|
+
message: `**Your message triggers:**
|
|
20310
|
+
|
|
20311
|
+
${lines.join("\n")}
|
|
20312
|
+
|
|
20313
|
+
To delete: "delete trigger <id>"
|
|
20314
|
+
To disable: "disable trigger <id>"`
|
|
20315
|
+
};
|
|
20316
|
+
}
|
|
20317
|
+
async function handleDeleteTrigger(args, context2, store) {
|
|
20318
|
+
if (!args.trigger_id) {
|
|
20319
|
+
return {
|
|
20320
|
+
success: false,
|
|
20321
|
+
message: "Missing trigger ID",
|
|
20322
|
+
error: "Please specify which trigger to delete."
|
|
20323
|
+
};
|
|
20324
|
+
}
|
|
20325
|
+
const userTriggers = await store.getTriggersByCreatorAsync(context2.userId);
|
|
20326
|
+
let trigger = userTriggers.find((t) => t.id === args.trigger_id);
|
|
20327
|
+
if (!trigger) {
|
|
20328
|
+
trigger = userTriggers.find((t) => t.id.startsWith(args.trigger_id));
|
|
20329
|
+
}
|
|
20330
|
+
if (!trigger) {
|
|
20331
|
+
return {
|
|
20332
|
+
success: false,
|
|
20333
|
+
message: "Trigger not found",
|
|
20334
|
+
error: `Could not find trigger with ID "${args.trigger_id}" in your triggers. Use "list my triggers" to see your triggers.`
|
|
20335
|
+
};
|
|
20336
|
+
}
|
|
20337
|
+
await store.deleteTriggerAsync(trigger.id);
|
|
20338
|
+
logger.info(`[ScheduleTool] Deleted trigger ${trigger.id} for user ${context2.userId}`);
|
|
20339
|
+
return {
|
|
20340
|
+
success: true,
|
|
20341
|
+
message: `**Trigger deleted!**
|
|
20342
|
+
|
|
20343
|
+
Was: watching for "${trigger.workflow}" ${trigger.channels?.length ? `in ${trigger.channels.join(", ")}` : ""}`
|
|
20344
|
+
};
|
|
20345
|
+
}
|
|
20346
|
+
async function handleUpdateTrigger(args, context2, store) {
|
|
20347
|
+
if (!args.trigger_id) {
|
|
20348
|
+
return {
|
|
20349
|
+
success: false,
|
|
20350
|
+
message: "Missing trigger ID",
|
|
20351
|
+
error: "Please specify which trigger to update."
|
|
20352
|
+
};
|
|
20353
|
+
}
|
|
20354
|
+
const userTriggers = await store.getTriggersByCreatorAsync(context2.userId);
|
|
20355
|
+
let trigger = userTriggers.find((t) => t.id === args.trigger_id);
|
|
20356
|
+
if (!trigger) {
|
|
20357
|
+
trigger = userTriggers.find((t) => t.id.startsWith(args.trigger_id));
|
|
20358
|
+
}
|
|
20359
|
+
if (!trigger) {
|
|
20360
|
+
return {
|
|
20361
|
+
success: false,
|
|
20362
|
+
message: "Trigger not found",
|
|
20363
|
+
error: `Could not find trigger with ID "${args.trigger_id}" in your triggers.`
|
|
20364
|
+
};
|
|
20365
|
+
}
|
|
20366
|
+
const patch = {};
|
|
20367
|
+
if (args.trigger_enabled !== void 0) {
|
|
20368
|
+
patch.enabled = args.trigger_enabled;
|
|
20369
|
+
}
|
|
20370
|
+
await store.updateTriggerAsync(trigger.id, patch);
|
|
20371
|
+
const action = args.trigger_enabled === false ? "disabled" : "enabled";
|
|
20372
|
+
logger.info(`[ScheduleTool] ${action} trigger ${trigger.id} for user ${context2.userId}`);
|
|
20373
|
+
return {
|
|
20374
|
+
success: true,
|
|
20375
|
+
message: `**Trigger ${action}!**
|
|
20376
|
+
|
|
20377
|
+
"${trigger.workflow}" ${trigger.channels?.length ? `in ${trigger.channels.join(", ")}` : ""}`
|
|
20378
|
+
};
|
|
20379
|
+
}
|
|
19861
20380
|
function getScheduleToolDefinition() {
|
|
19862
20381
|
return {
|
|
19863
20382
|
name: "schedule",
|
|
@@ -19877,6 +20396,17 @@ ACTIONS:
|
|
|
19877
20396
|
- cancel: Remove a schedule by ID
|
|
19878
20397
|
- pause/resume: Temporarily disable/enable a schedule
|
|
19879
20398
|
|
|
20399
|
+
MESSAGE-BASED TRIGGERS:
|
|
20400
|
+
In addition to time-based schedules, this tool can create/manage message-based triggers that react to
|
|
20401
|
+
Slack messages in specific channels. Use the create_trigger, list_triggers, delete_trigger, and update_trigger
|
|
20402
|
+
actions for this. Message triggers fire workflows based on message content, channel, sender, and thread scope.
|
|
20403
|
+
|
|
20404
|
+
TRIGGER ACTIONS:
|
|
20405
|
+
- create_trigger: Create a new message trigger (requires workflow + at least one filter)
|
|
20406
|
+
- list_triggers: Show user's message triggers
|
|
20407
|
+
- delete_trigger: Remove a trigger by ID
|
|
20408
|
+
- update_trigger: Enable/disable a trigger by ID
|
|
20409
|
+
|
|
19880
20410
|
FOR CREATE ACTION - Extract these from user's request:
|
|
19881
20411
|
1. WHAT:
|
|
19882
20412
|
- If user says "schedule %some-workflow ...", populate 'workflow' with "some-workflow".
|
|
@@ -19964,14 +20494,36 @@ User: "list my schedules"
|
|
|
19964
20494
|
\u2192 { "action": "list" }
|
|
19965
20495
|
|
|
19966
20496
|
User: "cancel schedule abc123"
|
|
19967
|
-
\u2192 { "action": "cancel", "schedule_id": "abc123" }
|
|
20497
|
+
\u2192 { "action": "cancel", "schedule_id": "abc123" }
|
|
20498
|
+
|
|
20499
|
+
User: "watch #cicd for messages containing 'failed' and run %handle-cicd"
|
|
20500
|
+
\u2192 { "action": "create_trigger", "trigger_channels": ["C0CICD"], "trigger_contains": ["failed"], "workflow": "handle-cicd" }
|
|
20501
|
+
|
|
20502
|
+
User: "list my message triggers"
|
|
20503
|
+
\u2192 { "action": "list_triggers" }
|
|
20504
|
+
|
|
20505
|
+
User: "delete trigger abc123"
|
|
20506
|
+
\u2192 { "action": "delete_trigger", "trigger_id": "abc123" }
|
|
20507
|
+
|
|
20508
|
+
User: "disable trigger abc123"
|
|
20509
|
+
\u2192 { "action": "update_trigger", "trigger_id": "abc123", "trigger_enabled": false }`,
|
|
19968
20510
|
inputSchema: {
|
|
19969
20511
|
type: "object",
|
|
19970
20512
|
properties: {
|
|
19971
20513
|
action: {
|
|
19972
20514
|
type: "string",
|
|
19973
|
-
enum: [
|
|
19974
|
-
|
|
20515
|
+
enum: [
|
|
20516
|
+
"create",
|
|
20517
|
+
"list",
|
|
20518
|
+
"cancel",
|
|
20519
|
+
"pause",
|
|
20520
|
+
"resume",
|
|
20521
|
+
"create_trigger",
|
|
20522
|
+
"list_triggers",
|
|
20523
|
+
"delete_trigger",
|
|
20524
|
+
"update_trigger"
|
|
20525
|
+
],
|
|
20526
|
+
description: "What to do: create/list/cancel/pause/resume for time-based schedules; create_trigger/list_triggers/delete_trigger/update_trigger for message-based triggers"
|
|
19975
20527
|
},
|
|
19976
20528
|
// WHAT to do
|
|
19977
20529
|
reminder_text: {
|
|
@@ -20021,6 +20573,42 @@ User: "cancel schedule abc123"
|
|
|
20021
20573
|
schedule_id: {
|
|
20022
20574
|
type: "string",
|
|
20023
20575
|
description: "For cancel/pause/resume: the schedule ID to act on (first 8 chars is enough)"
|
|
20576
|
+
},
|
|
20577
|
+
// For message trigger actions
|
|
20578
|
+
trigger_channels: {
|
|
20579
|
+
type: "array",
|
|
20580
|
+
items: { type: "string" },
|
|
20581
|
+
description: 'For create_trigger: Slack channel IDs to monitor (e.g., ["C0CICD"]). Supports wildcard suffix (e.g., "CENG*").'
|
|
20582
|
+
},
|
|
20583
|
+
trigger_from_bots: {
|
|
20584
|
+
type: "boolean",
|
|
20585
|
+
description: "For create_trigger: allow bot messages to trigger (default: false)"
|
|
20586
|
+
},
|
|
20587
|
+
trigger_contains: {
|
|
20588
|
+
type: "array",
|
|
20589
|
+
items: { type: "string" },
|
|
20590
|
+
description: 'For create_trigger: keywords to match (case-insensitive OR). E.g., ["failed", "error"].'
|
|
20591
|
+
},
|
|
20592
|
+
trigger_match: {
|
|
20593
|
+
type: "string",
|
|
20594
|
+
description: "For create_trigger: regex pattern to match against message text."
|
|
20595
|
+
},
|
|
20596
|
+
trigger_threads: {
|
|
20597
|
+
type: "string",
|
|
20598
|
+
enum: ["root_only", "thread_only", "any"],
|
|
20599
|
+
description: 'For create_trigger: thread scope filter (default: "any").'
|
|
20600
|
+
},
|
|
20601
|
+
trigger_description: {
|
|
20602
|
+
type: "string",
|
|
20603
|
+
description: "For create_trigger: human-readable description of the trigger."
|
|
20604
|
+
},
|
|
20605
|
+
trigger_id: {
|
|
20606
|
+
type: "string",
|
|
20607
|
+
description: "For delete_trigger/update_trigger: the trigger ID to act on (first 8 chars is enough)."
|
|
20608
|
+
},
|
|
20609
|
+
trigger_enabled: {
|
|
20610
|
+
type: "boolean",
|
|
20611
|
+
description: "For update_trigger: set to false to disable, true to enable."
|
|
20024
20612
|
}
|
|
20025
20613
|
},
|
|
20026
20614
|
required: ["action"]
|
|
@@ -20350,7 +20938,7 @@ var init_mcp_custom_sse_server = __esm({
|
|
|
20350
20938
|
* Returns the actual bound port number
|
|
20351
20939
|
*/
|
|
20352
20940
|
async start() {
|
|
20353
|
-
return new Promise((
|
|
20941
|
+
return new Promise((resolve18, reject) => {
|
|
20354
20942
|
try {
|
|
20355
20943
|
this.server = import_http.default.createServer((req, res) => {
|
|
20356
20944
|
this.handleRequest(req, res).catch((error) => {
|
|
@@ -20384,7 +20972,7 @@ var init_mcp_custom_sse_server = __esm({
|
|
|
20384
20972
|
);
|
|
20385
20973
|
}
|
|
20386
20974
|
this.startKeepalive();
|
|
20387
|
-
|
|
20975
|
+
resolve18(this.port);
|
|
20388
20976
|
});
|
|
20389
20977
|
} catch (error) {
|
|
20390
20978
|
reject(error);
|
|
@@ -20447,7 +21035,7 @@ var init_mcp_custom_sse_server = __esm({
|
|
|
20447
21035
|
logger.debug(
|
|
20448
21036
|
`[CustomToolsSSEServer:${this.sessionId}] Grace period before stop: ${waitMs}ms (activeToolCalls=${this.activeToolCalls})`
|
|
20449
21037
|
);
|
|
20450
|
-
await new Promise((
|
|
21038
|
+
await new Promise((resolve18) => setTimeout(resolve18, waitMs));
|
|
20451
21039
|
}
|
|
20452
21040
|
}
|
|
20453
21041
|
if (this.activeToolCalls > 0) {
|
|
@@ -20456,7 +21044,7 @@ var init_mcp_custom_sse_server = __esm({
|
|
|
20456
21044
|
`[CustomToolsSSEServer:${this.sessionId}] Waiting for ${this.activeToolCalls} active tool call(s) before stop`
|
|
20457
21045
|
);
|
|
20458
21046
|
while (this.activeToolCalls > 0 && Date.now() - startedAt < effectiveDrainTimeoutMs) {
|
|
20459
|
-
await new Promise((
|
|
21047
|
+
await new Promise((resolve18) => setTimeout(resolve18, 250));
|
|
20460
21048
|
}
|
|
20461
21049
|
if (this.activeToolCalls > 0) {
|
|
20462
21050
|
logger.warn(
|
|
@@ -20481,21 +21069,21 @@ var init_mcp_custom_sse_server = __esm({
|
|
|
20481
21069
|
}
|
|
20482
21070
|
this.connections.clear();
|
|
20483
21071
|
if (this.server) {
|
|
20484
|
-
await new Promise((
|
|
21072
|
+
await new Promise((resolve18, reject) => {
|
|
20485
21073
|
const timeout = setTimeout(() => {
|
|
20486
21074
|
if (this.debug) {
|
|
20487
21075
|
logger.debug(
|
|
20488
21076
|
`[CustomToolsSSEServer:${this.sessionId}] Force closing server after timeout`
|
|
20489
21077
|
);
|
|
20490
21078
|
}
|
|
20491
|
-
this.server?.close(() =>
|
|
21079
|
+
this.server?.close(() => resolve18());
|
|
20492
21080
|
}, 5e3);
|
|
20493
21081
|
this.server.close((error) => {
|
|
20494
21082
|
clearTimeout(timeout);
|
|
20495
21083
|
if (error) {
|
|
20496
21084
|
reject(error);
|
|
20497
21085
|
} else {
|
|
20498
|
-
|
|
21086
|
+
resolve18();
|
|
20499
21087
|
}
|
|
20500
21088
|
});
|
|
20501
21089
|
});
|
|
@@ -20921,7 +21509,7 @@ var init_mcp_custom_sse_server = __esm({
|
|
|
20921
21509
|
logger.warn(
|
|
20922
21510
|
`[CustomToolsSSEServer:${this.sessionId}] Tool ${toolName} failed (attempt ${attempt + 1}/${retryCount + 1}): ${errorMsg}. Retrying in ${delay}ms`
|
|
20923
21511
|
);
|
|
20924
|
-
await new Promise((
|
|
21512
|
+
await new Promise((resolve18) => setTimeout(resolve18, delay));
|
|
20925
21513
|
attempt++;
|
|
20926
21514
|
}
|
|
20927
21515
|
}
|
|
@@ -21234,9 +21822,9 @@ var init_ai_check_provider = __esm({
|
|
|
21234
21822
|
} else {
|
|
21235
21823
|
resolvedPath = import_path7.default.resolve(process.cwd(), str);
|
|
21236
21824
|
}
|
|
21237
|
-
const
|
|
21825
|
+
const fs27 = require("fs").promises;
|
|
21238
21826
|
try {
|
|
21239
|
-
const stat2 = await
|
|
21827
|
+
const stat2 = await fs27.stat(resolvedPath);
|
|
21240
21828
|
return stat2.isFile();
|
|
21241
21829
|
} catch {
|
|
21242
21830
|
return hasFileExtension && (isRelativePath || isAbsolutePath || hasPathSeparators);
|
|
@@ -27164,14 +27752,14 @@ var require_util = __commonJS({
|
|
|
27164
27752
|
}
|
|
27165
27753
|
const port = url.port != null ? url.port : url.protocol === "https:" ? 443 : 80;
|
|
27166
27754
|
let origin = url.origin != null ? url.origin : `${url.protocol}//${url.hostname}:${port}`;
|
|
27167
|
-
let
|
|
27755
|
+
let path31 = url.path != null ? url.path : `${url.pathname || ""}${url.search || ""}`;
|
|
27168
27756
|
if (origin.endsWith("/")) {
|
|
27169
27757
|
origin = origin.substring(0, origin.length - 1);
|
|
27170
27758
|
}
|
|
27171
|
-
if (
|
|
27172
|
-
|
|
27759
|
+
if (path31 && !path31.startsWith("/")) {
|
|
27760
|
+
path31 = `/${path31}`;
|
|
27173
27761
|
}
|
|
27174
|
-
url = new URL(origin +
|
|
27762
|
+
url = new URL(origin + path31);
|
|
27175
27763
|
}
|
|
27176
27764
|
return url;
|
|
27177
27765
|
}
|
|
@@ -28785,20 +29373,20 @@ var require_parseParams = __commonJS({
|
|
|
28785
29373
|
var require_basename = __commonJS({
|
|
28786
29374
|
"node_modules/@fastify/busboy/lib/utils/basename.js"(exports2, module2) {
|
|
28787
29375
|
"use strict";
|
|
28788
|
-
module2.exports = function basename4(
|
|
28789
|
-
if (typeof
|
|
29376
|
+
module2.exports = function basename4(path31) {
|
|
29377
|
+
if (typeof path31 !== "string") {
|
|
28790
29378
|
return "";
|
|
28791
29379
|
}
|
|
28792
|
-
for (var i =
|
|
28793
|
-
switch (
|
|
29380
|
+
for (var i = path31.length - 1; i >= 0; --i) {
|
|
29381
|
+
switch (path31.charCodeAt(i)) {
|
|
28794
29382
|
case 47:
|
|
28795
29383
|
// '/'
|
|
28796
29384
|
case 92:
|
|
28797
|
-
|
|
28798
|
-
return
|
|
29385
|
+
path31 = path31.slice(i + 1);
|
|
29386
|
+
return path31 === ".." || path31 === "." ? "" : path31;
|
|
28799
29387
|
}
|
|
28800
29388
|
}
|
|
28801
|
-
return
|
|
29389
|
+
return path31 === ".." || path31 === "." ? "" : path31;
|
|
28802
29390
|
};
|
|
28803
29391
|
}
|
|
28804
29392
|
});
|
|
@@ -29802,11 +30390,11 @@ var require_util2 = __commonJS({
|
|
|
29802
30390
|
var assert = require("assert");
|
|
29803
30391
|
var { isUint8Array } = require("util/types");
|
|
29804
30392
|
var supportedHashes = [];
|
|
29805
|
-
var
|
|
30393
|
+
var crypto4;
|
|
29806
30394
|
try {
|
|
29807
|
-
|
|
30395
|
+
crypto4 = require("crypto");
|
|
29808
30396
|
const possibleRelevantHashes = ["sha256", "sha384", "sha512"];
|
|
29809
|
-
supportedHashes =
|
|
30397
|
+
supportedHashes = crypto4.getHashes().filter((hash) => possibleRelevantHashes.includes(hash));
|
|
29810
30398
|
} catch {
|
|
29811
30399
|
}
|
|
29812
30400
|
function responseURL(response) {
|
|
@@ -30083,7 +30671,7 @@ var require_util2 = __commonJS({
|
|
|
30083
30671
|
}
|
|
30084
30672
|
}
|
|
30085
30673
|
function bytesMatch(bytes, metadataList) {
|
|
30086
|
-
if (
|
|
30674
|
+
if (crypto4 === void 0) {
|
|
30087
30675
|
return true;
|
|
30088
30676
|
}
|
|
30089
30677
|
const parsedMetadata = parseMetadata(metadataList);
|
|
@@ -30098,7 +30686,7 @@ var require_util2 = __commonJS({
|
|
|
30098
30686
|
for (const item of metadata) {
|
|
30099
30687
|
const algorithm = item.algo;
|
|
30100
30688
|
const expectedValue = item.hash;
|
|
30101
|
-
let actualValue =
|
|
30689
|
+
let actualValue = crypto4.createHash(algorithm).update(bytes).digest("base64");
|
|
30102
30690
|
if (actualValue[actualValue.length - 1] === "=") {
|
|
30103
30691
|
if (actualValue[actualValue.length - 2] === "=") {
|
|
30104
30692
|
actualValue = actualValue.slice(0, -2);
|
|
@@ -30191,8 +30779,8 @@ var require_util2 = __commonJS({
|
|
|
30191
30779
|
function createDeferredPromise() {
|
|
30192
30780
|
let res;
|
|
30193
30781
|
let rej;
|
|
30194
|
-
const promise = new Promise((
|
|
30195
|
-
res =
|
|
30782
|
+
const promise = new Promise((resolve18, reject) => {
|
|
30783
|
+
res = resolve18;
|
|
30196
30784
|
rej = reject;
|
|
30197
30785
|
});
|
|
30198
30786
|
return { promise, resolve: res, reject: rej };
|
|
@@ -31445,8 +32033,8 @@ var require_body = __commonJS({
|
|
|
31445
32033
|
var { parseMIMEType, serializeAMimeType } = require_dataURL();
|
|
31446
32034
|
var random;
|
|
31447
32035
|
try {
|
|
31448
|
-
const
|
|
31449
|
-
random = (max) =>
|
|
32036
|
+
const crypto4 = require("crypto");
|
|
32037
|
+
random = (max) => crypto4.randomInt(0, max);
|
|
31450
32038
|
} catch {
|
|
31451
32039
|
random = (max) => Math.floor(Math.random(max));
|
|
31452
32040
|
}
|
|
@@ -31697,8 +32285,8 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
31697
32285
|
});
|
|
31698
32286
|
}
|
|
31699
32287
|
});
|
|
31700
|
-
const busboyResolve = new Promise((
|
|
31701
|
-
busboy.on("finish",
|
|
32288
|
+
const busboyResolve = new Promise((resolve18, reject) => {
|
|
32289
|
+
busboy.on("finish", resolve18);
|
|
31702
32290
|
busboy.on("error", (err) => reject(new TypeError(err)));
|
|
31703
32291
|
});
|
|
31704
32292
|
if (this.body !== null) for await (const chunk of consumeBody(this[kState].body)) busboy.write(chunk);
|
|
@@ -31829,7 +32417,7 @@ var require_request = __commonJS({
|
|
|
31829
32417
|
}
|
|
31830
32418
|
var Request = class _Request {
|
|
31831
32419
|
constructor(origin, {
|
|
31832
|
-
path:
|
|
32420
|
+
path: path31,
|
|
31833
32421
|
method,
|
|
31834
32422
|
body,
|
|
31835
32423
|
headers,
|
|
@@ -31843,11 +32431,11 @@ var require_request = __commonJS({
|
|
|
31843
32431
|
throwOnError,
|
|
31844
32432
|
expectContinue
|
|
31845
32433
|
}, handler) {
|
|
31846
|
-
if (typeof
|
|
32434
|
+
if (typeof path31 !== "string") {
|
|
31847
32435
|
throw new InvalidArgumentError("path must be a string");
|
|
31848
|
-
} else if (
|
|
32436
|
+
} else if (path31[0] !== "/" && !(path31.startsWith("http://") || path31.startsWith("https://")) && method !== "CONNECT") {
|
|
31849
32437
|
throw new InvalidArgumentError("path must be an absolute URL or start with a slash");
|
|
31850
|
-
} else if (invalidPathRegex.exec(
|
|
32438
|
+
} else if (invalidPathRegex.exec(path31) !== null) {
|
|
31851
32439
|
throw new InvalidArgumentError("invalid request path");
|
|
31852
32440
|
}
|
|
31853
32441
|
if (typeof method !== "string") {
|
|
@@ -31910,7 +32498,7 @@ var require_request = __commonJS({
|
|
|
31910
32498
|
this.completed = false;
|
|
31911
32499
|
this.aborted = false;
|
|
31912
32500
|
this.upgrade = upgrade || null;
|
|
31913
|
-
this.path = query ? util.buildURL(
|
|
32501
|
+
this.path = query ? util.buildURL(path31, query) : path31;
|
|
31914
32502
|
this.origin = origin;
|
|
31915
32503
|
this.idempotent = idempotent == null ? method === "HEAD" || method === "GET" : idempotent;
|
|
31916
32504
|
this.blocking = blocking == null ? false : blocking;
|
|
@@ -32232,9 +32820,9 @@ var require_dispatcher_base = __commonJS({
|
|
|
32232
32820
|
}
|
|
32233
32821
|
close(callback) {
|
|
32234
32822
|
if (callback === void 0) {
|
|
32235
|
-
return new Promise((
|
|
32823
|
+
return new Promise((resolve18, reject) => {
|
|
32236
32824
|
this.close((err, data) => {
|
|
32237
|
-
return err ? reject(err) :
|
|
32825
|
+
return err ? reject(err) : resolve18(data);
|
|
32238
32826
|
});
|
|
32239
32827
|
});
|
|
32240
32828
|
}
|
|
@@ -32272,12 +32860,12 @@ var require_dispatcher_base = __commonJS({
|
|
|
32272
32860
|
err = null;
|
|
32273
32861
|
}
|
|
32274
32862
|
if (callback === void 0) {
|
|
32275
|
-
return new Promise((
|
|
32863
|
+
return new Promise((resolve18, reject) => {
|
|
32276
32864
|
this.destroy(err, (err2, data) => {
|
|
32277
32865
|
return err2 ? (
|
|
32278
32866
|
/* istanbul ignore next: should never error */
|
|
32279
32867
|
reject(err2)
|
|
32280
|
-
) :
|
|
32868
|
+
) : resolve18(data);
|
|
32281
32869
|
});
|
|
32282
32870
|
});
|
|
32283
32871
|
}
|
|
@@ -32918,9 +33506,9 @@ var require_RedirectHandler = __commonJS({
|
|
|
32918
33506
|
return this.handler.onHeaders(statusCode, headers, resume, statusText);
|
|
32919
33507
|
}
|
|
32920
33508
|
const { origin, pathname, search } = util.parseURL(new URL(this.location, this.opts.origin && new URL(this.opts.path, this.opts.origin)));
|
|
32921
|
-
const
|
|
33509
|
+
const path31 = search ? `${pathname}${search}` : pathname;
|
|
32922
33510
|
this.opts.headers = cleanRequestHeaders(this.opts.headers, statusCode === 303, this.opts.origin !== origin);
|
|
32923
|
-
this.opts.path =
|
|
33511
|
+
this.opts.path = path31;
|
|
32924
33512
|
this.opts.origin = origin;
|
|
32925
33513
|
this.opts.maxRedirections = 0;
|
|
32926
33514
|
this.opts.query = null;
|
|
@@ -33339,16 +33927,16 @@ var require_client = __commonJS({
|
|
|
33339
33927
|
return this[kNeedDrain] < 2;
|
|
33340
33928
|
}
|
|
33341
33929
|
async [kClose]() {
|
|
33342
|
-
return new Promise((
|
|
33930
|
+
return new Promise((resolve18) => {
|
|
33343
33931
|
if (!this[kSize]) {
|
|
33344
|
-
|
|
33932
|
+
resolve18(null);
|
|
33345
33933
|
} else {
|
|
33346
|
-
this[kClosedResolve] =
|
|
33934
|
+
this[kClosedResolve] = resolve18;
|
|
33347
33935
|
}
|
|
33348
33936
|
});
|
|
33349
33937
|
}
|
|
33350
33938
|
async [kDestroy](err) {
|
|
33351
|
-
return new Promise((
|
|
33939
|
+
return new Promise((resolve18) => {
|
|
33352
33940
|
const requests = this[kQueue].splice(this[kPendingIdx]);
|
|
33353
33941
|
for (let i = 0; i < requests.length; i++) {
|
|
33354
33942
|
const request = requests[i];
|
|
@@ -33359,7 +33947,7 @@ var require_client = __commonJS({
|
|
|
33359
33947
|
this[kClosedResolve]();
|
|
33360
33948
|
this[kClosedResolve] = null;
|
|
33361
33949
|
}
|
|
33362
|
-
|
|
33950
|
+
resolve18();
|
|
33363
33951
|
};
|
|
33364
33952
|
if (this[kHTTP2Session] != null) {
|
|
33365
33953
|
util.destroy(this[kHTTP2Session], err);
|
|
@@ -33939,7 +34527,7 @@ var require_client = __commonJS({
|
|
|
33939
34527
|
});
|
|
33940
34528
|
}
|
|
33941
34529
|
try {
|
|
33942
|
-
const socket = await new Promise((
|
|
34530
|
+
const socket = await new Promise((resolve18, reject) => {
|
|
33943
34531
|
client[kConnector]({
|
|
33944
34532
|
host,
|
|
33945
34533
|
hostname,
|
|
@@ -33951,7 +34539,7 @@ var require_client = __commonJS({
|
|
|
33951
34539
|
if (err) {
|
|
33952
34540
|
reject(err);
|
|
33953
34541
|
} else {
|
|
33954
|
-
|
|
34542
|
+
resolve18(socket2);
|
|
33955
34543
|
}
|
|
33956
34544
|
});
|
|
33957
34545
|
});
|
|
@@ -34162,7 +34750,7 @@ var require_client = __commonJS({
|
|
|
34162
34750
|
writeH2(client, client[kHTTP2Session], request);
|
|
34163
34751
|
return;
|
|
34164
34752
|
}
|
|
34165
|
-
const { body, method, path:
|
|
34753
|
+
const { body, method, path: path31, host, upgrade, headers, blocking, reset } = request;
|
|
34166
34754
|
const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH";
|
|
34167
34755
|
if (body && typeof body.read === "function") {
|
|
34168
34756
|
body.read(0);
|
|
@@ -34212,7 +34800,7 @@ var require_client = __commonJS({
|
|
|
34212
34800
|
if (blocking) {
|
|
34213
34801
|
socket[kBlocking] = true;
|
|
34214
34802
|
}
|
|
34215
|
-
let header = `${method} ${
|
|
34803
|
+
let header = `${method} ${path31} HTTP/1.1\r
|
|
34216
34804
|
`;
|
|
34217
34805
|
if (typeof host === "string") {
|
|
34218
34806
|
header += `host: ${host}\r
|
|
@@ -34275,7 +34863,7 @@ upgrade: ${upgrade}\r
|
|
|
34275
34863
|
return true;
|
|
34276
34864
|
}
|
|
34277
34865
|
function writeH2(client, session, request) {
|
|
34278
|
-
const { body, method, path:
|
|
34866
|
+
const { body, method, path: path31, host, upgrade, expectContinue, signal, headers: reqHeaders } = request;
|
|
34279
34867
|
let headers;
|
|
34280
34868
|
if (typeof reqHeaders === "string") headers = Request[kHTTP2CopyHeaders](reqHeaders.trim());
|
|
34281
34869
|
else headers = reqHeaders;
|
|
@@ -34318,7 +34906,7 @@ upgrade: ${upgrade}\r
|
|
|
34318
34906
|
});
|
|
34319
34907
|
return true;
|
|
34320
34908
|
}
|
|
34321
|
-
headers[HTTP2_HEADER_PATH] =
|
|
34909
|
+
headers[HTTP2_HEADER_PATH] = path31;
|
|
34322
34910
|
headers[HTTP2_HEADER_SCHEME] = "https";
|
|
34323
34911
|
const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH";
|
|
34324
34912
|
if (body && typeof body.read === "function") {
|
|
@@ -34575,12 +35163,12 @@ upgrade: ${upgrade}\r
|
|
|
34575
35163
|
cb();
|
|
34576
35164
|
}
|
|
34577
35165
|
}
|
|
34578
|
-
const waitForDrain = () => new Promise((
|
|
35166
|
+
const waitForDrain = () => new Promise((resolve18, reject) => {
|
|
34579
35167
|
assert(callback === null);
|
|
34580
35168
|
if (socket[kError]) {
|
|
34581
35169
|
reject(socket[kError]);
|
|
34582
35170
|
} else {
|
|
34583
|
-
callback =
|
|
35171
|
+
callback = resolve18;
|
|
34584
35172
|
}
|
|
34585
35173
|
});
|
|
34586
35174
|
if (client[kHTTPConnVersion] === "h2") {
|
|
@@ -34926,8 +35514,8 @@ var require_pool_base = __commonJS({
|
|
|
34926
35514
|
if (this[kQueue].isEmpty()) {
|
|
34927
35515
|
return Promise.all(this[kClients].map((c) => c.close()));
|
|
34928
35516
|
} else {
|
|
34929
|
-
return new Promise((
|
|
34930
|
-
this[kClosedResolve] =
|
|
35517
|
+
return new Promise((resolve18) => {
|
|
35518
|
+
this[kClosedResolve] = resolve18;
|
|
34931
35519
|
});
|
|
34932
35520
|
}
|
|
34933
35521
|
}
|
|
@@ -35505,7 +36093,7 @@ var require_readable = __commonJS({
|
|
|
35505
36093
|
if (this.closed) {
|
|
35506
36094
|
return Promise.resolve(null);
|
|
35507
36095
|
}
|
|
35508
|
-
return new Promise((
|
|
36096
|
+
return new Promise((resolve18, reject) => {
|
|
35509
36097
|
const signalListenerCleanup = signal ? util.addAbortListener(signal, () => {
|
|
35510
36098
|
this.destroy();
|
|
35511
36099
|
}) : noop;
|
|
@@ -35514,7 +36102,7 @@ var require_readable = __commonJS({
|
|
|
35514
36102
|
if (signal && signal.aborted) {
|
|
35515
36103
|
reject(signal.reason || Object.assign(new Error("The operation was aborted"), { name: "AbortError" }));
|
|
35516
36104
|
} else {
|
|
35517
|
-
|
|
36105
|
+
resolve18(null);
|
|
35518
36106
|
}
|
|
35519
36107
|
}).on("error", noop).on("data", function(chunk) {
|
|
35520
36108
|
limit -= chunk.length;
|
|
@@ -35536,11 +36124,11 @@ var require_readable = __commonJS({
|
|
|
35536
36124
|
throw new TypeError("unusable");
|
|
35537
36125
|
}
|
|
35538
36126
|
assert(!stream[kConsume]);
|
|
35539
|
-
return new Promise((
|
|
36127
|
+
return new Promise((resolve18, reject) => {
|
|
35540
36128
|
stream[kConsume] = {
|
|
35541
36129
|
type,
|
|
35542
36130
|
stream,
|
|
35543
|
-
resolve:
|
|
36131
|
+
resolve: resolve18,
|
|
35544
36132
|
reject,
|
|
35545
36133
|
length: 0,
|
|
35546
36134
|
body: []
|
|
@@ -35575,12 +36163,12 @@ var require_readable = __commonJS({
|
|
|
35575
36163
|
}
|
|
35576
36164
|
}
|
|
35577
36165
|
function consumeEnd(consume2) {
|
|
35578
|
-
const { type, body, resolve:
|
|
36166
|
+
const { type, body, resolve: resolve18, stream, length } = consume2;
|
|
35579
36167
|
try {
|
|
35580
36168
|
if (type === "text") {
|
|
35581
|
-
|
|
36169
|
+
resolve18(toUSVString(Buffer.concat(body)));
|
|
35582
36170
|
} else if (type === "json") {
|
|
35583
|
-
|
|
36171
|
+
resolve18(JSON.parse(Buffer.concat(body)));
|
|
35584
36172
|
} else if (type === "arrayBuffer") {
|
|
35585
36173
|
const dst = new Uint8Array(length);
|
|
35586
36174
|
let pos = 0;
|
|
@@ -35588,12 +36176,12 @@ var require_readable = __commonJS({
|
|
|
35588
36176
|
dst.set(buf, pos);
|
|
35589
36177
|
pos += buf.byteLength;
|
|
35590
36178
|
}
|
|
35591
|
-
|
|
36179
|
+
resolve18(dst.buffer);
|
|
35592
36180
|
} else if (type === "blob") {
|
|
35593
36181
|
if (!Blob2) {
|
|
35594
36182
|
Blob2 = require("buffer").Blob;
|
|
35595
36183
|
}
|
|
35596
|
-
|
|
36184
|
+
resolve18(new Blob2(body, { type: stream[kContentType] }));
|
|
35597
36185
|
}
|
|
35598
36186
|
consumeFinish(consume2);
|
|
35599
36187
|
} catch (err) {
|
|
@@ -35850,9 +36438,9 @@ var require_api_request = __commonJS({
|
|
|
35850
36438
|
};
|
|
35851
36439
|
function request(opts, callback) {
|
|
35852
36440
|
if (callback === void 0) {
|
|
35853
|
-
return new Promise((
|
|
36441
|
+
return new Promise((resolve18, reject) => {
|
|
35854
36442
|
request.call(this, opts, (err, data) => {
|
|
35855
|
-
return err ? reject(err) :
|
|
36443
|
+
return err ? reject(err) : resolve18(data);
|
|
35856
36444
|
});
|
|
35857
36445
|
});
|
|
35858
36446
|
}
|
|
@@ -36025,9 +36613,9 @@ var require_api_stream = __commonJS({
|
|
|
36025
36613
|
};
|
|
36026
36614
|
function stream(opts, factory, callback) {
|
|
36027
36615
|
if (callback === void 0) {
|
|
36028
|
-
return new Promise((
|
|
36616
|
+
return new Promise((resolve18, reject) => {
|
|
36029
36617
|
stream.call(this, opts, factory, (err, data) => {
|
|
36030
|
-
return err ? reject(err) :
|
|
36618
|
+
return err ? reject(err) : resolve18(data);
|
|
36031
36619
|
});
|
|
36032
36620
|
});
|
|
36033
36621
|
}
|
|
@@ -36308,9 +36896,9 @@ var require_api_upgrade = __commonJS({
|
|
|
36308
36896
|
};
|
|
36309
36897
|
function upgrade(opts, callback) {
|
|
36310
36898
|
if (callback === void 0) {
|
|
36311
|
-
return new Promise((
|
|
36899
|
+
return new Promise((resolve18, reject) => {
|
|
36312
36900
|
upgrade.call(this, opts, (err, data) => {
|
|
36313
|
-
return err ? reject(err) :
|
|
36901
|
+
return err ? reject(err) : resolve18(data);
|
|
36314
36902
|
});
|
|
36315
36903
|
});
|
|
36316
36904
|
}
|
|
@@ -36399,9 +36987,9 @@ var require_api_connect = __commonJS({
|
|
|
36399
36987
|
};
|
|
36400
36988
|
function connect(opts, callback) {
|
|
36401
36989
|
if (callback === void 0) {
|
|
36402
|
-
return new Promise((
|
|
36990
|
+
return new Promise((resolve18, reject) => {
|
|
36403
36991
|
connect.call(this, opts, (err, data) => {
|
|
36404
|
-
return err ? reject(err) :
|
|
36992
|
+
return err ? reject(err) : resolve18(data);
|
|
36405
36993
|
});
|
|
36406
36994
|
});
|
|
36407
36995
|
}
|
|
@@ -36561,20 +37149,20 @@ var require_mock_utils = __commonJS({
|
|
|
36561
37149
|
}
|
|
36562
37150
|
return true;
|
|
36563
37151
|
}
|
|
36564
|
-
function safeUrl(
|
|
36565
|
-
if (typeof
|
|
36566
|
-
return
|
|
37152
|
+
function safeUrl(path31) {
|
|
37153
|
+
if (typeof path31 !== "string") {
|
|
37154
|
+
return path31;
|
|
36567
37155
|
}
|
|
36568
|
-
const pathSegments =
|
|
37156
|
+
const pathSegments = path31.split("?");
|
|
36569
37157
|
if (pathSegments.length !== 2) {
|
|
36570
|
-
return
|
|
37158
|
+
return path31;
|
|
36571
37159
|
}
|
|
36572
37160
|
const qp = new URLSearchParams(pathSegments.pop());
|
|
36573
37161
|
qp.sort();
|
|
36574
37162
|
return [...pathSegments, qp.toString()].join("?");
|
|
36575
37163
|
}
|
|
36576
|
-
function matchKey(mockDispatch2, { path:
|
|
36577
|
-
const pathMatch = matchValue(mockDispatch2.path,
|
|
37164
|
+
function matchKey(mockDispatch2, { path: path31, method, body, headers }) {
|
|
37165
|
+
const pathMatch = matchValue(mockDispatch2.path, path31);
|
|
36578
37166
|
const methodMatch = matchValue(mockDispatch2.method, method);
|
|
36579
37167
|
const bodyMatch = typeof mockDispatch2.body !== "undefined" ? matchValue(mockDispatch2.body, body) : true;
|
|
36580
37168
|
const headersMatch = matchHeaders(mockDispatch2, headers);
|
|
@@ -36592,7 +37180,7 @@ var require_mock_utils = __commonJS({
|
|
|
36592
37180
|
function getMockDispatch(mockDispatches, key) {
|
|
36593
37181
|
const basePath = key.query ? buildURL(key.path, key.query) : key.path;
|
|
36594
37182
|
const resolvedPath = typeof basePath === "string" ? safeUrl(basePath) : basePath;
|
|
36595
|
-
let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path:
|
|
37183
|
+
let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path: path31 }) => matchValue(safeUrl(path31), resolvedPath));
|
|
36596
37184
|
if (matchedMockDispatches.length === 0) {
|
|
36597
37185
|
throw new MockNotMatchedError(`Mock dispatch not matched for path '${resolvedPath}'`);
|
|
36598
37186
|
}
|
|
@@ -36629,9 +37217,9 @@ var require_mock_utils = __commonJS({
|
|
|
36629
37217
|
}
|
|
36630
37218
|
}
|
|
36631
37219
|
function buildKey(opts) {
|
|
36632
|
-
const { path:
|
|
37220
|
+
const { path: path31, method, body, headers, query } = opts;
|
|
36633
37221
|
return {
|
|
36634
|
-
path:
|
|
37222
|
+
path: path31,
|
|
36635
37223
|
method,
|
|
36636
37224
|
body,
|
|
36637
37225
|
headers,
|
|
@@ -37080,10 +37668,10 @@ var require_pending_interceptors_formatter = __commonJS({
|
|
|
37080
37668
|
}
|
|
37081
37669
|
format(pendingInterceptors) {
|
|
37082
37670
|
const withPrettyHeaders = pendingInterceptors.map(
|
|
37083
|
-
({ method, path:
|
|
37671
|
+
({ method, path: path31, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
|
|
37084
37672
|
Method: method,
|
|
37085
37673
|
Origin: origin,
|
|
37086
|
-
Path:
|
|
37674
|
+
Path: path31,
|
|
37087
37675
|
"Status code": statusCode,
|
|
37088
37676
|
Persistent: persist ? "\u2705" : "\u274C",
|
|
37089
37677
|
Invocations: timesInvoked,
|
|
@@ -40024,7 +40612,7 @@ var require_fetch = __commonJS({
|
|
|
40024
40612
|
async function dispatch({ body }) {
|
|
40025
40613
|
const url = requestCurrentURL(request);
|
|
40026
40614
|
const agent = fetchParams.controller.dispatcher;
|
|
40027
|
-
return new Promise((
|
|
40615
|
+
return new Promise((resolve18, reject) => agent.dispatch(
|
|
40028
40616
|
{
|
|
40029
40617
|
path: url.pathname + url.search,
|
|
40030
40618
|
origin: url.origin,
|
|
@@ -40100,7 +40688,7 @@ var require_fetch = __commonJS({
|
|
|
40100
40688
|
}
|
|
40101
40689
|
}
|
|
40102
40690
|
}
|
|
40103
|
-
|
|
40691
|
+
resolve18({
|
|
40104
40692
|
status,
|
|
40105
40693
|
statusText,
|
|
40106
40694
|
headersList: headers[kHeadersList],
|
|
@@ -40143,7 +40731,7 @@ var require_fetch = __commonJS({
|
|
|
40143
40731
|
const val = headersList[n + 1].toString("latin1");
|
|
40144
40732
|
headers[kHeadersList].append(key, val);
|
|
40145
40733
|
}
|
|
40146
|
-
|
|
40734
|
+
resolve18({
|
|
40147
40735
|
status,
|
|
40148
40736
|
statusText: STATUS_CODES[status],
|
|
40149
40737
|
headersList: headers[kHeadersList],
|
|
@@ -41704,8 +42292,8 @@ var require_util6 = __commonJS({
|
|
|
41704
42292
|
}
|
|
41705
42293
|
}
|
|
41706
42294
|
}
|
|
41707
|
-
function validateCookiePath(
|
|
41708
|
-
for (const char of
|
|
42295
|
+
function validateCookiePath(path31) {
|
|
42296
|
+
for (const char of path31) {
|
|
41709
42297
|
const code = char.charCodeAt(0);
|
|
41710
42298
|
if (code < 33 || char === ";") {
|
|
41711
42299
|
throw new Error("Invalid cookie path");
|
|
@@ -42502,9 +43090,9 @@ var require_connection = __commonJS({
|
|
|
42502
43090
|
channels.open = diagnosticsChannel.channel("undici:websocket:open");
|
|
42503
43091
|
channels.close = diagnosticsChannel.channel("undici:websocket:close");
|
|
42504
43092
|
channels.socketError = diagnosticsChannel.channel("undici:websocket:socket_error");
|
|
42505
|
-
var
|
|
43093
|
+
var crypto4;
|
|
42506
43094
|
try {
|
|
42507
|
-
|
|
43095
|
+
crypto4 = require("crypto");
|
|
42508
43096
|
} catch {
|
|
42509
43097
|
}
|
|
42510
43098
|
function establishWebSocketConnection(url, protocols, ws, onEstablish, options) {
|
|
@@ -42523,7 +43111,7 @@ var require_connection = __commonJS({
|
|
|
42523
43111
|
const headersList = new Headers(options.headers)[kHeadersList];
|
|
42524
43112
|
request.headersList = headersList;
|
|
42525
43113
|
}
|
|
42526
|
-
const keyValue =
|
|
43114
|
+
const keyValue = crypto4.randomBytes(16).toString("base64");
|
|
42527
43115
|
request.headersList.append("sec-websocket-key", keyValue);
|
|
42528
43116
|
request.headersList.append("sec-websocket-version", "13");
|
|
42529
43117
|
for (const protocol of protocols) {
|
|
@@ -42552,7 +43140,7 @@ var require_connection = __commonJS({
|
|
|
42552
43140
|
return;
|
|
42553
43141
|
}
|
|
42554
43142
|
const secWSAccept = response.headersList.get("Sec-WebSocket-Accept");
|
|
42555
|
-
const digest =
|
|
43143
|
+
const digest = crypto4.createHash("sha1").update(keyValue + uid).digest("base64");
|
|
42556
43144
|
if (secWSAccept !== digest) {
|
|
42557
43145
|
failWebsocketConnection(ws, "Incorrect hash received in Sec-WebSocket-Accept header.");
|
|
42558
43146
|
return;
|
|
@@ -42632,9 +43220,9 @@ var require_frame = __commonJS({
|
|
|
42632
43220
|
"node_modules/undici/lib/websocket/frame.js"(exports2, module2) {
|
|
42633
43221
|
"use strict";
|
|
42634
43222
|
var { maxUnsigned16Bit } = require_constants5();
|
|
42635
|
-
var
|
|
43223
|
+
var crypto4;
|
|
42636
43224
|
try {
|
|
42637
|
-
|
|
43225
|
+
crypto4 = require("crypto");
|
|
42638
43226
|
} catch {
|
|
42639
43227
|
}
|
|
42640
43228
|
var WebsocketFrameSend = class {
|
|
@@ -42643,7 +43231,7 @@ var require_frame = __commonJS({
|
|
|
42643
43231
|
*/
|
|
42644
43232
|
constructor(data) {
|
|
42645
43233
|
this.frameData = data;
|
|
42646
|
-
this.maskKey =
|
|
43234
|
+
this.maskKey = crypto4.randomBytes(4);
|
|
42647
43235
|
}
|
|
42648
43236
|
createFrame(opcode) {
|
|
42649
43237
|
const bodyLength = this.frameData?.byteLength ?? 0;
|
|
@@ -43385,11 +43973,11 @@ var require_undici = __commonJS({
|
|
|
43385
43973
|
if (typeof opts.path !== "string") {
|
|
43386
43974
|
throw new InvalidArgumentError("invalid opts.path");
|
|
43387
43975
|
}
|
|
43388
|
-
let
|
|
43976
|
+
let path31 = opts.path;
|
|
43389
43977
|
if (!opts.path.startsWith("/")) {
|
|
43390
|
-
|
|
43978
|
+
path31 = `/${path31}`;
|
|
43391
43979
|
}
|
|
43392
|
-
url = new URL(util.parseOrigin(url).origin +
|
|
43980
|
+
url = new URL(util.parseOrigin(url).origin + path31);
|
|
43393
43981
|
} else {
|
|
43394
43982
|
if (!opts) {
|
|
43395
43983
|
opts = typeof url === "object" ? url : {};
|
|
@@ -43938,7 +44526,7 @@ var init_mcp_check_provider = __esm({
|
|
|
43938
44526
|
logger.warn(
|
|
43939
44527
|
`MCP ${transportName} failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${delay}ms: ${error instanceof Error ? error.message : String(error)}`
|
|
43940
44528
|
);
|
|
43941
|
-
await new Promise((
|
|
44529
|
+
await new Promise((resolve18) => setTimeout(resolve18, delay));
|
|
43942
44530
|
attempt += 1;
|
|
43943
44531
|
} finally {
|
|
43944
44532
|
try {
|
|
@@ -44220,7 +44808,7 @@ async function acquirePromptLock() {
|
|
|
44220
44808
|
activePrompt = true;
|
|
44221
44809
|
return;
|
|
44222
44810
|
}
|
|
44223
|
-
await new Promise((
|
|
44811
|
+
await new Promise((resolve18) => waiters.push(resolve18));
|
|
44224
44812
|
activePrompt = true;
|
|
44225
44813
|
}
|
|
44226
44814
|
function releasePromptLock() {
|
|
@@ -44230,7 +44818,7 @@ function releasePromptLock() {
|
|
|
44230
44818
|
}
|
|
44231
44819
|
async function interactivePrompt(options) {
|
|
44232
44820
|
await acquirePromptLock();
|
|
44233
|
-
return new Promise((
|
|
44821
|
+
return new Promise((resolve18, reject) => {
|
|
44234
44822
|
const dbg = process.env.VISOR_DEBUG === "true";
|
|
44235
44823
|
try {
|
|
44236
44824
|
if (dbg) {
|
|
@@ -44317,12 +44905,12 @@ async function interactivePrompt(options) {
|
|
|
44317
44905
|
};
|
|
44318
44906
|
const finish = (value) => {
|
|
44319
44907
|
cleanup();
|
|
44320
|
-
|
|
44908
|
+
resolve18(value);
|
|
44321
44909
|
};
|
|
44322
44910
|
if (options.timeout && options.timeout > 0) {
|
|
44323
44911
|
timeoutId = setTimeout(() => {
|
|
44324
44912
|
cleanup();
|
|
44325
|
-
if (defaultValue !== void 0) return
|
|
44913
|
+
if (defaultValue !== void 0) return resolve18(defaultValue);
|
|
44326
44914
|
return reject(new Error("Input timeout"));
|
|
44327
44915
|
}, options.timeout);
|
|
44328
44916
|
}
|
|
@@ -44454,7 +45042,7 @@ async function interactivePrompt(options) {
|
|
|
44454
45042
|
});
|
|
44455
45043
|
}
|
|
44456
45044
|
async function simplePrompt(prompt) {
|
|
44457
|
-
return new Promise((
|
|
45045
|
+
return new Promise((resolve18) => {
|
|
44458
45046
|
const rl = readline.createInterface({
|
|
44459
45047
|
input: process.stdin,
|
|
44460
45048
|
output: process.stdout
|
|
@@ -44470,7 +45058,7 @@ async function simplePrompt(prompt) {
|
|
|
44470
45058
|
rl.question(`${prompt}
|
|
44471
45059
|
> `, (answer) => {
|
|
44472
45060
|
rl.close();
|
|
44473
|
-
|
|
45061
|
+
resolve18(answer.trim());
|
|
44474
45062
|
});
|
|
44475
45063
|
});
|
|
44476
45064
|
}
|
|
@@ -44638,7 +45226,7 @@ function isStdinAvailable() {
|
|
|
44638
45226
|
return !process.stdin.isTTY;
|
|
44639
45227
|
}
|
|
44640
45228
|
async function readStdin(timeout, maxSize = 1024 * 1024) {
|
|
44641
|
-
return new Promise((
|
|
45229
|
+
return new Promise((resolve18, reject) => {
|
|
44642
45230
|
let data = "";
|
|
44643
45231
|
let timeoutId;
|
|
44644
45232
|
if (timeout) {
|
|
@@ -44665,7 +45253,7 @@ async function readStdin(timeout, maxSize = 1024 * 1024) {
|
|
|
44665
45253
|
};
|
|
44666
45254
|
const onEnd = () => {
|
|
44667
45255
|
cleanup();
|
|
44668
|
-
|
|
45256
|
+
resolve18(data.trim());
|
|
44669
45257
|
};
|
|
44670
45258
|
const onError = (err) => {
|
|
44671
45259
|
cleanup();
|
|
@@ -46314,7 +46902,7 @@ var init_worktree_manager = __esm({
|
|
|
46314
46902
|
await this.saveMetadata(worktreePath, updatedMetadata);
|
|
46315
46903
|
if (options.clean) {
|
|
46316
46904
|
logger.debug(`Cleaning updated worktree`);
|
|
46317
|
-
await this.cleanWorktree(worktreePath);
|
|
46905
|
+
await this.cleanWorktree(worktreePath, latestCommit);
|
|
46318
46906
|
}
|
|
46319
46907
|
this.activeWorktrees.set(worktreeId, updatedMetadata);
|
|
46320
46908
|
return {
|
|
@@ -46333,7 +46921,7 @@ var init_worktree_manager = __esm({
|
|
|
46333
46921
|
}
|
|
46334
46922
|
if (options.clean) {
|
|
46335
46923
|
logger.debug(`Cleaning existing worktree`);
|
|
46336
|
-
await this.cleanWorktree(worktreePath);
|
|
46924
|
+
await this.cleanWorktree(worktreePath, metadata2.commit);
|
|
46337
46925
|
}
|
|
46338
46926
|
this.activeWorktrees.set(worktreeId, metadata2);
|
|
46339
46927
|
return {
|
|
@@ -46375,7 +46963,7 @@ var init_worktree_manager = __esm({
|
|
|
46375
46963
|
await this.saveMetadata(worktreePath, updatedMetadata);
|
|
46376
46964
|
if (options.clean) {
|
|
46377
46965
|
logger.debug(`Cleaning updated worktree`);
|
|
46378
|
-
await this.cleanWorktree(worktreePath);
|
|
46966
|
+
await this.cleanWorktree(worktreePath, newCommit);
|
|
46379
46967
|
}
|
|
46380
46968
|
this.activeWorktrees.set(worktreeId, updatedMetadata);
|
|
46381
46969
|
logger.info(`Successfully updated worktree to ${ref} (${newCommit})`);
|
|
@@ -46465,13 +47053,54 @@ var init_worktree_manager = __esm({
|
|
|
46465
47053
|
return true;
|
|
46466
47054
|
}
|
|
46467
47055
|
/**
|
|
46468
|
-
* Clean worktree (reset and remove untracked files)
|
|
46469
|
-
|
|
46470
|
-
|
|
47056
|
+
* Clean worktree (reset and remove untracked files).
|
|
47057
|
+
*
|
|
47058
|
+
* When `expectedCommit` is provided the worktree is first forced back to a
|
|
47059
|
+
* detached HEAD at that commit. This is essential because AI agents or user
|
|
47060
|
+
* commands may have created local branches inside the worktree, switching
|
|
47061
|
+
* HEAD away from the detached state. A plain `reset --hard HEAD` would
|
|
47062
|
+
* then reset to the *wrong* commit. After resetting, any local branches
|
|
47063
|
+
* that were created inside the worktree are deleted so they cannot leak
|
|
47064
|
+
* into future runs or PRs.
|
|
47065
|
+
*/
|
|
47066
|
+
async cleanWorktree(worktreePath, expectedCommit) {
|
|
47067
|
+
if (expectedCommit) {
|
|
47068
|
+
const detachCmd = `git -C ${this.escapeShellArg(worktreePath)} checkout --detach ${this.escapeShellArg(expectedCommit)}`;
|
|
47069
|
+
const detachResult = await this.executeGitCommand(detachCmd, { timeout: 3e4 });
|
|
47070
|
+
if (detachResult.exitCode !== 0) {
|
|
47071
|
+
await this.executeGitCommand(`git -C ${this.escapeShellArg(worktreePath)} reset --hard`, {
|
|
47072
|
+
timeout: 1e4
|
|
47073
|
+
});
|
|
47074
|
+
await this.executeGitCommand(detachCmd, { timeout: 3e4 });
|
|
47075
|
+
}
|
|
47076
|
+
}
|
|
46471
47077
|
const resetCmd = `git -C ${this.escapeShellArg(worktreePath)} reset --hard HEAD`;
|
|
46472
47078
|
await this.executeGitCommand(resetCmd);
|
|
46473
47079
|
const cleanCmd = `git -C ${this.escapeShellArg(worktreePath)} clean -fdx`;
|
|
46474
47080
|
await this.executeGitCommand(cleanCmd);
|
|
47081
|
+
await this.deleteLocalBranches(worktreePath);
|
|
47082
|
+
}
|
|
47083
|
+
/**
|
|
47084
|
+
* Delete all local branches in a worktree.
|
|
47085
|
+
* Worktrees are always used in detached HEAD state, so any local branches
|
|
47086
|
+
* were unintentionally created and should be cleaned up.
|
|
47087
|
+
*/
|
|
47088
|
+
async deleteLocalBranches(worktreePath) {
|
|
47089
|
+
const listCmd = `git -C ${this.escapeShellArg(worktreePath)} branch --list --format='%(refname:short)'`;
|
|
47090
|
+
const listResult = await this.executeGitCommand(listCmd, { timeout: 1e4 });
|
|
47091
|
+
if (listResult.exitCode !== 0 || !listResult.stdout.trim()) {
|
|
47092
|
+
return;
|
|
47093
|
+
}
|
|
47094
|
+
const branches = listResult.stdout.trim().split("\n").map((b) => b.trim()).filter((b) => b.length > 0);
|
|
47095
|
+
for (const branch of branches) {
|
|
47096
|
+
const deleteCmd = `git -C ${this.escapeShellArg(worktreePath)} branch -D ${this.escapeShellArg(branch)}`;
|
|
47097
|
+
const deleteResult = await this.executeGitCommand(deleteCmd, { timeout: 1e4 });
|
|
47098
|
+
if (deleteResult.exitCode === 0) {
|
|
47099
|
+
logger.debug(`Deleted local branch '${branch}' from worktree`);
|
|
47100
|
+
} else {
|
|
47101
|
+
logger.warn(`Failed to delete branch '${branch}': ${deleteResult.stderr}`);
|
|
47102
|
+
}
|
|
47103
|
+
}
|
|
46475
47104
|
}
|
|
46476
47105
|
/**
|
|
46477
47106
|
* Get commit SHA for a given ref inside a bare repository.
|
|
@@ -48742,23 +49371,23 @@ __export(renderer_schema_exports, {
|
|
|
48742
49371
|
});
|
|
48743
49372
|
async function loadRendererSchema(name) {
|
|
48744
49373
|
try {
|
|
48745
|
-
const
|
|
48746
|
-
const
|
|
49374
|
+
const fs27 = await import("fs/promises");
|
|
49375
|
+
const path31 = await import("path");
|
|
48747
49376
|
const sanitized = String(name).replace(/[^a-zA-Z0-9-]/g, "");
|
|
48748
49377
|
if (!sanitized) return void 0;
|
|
48749
49378
|
const candidates = [
|
|
48750
49379
|
// When bundled with ncc, __dirname is dist/ and output/ is at dist/output/
|
|
48751
|
-
|
|
49380
|
+
path31.join(__dirname, "output", sanitized, "schema.json"),
|
|
48752
49381
|
// When running from source, __dirname is src/state-machine/dispatch/ and output/ is at output/
|
|
48753
|
-
|
|
49382
|
+
path31.join(__dirname, "..", "..", "output", sanitized, "schema.json"),
|
|
48754
49383
|
// When running from a checkout with output/ folder copied to CWD
|
|
48755
|
-
|
|
49384
|
+
path31.join(process.cwd(), "output", sanitized, "schema.json"),
|
|
48756
49385
|
// Fallback: cwd/dist/output/
|
|
48757
|
-
|
|
49386
|
+
path31.join(process.cwd(), "dist", "output", sanitized, "schema.json")
|
|
48758
49387
|
];
|
|
48759
49388
|
for (const p of candidates) {
|
|
48760
49389
|
try {
|
|
48761
|
-
const raw = await
|
|
49390
|
+
const raw = await fs27.readFile(p, "utf-8");
|
|
48762
49391
|
return JSON.parse(raw);
|
|
48763
49392
|
} catch {
|
|
48764
49393
|
}
|
|
@@ -51177,8 +51806,8 @@ function updateStats2(results, state, isForEachIteration = false) {
|
|
|
51177
51806
|
async function renderTemplateContent2(checkId, checkConfig, reviewSummary) {
|
|
51178
51807
|
try {
|
|
51179
51808
|
const { createExtendedLiquid: createExtendedLiquid2 } = await Promise.resolve().then(() => (init_liquid_extensions(), liquid_extensions_exports));
|
|
51180
|
-
const
|
|
51181
|
-
const
|
|
51809
|
+
const fs27 = await import("fs/promises");
|
|
51810
|
+
const path31 = await import("path");
|
|
51182
51811
|
const schemaRaw = checkConfig.schema || "plain";
|
|
51183
51812
|
const schema = typeof schemaRaw === "string" && !schemaRaw.includes("{{") && !schemaRaw.includes("{%") ? schemaRaw : typeof schemaRaw === "object" ? "code-review" : "plain";
|
|
51184
51813
|
let templateContent;
|
|
@@ -51187,27 +51816,27 @@ async function renderTemplateContent2(checkId, checkConfig, reviewSummary) {
|
|
|
51187
51816
|
logger.debug(`[LevelDispatch] Using inline template for ${checkId}`);
|
|
51188
51817
|
} else if (checkConfig.template && checkConfig.template.file) {
|
|
51189
51818
|
const file = String(checkConfig.template.file);
|
|
51190
|
-
const resolved =
|
|
51191
|
-
templateContent = await
|
|
51819
|
+
const resolved = path31.resolve(process.cwd(), file);
|
|
51820
|
+
templateContent = await fs27.readFile(resolved, "utf-8");
|
|
51192
51821
|
logger.debug(`[LevelDispatch] Using template file for ${checkId}: ${resolved}`);
|
|
51193
51822
|
} else if (schema && schema !== "plain") {
|
|
51194
51823
|
const sanitized = String(schema).replace(/[^a-zA-Z0-9-]/g, "");
|
|
51195
51824
|
if (sanitized) {
|
|
51196
51825
|
const candidatePaths = [
|
|
51197
|
-
|
|
51826
|
+
path31.join(__dirname, "output", sanitized, "template.liquid"),
|
|
51198
51827
|
// bundled: dist/output/
|
|
51199
|
-
|
|
51828
|
+
path31.join(__dirname, "..", "..", "output", sanitized, "template.liquid"),
|
|
51200
51829
|
// source (from state-machine/states)
|
|
51201
|
-
|
|
51830
|
+
path31.join(__dirname, "..", "..", "..", "output", sanitized, "template.liquid"),
|
|
51202
51831
|
// source (alternate)
|
|
51203
|
-
|
|
51832
|
+
path31.join(process.cwd(), "output", sanitized, "template.liquid"),
|
|
51204
51833
|
// fallback: cwd/output/
|
|
51205
|
-
|
|
51834
|
+
path31.join(process.cwd(), "dist", "output", sanitized, "template.liquid")
|
|
51206
51835
|
// fallback: cwd/dist/output/
|
|
51207
51836
|
];
|
|
51208
51837
|
for (const p of candidatePaths) {
|
|
51209
51838
|
try {
|
|
51210
|
-
templateContent = await
|
|
51839
|
+
templateContent = await fs27.readFile(p, "utf-8");
|
|
51211
51840
|
if (templateContent) {
|
|
51212
51841
|
logger.debug(`[LevelDispatch] Using schema template for ${checkId}: ${p}`);
|
|
51213
51842
|
break;
|
|
@@ -53022,9 +53651,40 @@ ${file.patch}`).join("\n\n");
|
|
|
53022
53651
|
/**
|
|
53023
53652
|
* Get diff between current branch and base branch (for feature branch analysis)
|
|
53024
53653
|
*/
|
|
53654
|
+
/**
|
|
53655
|
+
* Resolve the base branch ref to a revision that exists locally.
|
|
53656
|
+
* In CI (shallow clones), the local branch may not exist, so we try:
|
|
53657
|
+
* 1. The branch name as-is (e.g. "main")
|
|
53658
|
+
* 2. The remote-tracking ref (e.g. "origin/main")
|
|
53659
|
+
* 3. Fetch from origin then retry
|
|
53660
|
+
*/
|
|
53661
|
+
async resolveBaseBranchRef(baseBranch) {
|
|
53662
|
+
try {
|
|
53663
|
+
await this.git.revparse([baseBranch]);
|
|
53664
|
+
return baseBranch;
|
|
53665
|
+
} catch {
|
|
53666
|
+
}
|
|
53667
|
+
const remoteBranch = `origin/${baseBranch}`;
|
|
53668
|
+
try {
|
|
53669
|
+
await this.git.revparse([remoteBranch]);
|
|
53670
|
+
return remoteBranch;
|
|
53671
|
+
} catch {
|
|
53672
|
+
}
|
|
53673
|
+
try {
|
|
53674
|
+
await this.git.fetch(["origin", baseBranch]);
|
|
53675
|
+
await this.git.revparse([remoteBranch]);
|
|
53676
|
+
return remoteBranch;
|
|
53677
|
+
} catch {
|
|
53678
|
+
return baseBranch;
|
|
53679
|
+
}
|
|
53680
|
+
}
|
|
53025
53681
|
async getBranchDiff(baseBranch, includeContext = true) {
|
|
53026
53682
|
try {
|
|
53027
|
-
const
|
|
53683
|
+
const resolvedBase = await this.resolveBaseBranchRef(baseBranch);
|
|
53684
|
+
if (resolvedBase !== baseBranch) {
|
|
53685
|
+
console.error(`\u{1F4CE} Resolved base branch: ${baseBranch} \u2192 ${resolvedBase}`);
|
|
53686
|
+
}
|
|
53687
|
+
const diffSummary = await this.git.diffSummary([resolvedBase]);
|
|
53028
53688
|
const changes = [];
|
|
53029
53689
|
if (!diffSummary || !diffSummary.files) {
|
|
53030
53690
|
return [];
|
|
@@ -53052,7 +53712,7 @@ ${file.patch}`).join("\n\n");
|
|
|
53052
53712
|
let truncated = false;
|
|
53053
53713
|
if (includeContext && !isBinary) {
|
|
53054
53714
|
try {
|
|
53055
|
-
const rawPatch = await this.git.diff([
|
|
53715
|
+
const rawPatch = await this.git.diff([resolvedBase, "--", file.file]);
|
|
53056
53716
|
if (rawPatch) {
|
|
53057
53717
|
const result = this.truncatePatch(rawPatch, file.file);
|
|
53058
53718
|
patch = result.patch;
|
|
@@ -53290,8 +53950,8 @@ var init_workspace_manager = __esm({
|
|
|
53290
53950
|
);
|
|
53291
53951
|
if (this.cleanupRequested && this.activeOperations === 0) {
|
|
53292
53952
|
logger.debug(`[Workspace] All references released, proceeding with deferred cleanup`);
|
|
53293
|
-
for (const
|
|
53294
|
-
|
|
53953
|
+
for (const resolve18 of this.cleanupResolvers) {
|
|
53954
|
+
resolve18();
|
|
53295
53955
|
}
|
|
53296
53956
|
this.cleanupResolvers = [];
|
|
53297
53957
|
}
|
|
@@ -53448,19 +54108,19 @@ var init_workspace_manager = __esm({
|
|
|
53448
54108
|
);
|
|
53449
54109
|
this.cleanupRequested = true;
|
|
53450
54110
|
await Promise.race([
|
|
53451
|
-
new Promise((
|
|
54111
|
+
new Promise((resolve18) => {
|
|
53452
54112
|
if (this.activeOperations === 0) {
|
|
53453
|
-
|
|
54113
|
+
resolve18();
|
|
53454
54114
|
} else {
|
|
53455
|
-
this.cleanupResolvers.push(
|
|
54115
|
+
this.cleanupResolvers.push(resolve18);
|
|
53456
54116
|
}
|
|
53457
54117
|
}),
|
|
53458
|
-
new Promise((
|
|
54118
|
+
new Promise((resolve18) => {
|
|
53459
54119
|
setTimeout(() => {
|
|
53460
54120
|
logger.warn(
|
|
53461
54121
|
`[Workspace] Cleanup timeout after ${timeout}ms, proceeding anyway (${this.activeOperations} operations still active)`
|
|
53462
54122
|
);
|
|
53463
|
-
|
|
54123
|
+
resolve18();
|
|
53464
54124
|
}, timeout);
|
|
53465
54125
|
})
|
|
53466
54126
|
]);
|
|
@@ -53648,6 +54308,30 @@ var init_workspace_manager = __esm({
|
|
|
53648
54308
|
if (cleanResult.exitCode !== 0) {
|
|
53649
54309
|
logger.warn(`[Workspace] clean -fdx failed: ${cleanResult.stderr}`);
|
|
53650
54310
|
}
|
|
54311
|
+
await this.deleteLocalBranches(worktreePath);
|
|
54312
|
+
}
|
|
54313
|
+
/**
|
|
54314
|
+
* Delete all local branches in a worktree.
|
|
54315
|
+
*/
|
|
54316
|
+
async deleteLocalBranches(worktreePath) {
|
|
54317
|
+
const escapedPath = shellEscape(worktreePath);
|
|
54318
|
+
const listResult = await commandExecutor.execute(
|
|
54319
|
+
`git -C ${escapedPath} branch --list --format='%(refname:short)'`,
|
|
54320
|
+
{ timeout: 1e4 }
|
|
54321
|
+
);
|
|
54322
|
+
if (listResult.exitCode !== 0 || !listResult.stdout.trim()) {
|
|
54323
|
+
return;
|
|
54324
|
+
}
|
|
54325
|
+
const branches = listResult.stdout.trim().split("\n").map((b) => b.trim()).filter((b) => b.length > 0);
|
|
54326
|
+
for (const branch of branches) {
|
|
54327
|
+
const deleteResult = await commandExecutor.execute(
|
|
54328
|
+
`git -C ${escapedPath} branch -D ${shellEscape(branch)}`,
|
|
54329
|
+
{ timeout: 1e4 }
|
|
54330
|
+
);
|
|
54331
|
+
if (deleteResult.exitCode === 0) {
|
|
54332
|
+
logger.debug(`[Workspace] Deleted local branch '${branch}' from worktree`);
|
|
54333
|
+
}
|
|
54334
|
+
}
|
|
53651
54335
|
}
|
|
53652
54336
|
/**
|
|
53653
54337
|
* Refresh an existing worktree to the latest upstream default branch
|
|
@@ -53914,6 +54598,1380 @@ var init_build_engine_context = __esm({
|
|
|
53914
54598
|
}
|
|
53915
54599
|
});
|
|
53916
54600
|
|
|
54601
|
+
// src/policy/default-engine.ts
|
|
54602
|
+
var DefaultPolicyEngine;
|
|
54603
|
+
var init_default_engine = __esm({
|
|
54604
|
+
"src/policy/default-engine.ts"() {
|
|
54605
|
+
"use strict";
|
|
54606
|
+
DefaultPolicyEngine = class {
|
|
54607
|
+
async initialize(_config) {
|
|
54608
|
+
}
|
|
54609
|
+
async evaluateCheckExecution(_checkId, _checkConfig) {
|
|
54610
|
+
return { allowed: true };
|
|
54611
|
+
}
|
|
54612
|
+
async evaluateToolInvocation(_serverName, _methodName, _transport) {
|
|
54613
|
+
return { allowed: true };
|
|
54614
|
+
}
|
|
54615
|
+
async evaluateCapabilities(_checkId, _capabilities) {
|
|
54616
|
+
return { allowed: true };
|
|
54617
|
+
}
|
|
54618
|
+
async shutdown() {
|
|
54619
|
+
}
|
|
54620
|
+
};
|
|
54621
|
+
}
|
|
54622
|
+
});
|
|
54623
|
+
|
|
54624
|
+
// src/enterprise/license/validator.ts
|
|
54625
|
+
var validator_exports = {};
|
|
54626
|
+
__export(validator_exports, {
|
|
54627
|
+
LicenseValidator: () => LicenseValidator
|
|
54628
|
+
});
|
|
54629
|
+
var crypto2, fs21, path25, LicenseValidator;
|
|
54630
|
+
var init_validator = __esm({
|
|
54631
|
+
"src/enterprise/license/validator.ts"() {
|
|
54632
|
+
"use strict";
|
|
54633
|
+
crypto2 = __toESM(require("crypto"));
|
|
54634
|
+
fs21 = __toESM(require("fs"));
|
|
54635
|
+
path25 = __toESM(require("path"));
|
|
54636
|
+
LicenseValidator = class _LicenseValidator {
|
|
54637
|
+
/** Ed25519 public key for license verification (PEM format). */
|
|
54638
|
+
static PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAI/Zd08EFmgIdrDm/HXd0l3/5GBt7R1PrdvhdmEXhJlU=\n-----END PUBLIC KEY-----\n";
|
|
54639
|
+
cache = null;
|
|
54640
|
+
static CACHE_TTL = 5 * 60 * 1e3;
|
|
54641
|
+
// 5 minutes
|
|
54642
|
+
static GRACE_PERIOD = 72 * 3600 * 1e3;
|
|
54643
|
+
// 72 hours after expiry
|
|
54644
|
+
/**
|
|
54645
|
+
* Load and validate license from environment or file.
|
|
54646
|
+
*
|
|
54647
|
+
* Resolution order:
|
|
54648
|
+
* 1. VISOR_LICENSE env var (JWT string)
|
|
54649
|
+
* 2. VISOR_LICENSE_FILE env var (path to file)
|
|
54650
|
+
* 3. .visor-license in project root (cwd)
|
|
54651
|
+
* 4. .visor-license in ~/.config/visor/
|
|
54652
|
+
*/
|
|
54653
|
+
async loadAndValidate() {
|
|
54654
|
+
if (this.cache && Date.now() - this.cache.validatedAt < _LicenseValidator.CACHE_TTL) {
|
|
54655
|
+
return this.cache.payload;
|
|
54656
|
+
}
|
|
54657
|
+
const token = this.resolveToken();
|
|
54658
|
+
if (!token) return null;
|
|
54659
|
+
const payload = this.verifyAndDecode(token);
|
|
54660
|
+
if (!payload) return null;
|
|
54661
|
+
this.cache = { payload, validatedAt: Date.now() };
|
|
54662
|
+
return payload;
|
|
54663
|
+
}
|
|
54664
|
+
/** Check if a specific feature is licensed */
|
|
54665
|
+
hasFeature(feature) {
|
|
54666
|
+
if (!this.cache) return false;
|
|
54667
|
+
return this.cache.payload.features.includes(feature);
|
|
54668
|
+
}
|
|
54669
|
+
/** Check if license is valid (with grace period) */
|
|
54670
|
+
isValid() {
|
|
54671
|
+
if (!this.cache) return false;
|
|
54672
|
+
const now = Date.now();
|
|
54673
|
+
const expiryMs = this.cache.payload.exp * 1e3;
|
|
54674
|
+
return now < expiryMs + _LicenseValidator.GRACE_PERIOD;
|
|
54675
|
+
}
|
|
54676
|
+
/** Check if the license is within its grace period (expired but still valid) */
|
|
54677
|
+
isInGracePeriod() {
|
|
54678
|
+
if (!this.cache) return false;
|
|
54679
|
+
const now = Date.now();
|
|
54680
|
+
const expiryMs = this.cache.payload.exp * 1e3;
|
|
54681
|
+
return now >= expiryMs && now < expiryMs + _LicenseValidator.GRACE_PERIOD;
|
|
54682
|
+
}
|
|
54683
|
+
resolveToken() {
|
|
54684
|
+
if (process.env.VISOR_LICENSE) {
|
|
54685
|
+
return process.env.VISOR_LICENSE.trim();
|
|
54686
|
+
}
|
|
54687
|
+
if (process.env.VISOR_LICENSE_FILE) {
|
|
54688
|
+
const resolved = path25.resolve(process.env.VISOR_LICENSE_FILE);
|
|
54689
|
+
const home2 = process.env.HOME || process.env.USERPROFILE || "";
|
|
54690
|
+
const allowedPrefixes = [path25.normalize(process.cwd())];
|
|
54691
|
+
if (home2) allowedPrefixes.push(path25.normalize(path25.join(home2, ".config", "visor")));
|
|
54692
|
+
let realPath;
|
|
54693
|
+
try {
|
|
54694
|
+
realPath = fs21.realpathSync(resolved);
|
|
54695
|
+
} catch {
|
|
54696
|
+
return null;
|
|
54697
|
+
}
|
|
54698
|
+
const isSafe = allowedPrefixes.some(
|
|
54699
|
+
(prefix) => realPath === prefix || realPath.startsWith(prefix + path25.sep)
|
|
54700
|
+
);
|
|
54701
|
+
if (!isSafe) return null;
|
|
54702
|
+
return this.readFile(realPath);
|
|
54703
|
+
}
|
|
54704
|
+
const cwdPath = path25.join(process.cwd(), ".visor-license");
|
|
54705
|
+
const cwdToken = this.readFile(cwdPath);
|
|
54706
|
+
if (cwdToken) return cwdToken;
|
|
54707
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
54708
|
+
if (home) {
|
|
54709
|
+
const configPath = path25.join(home, ".config", "visor", ".visor-license");
|
|
54710
|
+
const configToken = this.readFile(configPath);
|
|
54711
|
+
if (configToken) return configToken;
|
|
54712
|
+
}
|
|
54713
|
+
return null;
|
|
54714
|
+
}
|
|
54715
|
+
readFile(filePath) {
|
|
54716
|
+
try {
|
|
54717
|
+
return fs21.readFileSync(filePath, "utf-8").trim();
|
|
54718
|
+
} catch {
|
|
54719
|
+
return null;
|
|
54720
|
+
}
|
|
54721
|
+
}
|
|
54722
|
+
verifyAndDecode(token) {
|
|
54723
|
+
try {
|
|
54724
|
+
const parts = token.split(".");
|
|
54725
|
+
if (parts.length !== 3) return null;
|
|
54726
|
+
const [headerB64, payloadB64, signatureB64] = parts;
|
|
54727
|
+
const header = JSON.parse(Buffer.from(headerB64, "base64url").toString());
|
|
54728
|
+
if (header.alg !== "EdDSA") return null;
|
|
54729
|
+
const data = `${headerB64}.${payloadB64}`;
|
|
54730
|
+
const signature = Buffer.from(signatureB64, "base64url");
|
|
54731
|
+
const publicKey = crypto2.createPublicKey(_LicenseValidator.PUBLIC_KEY);
|
|
54732
|
+
if (publicKey.asymmetricKeyType !== "ed25519") {
|
|
54733
|
+
return null;
|
|
54734
|
+
}
|
|
54735
|
+
const isValid = crypto2.verify(null, Buffer.from(data), publicKey, signature);
|
|
54736
|
+
if (!isValid) return null;
|
|
54737
|
+
const payload = JSON.parse(Buffer.from(payloadB64, "base64url").toString());
|
|
54738
|
+
if (!payload.org || !Array.isArray(payload.features) || typeof payload.exp !== "number" || typeof payload.iat !== "number" || !payload.sub) {
|
|
54739
|
+
return null;
|
|
54740
|
+
}
|
|
54741
|
+
const now = Date.now();
|
|
54742
|
+
const expiryMs = payload.exp * 1e3;
|
|
54743
|
+
if (now >= expiryMs + _LicenseValidator.GRACE_PERIOD) {
|
|
54744
|
+
return null;
|
|
54745
|
+
}
|
|
54746
|
+
return payload;
|
|
54747
|
+
} catch {
|
|
54748
|
+
return null;
|
|
54749
|
+
}
|
|
54750
|
+
}
|
|
54751
|
+
};
|
|
54752
|
+
}
|
|
54753
|
+
});
|
|
54754
|
+
|
|
54755
|
+
// src/enterprise/policy/opa-compiler.ts
|
|
54756
|
+
var fs22, path26, os2, crypto3, import_child_process8, OpaCompiler;
|
|
54757
|
+
var init_opa_compiler = __esm({
|
|
54758
|
+
"src/enterprise/policy/opa-compiler.ts"() {
|
|
54759
|
+
"use strict";
|
|
54760
|
+
fs22 = __toESM(require("fs"));
|
|
54761
|
+
path26 = __toESM(require("path"));
|
|
54762
|
+
os2 = __toESM(require("os"));
|
|
54763
|
+
crypto3 = __toESM(require("crypto"));
|
|
54764
|
+
import_child_process8 = require("child_process");
|
|
54765
|
+
OpaCompiler = class _OpaCompiler {
|
|
54766
|
+
static CACHE_DIR = path26.join(os2.tmpdir(), "visor-opa-cache");
|
|
54767
|
+
/**
|
|
54768
|
+
* Resolve the input paths to WASM bytes.
|
|
54769
|
+
*
|
|
54770
|
+
* Strategy:
|
|
54771
|
+
* 1. If any path is a .wasm file, read it directly
|
|
54772
|
+
* 2. If a directory contains policy.wasm, read it
|
|
54773
|
+
* 3. Otherwise, collect all .rego files and auto-compile via `opa build`
|
|
54774
|
+
*/
|
|
54775
|
+
async resolveWasmBytes(paths) {
|
|
54776
|
+
const regoFiles = [];
|
|
54777
|
+
for (const p of paths) {
|
|
54778
|
+
const resolved = path26.resolve(p);
|
|
54779
|
+
if (path26.normalize(resolved).includes("..")) {
|
|
54780
|
+
throw new Error(`Policy path contains traversal sequences: ${p}`);
|
|
54781
|
+
}
|
|
54782
|
+
if (resolved.endsWith(".wasm") && fs22.existsSync(resolved)) {
|
|
54783
|
+
return fs22.readFileSync(resolved);
|
|
54784
|
+
}
|
|
54785
|
+
if (!fs22.existsSync(resolved)) continue;
|
|
54786
|
+
const stat2 = fs22.statSync(resolved);
|
|
54787
|
+
if (stat2.isDirectory()) {
|
|
54788
|
+
const wasmCandidate = path26.join(resolved, "policy.wasm");
|
|
54789
|
+
if (fs22.existsSync(wasmCandidate)) {
|
|
54790
|
+
return fs22.readFileSync(wasmCandidate);
|
|
54791
|
+
}
|
|
54792
|
+
const files = fs22.readdirSync(resolved);
|
|
54793
|
+
for (const f of files) {
|
|
54794
|
+
if (f.endsWith(".rego")) {
|
|
54795
|
+
regoFiles.push(path26.join(resolved, f));
|
|
54796
|
+
}
|
|
54797
|
+
}
|
|
54798
|
+
} else if (resolved.endsWith(".rego")) {
|
|
54799
|
+
regoFiles.push(resolved);
|
|
54800
|
+
}
|
|
54801
|
+
}
|
|
54802
|
+
if (regoFiles.length === 0) {
|
|
54803
|
+
throw new Error(
|
|
54804
|
+
`OPA WASM evaluator: no .wasm bundle or .rego files found in: ${paths.join(", ")}`
|
|
54805
|
+
);
|
|
54806
|
+
}
|
|
54807
|
+
return this.compileRego(regoFiles);
|
|
54808
|
+
}
|
|
54809
|
+
/**
|
|
54810
|
+
* Auto-compile .rego files to a WASM bundle using the `opa` CLI.
|
|
54811
|
+
*
|
|
54812
|
+
* Caches the compiled bundle based on a content hash of all input .rego files
|
|
54813
|
+
* so subsequent runs skip compilation if policies haven't changed.
|
|
54814
|
+
*/
|
|
54815
|
+
compileRego(regoFiles) {
|
|
54816
|
+
try {
|
|
54817
|
+
(0, import_child_process8.execFileSync)("opa", ["version"], { stdio: "pipe" });
|
|
54818
|
+
} catch {
|
|
54819
|
+
throw new Error(
|
|
54820
|
+
"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(" ")
|
|
54821
|
+
);
|
|
54822
|
+
}
|
|
54823
|
+
const hash = crypto3.createHash("sha256");
|
|
54824
|
+
for (const f of regoFiles.sort()) {
|
|
54825
|
+
hash.update(fs22.readFileSync(f));
|
|
54826
|
+
hash.update(f);
|
|
54827
|
+
}
|
|
54828
|
+
const cacheKey = hash.digest("hex").slice(0, 16);
|
|
54829
|
+
const cacheDir = _OpaCompiler.CACHE_DIR;
|
|
54830
|
+
const cachedWasm = path26.join(cacheDir, `${cacheKey}.wasm`);
|
|
54831
|
+
if (fs22.existsSync(cachedWasm)) {
|
|
54832
|
+
return fs22.readFileSync(cachedWasm);
|
|
54833
|
+
}
|
|
54834
|
+
fs22.mkdirSync(cacheDir, { recursive: true });
|
|
54835
|
+
const bundleTar = path26.join(cacheDir, `${cacheKey}-bundle.tar.gz`);
|
|
54836
|
+
try {
|
|
54837
|
+
const args = [
|
|
54838
|
+
"build",
|
|
54839
|
+
"-t",
|
|
54840
|
+
"wasm",
|
|
54841
|
+
"-e",
|
|
54842
|
+
"visor",
|
|
54843
|
+
// entrypoint: the visor package tree
|
|
54844
|
+
"-o",
|
|
54845
|
+
bundleTar,
|
|
54846
|
+
...regoFiles
|
|
54847
|
+
];
|
|
54848
|
+
(0, import_child_process8.execFileSync)("opa", args, {
|
|
54849
|
+
stdio: "pipe",
|
|
54850
|
+
timeout: 3e4
|
|
54851
|
+
});
|
|
54852
|
+
} catch (err) {
|
|
54853
|
+
const stderr = err?.stderr?.toString() || "";
|
|
54854
|
+
throw new Error(
|
|
54855
|
+
`Failed to compile .rego files to WASM:
|
|
54856
|
+
${stderr}
|
|
54857
|
+
Ensure your .rego files are valid and the \`opa\` CLI is installed.`
|
|
54858
|
+
);
|
|
54859
|
+
}
|
|
54860
|
+
try {
|
|
54861
|
+
(0, import_child_process8.execFileSync)("tar", ["-xzf", bundleTar, "-C", cacheDir, "/policy.wasm"], {
|
|
54862
|
+
stdio: "pipe"
|
|
54863
|
+
});
|
|
54864
|
+
const extractedWasm = path26.join(cacheDir, "policy.wasm");
|
|
54865
|
+
if (fs22.existsSync(extractedWasm)) {
|
|
54866
|
+
fs22.renameSync(extractedWasm, cachedWasm);
|
|
54867
|
+
}
|
|
54868
|
+
} catch {
|
|
54869
|
+
try {
|
|
54870
|
+
(0, import_child_process8.execFileSync)("tar", ["-xzf", bundleTar, "-C", cacheDir, "policy.wasm"], {
|
|
54871
|
+
stdio: "pipe"
|
|
54872
|
+
});
|
|
54873
|
+
const extractedWasm = path26.join(cacheDir, "policy.wasm");
|
|
54874
|
+
if (fs22.existsSync(extractedWasm)) {
|
|
54875
|
+
fs22.renameSync(extractedWasm, cachedWasm);
|
|
54876
|
+
}
|
|
54877
|
+
} catch (err2) {
|
|
54878
|
+
throw new Error(`Failed to extract policy.wasm from OPA bundle: ${err2?.message || err2}`);
|
|
54879
|
+
}
|
|
54880
|
+
}
|
|
54881
|
+
try {
|
|
54882
|
+
fs22.unlinkSync(bundleTar);
|
|
54883
|
+
} catch {
|
|
54884
|
+
}
|
|
54885
|
+
if (!fs22.existsSync(cachedWasm)) {
|
|
54886
|
+
throw new Error("OPA build succeeded but policy.wasm was not found in the bundle");
|
|
54887
|
+
}
|
|
54888
|
+
return fs22.readFileSync(cachedWasm);
|
|
54889
|
+
}
|
|
54890
|
+
};
|
|
54891
|
+
}
|
|
54892
|
+
});
|
|
54893
|
+
|
|
54894
|
+
// src/enterprise/policy/opa-wasm-evaluator.ts
|
|
54895
|
+
var fs23, path27, OpaWasmEvaluator;
|
|
54896
|
+
var init_opa_wasm_evaluator = __esm({
|
|
54897
|
+
"src/enterprise/policy/opa-wasm-evaluator.ts"() {
|
|
54898
|
+
"use strict";
|
|
54899
|
+
fs23 = __toESM(require("fs"));
|
|
54900
|
+
path27 = __toESM(require("path"));
|
|
54901
|
+
init_opa_compiler();
|
|
54902
|
+
OpaWasmEvaluator = class {
|
|
54903
|
+
policy = null;
|
|
54904
|
+
dataDocument = {};
|
|
54905
|
+
compiler = new OpaCompiler();
|
|
54906
|
+
async initialize(rulesPath) {
|
|
54907
|
+
const paths = Array.isArray(rulesPath) ? rulesPath : [rulesPath];
|
|
54908
|
+
const wasmBytes = await this.compiler.resolveWasmBytes(paths);
|
|
54909
|
+
try {
|
|
54910
|
+
const { createRequire } = require("module");
|
|
54911
|
+
const runtimeRequire = createRequire(__filename);
|
|
54912
|
+
const opaWasm = runtimeRequire("@open-policy-agent/opa-wasm");
|
|
54913
|
+
const loadPolicy = opaWasm.loadPolicy || opaWasm.default?.loadPolicy;
|
|
54914
|
+
if (!loadPolicy) {
|
|
54915
|
+
throw new Error("loadPolicy not found in @open-policy-agent/opa-wasm");
|
|
54916
|
+
}
|
|
54917
|
+
this.policy = await loadPolicy(wasmBytes);
|
|
54918
|
+
} catch (err) {
|
|
54919
|
+
if (err?.code === "MODULE_NOT_FOUND" || err?.code === "ERR_MODULE_NOT_FOUND") {
|
|
54920
|
+
throw new Error(
|
|
54921
|
+
"OPA WASM evaluator requires @open-policy-agent/opa-wasm. Install it with: npm install @open-policy-agent/opa-wasm"
|
|
54922
|
+
);
|
|
54923
|
+
}
|
|
54924
|
+
throw err;
|
|
54925
|
+
}
|
|
54926
|
+
}
|
|
54927
|
+
/**
|
|
54928
|
+
* Load external data from a JSON file to use as the OPA data document.
|
|
54929
|
+
* The loaded data will be passed to `policy.setData()` during evaluation,
|
|
54930
|
+
* making it available in Rego via `data.<key>`.
|
|
54931
|
+
*/
|
|
54932
|
+
loadData(dataPath) {
|
|
54933
|
+
const resolved = path27.resolve(dataPath);
|
|
54934
|
+
if (path27.normalize(resolved).includes("..")) {
|
|
54935
|
+
throw new Error(`Data path contains traversal sequences: ${dataPath}`);
|
|
54936
|
+
}
|
|
54937
|
+
if (!fs23.existsSync(resolved)) {
|
|
54938
|
+
throw new Error(`OPA data file not found: ${resolved}`);
|
|
54939
|
+
}
|
|
54940
|
+
const stat2 = fs23.statSync(resolved);
|
|
54941
|
+
if (stat2.size > 10 * 1024 * 1024) {
|
|
54942
|
+
throw new Error(`OPA data file exceeds 10MB limit: ${resolved} (${stat2.size} bytes)`);
|
|
54943
|
+
}
|
|
54944
|
+
const raw = fs23.readFileSync(resolved, "utf-8");
|
|
54945
|
+
try {
|
|
54946
|
+
const parsed = JSON.parse(raw);
|
|
54947
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
54948
|
+
throw new Error("OPA data file must contain a JSON object (not an array or primitive)");
|
|
54949
|
+
}
|
|
54950
|
+
this.dataDocument = parsed;
|
|
54951
|
+
} catch (err) {
|
|
54952
|
+
if (err.message.startsWith("OPA data file must")) {
|
|
54953
|
+
throw err;
|
|
54954
|
+
}
|
|
54955
|
+
throw new Error(`Failed to parse OPA data file ${resolved}: ${err.message}`);
|
|
54956
|
+
}
|
|
54957
|
+
}
|
|
54958
|
+
async evaluate(input) {
|
|
54959
|
+
if (!this.policy) {
|
|
54960
|
+
throw new Error("OPA WASM evaluator not initialized");
|
|
54961
|
+
}
|
|
54962
|
+
this.policy.setData(this.dataDocument);
|
|
54963
|
+
const resultSet = this.policy.evaluate(input);
|
|
54964
|
+
if (Array.isArray(resultSet) && resultSet.length > 0) {
|
|
54965
|
+
return resultSet[0].result;
|
|
54966
|
+
}
|
|
54967
|
+
return void 0;
|
|
54968
|
+
}
|
|
54969
|
+
async shutdown() {
|
|
54970
|
+
if (this.policy) {
|
|
54971
|
+
if (typeof this.policy.close === "function") {
|
|
54972
|
+
try {
|
|
54973
|
+
this.policy.close();
|
|
54974
|
+
} catch {
|
|
54975
|
+
}
|
|
54976
|
+
} else if (typeof this.policy.free === "function") {
|
|
54977
|
+
try {
|
|
54978
|
+
this.policy.free();
|
|
54979
|
+
} catch {
|
|
54980
|
+
}
|
|
54981
|
+
}
|
|
54982
|
+
}
|
|
54983
|
+
this.policy = null;
|
|
54984
|
+
}
|
|
54985
|
+
};
|
|
54986
|
+
}
|
|
54987
|
+
});
|
|
54988
|
+
|
|
54989
|
+
// src/enterprise/policy/opa-http-evaluator.ts
|
|
54990
|
+
var OpaHttpEvaluator;
|
|
54991
|
+
var init_opa_http_evaluator = __esm({
|
|
54992
|
+
"src/enterprise/policy/opa-http-evaluator.ts"() {
|
|
54993
|
+
"use strict";
|
|
54994
|
+
OpaHttpEvaluator = class {
|
|
54995
|
+
baseUrl;
|
|
54996
|
+
timeout;
|
|
54997
|
+
constructor(baseUrl, timeout = 5e3) {
|
|
54998
|
+
let parsed;
|
|
54999
|
+
try {
|
|
55000
|
+
parsed = new URL(baseUrl);
|
|
55001
|
+
} catch {
|
|
55002
|
+
throw new Error(`OPA HTTP evaluator: invalid URL: ${baseUrl}`);
|
|
55003
|
+
}
|
|
55004
|
+
if (!["http:", "https:"].includes(parsed.protocol)) {
|
|
55005
|
+
throw new Error(
|
|
55006
|
+
`OPA HTTP evaluator: url must use http:// or https:// protocol, got: ${baseUrl}`
|
|
55007
|
+
);
|
|
55008
|
+
}
|
|
55009
|
+
const hostname = parsed.hostname;
|
|
55010
|
+
if (this.isBlockedHostname(hostname)) {
|
|
55011
|
+
throw new Error(
|
|
55012
|
+
`OPA HTTP evaluator: url must not point to internal, loopback, or private network addresses`
|
|
55013
|
+
);
|
|
55014
|
+
}
|
|
55015
|
+
this.baseUrl = baseUrl.replace(/\/+$/, "");
|
|
55016
|
+
this.timeout = timeout;
|
|
55017
|
+
}
|
|
55018
|
+
/**
|
|
55019
|
+
* Check if a hostname is blocked due to SSRF concerns.
|
|
55020
|
+
*
|
|
55021
|
+
* Blocks:
|
|
55022
|
+
* - Loopback addresses (127.x.x.x, localhost, 0.0.0.0, ::1)
|
|
55023
|
+
* - Link-local addresses (169.254.x.x)
|
|
55024
|
+
* - Private networks (10.x.x.x, 172.16-31.x.x, 192.168.x.x)
|
|
55025
|
+
* - IPv6 unique local addresses (fd00::/8)
|
|
55026
|
+
* - Cloud metadata services (*.internal)
|
|
55027
|
+
*/
|
|
55028
|
+
isBlockedHostname(hostname) {
|
|
55029
|
+
if (!hostname) return true;
|
|
55030
|
+
const normalized = hostname.toLowerCase().replace(/^\[|\]$/g, "");
|
|
55031
|
+
if (normalized === "metadata.google.internal" || normalized.endsWith(".internal")) {
|
|
55032
|
+
return true;
|
|
55033
|
+
}
|
|
55034
|
+
if (normalized === "localhost" || normalized === "localhost.localdomain") {
|
|
55035
|
+
return true;
|
|
55036
|
+
}
|
|
55037
|
+
if (normalized === "::1" || normalized === "0:0:0:0:0:0:0:1") {
|
|
55038
|
+
return true;
|
|
55039
|
+
}
|
|
55040
|
+
const ipv4Pattern = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
|
|
55041
|
+
const ipv4Match = normalized.match(ipv4Pattern);
|
|
55042
|
+
if (ipv4Match) {
|
|
55043
|
+
const octets = ipv4Match.slice(1, 5).map(Number);
|
|
55044
|
+
if (octets.some((octet) => octet > 255)) {
|
|
55045
|
+
return false;
|
|
55046
|
+
}
|
|
55047
|
+
const [a, b] = octets;
|
|
55048
|
+
if (a === 127) {
|
|
55049
|
+
return true;
|
|
55050
|
+
}
|
|
55051
|
+
if (a === 0) {
|
|
55052
|
+
return true;
|
|
55053
|
+
}
|
|
55054
|
+
if (a === 169 && b === 254) {
|
|
55055
|
+
return true;
|
|
55056
|
+
}
|
|
55057
|
+
if (a === 10) {
|
|
55058
|
+
return true;
|
|
55059
|
+
}
|
|
55060
|
+
if (a === 172 && b >= 16 && b <= 31) {
|
|
55061
|
+
return true;
|
|
55062
|
+
}
|
|
55063
|
+
if (a === 192 && b === 168) {
|
|
55064
|
+
return true;
|
|
55065
|
+
}
|
|
55066
|
+
}
|
|
55067
|
+
if (normalized.startsWith("fd") || normalized.startsWith("fc")) {
|
|
55068
|
+
return true;
|
|
55069
|
+
}
|
|
55070
|
+
if (normalized.startsWith("fe80:")) {
|
|
55071
|
+
return true;
|
|
55072
|
+
}
|
|
55073
|
+
return false;
|
|
55074
|
+
}
|
|
55075
|
+
/**
|
|
55076
|
+
* Evaluate a policy rule against an input document via OPA REST API.
|
|
55077
|
+
*
|
|
55078
|
+
* @param input - The input document to evaluate
|
|
55079
|
+
* @param rulePath - OPA rule path (e.g., 'visor/check/execute')
|
|
55080
|
+
* @returns The result object from OPA, or undefined on error
|
|
55081
|
+
*/
|
|
55082
|
+
async evaluate(input, rulePath) {
|
|
55083
|
+
const encodedPath = rulePath.split("/").map((s) => encodeURIComponent(s)).join("/");
|
|
55084
|
+
const url = `${this.baseUrl}/v1/data/${encodedPath}`;
|
|
55085
|
+
const controller = new AbortController();
|
|
55086
|
+
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
55087
|
+
try {
|
|
55088
|
+
const response = await fetch(url, {
|
|
55089
|
+
method: "POST",
|
|
55090
|
+
headers: { "Content-Type": "application/json" },
|
|
55091
|
+
body: JSON.stringify({ input }),
|
|
55092
|
+
signal: controller.signal
|
|
55093
|
+
});
|
|
55094
|
+
if (!response.ok) {
|
|
55095
|
+
throw new Error(`OPA HTTP ${response.status}: ${response.statusText}`);
|
|
55096
|
+
}
|
|
55097
|
+
let body;
|
|
55098
|
+
try {
|
|
55099
|
+
body = await response.json();
|
|
55100
|
+
} catch (jsonErr) {
|
|
55101
|
+
throw new Error(
|
|
55102
|
+
`OPA HTTP evaluator: failed to parse JSON response: ${jsonErr instanceof Error ? jsonErr.message : String(jsonErr)}`
|
|
55103
|
+
);
|
|
55104
|
+
}
|
|
55105
|
+
return body?.result;
|
|
55106
|
+
} finally {
|
|
55107
|
+
clearTimeout(timer);
|
|
55108
|
+
}
|
|
55109
|
+
}
|
|
55110
|
+
async shutdown() {
|
|
55111
|
+
}
|
|
55112
|
+
};
|
|
55113
|
+
}
|
|
55114
|
+
});
|
|
55115
|
+
|
|
55116
|
+
// src/enterprise/policy/policy-input-builder.ts
|
|
55117
|
+
var PolicyInputBuilder;
|
|
55118
|
+
var init_policy_input_builder = __esm({
|
|
55119
|
+
"src/enterprise/policy/policy-input-builder.ts"() {
|
|
55120
|
+
"use strict";
|
|
55121
|
+
PolicyInputBuilder = class {
|
|
55122
|
+
roles;
|
|
55123
|
+
actor;
|
|
55124
|
+
repository;
|
|
55125
|
+
pullRequest;
|
|
55126
|
+
constructor(policyConfig, actor, repository, pullRequest) {
|
|
55127
|
+
this.roles = policyConfig.roles || {};
|
|
55128
|
+
this.actor = actor;
|
|
55129
|
+
this.repository = repository;
|
|
55130
|
+
this.pullRequest = pullRequest;
|
|
55131
|
+
}
|
|
55132
|
+
/** Resolve which roles apply to the current actor. */
|
|
55133
|
+
resolveRoles() {
|
|
55134
|
+
const matched = [];
|
|
55135
|
+
for (const [roleName, roleConfig] of Object.entries(this.roles)) {
|
|
55136
|
+
let identityMatch = false;
|
|
55137
|
+
if (roleConfig.author_association && this.actor.authorAssociation && roleConfig.author_association.includes(this.actor.authorAssociation)) {
|
|
55138
|
+
identityMatch = true;
|
|
55139
|
+
}
|
|
55140
|
+
if (!identityMatch && roleConfig.users && this.actor.login && roleConfig.users.includes(this.actor.login)) {
|
|
55141
|
+
identityMatch = true;
|
|
55142
|
+
}
|
|
55143
|
+
if (!identityMatch && roleConfig.slack_users && this.actor.slack?.userId && roleConfig.slack_users.includes(this.actor.slack.userId)) {
|
|
55144
|
+
identityMatch = true;
|
|
55145
|
+
}
|
|
55146
|
+
if (!identityMatch && roleConfig.emails && this.actor.slack?.email) {
|
|
55147
|
+
const actorEmail = this.actor.slack.email.toLowerCase();
|
|
55148
|
+
if (roleConfig.emails.some((e) => e.toLowerCase() === actorEmail)) {
|
|
55149
|
+
identityMatch = true;
|
|
55150
|
+
}
|
|
55151
|
+
}
|
|
55152
|
+
if (!identityMatch) continue;
|
|
55153
|
+
if (roleConfig.slack_channels && roleConfig.slack_channels.length > 0) {
|
|
55154
|
+
if (!this.actor.slack?.channelId || !roleConfig.slack_channels.includes(this.actor.slack.channelId)) {
|
|
55155
|
+
continue;
|
|
55156
|
+
}
|
|
55157
|
+
}
|
|
55158
|
+
matched.push(roleName);
|
|
55159
|
+
}
|
|
55160
|
+
return matched;
|
|
55161
|
+
}
|
|
55162
|
+
buildActor() {
|
|
55163
|
+
return {
|
|
55164
|
+
authorAssociation: this.actor.authorAssociation,
|
|
55165
|
+
login: this.actor.login,
|
|
55166
|
+
roles: this.resolveRoles(),
|
|
55167
|
+
isLocalMode: this.actor.isLocalMode,
|
|
55168
|
+
...this.actor.slack && { slack: this.actor.slack }
|
|
55169
|
+
};
|
|
55170
|
+
}
|
|
55171
|
+
forCheckExecution(check) {
|
|
55172
|
+
return {
|
|
55173
|
+
scope: "check.execute",
|
|
55174
|
+
check: {
|
|
55175
|
+
id: check.id,
|
|
55176
|
+
type: check.type,
|
|
55177
|
+
group: check.group,
|
|
55178
|
+
tags: check.tags,
|
|
55179
|
+
criticality: check.criticality,
|
|
55180
|
+
sandbox: check.sandbox,
|
|
55181
|
+
policy: check.policy
|
|
55182
|
+
},
|
|
55183
|
+
actor: this.buildActor(),
|
|
55184
|
+
repository: this.repository,
|
|
55185
|
+
pullRequest: this.pullRequest
|
|
55186
|
+
};
|
|
55187
|
+
}
|
|
55188
|
+
forToolInvocation(serverName, methodName, transport) {
|
|
55189
|
+
return {
|
|
55190
|
+
scope: "tool.invoke",
|
|
55191
|
+
tool: { serverName, methodName, transport },
|
|
55192
|
+
actor: this.buildActor(),
|
|
55193
|
+
repository: this.repository,
|
|
55194
|
+
pullRequest: this.pullRequest
|
|
55195
|
+
};
|
|
55196
|
+
}
|
|
55197
|
+
forCapabilityResolve(checkId, capabilities) {
|
|
55198
|
+
return {
|
|
55199
|
+
scope: "capability.resolve",
|
|
55200
|
+
check: { id: checkId, type: "ai" },
|
|
55201
|
+
capability: capabilities,
|
|
55202
|
+
actor: this.buildActor(),
|
|
55203
|
+
repository: this.repository,
|
|
55204
|
+
pullRequest: this.pullRequest
|
|
55205
|
+
};
|
|
55206
|
+
}
|
|
55207
|
+
};
|
|
55208
|
+
}
|
|
55209
|
+
});
|
|
55210
|
+
|
|
55211
|
+
// src/enterprise/policy/opa-policy-engine.ts
|
|
55212
|
+
var opa_policy_engine_exports = {};
|
|
55213
|
+
__export(opa_policy_engine_exports, {
|
|
55214
|
+
OpaPolicyEngine: () => OpaPolicyEngine
|
|
55215
|
+
});
|
|
55216
|
+
var OpaPolicyEngine;
|
|
55217
|
+
var init_opa_policy_engine = __esm({
|
|
55218
|
+
"src/enterprise/policy/opa-policy-engine.ts"() {
|
|
55219
|
+
"use strict";
|
|
55220
|
+
init_opa_wasm_evaluator();
|
|
55221
|
+
init_opa_http_evaluator();
|
|
55222
|
+
init_policy_input_builder();
|
|
55223
|
+
OpaPolicyEngine = class {
|
|
55224
|
+
evaluator = null;
|
|
55225
|
+
fallback;
|
|
55226
|
+
timeout;
|
|
55227
|
+
config;
|
|
55228
|
+
inputBuilder = null;
|
|
55229
|
+
logger = null;
|
|
55230
|
+
constructor(config) {
|
|
55231
|
+
this.config = config;
|
|
55232
|
+
this.fallback = config.fallback || "deny";
|
|
55233
|
+
this.timeout = config.timeout || 5e3;
|
|
55234
|
+
}
|
|
55235
|
+
async initialize(config) {
|
|
55236
|
+
try {
|
|
55237
|
+
this.logger = (init_logger(), __toCommonJS(logger_exports)).logger;
|
|
55238
|
+
} catch {
|
|
55239
|
+
}
|
|
55240
|
+
const actor = {
|
|
55241
|
+
authorAssociation: process.env.VISOR_AUTHOR_ASSOCIATION,
|
|
55242
|
+
login: process.env.VISOR_AUTHOR_LOGIN || process.env.GITHUB_ACTOR,
|
|
55243
|
+
isLocalMode: !process.env.GITHUB_ACTIONS
|
|
55244
|
+
};
|
|
55245
|
+
const repo = {
|
|
55246
|
+
owner: process.env.GITHUB_REPOSITORY_OWNER,
|
|
55247
|
+
name: process.env.GITHUB_REPOSITORY?.split("/")[1],
|
|
55248
|
+
branch: process.env.GITHUB_HEAD_REF,
|
|
55249
|
+
baseBranch: process.env.GITHUB_BASE_REF,
|
|
55250
|
+
event: process.env.GITHUB_EVENT_NAME
|
|
55251
|
+
};
|
|
55252
|
+
const prNum = process.env.GITHUB_PR_NUMBER ? parseInt(process.env.GITHUB_PR_NUMBER, 10) : void 0;
|
|
55253
|
+
const pullRequest = {
|
|
55254
|
+
number: prNum !== void 0 && Number.isFinite(prNum) ? prNum : void 0
|
|
55255
|
+
};
|
|
55256
|
+
this.inputBuilder = new PolicyInputBuilder(config, actor, repo, pullRequest);
|
|
55257
|
+
if (config.engine === "local") {
|
|
55258
|
+
if (!config.rules) {
|
|
55259
|
+
throw new Error("OPA local mode requires `policy.rules` path to .wasm or .rego files");
|
|
55260
|
+
}
|
|
55261
|
+
const wasm = new OpaWasmEvaluator();
|
|
55262
|
+
await wasm.initialize(config.rules);
|
|
55263
|
+
if (config.data) {
|
|
55264
|
+
wasm.loadData(config.data);
|
|
55265
|
+
}
|
|
55266
|
+
this.evaluator = wasm;
|
|
55267
|
+
} else if (config.engine === "remote") {
|
|
55268
|
+
if (!config.url) {
|
|
55269
|
+
throw new Error("OPA remote mode requires `policy.url` pointing to OPA server");
|
|
55270
|
+
}
|
|
55271
|
+
this.evaluator = new OpaHttpEvaluator(config.url, this.timeout);
|
|
55272
|
+
} else {
|
|
55273
|
+
this.evaluator = null;
|
|
55274
|
+
}
|
|
55275
|
+
}
|
|
55276
|
+
/**
|
|
55277
|
+
* Update actor/repo/PR context (e.g., after PR info becomes available).
|
|
55278
|
+
* Called by the enterprise loader when engine context is enriched.
|
|
55279
|
+
*/
|
|
55280
|
+
setActorContext(actor, repo, pullRequest) {
|
|
55281
|
+
this.inputBuilder = new PolicyInputBuilder(this.config, actor, repo, pullRequest);
|
|
55282
|
+
}
|
|
55283
|
+
async evaluateCheckExecution(checkId, checkConfig) {
|
|
55284
|
+
if (!this.evaluator || !this.inputBuilder) return { allowed: true };
|
|
55285
|
+
const cfg = checkConfig && typeof checkConfig === "object" ? checkConfig : {};
|
|
55286
|
+
const policyOverride = cfg.policy;
|
|
55287
|
+
const input = this.inputBuilder.forCheckExecution({
|
|
55288
|
+
id: checkId,
|
|
55289
|
+
type: cfg.type || "ai",
|
|
55290
|
+
group: cfg.group,
|
|
55291
|
+
tags: cfg.tags,
|
|
55292
|
+
criticality: cfg.criticality,
|
|
55293
|
+
sandbox: cfg.sandbox,
|
|
55294
|
+
policy: policyOverride
|
|
55295
|
+
});
|
|
55296
|
+
return this.doEvaluate(input, this.resolveRulePath("check.execute", policyOverride?.rule));
|
|
55297
|
+
}
|
|
55298
|
+
async evaluateToolInvocation(serverName, methodName, transport) {
|
|
55299
|
+
if (!this.evaluator || !this.inputBuilder) return { allowed: true };
|
|
55300
|
+
const input = this.inputBuilder.forToolInvocation(serverName, methodName, transport);
|
|
55301
|
+
return this.doEvaluate(input, "visor/tool/invoke");
|
|
55302
|
+
}
|
|
55303
|
+
async evaluateCapabilities(checkId, capabilities) {
|
|
55304
|
+
if (!this.evaluator || !this.inputBuilder) return { allowed: true };
|
|
55305
|
+
const input = this.inputBuilder.forCapabilityResolve(checkId, capabilities);
|
|
55306
|
+
return this.doEvaluate(input, "visor/capability/resolve");
|
|
55307
|
+
}
|
|
55308
|
+
async shutdown() {
|
|
55309
|
+
if (this.evaluator && "shutdown" in this.evaluator) {
|
|
55310
|
+
await this.evaluator.shutdown();
|
|
55311
|
+
}
|
|
55312
|
+
this.evaluator = null;
|
|
55313
|
+
this.inputBuilder = null;
|
|
55314
|
+
}
|
|
55315
|
+
resolveRulePath(defaultScope, override) {
|
|
55316
|
+
if (override) {
|
|
55317
|
+
return override.startsWith("visor/") ? override : `visor/${override}`;
|
|
55318
|
+
}
|
|
55319
|
+
return `visor/${defaultScope.replace(/\./g, "/")}`;
|
|
55320
|
+
}
|
|
55321
|
+
async doEvaluate(input, rulePath) {
|
|
55322
|
+
try {
|
|
55323
|
+
this.logger?.debug(`[PolicyEngine] Evaluating ${rulePath}`, JSON.stringify(input));
|
|
55324
|
+
let timer;
|
|
55325
|
+
const timeoutPromise = new Promise((_resolve, reject) => {
|
|
55326
|
+
timer = setTimeout(() => reject(new Error("policy evaluation timeout")), this.timeout);
|
|
55327
|
+
});
|
|
55328
|
+
try {
|
|
55329
|
+
const result = await Promise.race([this.rawEvaluate(input, rulePath), timeoutPromise]);
|
|
55330
|
+
const decision = this.parseDecision(result);
|
|
55331
|
+
if (!decision.allowed && this.fallback === "warn") {
|
|
55332
|
+
decision.allowed = true;
|
|
55333
|
+
decision.warn = true;
|
|
55334
|
+
decision.reason = `audit: ${decision.reason || "policy denied"}`;
|
|
55335
|
+
}
|
|
55336
|
+
this.logger?.debug(
|
|
55337
|
+
`[PolicyEngine] Decision for ${rulePath}: allowed=${decision.allowed}, warn=${decision.warn || false}, reason=${decision.reason || "none"}`
|
|
55338
|
+
);
|
|
55339
|
+
return decision;
|
|
55340
|
+
} finally {
|
|
55341
|
+
if (timer) clearTimeout(timer);
|
|
55342
|
+
}
|
|
55343
|
+
} catch (err) {
|
|
55344
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
55345
|
+
this.logger?.warn(`[PolicyEngine] Evaluation failed for ${rulePath}: ${msg}`);
|
|
55346
|
+
return {
|
|
55347
|
+
allowed: this.fallback === "allow" || this.fallback === "warn",
|
|
55348
|
+
warn: this.fallback === "warn" ? true : void 0,
|
|
55349
|
+
reason: `policy evaluation failed, fallback=${this.fallback}`
|
|
55350
|
+
};
|
|
55351
|
+
}
|
|
55352
|
+
}
|
|
55353
|
+
async rawEvaluate(input, rulePath) {
|
|
55354
|
+
if (this.evaluator instanceof OpaWasmEvaluator) {
|
|
55355
|
+
const result = await this.evaluator.evaluate(input);
|
|
55356
|
+
return this.navigateWasmResult(result, rulePath);
|
|
55357
|
+
}
|
|
55358
|
+
return this.evaluator.evaluate(input, rulePath);
|
|
55359
|
+
}
|
|
55360
|
+
/**
|
|
55361
|
+
* Navigate nested OPA WASM result tree to reach the specific rule's output.
|
|
55362
|
+
* The WASM entrypoint `-e visor` means the result root IS the visor package,
|
|
55363
|
+
* so we strip the `visor/` prefix and walk the remaining segments.
|
|
55364
|
+
*/
|
|
55365
|
+
navigateWasmResult(result, rulePath) {
|
|
55366
|
+
if (!result || typeof result !== "object") return result;
|
|
55367
|
+
const segments = rulePath.replace(/^visor\//, "").split("/");
|
|
55368
|
+
let current = result;
|
|
55369
|
+
for (const seg of segments) {
|
|
55370
|
+
if (current && typeof current === "object" && seg in current) {
|
|
55371
|
+
current = current[seg];
|
|
55372
|
+
} else {
|
|
55373
|
+
return void 0;
|
|
55374
|
+
}
|
|
55375
|
+
}
|
|
55376
|
+
return current;
|
|
55377
|
+
}
|
|
55378
|
+
parseDecision(result) {
|
|
55379
|
+
if (result === void 0 || result === null) {
|
|
55380
|
+
return {
|
|
55381
|
+
allowed: this.fallback === "allow" || this.fallback === "warn",
|
|
55382
|
+
warn: this.fallback === "warn" ? true : void 0,
|
|
55383
|
+
reason: this.fallback === "warn" ? "audit: no policy result" : "no policy result"
|
|
55384
|
+
};
|
|
55385
|
+
}
|
|
55386
|
+
const allowed = result.allowed !== false;
|
|
55387
|
+
const decision = {
|
|
55388
|
+
allowed,
|
|
55389
|
+
reason: result.reason
|
|
55390
|
+
};
|
|
55391
|
+
if (result.capabilities) {
|
|
55392
|
+
decision.capabilities = result.capabilities;
|
|
55393
|
+
}
|
|
55394
|
+
return decision;
|
|
55395
|
+
}
|
|
55396
|
+
};
|
|
55397
|
+
}
|
|
55398
|
+
});
|
|
55399
|
+
|
|
55400
|
+
// src/enterprise/scheduler/knex-store.ts
|
|
55401
|
+
var knex_store_exports = {};
|
|
55402
|
+
__export(knex_store_exports, {
|
|
55403
|
+
KnexStoreBackend: () => KnexStoreBackend
|
|
55404
|
+
});
|
|
55405
|
+
function toNum(val) {
|
|
55406
|
+
if (val === null || val === void 0) return void 0;
|
|
55407
|
+
return typeof val === "string" ? parseInt(val, 10) : val;
|
|
55408
|
+
}
|
|
55409
|
+
function safeJsonParse2(value) {
|
|
55410
|
+
if (!value) return void 0;
|
|
55411
|
+
try {
|
|
55412
|
+
return JSON.parse(value);
|
|
55413
|
+
} catch {
|
|
55414
|
+
return void 0;
|
|
55415
|
+
}
|
|
55416
|
+
}
|
|
55417
|
+
function fromTriggerRow2(row) {
|
|
55418
|
+
return {
|
|
55419
|
+
id: row.id,
|
|
55420
|
+
creatorId: row.creator_id,
|
|
55421
|
+
creatorContext: row.creator_context ?? void 0,
|
|
55422
|
+
creatorName: row.creator_name ?? void 0,
|
|
55423
|
+
description: row.description ?? void 0,
|
|
55424
|
+
channels: safeJsonParse2(row.channels),
|
|
55425
|
+
fromUsers: safeJsonParse2(row.from_users),
|
|
55426
|
+
fromBots: row.from_bots === true || row.from_bots === 1,
|
|
55427
|
+
contains: safeJsonParse2(row.contains),
|
|
55428
|
+
matchPattern: row.match_pattern ?? void 0,
|
|
55429
|
+
threads: row.threads,
|
|
55430
|
+
workflow: row.workflow,
|
|
55431
|
+
inputs: safeJsonParse2(row.inputs),
|
|
55432
|
+
outputContext: safeJsonParse2(row.output_context),
|
|
55433
|
+
status: row.status,
|
|
55434
|
+
enabled: row.enabled === true || row.enabled === 1,
|
|
55435
|
+
createdAt: toNum(row.created_at)
|
|
55436
|
+
};
|
|
55437
|
+
}
|
|
55438
|
+
function toTriggerInsertRow(trigger) {
|
|
55439
|
+
return {
|
|
55440
|
+
id: trigger.id,
|
|
55441
|
+
creator_id: trigger.creatorId,
|
|
55442
|
+
creator_context: trigger.creatorContext ?? null,
|
|
55443
|
+
creator_name: trigger.creatorName ?? null,
|
|
55444
|
+
description: trigger.description ?? null,
|
|
55445
|
+
channels: trigger.channels ? JSON.stringify(trigger.channels) : null,
|
|
55446
|
+
from_users: trigger.fromUsers ? JSON.stringify(trigger.fromUsers) : null,
|
|
55447
|
+
from_bots: trigger.fromBots,
|
|
55448
|
+
contains: trigger.contains ? JSON.stringify(trigger.contains) : null,
|
|
55449
|
+
match_pattern: trigger.matchPattern ?? null,
|
|
55450
|
+
threads: trigger.threads,
|
|
55451
|
+
workflow: trigger.workflow,
|
|
55452
|
+
inputs: trigger.inputs ? JSON.stringify(trigger.inputs) : null,
|
|
55453
|
+
output_context: trigger.outputContext ? JSON.stringify(trigger.outputContext) : null,
|
|
55454
|
+
status: trigger.status,
|
|
55455
|
+
enabled: trigger.enabled,
|
|
55456
|
+
created_at: trigger.createdAt
|
|
55457
|
+
};
|
|
55458
|
+
}
|
|
55459
|
+
function fromDbRow2(row) {
|
|
55460
|
+
return {
|
|
55461
|
+
id: row.id,
|
|
55462
|
+
creatorId: row.creator_id,
|
|
55463
|
+
creatorContext: row.creator_context ?? void 0,
|
|
55464
|
+
creatorName: row.creator_name ?? void 0,
|
|
55465
|
+
timezone: row.timezone,
|
|
55466
|
+
schedule: row.schedule_expr,
|
|
55467
|
+
runAt: toNum(row.run_at),
|
|
55468
|
+
isRecurring: row.is_recurring === true || row.is_recurring === 1,
|
|
55469
|
+
originalExpression: row.original_expression,
|
|
55470
|
+
workflow: row.workflow ?? void 0,
|
|
55471
|
+
workflowInputs: safeJsonParse2(row.workflow_inputs),
|
|
55472
|
+
outputContext: safeJsonParse2(row.output_context),
|
|
55473
|
+
status: row.status,
|
|
55474
|
+
createdAt: toNum(row.created_at),
|
|
55475
|
+
lastRunAt: toNum(row.last_run_at),
|
|
55476
|
+
nextRunAt: toNum(row.next_run_at),
|
|
55477
|
+
runCount: row.run_count,
|
|
55478
|
+
failureCount: row.failure_count,
|
|
55479
|
+
lastError: row.last_error ?? void 0,
|
|
55480
|
+
previousResponse: row.previous_response ?? void 0
|
|
55481
|
+
};
|
|
55482
|
+
}
|
|
55483
|
+
function toInsertRow(schedule) {
|
|
55484
|
+
return {
|
|
55485
|
+
id: schedule.id,
|
|
55486
|
+
creator_id: schedule.creatorId,
|
|
55487
|
+
creator_context: schedule.creatorContext ?? null,
|
|
55488
|
+
creator_name: schedule.creatorName ?? null,
|
|
55489
|
+
timezone: schedule.timezone,
|
|
55490
|
+
schedule_expr: schedule.schedule,
|
|
55491
|
+
run_at: schedule.runAt ?? null,
|
|
55492
|
+
is_recurring: schedule.isRecurring,
|
|
55493
|
+
original_expression: schedule.originalExpression,
|
|
55494
|
+
workflow: schedule.workflow ?? null,
|
|
55495
|
+
workflow_inputs: schedule.workflowInputs ? JSON.stringify(schedule.workflowInputs) : null,
|
|
55496
|
+
output_context: schedule.outputContext ? JSON.stringify(schedule.outputContext) : null,
|
|
55497
|
+
status: schedule.status,
|
|
55498
|
+
created_at: schedule.createdAt,
|
|
55499
|
+
last_run_at: schedule.lastRunAt ?? null,
|
|
55500
|
+
next_run_at: schedule.nextRunAt ?? null,
|
|
55501
|
+
run_count: schedule.runCount,
|
|
55502
|
+
failure_count: schedule.failureCount,
|
|
55503
|
+
last_error: schedule.lastError ?? null,
|
|
55504
|
+
previous_response: schedule.previousResponse ?? null
|
|
55505
|
+
};
|
|
55506
|
+
}
|
|
55507
|
+
var fs24, path28, import_uuid2, KnexStoreBackend;
|
|
55508
|
+
var init_knex_store = __esm({
|
|
55509
|
+
"src/enterprise/scheduler/knex-store.ts"() {
|
|
55510
|
+
"use strict";
|
|
55511
|
+
fs24 = __toESM(require("fs"));
|
|
55512
|
+
path28 = __toESM(require("path"));
|
|
55513
|
+
import_uuid2 = require("uuid");
|
|
55514
|
+
init_logger();
|
|
55515
|
+
KnexStoreBackend = class {
|
|
55516
|
+
knex = null;
|
|
55517
|
+
driver;
|
|
55518
|
+
connection;
|
|
55519
|
+
constructor(driver, storageConfig, _haConfig) {
|
|
55520
|
+
this.driver = driver;
|
|
55521
|
+
this.connection = storageConfig.connection || {};
|
|
55522
|
+
}
|
|
55523
|
+
async initialize() {
|
|
55524
|
+
const { createRequire } = require("module");
|
|
55525
|
+
const runtimeRequire = createRequire(__filename);
|
|
55526
|
+
let knexFactory;
|
|
55527
|
+
try {
|
|
55528
|
+
knexFactory = runtimeRequire("knex");
|
|
55529
|
+
} catch (err) {
|
|
55530
|
+
const code = err?.code;
|
|
55531
|
+
if (code === "MODULE_NOT_FOUND" || code === "ERR_MODULE_NOT_FOUND") {
|
|
55532
|
+
throw new Error(
|
|
55533
|
+
"knex is required for PostgreSQL/MySQL/MSSQL schedule storage. Install it with: npm install knex"
|
|
55534
|
+
);
|
|
55535
|
+
}
|
|
55536
|
+
throw err;
|
|
55537
|
+
}
|
|
55538
|
+
const clientMap = {
|
|
55539
|
+
postgresql: "pg",
|
|
55540
|
+
mysql: "mysql2",
|
|
55541
|
+
mssql: "tedious"
|
|
55542
|
+
};
|
|
55543
|
+
const client = clientMap[this.driver];
|
|
55544
|
+
let connection;
|
|
55545
|
+
if (this.connection.connection_string) {
|
|
55546
|
+
connection = this.connection.connection_string;
|
|
55547
|
+
} else if (this.driver === "mssql") {
|
|
55548
|
+
connection = this.buildMssqlConnection();
|
|
55549
|
+
} else {
|
|
55550
|
+
connection = this.buildStandardConnection();
|
|
55551
|
+
}
|
|
55552
|
+
this.knex = knexFactory({
|
|
55553
|
+
client,
|
|
55554
|
+
connection,
|
|
55555
|
+
pool: {
|
|
55556
|
+
min: this.connection.pool?.min ?? 0,
|
|
55557
|
+
max: this.connection.pool?.max ?? 10
|
|
55558
|
+
}
|
|
55559
|
+
});
|
|
55560
|
+
await this.migrateSchema();
|
|
55561
|
+
logger.info(`[KnexStore] Initialized (${this.driver})`);
|
|
55562
|
+
}
|
|
55563
|
+
buildStandardConnection() {
|
|
55564
|
+
return {
|
|
55565
|
+
host: this.connection.host || "localhost",
|
|
55566
|
+
port: this.connection.port,
|
|
55567
|
+
database: this.connection.database || "visor",
|
|
55568
|
+
user: this.connection.user,
|
|
55569
|
+
password: this.connection.password,
|
|
55570
|
+
ssl: this.resolveSslConfig()
|
|
55571
|
+
};
|
|
55572
|
+
}
|
|
55573
|
+
buildMssqlConnection() {
|
|
55574
|
+
const ssl = this.connection.ssl;
|
|
55575
|
+
const sslEnabled = ssl === true || typeof ssl === "object" && ssl.enabled !== false;
|
|
55576
|
+
return {
|
|
55577
|
+
server: this.connection.host || "localhost",
|
|
55578
|
+
port: this.connection.port,
|
|
55579
|
+
database: this.connection.database || "visor",
|
|
55580
|
+
user: this.connection.user,
|
|
55581
|
+
password: this.connection.password,
|
|
55582
|
+
options: {
|
|
55583
|
+
encrypt: sslEnabled,
|
|
55584
|
+
trustServerCertificate: typeof ssl === "object" ? ssl.reject_unauthorized === false : !sslEnabled
|
|
55585
|
+
}
|
|
55586
|
+
};
|
|
55587
|
+
}
|
|
55588
|
+
resolveSslConfig() {
|
|
55589
|
+
const ssl = this.connection.ssl;
|
|
55590
|
+
if (ssl === false || ssl === void 0) return false;
|
|
55591
|
+
if (ssl === true) return { rejectUnauthorized: true };
|
|
55592
|
+
if (ssl.enabled === false) return false;
|
|
55593
|
+
const result = {
|
|
55594
|
+
rejectUnauthorized: ssl.reject_unauthorized !== false
|
|
55595
|
+
};
|
|
55596
|
+
if (ssl.ca) {
|
|
55597
|
+
const caPath = this.validateSslPath(ssl.ca, "CA certificate");
|
|
55598
|
+
result.ca = fs24.readFileSync(caPath, "utf8");
|
|
55599
|
+
}
|
|
55600
|
+
if (ssl.cert) {
|
|
55601
|
+
const certPath = this.validateSslPath(ssl.cert, "client certificate");
|
|
55602
|
+
result.cert = fs24.readFileSync(certPath, "utf8");
|
|
55603
|
+
}
|
|
55604
|
+
if (ssl.key) {
|
|
55605
|
+
const keyPath = this.validateSslPath(ssl.key, "client key");
|
|
55606
|
+
result.key = fs24.readFileSync(keyPath, "utf8");
|
|
55607
|
+
}
|
|
55608
|
+
return result;
|
|
55609
|
+
}
|
|
55610
|
+
validateSslPath(filePath, label) {
|
|
55611
|
+
const resolved = path28.resolve(filePath);
|
|
55612
|
+
if (resolved !== path28.normalize(resolved)) {
|
|
55613
|
+
throw new Error(`SSL ${label} path contains invalid sequences: ${filePath}`);
|
|
55614
|
+
}
|
|
55615
|
+
if (!fs24.existsSync(resolved)) {
|
|
55616
|
+
throw new Error(`SSL ${label} not found: ${filePath}`);
|
|
55617
|
+
}
|
|
55618
|
+
return resolved;
|
|
55619
|
+
}
|
|
55620
|
+
async shutdown() {
|
|
55621
|
+
if (this.knex) {
|
|
55622
|
+
await this.knex.destroy();
|
|
55623
|
+
this.knex = null;
|
|
55624
|
+
}
|
|
55625
|
+
}
|
|
55626
|
+
async migrateSchema() {
|
|
55627
|
+
const knex = this.getKnex();
|
|
55628
|
+
const exists = await knex.schema.hasTable("schedules");
|
|
55629
|
+
if (!exists) {
|
|
55630
|
+
await knex.schema.createTable("schedules", (table) => {
|
|
55631
|
+
table.string("id", 36).primary();
|
|
55632
|
+
table.string("creator_id", 255).notNullable().index();
|
|
55633
|
+
table.string("creator_context", 255);
|
|
55634
|
+
table.string("creator_name", 255);
|
|
55635
|
+
table.string("timezone", 64).notNullable().defaultTo("UTC");
|
|
55636
|
+
table.string("schedule_expr", 255);
|
|
55637
|
+
table.bigInteger("run_at");
|
|
55638
|
+
table.boolean("is_recurring").notNullable();
|
|
55639
|
+
table.text("original_expression");
|
|
55640
|
+
table.string("workflow", 255);
|
|
55641
|
+
table.text("workflow_inputs");
|
|
55642
|
+
table.text("output_context");
|
|
55643
|
+
table.string("status", 20).notNullable().index();
|
|
55644
|
+
table.bigInteger("created_at").notNullable();
|
|
55645
|
+
table.bigInteger("last_run_at");
|
|
55646
|
+
table.bigInteger("next_run_at");
|
|
55647
|
+
table.integer("run_count").notNullable().defaultTo(0);
|
|
55648
|
+
table.integer("failure_count").notNullable().defaultTo(0);
|
|
55649
|
+
table.text("last_error");
|
|
55650
|
+
table.text("previous_response");
|
|
55651
|
+
table.index(["status", "next_run_at"]);
|
|
55652
|
+
});
|
|
55653
|
+
}
|
|
55654
|
+
const triggersExist = await knex.schema.hasTable("message_triggers");
|
|
55655
|
+
if (!triggersExist) {
|
|
55656
|
+
await knex.schema.createTable("message_triggers", (table) => {
|
|
55657
|
+
table.string("id", 36).primary();
|
|
55658
|
+
table.string("creator_id", 255).notNullable().index();
|
|
55659
|
+
table.string("creator_context", 255);
|
|
55660
|
+
table.string("creator_name", 255);
|
|
55661
|
+
table.text("description");
|
|
55662
|
+
table.text("channels");
|
|
55663
|
+
table.text("from_users");
|
|
55664
|
+
table.boolean("from_bots").notNullable().defaultTo(false);
|
|
55665
|
+
table.text("contains");
|
|
55666
|
+
table.text("match_pattern");
|
|
55667
|
+
table.string("threads", 20).notNullable().defaultTo("any");
|
|
55668
|
+
table.string("workflow", 255).notNullable();
|
|
55669
|
+
table.text("inputs");
|
|
55670
|
+
table.text("output_context");
|
|
55671
|
+
table.string("status", 20).notNullable().defaultTo("active").index();
|
|
55672
|
+
table.boolean("enabled").notNullable().defaultTo(true);
|
|
55673
|
+
table.bigInteger("created_at").notNullable();
|
|
55674
|
+
});
|
|
55675
|
+
}
|
|
55676
|
+
const locksExist = await knex.schema.hasTable("scheduler_locks");
|
|
55677
|
+
if (!locksExist) {
|
|
55678
|
+
await knex.schema.createTable("scheduler_locks", (table) => {
|
|
55679
|
+
table.string("lock_id", 255).primary();
|
|
55680
|
+
table.string("node_id", 255).notNullable();
|
|
55681
|
+
table.string("lock_token", 36).notNullable();
|
|
55682
|
+
table.bigInteger("acquired_at").notNullable();
|
|
55683
|
+
table.bigInteger("expires_at").notNullable();
|
|
55684
|
+
});
|
|
55685
|
+
}
|
|
55686
|
+
}
|
|
55687
|
+
getKnex() {
|
|
55688
|
+
if (!this.knex) {
|
|
55689
|
+
throw new Error("[KnexStore] Not initialized. Call initialize() first.");
|
|
55690
|
+
}
|
|
55691
|
+
return this.knex;
|
|
55692
|
+
}
|
|
55693
|
+
// --- CRUD ---
|
|
55694
|
+
async create(schedule) {
|
|
55695
|
+
const knex = this.getKnex();
|
|
55696
|
+
const newSchedule = {
|
|
55697
|
+
...schedule,
|
|
55698
|
+
id: (0, import_uuid2.v4)(),
|
|
55699
|
+
createdAt: Date.now(),
|
|
55700
|
+
runCount: 0,
|
|
55701
|
+
failureCount: 0,
|
|
55702
|
+
status: "active"
|
|
55703
|
+
};
|
|
55704
|
+
await knex("schedules").insert(toInsertRow(newSchedule));
|
|
55705
|
+
logger.info(`[KnexStore] Created schedule ${newSchedule.id} for user ${newSchedule.creatorId}`);
|
|
55706
|
+
return newSchedule;
|
|
55707
|
+
}
|
|
55708
|
+
async importSchedule(schedule) {
|
|
55709
|
+
const knex = this.getKnex();
|
|
55710
|
+
const existing = await knex("schedules").where("id", schedule.id).first();
|
|
55711
|
+
if (existing) return;
|
|
55712
|
+
await knex("schedules").insert(toInsertRow(schedule));
|
|
55713
|
+
}
|
|
55714
|
+
async get(id) {
|
|
55715
|
+
const knex = this.getKnex();
|
|
55716
|
+
const row = await knex("schedules").where("id", id).first();
|
|
55717
|
+
return row ? fromDbRow2(row) : void 0;
|
|
55718
|
+
}
|
|
55719
|
+
async update(id, patch) {
|
|
55720
|
+
const knex = this.getKnex();
|
|
55721
|
+
const existing = await knex("schedules").where("id", id).first();
|
|
55722
|
+
if (!existing) return void 0;
|
|
55723
|
+
const current = fromDbRow2(existing);
|
|
55724
|
+
const updated = { ...current, ...patch, id: current.id };
|
|
55725
|
+
const row = toInsertRow(updated);
|
|
55726
|
+
delete row.id;
|
|
55727
|
+
await knex("schedules").where("id", id).update(row);
|
|
55728
|
+
return updated;
|
|
55729
|
+
}
|
|
55730
|
+
async delete(id) {
|
|
55731
|
+
const knex = this.getKnex();
|
|
55732
|
+
const deleted = await knex("schedules").where("id", id).del();
|
|
55733
|
+
if (deleted > 0) {
|
|
55734
|
+
logger.info(`[KnexStore] Deleted schedule ${id}`);
|
|
55735
|
+
return true;
|
|
55736
|
+
}
|
|
55737
|
+
return false;
|
|
55738
|
+
}
|
|
55739
|
+
// --- Queries ---
|
|
55740
|
+
async getByCreator(creatorId) {
|
|
55741
|
+
const knex = this.getKnex();
|
|
55742
|
+
const rows = await knex("schedules").where("creator_id", creatorId);
|
|
55743
|
+
return rows.map((r) => fromDbRow2(r));
|
|
55744
|
+
}
|
|
55745
|
+
async getActiveSchedules() {
|
|
55746
|
+
const knex = this.getKnex();
|
|
55747
|
+
const rows = await knex("schedules").where("status", "active");
|
|
55748
|
+
return rows.map((r) => fromDbRow2(r));
|
|
55749
|
+
}
|
|
55750
|
+
async getDueSchedules(now) {
|
|
55751
|
+
const ts = now ?? Date.now();
|
|
55752
|
+
const knex = this.getKnex();
|
|
55753
|
+
const bFalse = this.driver === "mssql" ? 0 : false;
|
|
55754
|
+
const bTrue = this.driver === "mssql" ? 1 : true;
|
|
55755
|
+
const rows = await knex("schedules").where("status", "active").andWhere(function() {
|
|
55756
|
+
this.where(function() {
|
|
55757
|
+
this.where("is_recurring", bFalse).whereNotNull("run_at").where("run_at", "<=", ts);
|
|
55758
|
+
}).orWhere(function() {
|
|
55759
|
+
this.where("is_recurring", bTrue).whereNotNull("next_run_at").where("next_run_at", "<=", ts);
|
|
55760
|
+
});
|
|
55761
|
+
});
|
|
55762
|
+
return rows.map((r) => fromDbRow2(r));
|
|
55763
|
+
}
|
|
55764
|
+
async findByWorkflow(creatorId, workflowName) {
|
|
55765
|
+
const knex = this.getKnex();
|
|
55766
|
+
const escaped = workflowName.toLowerCase().replace(/[%_\\]/g, "\\$&");
|
|
55767
|
+
const pattern = `%${escaped}%`;
|
|
55768
|
+
const rows = await knex("schedules").where("creator_id", creatorId).where("status", "active").whereRaw("LOWER(workflow) LIKE ? ESCAPE '\\'", [pattern]);
|
|
55769
|
+
return rows.map((r) => fromDbRow2(r));
|
|
55770
|
+
}
|
|
55771
|
+
async getAll() {
|
|
55772
|
+
const knex = this.getKnex();
|
|
55773
|
+
const rows = await knex("schedules");
|
|
55774
|
+
return rows.map((r) => fromDbRow2(r));
|
|
55775
|
+
}
|
|
55776
|
+
async getStats() {
|
|
55777
|
+
const knex = this.getKnex();
|
|
55778
|
+
const boolTrue = this.driver === "mssql" ? "1" : "true";
|
|
55779
|
+
const boolFalse = this.driver === "mssql" ? "0" : "false";
|
|
55780
|
+
const result = await knex("schedules").select(
|
|
55781
|
+
knex.raw("COUNT(*) as total"),
|
|
55782
|
+
knex.raw("SUM(CASE WHEN status = 'active' THEN 1 ELSE 0 END) as active"),
|
|
55783
|
+
knex.raw("SUM(CASE WHEN status = 'paused' THEN 1 ELSE 0 END) as paused"),
|
|
55784
|
+
knex.raw("SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed"),
|
|
55785
|
+
knex.raw("SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed"),
|
|
55786
|
+
knex.raw(`SUM(CASE WHEN is_recurring = ${boolTrue} THEN 1 ELSE 0 END) as recurring`),
|
|
55787
|
+
knex.raw(`SUM(CASE WHEN is_recurring = ${boolFalse} THEN 1 ELSE 0 END) as one_time`)
|
|
55788
|
+
).first();
|
|
55789
|
+
return {
|
|
55790
|
+
total: Number(result.total) || 0,
|
|
55791
|
+
active: Number(result.active) || 0,
|
|
55792
|
+
paused: Number(result.paused) || 0,
|
|
55793
|
+
completed: Number(result.completed) || 0,
|
|
55794
|
+
failed: Number(result.failed) || 0,
|
|
55795
|
+
recurring: Number(result.recurring) || 0,
|
|
55796
|
+
oneTime: Number(result.one_time) || 0
|
|
55797
|
+
};
|
|
55798
|
+
}
|
|
55799
|
+
async validateLimits(creatorId, isRecurring, limits) {
|
|
55800
|
+
const knex = this.getKnex();
|
|
55801
|
+
if (limits.maxGlobal) {
|
|
55802
|
+
const result = await knex("schedules").count("* as cnt").first();
|
|
55803
|
+
if (Number(result?.cnt) >= limits.maxGlobal) {
|
|
55804
|
+
throw new Error(`Global schedule limit reached (${limits.maxGlobal})`);
|
|
55805
|
+
}
|
|
55806
|
+
}
|
|
55807
|
+
if (limits.maxPerUser) {
|
|
55808
|
+
const result = await knex("schedules").where("creator_id", creatorId).count("* as cnt").first();
|
|
55809
|
+
if (Number(result?.cnt) >= limits.maxPerUser) {
|
|
55810
|
+
throw new Error(`You have reached the maximum number of schedules (${limits.maxPerUser})`);
|
|
55811
|
+
}
|
|
55812
|
+
}
|
|
55813
|
+
if (isRecurring && limits.maxRecurringPerUser) {
|
|
55814
|
+
const bTrue = this.driver === "mssql" ? 1 : true;
|
|
55815
|
+
const result = await knex("schedules").where("creator_id", creatorId).where("is_recurring", bTrue).count("* as cnt").first();
|
|
55816
|
+
if (Number(result?.cnt) >= limits.maxRecurringPerUser) {
|
|
55817
|
+
throw new Error(
|
|
55818
|
+
`You have reached the maximum number of recurring schedules (${limits.maxRecurringPerUser})`
|
|
55819
|
+
);
|
|
55820
|
+
}
|
|
55821
|
+
}
|
|
55822
|
+
}
|
|
55823
|
+
// --- HA Distributed Locking (via scheduler_locks table) ---
|
|
55824
|
+
async tryAcquireLock(lockId, nodeId, ttlSeconds) {
|
|
55825
|
+
const knex = this.getKnex();
|
|
55826
|
+
const now = Date.now();
|
|
55827
|
+
const expiresAt = now + ttlSeconds * 1e3;
|
|
55828
|
+
const token = (0, import_uuid2.v4)();
|
|
55829
|
+
const updated = await knex("scheduler_locks").where("lock_id", lockId).where("expires_at", "<", now).update({
|
|
55830
|
+
node_id: nodeId,
|
|
55831
|
+
lock_token: token,
|
|
55832
|
+
acquired_at: now,
|
|
55833
|
+
expires_at: expiresAt
|
|
55834
|
+
});
|
|
55835
|
+
if (updated > 0) return token;
|
|
55836
|
+
try {
|
|
55837
|
+
await knex("scheduler_locks").insert({
|
|
55838
|
+
lock_id: lockId,
|
|
55839
|
+
node_id: nodeId,
|
|
55840
|
+
lock_token: token,
|
|
55841
|
+
acquired_at: now,
|
|
55842
|
+
expires_at: expiresAt
|
|
55843
|
+
});
|
|
55844
|
+
return token;
|
|
55845
|
+
} catch {
|
|
55846
|
+
return null;
|
|
55847
|
+
}
|
|
55848
|
+
}
|
|
55849
|
+
async releaseLock(lockId, lockToken) {
|
|
55850
|
+
const knex = this.getKnex();
|
|
55851
|
+
await knex("scheduler_locks").where("lock_id", lockId).where("lock_token", lockToken).del();
|
|
55852
|
+
}
|
|
55853
|
+
async renewLock(lockId, lockToken, ttlSeconds) {
|
|
55854
|
+
const knex = this.getKnex();
|
|
55855
|
+
const now = Date.now();
|
|
55856
|
+
const expiresAt = now + ttlSeconds * 1e3;
|
|
55857
|
+
const updated = await knex("scheduler_locks").where("lock_id", lockId).where("lock_token", lockToken).update({ acquired_at: now, expires_at: expiresAt });
|
|
55858
|
+
return updated > 0;
|
|
55859
|
+
}
|
|
55860
|
+
async flush() {
|
|
55861
|
+
}
|
|
55862
|
+
// --- Message Trigger CRUD ---
|
|
55863
|
+
async createTrigger(trigger) {
|
|
55864
|
+
const knex = this.getKnex();
|
|
55865
|
+
const newTrigger = {
|
|
55866
|
+
...trigger,
|
|
55867
|
+
id: (0, import_uuid2.v4)(),
|
|
55868
|
+
createdAt: Date.now()
|
|
55869
|
+
};
|
|
55870
|
+
await knex("message_triggers").insert(toTriggerInsertRow(newTrigger));
|
|
55871
|
+
logger.info(`[KnexStore] Created trigger ${newTrigger.id} for user ${newTrigger.creatorId}`);
|
|
55872
|
+
return newTrigger;
|
|
55873
|
+
}
|
|
55874
|
+
async getTrigger(id) {
|
|
55875
|
+
const knex = this.getKnex();
|
|
55876
|
+
const row = await knex("message_triggers").where("id", id).first();
|
|
55877
|
+
return row ? fromTriggerRow2(row) : void 0;
|
|
55878
|
+
}
|
|
55879
|
+
async updateTrigger(id, patch) {
|
|
55880
|
+
const knex = this.getKnex();
|
|
55881
|
+
const existing = await knex("message_triggers").where("id", id).first();
|
|
55882
|
+
if (!existing) return void 0;
|
|
55883
|
+
const current = fromTriggerRow2(existing);
|
|
55884
|
+
const updated = {
|
|
55885
|
+
...current,
|
|
55886
|
+
...patch,
|
|
55887
|
+
id: current.id,
|
|
55888
|
+
createdAt: current.createdAt
|
|
55889
|
+
};
|
|
55890
|
+
const row = toTriggerInsertRow(updated);
|
|
55891
|
+
delete row.id;
|
|
55892
|
+
await knex("message_triggers").where("id", id).update(row);
|
|
55893
|
+
return updated;
|
|
55894
|
+
}
|
|
55895
|
+
async deleteTrigger(id) {
|
|
55896
|
+
const knex = this.getKnex();
|
|
55897
|
+
const deleted = await knex("message_triggers").where("id", id).del();
|
|
55898
|
+
if (deleted > 0) {
|
|
55899
|
+
logger.info(`[KnexStore] Deleted trigger ${id}`);
|
|
55900
|
+
return true;
|
|
55901
|
+
}
|
|
55902
|
+
return false;
|
|
55903
|
+
}
|
|
55904
|
+
async getTriggersByCreator(creatorId) {
|
|
55905
|
+
const knex = this.getKnex();
|
|
55906
|
+
const rows = await knex("message_triggers").where("creator_id", creatorId);
|
|
55907
|
+
return rows.map((r) => fromTriggerRow2(r));
|
|
55908
|
+
}
|
|
55909
|
+
async getActiveTriggers() {
|
|
55910
|
+
const knex = this.getKnex();
|
|
55911
|
+
const rows = await knex("message_triggers").where("status", "active").where("enabled", this.driver === "mssql" ? 1 : true);
|
|
55912
|
+
return rows.map((r) => fromTriggerRow2(r));
|
|
55913
|
+
}
|
|
55914
|
+
};
|
|
55915
|
+
}
|
|
55916
|
+
});
|
|
55917
|
+
|
|
55918
|
+
// src/enterprise/loader.ts
|
|
55919
|
+
var loader_exports = {};
|
|
55920
|
+
__export(loader_exports, {
|
|
55921
|
+
loadEnterprisePolicyEngine: () => loadEnterprisePolicyEngine,
|
|
55922
|
+
loadEnterpriseStoreBackend: () => loadEnterpriseStoreBackend
|
|
55923
|
+
});
|
|
55924
|
+
async function loadEnterprisePolicyEngine(config) {
|
|
55925
|
+
try {
|
|
55926
|
+
const { LicenseValidator: LicenseValidator2 } = await Promise.resolve().then(() => (init_validator(), validator_exports));
|
|
55927
|
+
const validator = new LicenseValidator2();
|
|
55928
|
+
const license = await validator.loadAndValidate();
|
|
55929
|
+
if (!license || !validator.hasFeature("policy")) {
|
|
55930
|
+
return new DefaultPolicyEngine();
|
|
55931
|
+
}
|
|
55932
|
+
if (validator.isInGracePeriod()) {
|
|
55933
|
+
console.warn(
|
|
55934
|
+
"[visor:enterprise] License has expired but is within the 72-hour grace period. Please renew your license."
|
|
55935
|
+
);
|
|
55936
|
+
}
|
|
55937
|
+
const { OpaPolicyEngine: OpaPolicyEngine2 } = await Promise.resolve().then(() => (init_opa_policy_engine(), opa_policy_engine_exports));
|
|
55938
|
+
const engine = new OpaPolicyEngine2(config);
|
|
55939
|
+
await engine.initialize(config);
|
|
55940
|
+
return engine;
|
|
55941
|
+
} catch (err) {
|
|
55942
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
55943
|
+
try {
|
|
55944
|
+
const { logger: logger2 } = (init_logger(), __toCommonJS(logger_exports));
|
|
55945
|
+
logger2.warn(`[PolicyEngine] Enterprise policy init failed, falling back to default: ${msg}`);
|
|
55946
|
+
} catch {
|
|
55947
|
+
}
|
|
55948
|
+
return new DefaultPolicyEngine();
|
|
55949
|
+
}
|
|
55950
|
+
}
|
|
55951
|
+
async function loadEnterpriseStoreBackend(driver, storageConfig, haConfig) {
|
|
55952
|
+
const { LicenseValidator: LicenseValidator2 } = await Promise.resolve().then(() => (init_validator(), validator_exports));
|
|
55953
|
+
const validator = new LicenseValidator2();
|
|
55954
|
+
const license = await validator.loadAndValidate();
|
|
55955
|
+
if (!license || !validator.hasFeature("scheduler-sql")) {
|
|
55956
|
+
throw new Error(
|
|
55957
|
+
`The ${driver} schedule storage driver requires a Visor Enterprise license with the 'scheduler-sql' feature. Please upgrade or use driver: 'sqlite' (default).`
|
|
55958
|
+
);
|
|
55959
|
+
}
|
|
55960
|
+
if (validator.isInGracePeriod()) {
|
|
55961
|
+
console.warn(
|
|
55962
|
+
"[visor:enterprise] License has expired but is within the 72-hour grace period. Please renew your license."
|
|
55963
|
+
);
|
|
55964
|
+
}
|
|
55965
|
+
const { KnexStoreBackend: KnexStoreBackend2 } = await Promise.resolve().then(() => (init_knex_store(), knex_store_exports));
|
|
55966
|
+
return new KnexStoreBackend2(driver, storageConfig, haConfig);
|
|
55967
|
+
}
|
|
55968
|
+
var init_loader = __esm({
|
|
55969
|
+
"src/enterprise/loader.ts"() {
|
|
55970
|
+
"use strict";
|
|
55971
|
+
init_default_engine();
|
|
55972
|
+
}
|
|
55973
|
+
});
|
|
55974
|
+
|
|
53917
55975
|
// src/event-bus/event-bus.ts
|
|
53918
55976
|
var event_bus_exports = {};
|
|
53919
55977
|
__export(event_bus_exports, {
|
|
@@ -54820,8 +56878,8 @@ ${content}
|
|
|
54820
56878
|
* Sleep utility
|
|
54821
56879
|
*/
|
|
54822
56880
|
sleep(ms) {
|
|
54823
|
-
return new Promise((
|
|
54824
|
-
const t = setTimeout(
|
|
56881
|
+
return new Promise((resolve18) => {
|
|
56882
|
+
const t = setTimeout(resolve18, ms);
|
|
54825
56883
|
if (typeof t.unref === "function") {
|
|
54826
56884
|
try {
|
|
54827
56885
|
t.unref();
|
|
@@ -55106,8 +57164,8 @@ ${end}`);
|
|
|
55106
57164
|
async updateGroupedComment(ctx, comments, group, changedIds) {
|
|
55107
57165
|
const existingLock = this.updateLocks.get(group);
|
|
55108
57166
|
let resolveLock;
|
|
55109
|
-
const ourLock = new Promise((
|
|
55110
|
-
resolveLock =
|
|
57167
|
+
const ourLock = new Promise((resolve18) => {
|
|
57168
|
+
resolveLock = resolve18;
|
|
55111
57169
|
});
|
|
55112
57170
|
this.updateLocks.set(group, ourLock);
|
|
55113
57171
|
try {
|
|
@@ -55420,7 +57478,7 @@ ${blocks}
|
|
|
55420
57478
|
* Sleep utility for enforcing delays
|
|
55421
57479
|
*/
|
|
55422
57480
|
sleep(ms) {
|
|
55423
|
-
return new Promise((
|
|
57481
|
+
return new Promise((resolve18) => setTimeout(resolve18, ms));
|
|
55424
57482
|
}
|
|
55425
57483
|
};
|
|
55426
57484
|
}
|
|
@@ -56691,15 +58749,15 @@ function serializeRunState(state) {
|
|
|
56691
58749
|
])
|
|
56692
58750
|
};
|
|
56693
58751
|
}
|
|
56694
|
-
var
|
|
58752
|
+
var path30, fs26, StateMachineExecutionEngine;
|
|
56695
58753
|
var init_state_machine_execution_engine = __esm({
|
|
56696
58754
|
"src/state-machine-execution-engine.ts"() {
|
|
56697
58755
|
"use strict";
|
|
56698
58756
|
init_runner();
|
|
56699
58757
|
init_logger();
|
|
56700
58758
|
init_sandbox_manager();
|
|
56701
|
-
|
|
56702
|
-
|
|
58759
|
+
path30 = __toESM(require("path"));
|
|
58760
|
+
fs26 = __toESM(require("fs"));
|
|
56703
58761
|
StateMachineExecutionEngine = class _StateMachineExecutionEngine {
|
|
56704
58762
|
workingDirectory;
|
|
56705
58763
|
executionContext;
|
|
@@ -56931,8 +58989,8 @@ var init_state_machine_execution_engine = __esm({
|
|
|
56931
58989
|
logger.debug(
|
|
56932
58990
|
`[PolicyEngine] Loading enterprise policy engine (engine=${configWithTagFilter.policy.engine})`
|
|
56933
58991
|
);
|
|
56934
|
-
const { loadEnterprisePolicyEngine } = await
|
|
56935
|
-
context2.policyEngine = await
|
|
58992
|
+
const { loadEnterprisePolicyEngine: loadEnterprisePolicyEngine2 } = await Promise.resolve().then(() => (init_loader(), loader_exports));
|
|
58993
|
+
context2.policyEngine = await loadEnterprisePolicyEngine2(configWithTagFilter.policy);
|
|
56936
58994
|
logger.debug(
|
|
56937
58995
|
`[PolicyEngine] Initialized: ${context2.policyEngine?.constructor?.name || "unknown"}`
|
|
56938
58996
|
);
|
|
@@ -57084,9 +59142,9 @@ var init_state_machine_execution_engine = __esm({
|
|
|
57084
59142
|
}
|
|
57085
59143
|
const checkId = String(ev?.checkId || "unknown");
|
|
57086
59144
|
const threadKey = ev?.threadKey || (channel && threadTs ? `${channel}:${threadTs}` : "session");
|
|
57087
|
-
const baseDir = process.env.VISOR_SNAPSHOT_DIR ||
|
|
57088
|
-
|
|
57089
|
-
const filePath =
|
|
59145
|
+
const baseDir = process.env.VISOR_SNAPSHOT_DIR || path30.resolve(process.cwd(), ".visor", "snapshots");
|
|
59146
|
+
fs26.mkdirSync(baseDir, { recursive: true });
|
|
59147
|
+
const filePath = path30.join(baseDir, `${threadKey}-${checkId}.json`);
|
|
57090
59148
|
await this.saveSnapshotToFile(filePath);
|
|
57091
59149
|
logger.info(`[Snapshot] Saved run snapshot: ${filePath}`);
|
|
57092
59150
|
try {
|
|
@@ -57227,7 +59285,7 @@ var init_state_machine_execution_engine = __esm({
|
|
|
57227
59285
|
* Does not include secrets. Intended for debugging and future resume support.
|
|
57228
59286
|
*/
|
|
57229
59287
|
async saveSnapshotToFile(filePath) {
|
|
57230
|
-
const
|
|
59288
|
+
const fs27 = await import("fs/promises");
|
|
57231
59289
|
const ctx = this._lastContext;
|
|
57232
59290
|
const runner = this._lastRunner;
|
|
57233
59291
|
if (!ctx || !runner) {
|
|
@@ -57247,14 +59305,14 @@ var init_state_machine_execution_engine = __esm({
|
|
|
57247
59305
|
journal: entries,
|
|
57248
59306
|
requestedChecks: ctx.requestedChecks || []
|
|
57249
59307
|
};
|
|
57250
|
-
await
|
|
59308
|
+
await fs27.writeFile(filePath, JSON.stringify(payload, null, 2), "utf8");
|
|
57251
59309
|
}
|
|
57252
59310
|
/**
|
|
57253
59311
|
* Load a snapshot JSON from file and return it. Resume support can build on this.
|
|
57254
59312
|
*/
|
|
57255
59313
|
async loadSnapshotFromFile(filePath) {
|
|
57256
|
-
const
|
|
57257
|
-
const raw = await
|
|
59314
|
+
const fs27 = await import("fs/promises");
|
|
59315
|
+
const raw = await fs27.readFile(filePath, "utf8");
|
|
57258
59316
|
return JSON.parse(raw);
|
|
57259
59317
|
}
|
|
57260
59318
|
/**
|