@probelabs/visor 0.1.132-ee → 0.1.137-ee
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/config-reloader.d.ts +1 -0
- package/dist/config/config-reloader.d.ts.map +1 -1
- package/dist/config/config-watcher.d.ts +1 -0
- package/dist/config/config-watcher.d.ts.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/docs/ai-custom-tools-usage.md +37 -0
- package/dist/docs/ai-custom-tools.md +43 -1
- package/dist/docs/custom-tools.md +70 -1
- package/dist/docs/script.md +542 -27
- package/dist/docs/testing/cookbook.md +47 -0
- package/dist/examples/README.md +4 -0
- package/dist/examples/api-tools-ai-example.yaml +63 -0
- package/dist/examples/api-tools-inline-overlay-example.yaml +126 -0
- package/dist/examples/api-tools-library.yaml +18 -0
- package/dist/examples/api-tools-mcp-example.yaml +55 -0
- package/dist/examples/openapi/profiles-overlay-rename.yaml +3 -0
- package/dist/examples/openapi/users-api.json +91 -0
- package/dist/examples/openapi/users-overlay-rename.yaml +3 -0
- package/dist/generated/config-schema.d.ts +223 -74
- package/dist/generated/config-schema.d.ts.map +1 -1
- package/dist/generated/config-schema.json +251 -79
- package/dist/index.js +45128 -25001
- package/dist/providers/ai-check-provider.d.ts.map +1 -1
- package/dist/providers/api-tool-executor.d.ts +43 -0
- package/dist/providers/api-tool-executor.d.ts.map +1 -0
- package/dist/providers/command-check-provider.d.ts.map +1 -1
- package/dist/providers/custom-tool-executor.d.ts +21 -0
- package/dist/providers/custom-tool-executor.d.ts.map +1 -1
- package/dist/providers/mcp-check-provider.d.ts.map +1 -1
- package/dist/providers/mcp-custom-sse-server.d.ts.map +1 -1
- package/dist/providers/script-check-provider.d.ts +18 -2
- package/dist/providers/script-check-provider.d.ts.map +1 -1
- package/dist/sdk/{check-provider-registry-7TCA3NSG.mjs → check-provider-registry-SCL4KP55.mjs} +9 -8
- package/dist/sdk/{check-provider-registry-KUDVEKAC.mjs → check-provider-registry-ULZRI3TC.mjs} +9 -8
- package/dist/sdk/{chunk-27RV5RR2.mjs → chunk-BRD36I43.mjs} +3 -3
- package/dist/sdk/{chunk-BGBXLPLL.mjs → chunk-E2N3U5HU.mjs} +5 -5
- package/dist/sdk/{chunk-XGI47XIH.mjs → chunk-F4K5WFSM.mjs} +1896 -1762
- package/dist/sdk/chunk-F4K5WFSM.mjs.map +1 -0
- package/dist/sdk/{chunk-2RNTEWOA.mjs → chunk-HQIVGUSV.mjs} +1896 -1762
- package/dist/sdk/chunk-HQIVGUSV.mjs.map +1 -0
- package/dist/sdk/{chunk-U3BLLEW3.mjs → chunk-KPRFDKQX.mjs} +329 -80
- package/dist/sdk/chunk-KPRFDKQX.mjs.map +1 -0
- package/dist/sdk/{chunk-VF6XIUE4.mjs → chunk-LW3INISN.mjs} +32 -1
- package/dist/sdk/{chunk-VF6XIUE4.mjs.map → chunk-LW3INISN.mjs.map} +1 -1
- package/dist/sdk/{chunk-VG7FWDC2.mjs → chunk-QUEWQWDX.mjs} +11 -4
- package/dist/sdk/{chunk-VG7FWDC2.mjs.map → chunk-QUEWQWDX.mjs.map} +1 -1
- package/dist/sdk/chunk-XKCER23W.mjs +1490 -0
- package/dist/sdk/chunk-XKCER23W.mjs.map +1 -0
- package/dist/sdk/{chunk-XJQKTK6V.mjs → chunk-ZUEQNCKB.mjs} +2 -2
- package/dist/sdk/{config-FMIIATKX.mjs → config-3UIU4TMP.mjs} +3 -3
- package/dist/sdk/{failure-condition-evaluator-PNONVBXD.mjs → failure-condition-evaluator-B5JJFYKU.mjs} +4 -4
- package/dist/sdk/{github-frontend-WR4S3NG5.mjs → github-frontend-VAWVSCNX.mjs} +4 -4
- package/dist/sdk/{host-TROSAWTE.mjs → host-67XTJ3BN.mjs} +2 -2
- package/dist/sdk/{host-U7V54J2H.mjs → host-TEQ7HKKH.mjs} +2 -2
- package/dist/sdk/{liquid-extensions-YDIIH33Q.mjs → liquid-extensions-PLBOMRLI.mjs} +3 -3
- package/dist/sdk/{routing-F4FOWVKF.mjs → routing-SEQYM4N6.mjs} +6 -6
- package/dist/sdk/schedule-tool-2COUUTF7.mjs +18 -0
- package/dist/sdk/{schedule-tool-handler-ULNF7TZW.mjs → schedule-tool-handler-5BDMLHS5.mjs} +10 -9
- package/dist/sdk/{schedule-tool-handler-VFES42DD.mjs → schedule-tool-handler-D7XX7WM4.mjs} +10 -9
- package/dist/sdk/sdk.d.mts +55 -2
- package/dist/sdk/sdk.d.ts +55 -2
- package/dist/sdk/sdk.js +2424 -539
- package/dist/sdk/sdk.js.map +1 -1
- package/dist/sdk/sdk.mjs +8 -7
- package/dist/sdk/sdk.mjs.map +1 -1
- package/dist/sdk/{trace-helpers-RDPXIN4S.mjs → trace-helpers-FAAGLXBI.mjs} +2 -2
- package/dist/sdk/{workflow-check-provider-4NFWH6YO.mjs → workflow-check-provider-K4MQQOYQ.mjs} +10 -9
- package/dist/sdk/{workflow-check-provider-5XS62BCJ.mjs → workflow-check-provider-WLA7LO56.mjs} +10 -9
- package/dist/sdk/workflow-check-provider-WLA7LO56.mjs.map +1 -0
- package/dist/state-machine/dispatch/execution-invoker.d.ts.map +1 -1
- package/dist/state-machine-execution-engine.d.ts.map +1 -1
- package/dist/test-runner/core/test-execution-wrapper.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 +55 -2
- package/dist/types/config.d.ts.map +1 -1
- package/dist/utils/config-loader.d.ts +5 -0
- package/dist/utils/config-loader.d.ts.map +1 -1
- package/dist/utils/sandbox.d.ts +8 -0
- package/dist/utils/sandbox.d.ts.map +1 -1
- package/dist/utils/script-tool-environment.d.ts +90 -0
- package/dist/utils/script-tool-environment.d.ts.map +1 -0
- package/dist/utils/tool-resolver.d.ts +18 -0
- package/dist/utils/tool-resolver.d.ts.map +1 -0
- package/package.json +11 -4
- package/dist/sdk/chunk-2RNTEWOA.mjs.map +0 -1
- package/dist/sdk/chunk-U3BLLEW3.mjs.map +0 -1
- package/dist/sdk/chunk-XGI47XIH.mjs.map +0 -1
- /package/dist/sdk/{check-provider-registry-7TCA3NSG.mjs.map → check-provider-registry-SCL4KP55.mjs.map} +0 -0
- /package/dist/sdk/{check-provider-registry-KUDVEKAC.mjs.map → check-provider-registry-ULZRI3TC.mjs.map} +0 -0
- /package/dist/sdk/{chunk-27RV5RR2.mjs.map → chunk-BRD36I43.mjs.map} +0 -0
- /package/dist/sdk/{chunk-BGBXLPLL.mjs.map → chunk-E2N3U5HU.mjs.map} +0 -0
- /package/dist/sdk/{chunk-XJQKTK6V.mjs.map → chunk-ZUEQNCKB.mjs.map} +0 -0
- /package/dist/sdk/{config-FMIIATKX.mjs.map → config-3UIU4TMP.mjs.map} +0 -0
- /package/dist/sdk/{failure-condition-evaluator-PNONVBXD.mjs.map → failure-condition-evaluator-B5JJFYKU.mjs.map} +0 -0
- /package/dist/sdk/{github-frontend-WR4S3NG5.mjs.map → github-frontend-VAWVSCNX.mjs.map} +0 -0
- /package/dist/sdk/{host-TROSAWTE.mjs.map → host-67XTJ3BN.mjs.map} +0 -0
- /package/dist/sdk/{host-U7V54J2H.mjs.map → host-TEQ7HKKH.mjs.map} +0 -0
- /package/dist/sdk/{liquid-extensions-YDIIH33Q.mjs.map → liquid-extensions-PLBOMRLI.mjs.map} +0 -0
- /package/dist/sdk/{routing-F4FOWVKF.mjs.map → routing-SEQYM4N6.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-handler-ULNF7TZW.mjs.map → schedule-tool-2COUUTF7.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-handler-VFES42DD.mjs.map → schedule-tool-handler-5BDMLHS5.mjs.map} +0 -0
- /package/dist/sdk/{trace-helpers-RDPXIN4S.mjs.map → schedule-tool-handler-D7XX7WM4.mjs.map} +0 -0
- /package/dist/sdk/{workflow-check-provider-4NFWH6YO.mjs.map → trace-helpers-FAAGLXBI.mjs.map} +0 -0
- /package/dist/sdk/{workflow-check-provider-5XS62BCJ.mjs.map → workflow-check-provider-K4MQQOYQ.mjs.map} +0 -0
package/dist/sdk/sdk.js
CHANGED
|
@@ -693,9 +693,10 @@ var require_package = __commonJS({
|
|
|
693
693
|
format: "prettier --write src tests",
|
|
694
694
|
"format:check": "prettier --check src tests",
|
|
695
695
|
clean: "",
|
|
696
|
+
"clean:traces": "node scripts/clean-traces.js",
|
|
696
697
|
prebuild: "npm run clean && node scripts/generate-config-schema.js",
|
|
697
|
-
pretest: "node scripts/generate-config-schema.js && npm run build:cli",
|
|
698
|
-
"pretest:unit": "node scripts/generate-config-schema.js && npm run build:cli",
|
|
698
|
+
pretest: "npm run clean:traces && node scripts/generate-config-schema.js && npm run build:cli",
|
|
699
|
+
"pretest:unit": "npm run clean:traces && node scripts/generate-config-schema.js && npm run build:cli",
|
|
699
700
|
"test:with-build": "npm run build:cli && jest",
|
|
700
701
|
"test:yaml": "node dist/index.js test --progress compact",
|
|
701
702
|
"test:yaml:parallel": "node dist/index.js test --progress compact --max-parallel 4",
|
|
@@ -742,25 +743,31 @@ var require_package = __commonJS({
|
|
|
742
743
|
homepage: "https://github.com/probelabs/visor#readme",
|
|
743
744
|
dependencies: {
|
|
744
745
|
"@actions/core": "^1.11.1",
|
|
746
|
+
"@apidevtools/swagger-parser": "^12.1.0",
|
|
745
747
|
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
746
748
|
"@nyariv/sandboxjs": "github:probelabs/SandboxJS#f1c13b8eee98734a8ea024061eada4aa9a9ff2e9",
|
|
747
749
|
"@octokit/action": "^8.0.2",
|
|
748
750
|
"@octokit/auth-app": "^8.1.0",
|
|
749
751
|
"@octokit/core": "^7.0.3",
|
|
750
752
|
"@octokit/rest": "^22.0.0",
|
|
751
|
-
"@probelabs/probe": "^0.6.0-
|
|
753
|
+
"@probelabs/probe": "^0.6.0-rc255",
|
|
752
754
|
"@types/commander": "^2.12.0",
|
|
753
755
|
"@types/uuid": "^10.0.0",
|
|
756
|
+
acorn: "^8.16.0",
|
|
757
|
+
"acorn-walk": "^8.3.5",
|
|
754
758
|
ajv: "^8.17.1",
|
|
755
759
|
"ajv-formats": "^3.0.1",
|
|
756
760
|
"better-sqlite3": "^11.0.0",
|
|
757
761
|
blessed: "^0.1.81",
|
|
758
762
|
"cli-table3": "^0.6.5",
|
|
759
763
|
commander: "^14.0.0",
|
|
764
|
+
deepmerge: "^4.3.1",
|
|
760
765
|
dotenv: "^17.2.3",
|
|
761
766
|
ignore: "^7.0.5",
|
|
762
767
|
"js-yaml": "^4.1.0",
|
|
768
|
+
"jsonpath-plus": "^10.4.0",
|
|
763
769
|
liquidjs: "^10.21.1",
|
|
770
|
+
minimatch: "^10.2.2",
|
|
764
771
|
"node-cron": "^3.0.3",
|
|
765
772
|
open: "^9.1.0",
|
|
766
773
|
"simple-git": "^3.28.0",
|
|
@@ -938,19 +945,19 @@ function __getOrCreateNdjsonPath() {
|
|
|
938
945
|
try {
|
|
939
946
|
if (process.env.VISOR_TELEMETRY_SINK && process.env.VISOR_TELEMETRY_SINK !== "file")
|
|
940
947
|
return null;
|
|
941
|
-
const
|
|
942
|
-
const
|
|
948
|
+
const path30 = require("path");
|
|
949
|
+
const fs26 = require("fs");
|
|
943
950
|
if (process.env.VISOR_FALLBACK_TRACE_FILE) {
|
|
944
951
|
__ndjsonPath = process.env.VISOR_FALLBACK_TRACE_FILE;
|
|
945
|
-
const dir =
|
|
946
|
-
if (!
|
|
952
|
+
const dir = path30.dirname(__ndjsonPath);
|
|
953
|
+
if (!fs26.existsSync(dir)) fs26.mkdirSync(dir, { recursive: true });
|
|
947
954
|
return __ndjsonPath;
|
|
948
955
|
}
|
|
949
|
-
const outDir = process.env.VISOR_TRACE_DIR ||
|
|
950
|
-
if (!
|
|
956
|
+
const outDir = process.env.VISOR_TRACE_DIR || path30.join(process.cwd(), "output", "traces");
|
|
957
|
+
if (!fs26.existsSync(outDir)) fs26.mkdirSync(outDir, { recursive: true });
|
|
951
958
|
if (!__ndjsonPath) {
|
|
952
959
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
953
|
-
__ndjsonPath =
|
|
960
|
+
__ndjsonPath = path30.join(outDir, `${ts}.ndjson`);
|
|
954
961
|
}
|
|
955
962
|
return __ndjsonPath;
|
|
956
963
|
} catch {
|
|
@@ -959,11 +966,11 @@ function __getOrCreateNdjsonPath() {
|
|
|
959
966
|
}
|
|
960
967
|
function _appendRunMarker() {
|
|
961
968
|
try {
|
|
962
|
-
const
|
|
969
|
+
const fs26 = require("fs");
|
|
963
970
|
const p = __getOrCreateNdjsonPath();
|
|
964
971
|
if (!p) return;
|
|
965
972
|
const line = { name: "visor.run", attributes: { started: true } };
|
|
966
|
-
|
|
973
|
+
fs26.appendFileSync(p, JSON.stringify(line) + "\n", "utf8");
|
|
967
974
|
} catch {
|
|
968
975
|
}
|
|
969
976
|
}
|
|
@@ -2311,6 +2318,36 @@ ${src}
|
|
|
2311
2318
|
}
|
|
2312
2319
|
return out;
|
|
2313
2320
|
}
|
|
2321
|
+
async function compileAndRunAsync(sandbox, transformedCode, scope, opts = { injectLog: true, logPrefix: "[async-sandbox]" }) {
|
|
2322
|
+
const inject = opts?.injectLog === true;
|
|
2323
|
+
let safePrefix = String(opts?.logPrefix ?? "[async-sandbox]");
|
|
2324
|
+
safePrefix = safePrefix.replace(/[\r\n\t\0]/g, "").replace(/[`$\\]/g, "").replace(/\$\{/g, "").slice(0, 64);
|
|
2325
|
+
const scopeWithLog = inject ? {
|
|
2326
|
+
...scope,
|
|
2327
|
+
log: (...args) => {
|
|
2328
|
+
try {
|
|
2329
|
+
console.log(safePrefix, ...args);
|
|
2330
|
+
} catch {
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
} : scope;
|
|
2334
|
+
const codePreview = transformedCode.replace(/\s+/g, " ").trim().slice(0, 100);
|
|
2335
|
+
const contextInfo = safePrefix !== "[async-sandbox]" ? ` [${safePrefix}]` : "";
|
|
2336
|
+
let exec2;
|
|
2337
|
+
try {
|
|
2338
|
+
exec2 = sandbox.compileAsync(transformedCode);
|
|
2339
|
+
} catch (e) {
|
|
2340
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
2341
|
+
throw new Error(`async_sandbox_compile_error${contextInfo}: ${msg} | code: ${codePreview}`);
|
|
2342
|
+
}
|
|
2343
|
+
try {
|
|
2344
|
+
const result = await exec2(scopeWithLog).run();
|
|
2345
|
+
return result;
|
|
2346
|
+
} catch (e) {
|
|
2347
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
2348
|
+
throw new Error(`async_sandbox_execution_error${contextInfo}: ${msg} | code: ${codePreview}`);
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2314
2351
|
var import_sandboxjs;
|
|
2315
2352
|
var init_sandbox = __esm({
|
|
2316
2353
|
"src/utils/sandbox.ts"() {
|
|
@@ -3697,9 +3734,9 @@ function configureLiquidWithExtensions(liquid) {
|
|
|
3697
3734
|
});
|
|
3698
3735
|
liquid.registerFilter("get", (obj, pathExpr) => {
|
|
3699
3736
|
if (obj == null) return void 0;
|
|
3700
|
-
const
|
|
3701
|
-
if (!
|
|
3702
|
-
const parts =
|
|
3737
|
+
const path30 = typeof pathExpr === "string" ? pathExpr : String(pathExpr || "");
|
|
3738
|
+
if (!path30) return obj;
|
|
3739
|
+
const parts = path30.split(".");
|
|
3703
3740
|
let cur = obj;
|
|
3704
3741
|
for (const p of parts) {
|
|
3705
3742
|
if (cur == null) return void 0;
|
|
@@ -3818,9 +3855,9 @@ function configureLiquidWithExtensions(liquid) {
|
|
|
3818
3855
|
}
|
|
3819
3856
|
}
|
|
3820
3857
|
const defaultRole = typeof rolesCfg.default === "string" && rolesCfg.default.trim() ? rolesCfg.default.trim() : void 0;
|
|
3821
|
-
const getNested = (obj,
|
|
3822
|
-
if (!obj || !
|
|
3823
|
-
const parts =
|
|
3858
|
+
const getNested = (obj, path30) => {
|
|
3859
|
+
if (!obj || !path30) return void 0;
|
|
3860
|
+
const parts = path30.split(".");
|
|
3824
3861
|
let cur = obj;
|
|
3825
3862
|
for (const p of parts) {
|
|
3826
3863
|
if (cur == null) return void 0;
|
|
@@ -6372,8 +6409,8 @@ var init_dependency_gating = __esm({
|
|
|
6372
6409
|
async function renderTemplateContent(checkId, checkConfig, reviewSummary) {
|
|
6373
6410
|
try {
|
|
6374
6411
|
const { createExtendedLiquid: createExtendedLiquid2 } = await Promise.resolve().then(() => (init_liquid_extensions(), liquid_extensions_exports));
|
|
6375
|
-
const
|
|
6376
|
-
const
|
|
6412
|
+
const fs26 = await import("fs/promises");
|
|
6413
|
+
const path30 = await import("path");
|
|
6377
6414
|
const schemaRaw = checkConfig.schema || "plain";
|
|
6378
6415
|
const schema = typeof schemaRaw === "string" ? schemaRaw : "code-review";
|
|
6379
6416
|
let templateContent;
|
|
@@ -6381,24 +6418,24 @@ async function renderTemplateContent(checkId, checkConfig, reviewSummary) {
|
|
|
6381
6418
|
templateContent = String(checkConfig.template.content);
|
|
6382
6419
|
} else if (checkConfig.template && checkConfig.template.file) {
|
|
6383
6420
|
const file = String(checkConfig.template.file);
|
|
6384
|
-
const resolved =
|
|
6385
|
-
templateContent = await
|
|
6421
|
+
const resolved = path30.resolve(process.cwd(), file);
|
|
6422
|
+
templateContent = await fs26.readFile(resolved, "utf-8");
|
|
6386
6423
|
} else if (schema && schema !== "plain") {
|
|
6387
6424
|
const sanitized = String(schema).replace(/[^a-zA-Z0-9-]/g, "");
|
|
6388
6425
|
if (sanitized) {
|
|
6389
6426
|
const candidatePaths = [
|
|
6390
|
-
|
|
6427
|
+
path30.join(__dirname, "output", sanitized, "template.liquid"),
|
|
6391
6428
|
// bundled: dist/output/
|
|
6392
|
-
|
|
6429
|
+
path30.join(__dirname, "..", "..", "output", sanitized, "template.liquid"),
|
|
6393
6430
|
// source: output/
|
|
6394
|
-
|
|
6431
|
+
path30.join(process.cwd(), "output", sanitized, "template.liquid"),
|
|
6395
6432
|
// fallback: cwd/output/
|
|
6396
|
-
|
|
6433
|
+
path30.join(process.cwd(), "dist", "output", sanitized, "template.liquid")
|
|
6397
6434
|
// fallback: cwd/dist/output/
|
|
6398
6435
|
];
|
|
6399
6436
|
for (const p of candidatePaths) {
|
|
6400
6437
|
try {
|
|
6401
|
-
templateContent = await
|
|
6438
|
+
templateContent = await fs26.readFile(p, "utf-8");
|
|
6402
6439
|
if (templateContent) break;
|
|
6403
6440
|
} catch {
|
|
6404
6441
|
}
|
|
@@ -6803,7 +6840,7 @@ async function processDiffWithOutline(diffContent) {
|
|
|
6803
6840
|
}
|
|
6804
6841
|
try {
|
|
6805
6842
|
const originalProbePath = process.env.PROBE_PATH;
|
|
6806
|
-
const
|
|
6843
|
+
const fs26 = require("fs");
|
|
6807
6844
|
const possiblePaths = [
|
|
6808
6845
|
// Relative to current working directory (most common in production)
|
|
6809
6846
|
path6.join(process.cwd(), "node_modules/@probelabs/probe/bin/probe-binary"),
|
|
@@ -6814,7 +6851,7 @@ async function processDiffWithOutline(diffContent) {
|
|
|
6814
6851
|
];
|
|
6815
6852
|
let probeBinaryPath;
|
|
6816
6853
|
for (const candidatePath of possiblePaths) {
|
|
6817
|
-
if (
|
|
6854
|
+
if (fs26.existsSync(candidatePath)) {
|
|
6818
6855
|
probeBinaryPath = candidatePath;
|
|
6819
6856
|
break;
|
|
6820
6857
|
}
|
|
@@ -7900,8 +7937,8 @@ ${schemaString}`);
|
|
|
7900
7937
|
}
|
|
7901
7938
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
7902
7939
|
try {
|
|
7903
|
-
const
|
|
7904
|
-
const
|
|
7940
|
+
const fs26 = require("fs");
|
|
7941
|
+
const path30 = require("path");
|
|
7905
7942
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
7906
7943
|
const provider = this.config.provider || "auto";
|
|
7907
7944
|
const model = this.config.model || "default";
|
|
@@ -8015,20 +8052,20 @@ ${"=".repeat(60)}
|
|
|
8015
8052
|
`;
|
|
8016
8053
|
readableVersion += `${"=".repeat(60)}
|
|
8017
8054
|
`;
|
|
8018
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
8019
|
-
if (!
|
|
8020
|
-
|
|
8055
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path30.join(process.cwd(), "debug-artifacts");
|
|
8056
|
+
if (!fs26.existsSync(debugArtifactsDir)) {
|
|
8057
|
+
fs26.mkdirSync(debugArtifactsDir, { recursive: true });
|
|
8021
8058
|
}
|
|
8022
|
-
const debugFile =
|
|
8059
|
+
const debugFile = path30.join(
|
|
8023
8060
|
debugArtifactsDir,
|
|
8024
8061
|
`prompt-${_checkName || "unknown"}-${timestamp}.json`
|
|
8025
8062
|
);
|
|
8026
|
-
|
|
8027
|
-
const readableFile =
|
|
8063
|
+
fs26.writeFileSync(debugFile, debugJson, "utf-8");
|
|
8064
|
+
const readableFile = path30.join(
|
|
8028
8065
|
debugArtifactsDir,
|
|
8029
8066
|
`prompt-${_checkName || "unknown"}-${timestamp}.txt`
|
|
8030
8067
|
);
|
|
8031
|
-
|
|
8068
|
+
fs26.writeFileSync(readableFile, readableVersion, "utf-8");
|
|
8032
8069
|
log(`
|
|
8033
8070
|
\u{1F4BE} Full debug info saved to:`);
|
|
8034
8071
|
log(` JSON: ${debugFile}`);
|
|
@@ -8061,8 +8098,8 @@ ${"=".repeat(60)}
|
|
|
8061
8098
|
log(`\u{1F4E4} Response length: ${response.length} characters`);
|
|
8062
8099
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
8063
8100
|
try {
|
|
8064
|
-
const
|
|
8065
|
-
const
|
|
8101
|
+
const fs26 = require("fs");
|
|
8102
|
+
const path30 = require("path");
|
|
8066
8103
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8067
8104
|
const agentAny2 = agent;
|
|
8068
8105
|
let fullHistory = [];
|
|
@@ -8073,8 +8110,8 @@ ${"=".repeat(60)}
|
|
|
8073
8110
|
} else if (agentAny2._messages) {
|
|
8074
8111
|
fullHistory = agentAny2._messages;
|
|
8075
8112
|
}
|
|
8076
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
8077
|
-
const sessionBase =
|
|
8113
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path30.join(process.cwd(), "debug-artifacts");
|
|
8114
|
+
const sessionBase = path30.join(
|
|
8078
8115
|
debugArtifactsDir,
|
|
8079
8116
|
`session-${_checkName || "unknown"}-${timestamp}`
|
|
8080
8117
|
);
|
|
@@ -8086,7 +8123,7 @@ ${"=".repeat(60)}
|
|
|
8086
8123
|
schema: effectiveSchema,
|
|
8087
8124
|
totalMessages: fullHistory.length
|
|
8088
8125
|
};
|
|
8089
|
-
|
|
8126
|
+
fs26.writeFileSync(sessionBase + ".json", JSON.stringify(sessionData, null, 2), "utf-8");
|
|
8090
8127
|
let readable = `=============================================================
|
|
8091
8128
|
`;
|
|
8092
8129
|
readable += `COMPLETE AI SESSION HISTORY (AFTER RESPONSE)
|
|
@@ -8113,7 +8150,7 @@ ${"=".repeat(60)}
|
|
|
8113
8150
|
`;
|
|
8114
8151
|
readable += content + "\n";
|
|
8115
8152
|
});
|
|
8116
|
-
|
|
8153
|
+
fs26.writeFileSync(sessionBase + ".summary.txt", readable, "utf-8");
|
|
8117
8154
|
log(`\u{1F4BE} Complete session history saved:`);
|
|
8118
8155
|
log(` - Contains ALL ${fullHistory.length} messages (prompts + responses)`);
|
|
8119
8156
|
} catch (error) {
|
|
@@ -8122,11 +8159,11 @@ ${"=".repeat(60)}
|
|
|
8122
8159
|
}
|
|
8123
8160
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
8124
8161
|
try {
|
|
8125
|
-
const
|
|
8126
|
-
const
|
|
8162
|
+
const fs26 = require("fs");
|
|
8163
|
+
const path30 = require("path");
|
|
8127
8164
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8128
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
8129
|
-
const responseFile =
|
|
8165
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path30.join(process.cwd(), "debug-artifacts");
|
|
8166
|
+
const responseFile = path30.join(
|
|
8130
8167
|
debugArtifactsDir,
|
|
8131
8168
|
`response-${_checkName || "unknown"}-${timestamp}.txt`
|
|
8132
8169
|
);
|
|
@@ -8159,7 +8196,7 @@ ${"=".repeat(60)}
|
|
|
8159
8196
|
`;
|
|
8160
8197
|
responseContent += `${"=".repeat(60)}
|
|
8161
8198
|
`;
|
|
8162
|
-
|
|
8199
|
+
fs26.writeFileSync(responseFile, responseContent, "utf-8");
|
|
8163
8200
|
log(`\u{1F4BE} Response saved to: ${responseFile}`);
|
|
8164
8201
|
} catch (error) {
|
|
8165
8202
|
log(`\u26A0\uFE0F Could not save response file: ${error}`);
|
|
@@ -8175,9 +8212,9 @@ ${"=".repeat(60)}
|
|
|
8175
8212
|
await agentAny._telemetryConfig.shutdown();
|
|
8176
8213
|
log(`\u{1F4CA} OpenTelemetry trace saved to: ${agentAny._traceFilePath}`);
|
|
8177
8214
|
if (process.env.GITHUB_ACTIONS) {
|
|
8178
|
-
const
|
|
8179
|
-
if (
|
|
8180
|
-
const stats =
|
|
8215
|
+
const fs26 = require("fs");
|
|
8216
|
+
if (fs26.existsSync(agentAny._traceFilePath)) {
|
|
8217
|
+
const stats = fs26.statSync(agentAny._traceFilePath);
|
|
8181
8218
|
console.log(
|
|
8182
8219
|
`::notice title=AI Trace Saved::${agentAny._traceFilePath} (${stats.size} bytes)`
|
|
8183
8220
|
);
|
|
@@ -8378,8 +8415,8 @@ ${schemaString}`);
|
|
|
8378
8415
|
const model = this.config.model || "default";
|
|
8379
8416
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
8380
8417
|
try {
|
|
8381
|
-
const
|
|
8382
|
-
const
|
|
8418
|
+
const fs26 = require("fs");
|
|
8419
|
+
const path30 = require("path");
|
|
8383
8420
|
const os3 = require("os");
|
|
8384
8421
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8385
8422
|
const debugData = {
|
|
@@ -8453,18 +8490,18 @@ ${"=".repeat(60)}
|
|
|
8453
8490
|
readableVersion += `${"=".repeat(60)}
|
|
8454
8491
|
`;
|
|
8455
8492
|
const tempDir = os3.tmpdir();
|
|
8456
|
-
const promptFile =
|
|
8457
|
-
|
|
8493
|
+
const promptFile = path30.join(tempDir, `visor-prompt-${timestamp}.txt`);
|
|
8494
|
+
fs26.writeFileSync(promptFile, prompt, "utf-8");
|
|
8458
8495
|
log(`
|
|
8459
8496
|
\u{1F4BE} Prompt saved to: ${promptFile}`);
|
|
8460
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
8497
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path30.join(process.cwd(), "debug-artifacts");
|
|
8461
8498
|
try {
|
|
8462
|
-
const base =
|
|
8499
|
+
const base = path30.join(
|
|
8463
8500
|
debugArtifactsDir,
|
|
8464
8501
|
`prompt-${_checkName || "unknown"}-${timestamp}`
|
|
8465
8502
|
);
|
|
8466
|
-
|
|
8467
|
-
|
|
8503
|
+
fs26.writeFileSync(base + ".json", debugJson, "utf-8");
|
|
8504
|
+
fs26.writeFileSync(base + ".summary.txt", readableVersion, "utf-8");
|
|
8468
8505
|
log(`
|
|
8469
8506
|
\u{1F4BE} Full debug info saved to directory: ${debugArtifactsDir}`);
|
|
8470
8507
|
} catch {
|
|
@@ -8509,8 +8546,8 @@ $ ${cliCommand}
|
|
|
8509
8546
|
log(`\u{1F4E4} Response length: ${response.length} characters`);
|
|
8510
8547
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
8511
8548
|
try {
|
|
8512
|
-
const
|
|
8513
|
-
const
|
|
8549
|
+
const fs26 = require("fs");
|
|
8550
|
+
const path30 = require("path");
|
|
8514
8551
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8515
8552
|
const agentAny = agent;
|
|
8516
8553
|
let fullHistory = [];
|
|
@@ -8521,8 +8558,8 @@ $ ${cliCommand}
|
|
|
8521
8558
|
} else if (agentAny._messages) {
|
|
8522
8559
|
fullHistory = agentAny._messages;
|
|
8523
8560
|
}
|
|
8524
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
8525
|
-
const sessionBase =
|
|
8561
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path30.join(process.cwd(), "debug-artifacts");
|
|
8562
|
+
const sessionBase = path30.join(
|
|
8526
8563
|
debugArtifactsDir,
|
|
8527
8564
|
`session-${_checkName || "unknown"}-${timestamp}`
|
|
8528
8565
|
);
|
|
@@ -8534,7 +8571,7 @@ $ ${cliCommand}
|
|
|
8534
8571
|
schema: effectiveSchema,
|
|
8535
8572
|
totalMessages: fullHistory.length
|
|
8536
8573
|
};
|
|
8537
|
-
|
|
8574
|
+
fs26.writeFileSync(sessionBase + ".json", JSON.stringify(sessionData, null, 2), "utf-8");
|
|
8538
8575
|
let readable = `=============================================================
|
|
8539
8576
|
`;
|
|
8540
8577
|
readable += `COMPLETE AI SESSION HISTORY (AFTER RESPONSE)
|
|
@@ -8561,7 +8598,7 @@ ${"=".repeat(60)}
|
|
|
8561
8598
|
`;
|
|
8562
8599
|
readable += content + "\n";
|
|
8563
8600
|
});
|
|
8564
|
-
|
|
8601
|
+
fs26.writeFileSync(sessionBase + ".summary.txt", readable, "utf-8");
|
|
8565
8602
|
log(`\u{1F4BE} Complete session history saved:`);
|
|
8566
8603
|
log(` - Contains ALL ${fullHistory.length} messages (prompts + responses)`);
|
|
8567
8604
|
} catch (error) {
|
|
@@ -8570,11 +8607,11 @@ ${"=".repeat(60)}
|
|
|
8570
8607
|
}
|
|
8571
8608
|
if (process.env.VISOR_DEBUG_AI_SESSIONS === "true") {
|
|
8572
8609
|
try {
|
|
8573
|
-
const
|
|
8574
|
-
const
|
|
8610
|
+
const fs26 = require("fs");
|
|
8611
|
+
const path30 = require("path");
|
|
8575
8612
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8576
|
-
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS ||
|
|
8577
|
-
const responseFile =
|
|
8613
|
+
const debugArtifactsDir = process.env.VISOR_DEBUG_ARTIFACTS || path30.join(process.cwd(), "debug-artifacts");
|
|
8614
|
+
const responseFile = path30.join(
|
|
8578
8615
|
debugArtifactsDir,
|
|
8579
8616
|
`response-${_checkName || "unknown"}-${timestamp}.txt`
|
|
8580
8617
|
);
|
|
@@ -8607,7 +8644,7 @@ ${"=".repeat(60)}
|
|
|
8607
8644
|
`;
|
|
8608
8645
|
responseContent += `${"=".repeat(60)}
|
|
8609
8646
|
`;
|
|
8610
|
-
|
|
8647
|
+
fs26.writeFileSync(responseFile, responseContent, "utf-8");
|
|
8611
8648
|
log(`\u{1F4BE} Response saved to: ${responseFile}`);
|
|
8612
8649
|
} catch (error) {
|
|
8613
8650
|
log(`\u26A0\uFE0F Could not save response file: ${error}`);
|
|
@@ -8625,9 +8662,9 @@ ${"=".repeat(60)}
|
|
|
8625
8662
|
await telemetry.shutdown();
|
|
8626
8663
|
log(`\u{1F4CA} OpenTelemetry trace saved to: ${traceFilePath}`);
|
|
8627
8664
|
if (process.env.GITHUB_ACTIONS) {
|
|
8628
|
-
const
|
|
8629
|
-
if (
|
|
8630
|
-
const stats =
|
|
8665
|
+
const fs26 = require("fs");
|
|
8666
|
+
if (fs26.existsSync(traceFilePath)) {
|
|
8667
|
+
const stats = fs26.statSync(traceFilePath);
|
|
8631
8668
|
console.log(
|
|
8632
8669
|
`::notice title=AI Trace Saved::OpenTelemetry trace file size: ${stats.size} bytes`
|
|
8633
8670
|
);
|
|
@@ -8665,8 +8702,8 @@ ${"=".repeat(60)}
|
|
|
8665
8702
|
* Load schema content from schema files or inline definitions
|
|
8666
8703
|
*/
|
|
8667
8704
|
async loadSchemaContent(schema) {
|
|
8668
|
-
const
|
|
8669
|
-
const
|
|
8705
|
+
const fs26 = require("fs").promises;
|
|
8706
|
+
const path30 = require("path");
|
|
8670
8707
|
if (typeof schema === "object" && schema !== null) {
|
|
8671
8708
|
log("\u{1F4CB} Using inline schema object from configuration");
|
|
8672
8709
|
return JSON.stringify(schema);
|
|
@@ -8679,14 +8716,14 @@ ${"=".repeat(60)}
|
|
|
8679
8716
|
}
|
|
8680
8717
|
} catch {
|
|
8681
8718
|
}
|
|
8682
|
-
if ((schema.startsWith("./") || schema.includes(".json")) && !
|
|
8719
|
+
if ((schema.startsWith("./") || schema.includes(".json")) && !path30.isAbsolute(schema)) {
|
|
8683
8720
|
if (schema.includes("..") || schema.includes("\0")) {
|
|
8684
8721
|
throw new Error("Invalid schema path: path traversal not allowed");
|
|
8685
8722
|
}
|
|
8686
8723
|
try {
|
|
8687
|
-
const schemaPath =
|
|
8724
|
+
const schemaPath = path30.resolve(process.cwd(), schema);
|
|
8688
8725
|
log(`\u{1F4CB} Loading custom schema from file: ${schemaPath}`);
|
|
8689
|
-
const schemaContent = await
|
|
8726
|
+
const schemaContent = await fs26.readFile(schemaPath, "utf-8");
|
|
8690
8727
|
return schemaContent.trim();
|
|
8691
8728
|
} catch (error) {
|
|
8692
8729
|
throw new Error(
|
|
@@ -8700,22 +8737,22 @@ ${"=".repeat(60)}
|
|
|
8700
8737
|
}
|
|
8701
8738
|
const candidatePaths = [
|
|
8702
8739
|
// GitHub Action bundle location
|
|
8703
|
-
|
|
8740
|
+
path30.join(__dirname, "output", sanitizedSchemaName, "schema.json"),
|
|
8704
8741
|
// Historical fallback when src/output was inadvertently bundled as output1/
|
|
8705
|
-
|
|
8742
|
+
path30.join(__dirname, "output1", sanitizedSchemaName, "schema.json"),
|
|
8706
8743
|
// Local dev (repo root)
|
|
8707
|
-
|
|
8744
|
+
path30.join(process.cwd(), "output", sanitizedSchemaName, "schema.json")
|
|
8708
8745
|
];
|
|
8709
8746
|
for (const schemaPath of candidatePaths) {
|
|
8710
8747
|
try {
|
|
8711
|
-
const schemaContent = await
|
|
8748
|
+
const schemaContent = await fs26.readFile(schemaPath, "utf-8");
|
|
8712
8749
|
return schemaContent.trim();
|
|
8713
8750
|
} catch {
|
|
8714
8751
|
}
|
|
8715
8752
|
}
|
|
8716
|
-
const distPath =
|
|
8717
|
-
const distAltPath =
|
|
8718
|
-
const cwdPath =
|
|
8753
|
+
const distPath = path30.join(__dirname, "output", sanitizedSchemaName, "schema.json");
|
|
8754
|
+
const distAltPath = path30.join(__dirname, "output1", sanitizedSchemaName, "schema.json");
|
|
8755
|
+
const cwdPath = path30.join(process.cwd(), "output", sanitizedSchemaName, "schema.json");
|
|
8719
8756
|
throw new Error(
|
|
8720
8757
|
`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.`
|
|
8721
8758
|
);
|
|
@@ -9391,6 +9428,758 @@ var init_command_executor = __esm({
|
|
|
9391
9428
|
}
|
|
9392
9429
|
});
|
|
9393
9430
|
|
|
9431
|
+
// src/providers/api-tool-executor.ts
|
|
9432
|
+
function isHttpUrl(value) {
|
|
9433
|
+
return value.startsWith("http://") || value.startsWith("https://");
|
|
9434
|
+
}
|
|
9435
|
+
function toStringArray(value) {
|
|
9436
|
+
if (!value) return [];
|
|
9437
|
+
if (Array.isArray(value)) {
|
|
9438
|
+
return value.map((item) => String(item).trim()).filter(Boolean);
|
|
9439
|
+
}
|
|
9440
|
+
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
9441
|
+
}
|
|
9442
|
+
function isPlainObject(value) {
|
|
9443
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
9444
|
+
}
|
|
9445
|
+
function toOverlaySourceArray(value) {
|
|
9446
|
+
if (!value) return [];
|
|
9447
|
+
if (typeof value === "string" || isPlainObject(value)) {
|
|
9448
|
+
return [value];
|
|
9449
|
+
}
|
|
9450
|
+
if (Array.isArray(value)) {
|
|
9451
|
+
return value.filter((item) => typeof item === "string" || isPlainObject(item));
|
|
9452
|
+
}
|
|
9453
|
+
return [];
|
|
9454
|
+
}
|
|
9455
|
+
function resolvePathOrUrl(candidate, baseDir) {
|
|
9456
|
+
if (isHttpUrl(candidate)) return candidate;
|
|
9457
|
+
if (import_path4.default.isAbsolute(candidate)) return candidate;
|
|
9458
|
+
if (isHttpUrl(baseDir)) {
|
|
9459
|
+
return new URL(candidate, baseDir).toString();
|
|
9460
|
+
}
|
|
9461
|
+
return import_path4.default.resolve(baseDir, candidate);
|
|
9462
|
+
}
|
|
9463
|
+
async function readTextFromPathOrUrl(location) {
|
|
9464
|
+
if (isHttpUrl(location)) {
|
|
9465
|
+
const res = await fetch(location);
|
|
9466
|
+
if (!res.ok) {
|
|
9467
|
+
throw new Error(`Failed to fetch ${location}: ${res.status} ${res.statusText}`);
|
|
9468
|
+
}
|
|
9469
|
+
return await res.text();
|
|
9470
|
+
}
|
|
9471
|
+
return await import_promises2.default.readFile(location, "utf8");
|
|
9472
|
+
}
|
|
9473
|
+
function parseJsonOrYaml(raw, location) {
|
|
9474
|
+
const ext = import_path4.default.extname(location).toLowerCase();
|
|
9475
|
+
if (ext === ".yaml" || ext === ".yml") {
|
|
9476
|
+
return import_js_yaml.default.load(raw);
|
|
9477
|
+
}
|
|
9478
|
+
try {
|
|
9479
|
+
return JSON.parse(raw);
|
|
9480
|
+
} catch {
|
|
9481
|
+
return import_js_yaml.default.load(raw);
|
|
9482
|
+
}
|
|
9483
|
+
}
|
|
9484
|
+
function parseOverlayTargetPath(pathValue) {
|
|
9485
|
+
if (!Array.isArray(pathValue) || pathValue.length === 0) return "$";
|
|
9486
|
+
return pathValue.map((segment, index) => {
|
|
9487
|
+
if (index === 0) return "$";
|
|
9488
|
+
if (typeof segment === "number") return `[${segment}]`;
|
|
9489
|
+
if (/^[A-Za-z_][\w$]*$/.test(segment)) return `.${segment}`;
|
|
9490
|
+
return `['${segment.replace(/'/g, "\\'")}']`;
|
|
9491
|
+
}).join("");
|
|
9492
|
+
}
|
|
9493
|
+
function applyOverlayActions(target, overlay) {
|
|
9494
|
+
const cloned = JSON.parse(JSON.stringify(target));
|
|
9495
|
+
if (!overlay || typeof overlay !== "object") return cloned;
|
|
9496
|
+
const actions = Array.isArray(overlay.actions) ? overlay.actions : [];
|
|
9497
|
+
if (actions.length === 0) {
|
|
9498
|
+
return (0, import_deepmerge.default)(cloned, overlay, {
|
|
9499
|
+
arrayMerge: (dst, src) => dst.concat(src)
|
|
9500
|
+
});
|
|
9501
|
+
}
|
|
9502
|
+
for (const action of actions) {
|
|
9503
|
+
const targetExpr = action?.target;
|
|
9504
|
+
if (!targetExpr || typeof targetExpr !== "string") continue;
|
|
9505
|
+
let matches = [];
|
|
9506
|
+
try {
|
|
9507
|
+
matches = (0, import_jsonpath_plus.JSONPath)({
|
|
9508
|
+
path: targetExpr,
|
|
9509
|
+
json: cloned,
|
|
9510
|
+
resultType: "all"
|
|
9511
|
+
});
|
|
9512
|
+
} catch (error) {
|
|
9513
|
+
logger.warn(`[ApiToolExecutor] Invalid overlay target "${targetExpr}": ${error}`);
|
|
9514
|
+
continue;
|
|
9515
|
+
}
|
|
9516
|
+
if (matches.length === 0) {
|
|
9517
|
+
continue;
|
|
9518
|
+
}
|
|
9519
|
+
for (const match of matches) {
|
|
9520
|
+
if (!match || typeof match !== "object") continue;
|
|
9521
|
+
const parent = match.parent;
|
|
9522
|
+
const key = match.parentProperty;
|
|
9523
|
+
if (parent === void 0 || key === void 0) {
|
|
9524
|
+
const jsonPath = parseOverlayTargetPath(
|
|
9525
|
+
match.path || []
|
|
9526
|
+
);
|
|
9527
|
+
logger.debug(`[ApiToolExecutor] Overlay target has no writable parent: ${jsonPath}`);
|
|
9528
|
+
continue;
|
|
9529
|
+
}
|
|
9530
|
+
if (action.remove === true) {
|
|
9531
|
+
if (Array.isArray(parent)) {
|
|
9532
|
+
parent.splice(Number(key), 1);
|
|
9533
|
+
} else if (parent && typeof parent === "object") {
|
|
9534
|
+
delete parent[key];
|
|
9535
|
+
}
|
|
9536
|
+
continue;
|
|
9537
|
+
}
|
|
9538
|
+
if (action.update === void 0) {
|
|
9539
|
+
continue;
|
|
9540
|
+
}
|
|
9541
|
+
const current = match.value;
|
|
9542
|
+
if (Array.isArray(current)) {
|
|
9543
|
+
current.push(action.update);
|
|
9544
|
+
} else if (current && typeof current === "object") {
|
|
9545
|
+
const merged = (0, import_deepmerge.default)(current, action.update, {
|
|
9546
|
+
arrayMerge: (dst, src) => dst.concat(src)
|
|
9547
|
+
});
|
|
9548
|
+
if (Array.isArray(parent)) {
|
|
9549
|
+
parent[Number(key)] = merged;
|
|
9550
|
+
} else {
|
|
9551
|
+
parent[key] = merged;
|
|
9552
|
+
}
|
|
9553
|
+
} else if (Array.isArray(parent)) {
|
|
9554
|
+
parent[Number(key)] = action.update;
|
|
9555
|
+
} else {
|
|
9556
|
+
parent[key] = action.update;
|
|
9557
|
+
}
|
|
9558
|
+
}
|
|
9559
|
+
}
|
|
9560
|
+
return cloned;
|
|
9561
|
+
}
|
|
9562
|
+
function isRefObject(value) {
|
|
9563
|
+
return Boolean(value && typeof value === "object" && "$ref" in value);
|
|
9564
|
+
}
|
|
9565
|
+
function isSchemaObject(value) {
|
|
9566
|
+
return Boolean(
|
|
9567
|
+
value && typeof value === "object" && !Array.isArray(value) && !isRefObject(value)
|
|
9568
|
+
);
|
|
9569
|
+
}
|
|
9570
|
+
function getSchemaFromContent(content) {
|
|
9571
|
+
if (!content || typeof content !== "object") return void 0;
|
|
9572
|
+
const entries = Object.values(content);
|
|
9573
|
+
const withSchema = entries.find((entry) => entry && typeof entry === "object" && entry.schema);
|
|
9574
|
+
return withSchema?.schema;
|
|
9575
|
+
}
|
|
9576
|
+
function mapOpenApiTypeToJsonType(schema) {
|
|
9577
|
+
if (!schema || !schema.type) return { type: "string" };
|
|
9578
|
+
const openApiType = String(schema.type);
|
|
9579
|
+
const nullable = schema.nullable === true;
|
|
9580
|
+
switch (openApiType) {
|
|
9581
|
+
case "integer":
|
|
9582
|
+
case "number":
|
|
9583
|
+
case "boolean":
|
|
9584
|
+
case "string":
|
|
9585
|
+
case "array":
|
|
9586
|
+
case "object":
|
|
9587
|
+
return { type: openApiType, format: schema.format, nullable };
|
|
9588
|
+
default:
|
|
9589
|
+
return { type: "string", format: schema.format, nullable };
|
|
9590
|
+
}
|
|
9591
|
+
}
|
|
9592
|
+
function openApiSchemaToJsonSchema(schema) {
|
|
9593
|
+
if (!schema) {
|
|
9594
|
+
return { type: "string" };
|
|
9595
|
+
}
|
|
9596
|
+
const mapped = mapOpenApiTypeToJsonType(schema);
|
|
9597
|
+
const result = {
|
|
9598
|
+
type: mapped.nullable ? [mapped.type, "null"] : mapped.type
|
|
9599
|
+
};
|
|
9600
|
+
if (mapped.format) result.format = mapped.format;
|
|
9601
|
+
if (schema.description !== void 0) result.description = schema.description;
|
|
9602
|
+
if (schema.default !== void 0) result.default = schema.default;
|
|
9603
|
+
if (schema.enum !== void 0) result.enum = schema.enum;
|
|
9604
|
+
if (schema.example !== void 0) result.example = schema.example;
|
|
9605
|
+
if (schema.minimum !== void 0) result.minimum = schema.minimum;
|
|
9606
|
+
if (schema.maximum !== void 0) result.maximum = schema.maximum;
|
|
9607
|
+
if (schema.minLength !== void 0) result.minLength = schema.minLength;
|
|
9608
|
+
if (schema.maxLength !== void 0) result.maxLength = schema.maxLength;
|
|
9609
|
+
if (schema.pattern !== void 0) result.pattern = schema.pattern;
|
|
9610
|
+
if (schema.multipleOf !== void 0) result.multipleOf = schema.multipleOf;
|
|
9611
|
+
if (schema.minItems !== void 0) result.minItems = schema.minItems;
|
|
9612
|
+
if (schema.maxItems !== void 0) result.maxItems = schema.maxItems;
|
|
9613
|
+
if (schema.uniqueItems !== void 0) result.uniqueItems = schema.uniqueItems;
|
|
9614
|
+
if (schema.type === "object" && schema.properties && typeof schema.properties === "object") {
|
|
9615
|
+
const props = {};
|
|
9616
|
+
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
|
9617
|
+
if (isSchemaObject(propSchema)) {
|
|
9618
|
+
props[propName] = openApiSchemaToJsonSchema(propSchema);
|
|
9619
|
+
}
|
|
9620
|
+
}
|
|
9621
|
+
result.properties = props;
|
|
9622
|
+
if (Array.isArray(schema.required) && schema.required.length > 0) {
|
|
9623
|
+
result.required = schema.required;
|
|
9624
|
+
}
|
|
9625
|
+
if (schema.additionalProperties === true || schema.additionalProperties === false) {
|
|
9626
|
+
result.additionalProperties = schema.additionalProperties;
|
|
9627
|
+
} else if (isSchemaObject(schema.additionalProperties)) {
|
|
9628
|
+
result.additionalProperties = openApiSchemaToJsonSchema(schema.additionalProperties);
|
|
9629
|
+
}
|
|
9630
|
+
}
|
|
9631
|
+
if (schema.type === "array" && isSchemaObject(schema.items)) {
|
|
9632
|
+
result.items = openApiSchemaToJsonSchema(schema.items);
|
|
9633
|
+
}
|
|
9634
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
9635
|
+
if (key.startsWith("x-")) {
|
|
9636
|
+
result[key] = value;
|
|
9637
|
+
}
|
|
9638
|
+
}
|
|
9639
|
+
return result;
|
|
9640
|
+
}
|
|
9641
|
+
function shouldIncludeOperation(operationId, pathValue, method, whitelist, blacklist) {
|
|
9642
|
+
const methodPath = `${method.toUpperCase()}:${pathValue}`;
|
|
9643
|
+
const opKey = operationId || methodPath;
|
|
9644
|
+
if (whitelist.length > 0) {
|
|
9645
|
+
return whitelist.some((pattern) => (0, import_minimatch.minimatch)(opKey, pattern) || (0, import_minimatch.minimatch)(methodPath, pattern));
|
|
9646
|
+
}
|
|
9647
|
+
if (blacklist.length > 0) {
|
|
9648
|
+
return !blacklist.some((pattern) => (0, import_minimatch.minimatch)(opKey, pattern) || (0, import_minimatch.minimatch)(methodPath, pattern));
|
|
9649
|
+
}
|
|
9650
|
+
return true;
|
|
9651
|
+
}
|
|
9652
|
+
function getApiToolConfig(tool) {
|
|
9653
|
+
return {
|
|
9654
|
+
customHeaders: tool.headers || {},
|
|
9655
|
+
disableXMcp: Boolean(tool.disableXMcp ?? tool.disable_x_mcp ?? false),
|
|
9656
|
+
apiKey: tool.apiKey ?? tool.api_key,
|
|
9657
|
+
securitySchemeName: tool.securitySchemeName ?? tool.security_scheme_name,
|
|
9658
|
+
securityCredentials: tool.securityCredentials || tool.security_credentials || {},
|
|
9659
|
+
requestTimeoutMs: tool.requestTimeoutMs ?? tool.request_timeout_ms ?? tool.timeout ?? 3e4
|
|
9660
|
+
};
|
|
9661
|
+
}
|
|
9662
|
+
function buildOutputSchema(operation) {
|
|
9663
|
+
const responses = operation.responses;
|
|
9664
|
+
if (!responses || typeof responses !== "object") return void 0;
|
|
9665
|
+
const successCode = Object.keys(responses).find((code) => code.startsWith("2"));
|
|
9666
|
+
if (!successCode) return void 0;
|
|
9667
|
+
const response = responses[successCode];
|
|
9668
|
+
if (!response || typeof response !== "object" || isRefObject(response)) return void 0;
|
|
9669
|
+
const jsonSchema = response.content?.["application/json"]?.schema || getSchemaFromContent(response.content);
|
|
9670
|
+
if (!isSchemaObject(jsonSchema)) return void 0;
|
|
9671
|
+
const mapped = openApiSchemaToJsonSchema(jsonSchema);
|
|
9672
|
+
if (response.description && typeof response.description === "string") {
|
|
9673
|
+
mapped.description = response.description;
|
|
9674
|
+
}
|
|
9675
|
+
return mapped;
|
|
9676
|
+
}
|
|
9677
|
+
function getToolName(operationId, operation, pathItem, tool) {
|
|
9678
|
+
let toolName = operationId;
|
|
9679
|
+
const opExtension = operation["x-mcp"];
|
|
9680
|
+
const pathExtension = pathItem["x-mcp"];
|
|
9681
|
+
if (opExtension && typeof opExtension === "object" && typeof opExtension.name === "string") {
|
|
9682
|
+
toolName = opExtension.name;
|
|
9683
|
+
} else if (pathExtension && typeof pathExtension === "object" && typeof pathExtension.name === "string") {
|
|
9684
|
+
toolName = pathExtension.name;
|
|
9685
|
+
}
|
|
9686
|
+
const prefix = tool.namePrefix || tool.name_prefix;
|
|
9687
|
+
if (prefix) {
|
|
9688
|
+
return `${prefix}${toolName}`;
|
|
9689
|
+
}
|
|
9690
|
+
return toolName;
|
|
9691
|
+
}
|
|
9692
|
+
function getToolDescription(operation, pathItem) {
|
|
9693
|
+
const opExtension = operation["x-mcp"];
|
|
9694
|
+
const pathExtension = pathItem["x-mcp"];
|
|
9695
|
+
if (opExtension && typeof opExtension === "object" && typeof opExtension.description === "string") {
|
|
9696
|
+
return opExtension.description;
|
|
9697
|
+
}
|
|
9698
|
+
if (pathExtension && typeof pathExtension === "object" && typeof pathExtension.description === "string") {
|
|
9699
|
+
return pathExtension.description;
|
|
9700
|
+
}
|
|
9701
|
+
return typeof operation.description === "string" && operation.description || typeof operation.summary === "string" && operation.summary || typeof pathItem.summary === "string" && pathItem.summary || "No description available.";
|
|
9702
|
+
}
|
|
9703
|
+
function isApiToolDefinition(tool) {
|
|
9704
|
+
return Boolean(tool && tool.type === "api");
|
|
9705
|
+
}
|
|
9706
|
+
async function loadOpenApiDocument(tool) {
|
|
9707
|
+
if (!tool.spec) {
|
|
9708
|
+
throw new Error(`API tool '${tool.name}' is missing required field: spec`);
|
|
9709
|
+
}
|
|
9710
|
+
const configuredBaseDir = tool.__baseDir;
|
|
9711
|
+
const baseDir = (() => {
|
|
9712
|
+
if (tool.cwd) {
|
|
9713
|
+
if (import_path4.default.isAbsolute(tool.cwd) || isHttpUrl(tool.cwd)) {
|
|
9714
|
+
return tool.cwd;
|
|
9715
|
+
}
|
|
9716
|
+
if (configuredBaseDir) {
|
|
9717
|
+
return resolvePathOrUrl(tool.cwd, configuredBaseDir);
|
|
9718
|
+
}
|
|
9719
|
+
return import_path4.default.resolve(tool.cwd);
|
|
9720
|
+
}
|
|
9721
|
+
return configuredBaseDir || process.cwd();
|
|
9722
|
+
})();
|
|
9723
|
+
const dereferenceWithContext = async (source, spec) => {
|
|
9724
|
+
try {
|
|
9725
|
+
return await import_swagger_parser.default.dereference(spec);
|
|
9726
|
+
} catch (error) {
|
|
9727
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
9728
|
+
throw new Error(
|
|
9729
|
+
`Failed to dereference OpenAPI spec for API tool '${tool.name}' from ${source}: ${errorMessage}`
|
|
9730
|
+
);
|
|
9731
|
+
}
|
|
9732
|
+
};
|
|
9733
|
+
let openapi;
|
|
9734
|
+
if (typeof tool.spec === "string") {
|
|
9735
|
+
const specLocation = resolvePathOrUrl(tool.spec, baseDir);
|
|
9736
|
+
if (isHttpUrl(specLocation)) {
|
|
9737
|
+
const raw = await readTextFromPathOrUrl(specLocation);
|
|
9738
|
+
const parsed = parseJsonOrYaml(raw, specLocation);
|
|
9739
|
+
openapi = await dereferenceWithContext(specLocation, parsed);
|
|
9740
|
+
} else {
|
|
9741
|
+
openapi = await dereferenceWithContext(specLocation, specLocation);
|
|
9742
|
+
}
|
|
9743
|
+
} else if (isPlainObject(tool.spec)) {
|
|
9744
|
+
openapi = await dereferenceWithContext("inline spec", JSON.parse(JSON.stringify(tool.spec)));
|
|
9745
|
+
} else {
|
|
9746
|
+
throw new Error(
|
|
9747
|
+
`API tool '${tool.name}' has invalid spec field (expected string path/URL or object)`
|
|
9748
|
+
);
|
|
9749
|
+
}
|
|
9750
|
+
const overlays = toOverlaySourceArray(tool.overlays);
|
|
9751
|
+
let working = openapi;
|
|
9752
|
+
for (const overlaySource of overlays) {
|
|
9753
|
+
let overlay = overlaySource;
|
|
9754
|
+
if (typeof overlaySource === "string") {
|
|
9755
|
+
const resolved = resolvePathOrUrl(overlaySource, baseDir);
|
|
9756
|
+
const raw = await readTextFromPathOrUrl(resolved);
|
|
9757
|
+
overlay = parseJsonOrYaml(raw, resolved);
|
|
9758
|
+
}
|
|
9759
|
+
working = applyOverlayActions(working, overlay);
|
|
9760
|
+
}
|
|
9761
|
+
return working;
|
|
9762
|
+
}
|
|
9763
|
+
function mapOpenApiToTools(openapi, tool) {
|
|
9764
|
+
const paths = openapi?.paths;
|
|
9765
|
+
if (!paths || typeof paths !== "object") {
|
|
9766
|
+
return [];
|
|
9767
|
+
}
|
|
9768
|
+
const targetUrl = tool.targetUrl || tool.target_url;
|
|
9769
|
+
const baseServerUrl = String(targetUrl || openapi?.servers?.[0]?.url || "").replace(/\/$/, "");
|
|
9770
|
+
if (!baseServerUrl) {
|
|
9771
|
+
throw new Error(
|
|
9772
|
+
`API tool '${tool.name}' cannot determine target API URL. Set targetUrl/target_url or provide OpenAPI servers[].`
|
|
9773
|
+
);
|
|
9774
|
+
}
|
|
9775
|
+
const whitelist = toStringArray(tool.whitelist);
|
|
9776
|
+
const blacklist = toStringArray(tool.blacklist);
|
|
9777
|
+
const globalSecurity = Array.isArray(openapi?.security) ? openapi.security : null;
|
|
9778
|
+
const securitySchemes = openapi?.components?.securitySchemes;
|
|
9779
|
+
const mapped = [];
|
|
9780
|
+
const apiToolConfig = getApiToolConfig(tool);
|
|
9781
|
+
for (const [pathValue, pathItemRaw] of Object.entries(paths)) {
|
|
9782
|
+
const pathItem = pathItemRaw;
|
|
9783
|
+
if (!pathItem || typeof pathItem !== "object") continue;
|
|
9784
|
+
for (const [method, operationRaw] of Object.entries(pathItem)) {
|
|
9785
|
+
if (!HTTP_METHODS.has(method.toLowerCase())) continue;
|
|
9786
|
+
const operation = operationRaw;
|
|
9787
|
+
if (!operation || typeof operation !== "object") continue;
|
|
9788
|
+
const operationId = typeof operation.operationId === "string" ? String(operation.operationId) : void 0;
|
|
9789
|
+
if (!operationId) {
|
|
9790
|
+
logger.debug(
|
|
9791
|
+
`[ApiToolExecutor] Skipping ${method.toUpperCase()} ${pathValue} (missing operationId)`
|
|
9792
|
+
);
|
|
9793
|
+
continue;
|
|
9794
|
+
}
|
|
9795
|
+
if (!shouldIncludeOperation(operationId, pathValue, method, whitelist, blacklist)) {
|
|
9796
|
+
continue;
|
|
9797
|
+
}
|
|
9798
|
+
const toolName = getToolName(operationId, operation, pathItem, tool);
|
|
9799
|
+
const toolDescription = getToolDescription(operation, pathItem);
|
|
9800
|
+
const inputSchema = {
|
|
9801
|
+
type: "object",
|
|
9802
|
+
properties: {}
|
|
9803
|
+
};
|
|
9804
|
+
const requiredNames = /* @__PURE__ */ new Set();
|
|
9805
|
+
const allParameters = [
|
|
9806
|
+
...Array.isArray(pathItem.parameters) ? pathItem.parameters : [],
|
|
9807
|
+
...Array.isArray(operation.parameters) ? operation.parameters : []
|
|
9808
|
+
].filter((param) => param && typeof param === "object" && !isRefObject(param));
|
|
9809
|
+
for (const param of allParameters) {
|
|
9810
|
+
const paramObj = param;
|
|
9811
|
+
const paramName = typeof paramObj.name === "string" ? paramObj.name : "";
|
|
9812
|
+
if (!paramName) continue;
|
|
9813
|
+
if (!isSchemaObject(paramObj.schema)) continue;
|
|
9814
|
+
const schema = openApiSchemaToJsonSchema(paramObj.schema);
|
|
9815
|
+
if (typeof paramObj.description === "string") {
|
|
9816
|
+
schema.description = paramObj.description;
|
|
9817
|
+
}
|
|
9818
|
+
schema["x-parameter-location"] = paramObj.in || "query";
|
|
9819
|
+
if (paramObj.example !== void 0) schema.example = paramObj.example;
|
|
9820
|
+
if (paramObj.deprecated === true) schema.deprecated = true;
|
|
9821
|
+
inputSchema.properties[paramName] = schema;
|
|
9822
|
+
if (paramObj.required === true) requiredNames.add(paramName);
|
|
9823
|
+
}
|
|
9824
|
+
const requestBody = !isRefObject(operation.requestBody) ? operation.requestBody : void 0;
|
|
9825
|
+
if (requestBody && typeof requestBody === "object") {
|
|
9826
|
+
const reqSchema = requestBody.content?.["application/json"]?.schema || getSchemaFromContent(requestBody.content);
|
|
9827
|
+
if (isSchemaObject(reqSchema)) {
|
|
9828
|
+
const bodySchema = openApiSchemaToJsonSchema(reqSchema);
|
|
9829
|
+
if (typeof requestBody.description === "string") {
|
|
9830
|
+
bodySchema.description = requestBody.description;
|
|
9831
|
+
}
|
|
9832
|
+
const contentTypes = Object.keys(requestBody.content || {});
|
|
9833
|
+
if (contentTypes.length > 0) {
|
|
9834
|
+
bodySchema["x-content-types"] = contentTypes;
|
|
9835
|
+
}
|
|
9836
|
+
inputSchema.properties.requestBody = bodySchema;
|
|
9837
|
+
if (requestBody.required === true) {
|
|
9838
|
+
requiredNames.add("requestBody");
|
|
9839
|
+
}
|
|
9840
|
+
}
|
|
9841
|
+
}
|
|
9842
|
+
if (requiredNames.size > 0) {
|
|
9843
|
+
inputSchema.required = Array.from(requiredNames);
|
|
9844
|
+
}
|
|
9845
|
+
const securityRequirements = Array.isArray(operation.security) ? operation.security : globalSecurity;
|
|
9846
|
+
mapped.push({
|
|
9847
|
+
sourceToolName: tool.name,
|
|
9848
|
+
mcpToolDefinition: {
|
|
9849
|
+
name: toolName,
|
|
9850
|
+
description: toolDescription,
|
|
9851
|
+
inputSchema,
|
|
9852
|
+
outputSchema: buildOutputSchema(operation)
|
|
9853
|
+
},
|
|
9854
|
+
apiCallDetails: {
|
|
9855
|
+
method: method.toUpperCase(),
|
|
9856
|
+
pathTemplate: pathValue,
|
|
9857
|
+
serverUrl: baseServerUrl,
|
|
9858
|
+
parameters: allParameters,
|
|
9859
|
+
requestBody,
|
|
9860
|
+
securityRequirements,
|
|
9861
|
+
securitySchemes,
|
|
9862
|
+
apiToolConfig
|
|
9863
|
+
}
|
|
9864
|
+
});
|
|
9865
|
+
}
|
|
9866
|
+
}
|
|
9867
|
+
return mapped;
|
|
9868
|
+
}
|
|
9869
|
+
function validateParameterValue(value, paramDef) {
|
|
9870
|
+
const schema = paramDef.schema;
|
|
9871
|
+
if (!schema || typeof schema !== "object") return null;
|
|
9872
|
+
const schemaType = schema.type;
|
|
9873
|
+
if (schemaType === "integer") {
|
|
9874
|
+
if (typeof value !== "number" || !Number.isInteger(value)) {
|
|
9875
|
+
return "must be an integer";
|
|
9876
|
+
}
|
|
9877
|
+
} else if (schemaType === "number") {
|
|
9878
|
+
if (typeof value !== "number") {
|
|
9879
|
+
return `expected number, got ${typeof value}`;
|
|
9880
|
+
}
|
|
9881
|
+
} else if (schemaType === "boolean") {
|
|
9882
|
+
if (typeof value !== "boolean") {
|
|
9883
|
+
return `expected boolean, got ${typeof value}`;
|
|
9884
|
+
}
|
|
9885
|
+
} else if (schemaType === "string") {
|
|
9886
|
+
if (typeof value !== "string") {
|
|
9887
|
+
return `expected string, got ${typeof value}`;
|
|
9888
|
+
}
|
|
9889
|
+
} else if (schemaType === "array") {
|
|
9890
|
+
if (!Array.isArray(value)) {
|
|
9891
|
+
return `expected array, got ${typeof value}`;
|
|
9892
|
+
}
|
|
9893
|
+
} else if (schemaType === "object") {
|
|
9894
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
9895
|
+
return `expected object, got ${Array.isArray(value) ? "array" : typeof value}`;
|
|
9896
|
+
}
|
|
9897
|
+
}
|
|
9898
|
+
if (schema.minimum !== void 0 && typeof value === "number" && value < schema.minimum) {
|
|
9899
|
+
return `must be >= ${schema.minimum}`;
|
|
9900
|
+
}
|
|
9901
|
+
if (schema.maximum !== void 0 && typeof value === "number" && value > schema.maximum) {
|
|
9902
|
+
return `must be <= ${schema.maximum}`;
|
|
9903
|
+
}
|
|
9904
|
+
if (schema.minLength !== void 0 && typeof value === "string" && value.length < schema.minLength) {
|
|
9905
|
+
return `length must be >= ${schema.minLength}`;
|
|
9906
|
+
}
|
|
9907
|
+
if (schema.maxLength !== void 0 && typeof value === "string" && value.length > schema.maxLength) {
|
|
9908
|
+
return `length must be <= ${schema.maxLength}`;
|
|
9909
|
+
}
|
|
9910
|
+
if (Array.isArray(schema.enum) && !schema.enum.includes(value)) {
|
|
9911
|
+
return `must be one of: ${schema.enum.join(", ")}`;
|
|
9912
|
+
}
|
|
9913
|
+
return null;
|
|
9914
|
+
}
|
|
9915
|
+
function applySecurityToRequest(details, headers, queryParams) {
|
|
9916
|
+
const requirements = details.securityRequirements;
|
|
9917
|
+
if (!requirements || requirements.length === 0) {
|
|
9918
|
+
return;
|
|
9919
|
+
}
|
|
9920
|
+
const schemes = details.securitySchemes || {};
|
|
9921
|
+
const cfg = details.apiToolConfig;
|
|
9922
|
+
const tryResolveCredential = (schemeName) => {
|
|
9923
|
+
if (cfg.securityCredentials[schemeName]) {
|
|
9924
|
+
return cfg.securityCredentials[schemeName];
|
|
9925
|
+
}
|
|
9926
|
+
if (cfg.securitySchemeName && cfg.securitySchemeName !== schemeName) {
|
|
9927
|
+
return void 0;
|
|
9928
|
+
}
|
|
9929
|
+
return cfg.apiKey;
|
|
9930
|
+
};
|
|
9931
|
+
for (const requirement of requirements) {
|
|
9932
|
+
const schemeNames = Object.keys(requirement);
|
|
9933
|
+
if (schemeNames.length === 0) return;
|
|
9934
|
+
const tempHeaders = { ...headers };
|
|
9935
|
+
const tempQuery = new URLSearchParams(queryParams);
|
|
9936
|
+
let satisfied = true;
|
|
9937
|
+
for (const schemeName of schemeNames) {
|
|
9938
|
+
const scheme = schemes[schemeName];
|
|
9939
|
+
if (!scheme || typeof scheme !== "object") {
|
|
9940
|
+
satisfied = false;
|
|
9941
|
+
break;
|
|
9942
|
+
}
|
|
9943
|
+
const credential = tryResolveCredential(schemeName);
|
|
9944
|
+
if (!credential) {
|
|
9945
|
+
satisfied = false;
|
|
9946
|
+
break;
|
|
9947
|
+
}
|
|
9948
|
+
switch (scheme.type) {
|
|
9949
|
+
case "apiKey":
|
|
9950
|
+
if (scheme.in === "header") {
|
|
9951
|
+
tempHeaders[String(scheme.name)] = credential;
|
|
9952
|
+
} else if (scheme.in === "query") {
|
|
9953
|
+
tempQuery.set(String(scheme.name), credential);
|
|
9954
|
+
} else if (scheme.in === "cookie") {
|
|
9955
|
+
const cookieName = String(scheme.name);
|
|
9956
|
+
const existing = tempHeaders["Cookie"];
|
|
9957
|
+
tempHeaders["Cookie"] = existing ? `${existing}; ${cookieName}=${credential}` : `${cookieName}=${credential}`;
|
|
9958
|
+
} else {
|
|
9959
|
+
satisfied = false;
|
|
9960
|
+
}
|
|
9961
|
+
break;
|
|
9962
|
+
case "http":
|
|
9963
|
+
if (typeof scheme.scheme !== "string") {
|
|
9964
|
+
satisfied = false;
|
|
9965
|
+
break;
|
|
9966
|
+
}
|
|
9967
|
+
if (scheme.scheme.toLowerCase() === "bearer") {
|
|
9968
|
+
tempHeaders["Authorization"] = `Bearer ${credential}`;
|
|
9969
|
+
} else if (scheme.scheme.toLowerCase() === "basic") {
|
|
9970
|
+
tempHeaders["Authorization"] = `Basic ${Buffer.from(credential).toString("base64")}`;
|
|
9971
|
+
} else {
|
|
9972
|
+
satisfied = false;
|
|
9973
|
+
}
|
|
9974
|
+
break;
|
|
9975
|
+
case "oauth2":
|
|
9976
|
+
case "openIdConnect":
|
|
9977
|
+
tempHeaders["Authorization"] = `Bearer ${credential}`;
|
|
9978
|
+
break;
|
|
9979
|
+
default:
|
|
9980
|
+
satisfied = false;
|
|
9981
|
+
}
|
|
9982
|
+
if (!satisfied) {
|
|
9983
|
+
break;
|
|
9984
|
+
}
|
|
9985
|
+
}
|
|
9986
|
+
if (satisfied) {
|
|
9987
|
+
Object.assign(headers, tempHeaders);
|
|
9988
|
+
queryParams.forEach((_value, key) => queryParams.delete(key));
|
|
9989
|
+
tempQuery.forEach((value, key) => queryParams.append(key, value));
|
|
9990
|
+
return;
|
|
9991
|
+
}
|
|
9992
|
+
}
|
|
9993
|
+
}
|
|
9994
|
+
function responseBodyToString(body) {
|
|
9995
|
+
if (typeof body === "string") return body;
|
|
9996
|
+
try {
|
|
9997
|
+
return JSON.stringify(body);
|
|
9998
|
+
} catch {
|
|
9999
|
+
return String(body);
|
|
10000
|
+
}
|
|
10001
|
+
}
|
|
10002
|
+
async function executeMappedApiTool(mappedTool, args) {
|
|
10003
|
+
const { apiCallDetails } = mappedTool;
|
|
10004
|
+
const { method, pathTemplate, serverUrl, parameters, requestBody, apiToolConfig } = apiCallDetails;
|
|
10005
|
+
const urlPath = pathTemplate.replace(/{([^}]+)}/g, (_token, rawName) => {
|
|
10006
|
+
const value = args[rawName];
|
|
10007
|
+
if (value === void 0 || value === null) {
|
|
10008
|
+
return `{${rawName}}`;
|
|
10009
|
+
}
|
|
10010
|
+
return encodeURIComponent(String(value));
|
|
10011
|
+
});
|
|
10012
|
+
if (urlPath.includes("{") || urlPath.includes("}")) {
|
|
10013
|
+
throw new Error(`Missing required path parameters for ${method} ${pathTemplate}`);
|
|
10014
|
+
}
|
|
10015
|
+
let endpoint;
|
|
10016
|
+
try {
|
|
10017
|
+
endpoint = new URL(`${serverUrl}${urlPath}`);
|
|
10018
|
+
} catch (error) {
|
|
10019
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
10020
|
+
throw new Error(
|
|
10021
|
+
`Failed to construct endpoint URL for API tool '${mappedTool.sourceToolName}' operation '${mappedTool.mcpToolDefinition.name}' (${method} ${pathTemplate}) with serverUrl '${serverUrl}': ${errorMessage}`
|
|
10022
|
+
);
|
|
10023
|
+
}
|
|
10024
|
+
const queryParams = new URLSearchParams(endpoint.search);
|
|
10025
|
+
const headers = { ...apiToolConfig.customHeaders };
|
|
10026
|
+
let requestBodyValue;
|
|
10027
|
+
for (const param of parameters) {
|
|
10028
|
+
const name = String(param.name || "");
|
|
10029
|
+
if (!name) continue;
|
|
10030
|
+
const value = args[name];
|
|
10031
|
+
if (value === void 0 || value === null) {
|
|
10032
|
+
if (param.required) {
|
|
10033
|
+
throw new Error(`Missing required parameter: ${name}`);
|
|
10034
|
+
}
|
|
10035
|
+
continue;
|
|
10036
|
+
}
|
|
10037
|
+
const validationError = validateParameterValue(value, param);
|
|
10038
|
+
if (validationError) {
|
|
10039
|
+
throw new Error(`Parameter '${name}' ${validationError}`);
|
|
10040
|
+
}
|
|
10041
|
+
switch (param.in) {
|
|
10042
|
+
case "query":
|
|
10043
|
+
if (Array.isArray(value)) {
|
|
10044
|
+
for (const item of value) {
|
|
10045
|
+
queryParams.append(name, String(item));
|
|
10046
|
+
}
|
|
10047
|
+
} else {
|
|
10048
|
+
queryParams.set(name, String(value));
|
|
10049
|
+
}
|
|
10050
|
+
break;
|
|
10051
|
+
case "header":
|
|
10052
|
+
headers[name] = String(value);
|
|
10053
|
+
break;
|
|
10054
|
+
case "path":
|
|
10055
|
+
break;
|
|
10056
|
+
case "cookie": {
|
|
10057
|
+
const existing = headers["Cookie"];
|
|
10058
|
+
headers["Cookie"] = existing ? `${existing}; ${name}=${String(value)}` : `${name}=${String(value)}`;
|
|
10059
|
+
break;
|
|
10060
|
+
}
|
|
10061
|
+
default:
|
|
10062
|
+
break;
|
|
10063
|
+
}
|
|
10064
|
+
}
|
|
10065
|
+
if (requestBody && requestBody.required && args.requestBody === void 0) {
|
|
10066
|
+
throw new Error("Missing required requestBody parameter");
|
|
10067
|
+
}
|
|
10068
|
+
if (args.requestBody !== void 0) {
|
|
10069
|
+
requestBodyValue = args.requestBody;
|
|
10070
|
+
if (!headers["Content-Type"]) {
|
|
10071
|
+
headers["Content-Type"] = "application/json";
|
|
10072
|
+
}
|
|
10073
|
+
}
|
|
10074
|
+
if (!apiToolConfig.disableXMcp) {
|
|
10075
|
+
headers["X-MCP"] = "1";
|
|
10076
|
+
}
|
|
10077
|
+
applySecurityToRequest(apiCallDetails, headers, queryParams);
|
|
10078
|
+
endpoint.search = queryParams.toString();
|
|
10079
|
+
const controller = new AbortController();
|
|
10080
|
+
const timeout = setTimeout(() => controller.abort(), apiToolConfig.requestTimeoutMs);
|
|
10081
|
+
try {
|
|
10082
|
+
const response = await fetch(endpoint.toString(), {
|
|
10083
|
+
method,
|
|
10084
|
+
headers,
|
|
10085
|
+
body: requestBodyValue === void 0 ? void 0 : headers["Content-Type"]?.includes("application/json") ? JSON.stringify(requestBodyValue) : String(requestBodyValue),
|
|
10086
|
+
signal: controller.signal
|
|
10087
|
+
});
|
|
10088
|
+
const raw = await response.text();
|
|
10089
|
+
let body = raw;
|
|
10090
|
+
const contentType = response.headers.get("content-type") || "";
|
|
10091
|
+
if (contentType.includes("json") && raw.trim().length > 0) {
|
|
10092
|
+
try {
|
|
10093
|
+
body = JSON.parse(raw);
|
|
10094
|
+
} catch {
|
|
10095
|
+
body = raw;
|
|
10096
|
+
}
|
|
10097
|
+
} else if (raw.trim().length > 0) {
|
|
10098
|
+
try {
|
|
10099
|
+
body = JSON.parse(raw);
|
|
10100
|
+
} catch {
|
|
10101
|
+
body = raw;
|
|
10102
|
+
}
|
|
10103
|
+
} else {
|
|
10104
|
+
body = null;
|
|
10105
|
+
}
|
|
10106
|
+
if (response.ok) {
|
|
10107
|
+
return body;
|
|
10108
|
+
}
|
|
10109
|
+
throw new Error(`API Error ${response.status}: ${responseBodyToString(body)}`);
|
|
10110
|
+
} catch (error) {
|
|
10111
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
10112
|
+
throw new Error(`API request timed out after ${apiToolConfig.requestTimeoutMs}ms`);
|
|
10113
|
+
}
|
|
10114
|
+
throw error;
|
|
10115
|
+
} finally {
|
|
10116
|
+
clearTimeout(timeout);
|
|
10117
|
+
}
|
|
10118
|
+
}
|
|
10119
|
+
var import_promises2, import_path4, import_js_yaml, import_swagger_parser, import_deepmerge, import_jsonpath_plus, import_minimatch, HTTP_METHODS, ApiToolRegistry;
|
|
10120
|
+
var init_api_tool_executor = __esm({
|
|
10121
|
+
"src/providers/api-tool-executor.ts"() {
|
|
10122
|
+
"use strict";
|
|
10123
|
+
import_promises2 = __toESM(require("fs/promises"));
|
|
10124
|
+
import_path4 = __toESM(require("path"));
|
|
10125
|
+
import_js_yaml = __toESM(require("js-yaml"));
|
|
10126
|
+
import_swagger_parser = __toESM(require("@apidevtools/swagger-parser"));
|
|
10127
|
+
import_deepmerge = __toESM(require("deepmerge"));
|
|
10128
|
+
import_jsonpath_plus = require("jsonpath-plus");
|
|
10129
|
+
import_minimatch = require("minimatch");
|
|
10130
|
+
init_logger();
|
|
10131
|
+
HTTP_METHODS = /* @__PURE__ */ new Set(["get", "put", "post", "delete", "options", "head", "patch", "trace"]);
|
|
10132
|
+
ApiToolRegistry = class {
|
|
10133
|
+
bundleCache = /* @__PURE__ */ new Map();
|
|
10134
|
+
operationCache = /* @__PURE__ */ new Map();
|
|
10135
|
+
registerMappedTools(sourceToolName, mappedTools) {
|
|
10136
|
+
this.bundleCache.set(sourceToolName, mappedTools);
|
|
10137
|
+
for (const mapped of mappedTools) {
|
|
10138
|
+
const existing = this.operationCache.get(mapped.mcpToolDefinition.name);
|
|
10139
|
+
if (existing && existing.sourceToolName !== sourceToolName) {
|
|
10140
|
+
logger.warn(
|
|
10141
|
+
`[ApiToolExecutor] Tool name collision: '${mapped.mcpToolDefinition.name}' from '${sourceToolName}' overrides '${existing.sourceToolName}'. Use namePrefix to avoid collisions.`
|
|
10142
|
+
);
|
|
10143
|
+
}
|
|
10144
|
+
this.operationCache.set(mapped.mcpToolDefinition.name, mapped);
|
|
10145
|
+
}
|
|
10146
|
+
}
|
|
10147
|
+
async ensureBundle(sourceToolName, tool) {
|
|
10148
|
+
const cached = this.bundleCache.get(sourceToolName);
|
|
10149
|
+
if (cached) return cached;
|
|
10150
|
+
if (!isApiToolDefinition(tool)) {
|
|
10151
|
+
return [];
|
|
10152
|
+
}
|
|
10153
|
+
const openapi = await loadOpenApiDocument(tool);
|
|
10154
|
+
const mapped = mapOpenApiToTools(openapi, tool);
|
|
10155
|
+
this.registerMappedTools(sourceToolName, mapped);
|
|
10156
|
+
return mapped;
|
|
10157
|
+
}
|
|
10158
|
+
async ensureAll(toolMap) {
|
|
10159
|
+
for (const [sourceToolName, tool] of toolMap.entries()) {
|
|
10160
|
+
if (!isApiToolDefinition(tool)) continue;
|
|
10161
|
+
await this.ensureBundle(sourceToolName, tool);
|
|
10162
|
+
}
|
|
10163
|
+
}
|
|
10164
|
+
async listMappedTools(toolMap) {
|
|
10165
|
+
await this.ensureAll(toolMap);
|
|
10166
|
+
return Array.from(this.operationCache.values());
|
|
10167
|
+
}
|
|
10168
|
+
async getMappedTool(toolName, toolMap) {
|
|
10169
|
+
const cached = this.operationCache.get(toolName);
|
|
10170
|
+
if (cached) return cached;
|
|
10171
|
+
for (const [sourceToolName, tool] of toolMap.entries()) {
|
|
10172
|
+
if (!isApiToolDefinition(tool)) continue;
|
|
10173
|
+
await this.ensureBundle(sourceToolName, tool);
|
|
10174
|
+
const resolved = this.operationCache.get(toolName);
|
|
10175
|
+
if (resolved) return resolved;
|
|
10176
|
+
}
|
|
10177
|
+
return void 0;
|
|
10178
|
+
}
|
|
10179
|
+
};
|
|
10180
|
+
}
|
|
10181
|
+
});
|
|
10182
|
+
|
|
9394
10183
|
// src/providers/custom-tool-executor.ts
|
|
9395
10184
|
var import_ajv, CustomToolExecutor;
|
|
9396
10185
|
var init_custom_tool_executor = __esm({
|
|
@@ -9401,11 +10190,13 @@ var init_custom_tool_executor = __esm({
|
|
|
9401
10190
|
init_logger();
|
|
9402
10191
|
init_command_executor();
|
|
9403
10192
|
import_ajv = __toESM(require("ajv"));
|
|
10193
|
+
init_api_tool_executor();
|
|
9404
10194
|
CustomToolExecutor = class {
|
|
9405
10195
|
liquid;
|
|
9406
10196
|
sandbox;
|
|
9407
10197
|
tools;
|
|
9408
10198
|
ajv;
|
|
10199
|
+
apiToolRegistry;
|
|
9409
10200
|
constructor(tools) {
|
|
9410
10201
|
this.liquid = createExtendedLiquid({
|
|
9411
10202
|
cache: false,
|
|
@@ -9413,7 +10204,8 @@ var init_custom_tool_executor = __esm({
|
|
|
9413
10204
|
strictVariables: false
|
|
9414
10205
|
});
|
|
9415
10206
|
this.tools = new Map(Object.entries(tools || {}));
|
|
9416
|
-
this.ajv = new import_ajv.default({ allErrors: true, verbose: true });
|
|
10207
|
+
this.ajv = new import_ajv.default({ allErrors: true, verbose: true, strict: false });
|
|
10208
|
+
this.apiToolRegistry = new ApiToolRegistry();
|
|
9417
10209
|
}
|
|
9418
10210
|
/**
|
|
9419
10211
|
* Register a custom tool
|
|
@@ -9422,6 +10214,13 @@ var init_custom_tool_executor = __esm({
|
|
|
9422
10214
|
if (!tool.name) {
|
|
9423
10215
|
throw new Error("Tool must have a name");
|
|
9424
10216
|
}
|
|
10217
|
+
if (isApiToolDefinition(tool)) {
|
|
10218
|
+
if (!tool.spec) {
|
|
10219
|
+
throw new Error(`API tool '${tool.name}' must define 'spec'`);
|
|
10220
|
+
}
|
|
10221
|
+
} else if (!tool.exec) {
|
|
10222
|
+
throw new Error(`Tool '${tool.name}' must define 'exec' (or set type: 'api')`);
|
|
10223
|
+
}
|
|
9425
10224
|
this.tools.set(tool.name, tool);
|
|
9426
10225
|
}
|
|
9427
10226
|
/**
|
|
@@ -9464,12 +10263,45 @@ var init_custom_tool_executor = __esm({
|
|
|
9464
10263
|
throw new Error(`Input validation failed for tool '${tool.name}': ${errors}`);
|
|
9465
10264
|
}
|
|
9466
10265
|
}
|
|
10266
|
+
/**
|
|
10267
|
+
* Validate input against a JSON schema object
|
|
10268
|
+
*/
|
|
10269
|
+
validateInputSchema(toolName, schema, input) {
|
|
10270
|
+
if (!schema) {
|
|
10271
|
+
return;
|
|
10272
|
+
}
|
|
10273
|
+
const validate = this.ajv.compile(schema);
|
|
10274
|
+
const valid = validate(input);
|
|
10275
|
+
if (!valid) {
|
|
10276
|
+
const errors = validate.errors?.map((err) => {
|
|
10277
|
+
if (err.instancePath) {
|
|
10278
|
+
return `${err.instancePath}: ${err.message}`;
|
|
10279
|
+
}
|
|
10280
|
+
return err.message;
|
|
10281
|
+
}).join(", ");
|
|
10282
|
+
throw new Error(`Input validation failed for tool '${toolName}': ${errors}`);
|
|
10283
|
+
}
|
|
10284
|
+
}
|
|
9467
10285
|
/**
|
|
9468
10286
|
* Execute a custom tool
|
|
9469
10287
|
*/
|
|
9470
10288
|
async execute(toolName, args, context2) {
|
|
9471
10289
|
const tool = this.tools.get(toolName);
|
|
10290
|
+
if (tool && isApiToolDefinition(tool)) {
|
|
10291
|
+
throw new Error(
|
|
10292
|
+
`Tool '${toolName}' is an API bundle. Call one of its generated operations instead.`
|
|
10293
|
+
);
|
|
10294
|
+
}
|
|
9472
10295
|
if (!tool) {
|
|
10296
|
+
const apiMappedTool = await this.apiToolRegistry.getMappedTool(toolName, this.tools);
|
|
10297
|
+
if (apiMappedTool) {
|
|
10298
|
+
this.validateInputSchema(
|
|
10299
|
+
toolName,
|
|
10300
|
+
apiMappedTool.mcpToolDefinition.inputSchema,
|
|
10301
|
+
args
|
|
10302
|
+
);
|
|
10303
|
+
return await executeMappedApiTool(apiMappedTool, args);
|
|
10304
|
+
}
|
|
9473
10305
|
throw new Error(`Tool not found: ${toolName}`);
|
|
9474
10306
|
}
|
|
9475
10307
|
this.validateInput(tool, args);
|
|
@@ -9478,6 +10310,9 @@ var init_custom_tool_executor = __esm({
|
|
|
9478
10310
|
args,
|
|
9479
10311
|
input: args
|
|
9480
10312
|
};
|
|
10313
|
+
if (!tool.exec) {
|
|
10314
|
+
throw new Error(`Tool '${toolName}' is missing exec command`);
|
|
10315
|
+
}
|
|
9481
10316
|
const command = await this.liquid.parseAndRender(tool.exec, templateContext);
|
|
9482
10317
|
let stdin;
|
|
9483
10318
|
if (tool.stdin) {
|
|
@@ -9537,6 +10372,50 @@ var init_custom_tool_executor = __esm({
|
|
|
9537
10372
|
}
|
|
9538
10373
|
return output;
|
|
9539
10374
|
}
|
|
10375
|
+
/**
|
|
10376
|
+
* Check if a tool exists (direct or API-generated)
|
|
10377
|
+
*/
|
|
10378
|
+
async hasTool(toolName) {
|
|
10379
|
+
if (this.tools.has(toolName)) {
|
|
10380
|
+
const tool = this.tools.get(toolName);
|
|
10381
|
+
return !isApiToolDefinition(tool);
|
|
10382
|
+
}
|
|
10383
|
+
const apiMappedTool = await this.apiToolRegistry.getMappedTool(toolName, this.tools);
|
|
10384
|
+
return Boolean(apiMappedTool);
|
|
10385
|
+
}
|
|
10386
|
+
/**
|
|
10387
|
+
* Get all available tool names, including API-generated operations
|
|
10388
|
+
*/
|
|
10389
|
+
async getToolNames() {
|
|
10390
|
+
const names = [];
|
|
10391
|
+
for (const tool of this.tools.values()) {
|
|
10392
|
+
if (!isApiToolDefinition(tool)) {
|
|
10393
|
+
names.push(tool.name);
|
|
10394
|
+
}
|
|
10395
|
+
}
|
|
10396
|
+
const apiTools = await this.apiToolRegistry.listMappedTools(this.tools);
|
|
10397
|
+
for (const mapped of apiTools) {
|
|
10398
|
+
names.push(mapped.mcpToolDefinition.name);
|
|
10399
|
+
}
|
|
10400
|
+
return names;
|
|
10401
|
+
}
|
|
10402
|
+
/**
|
|
10403
|
+
* List MCP-compatible tool definitions including API-generated operations
|
|
10404
|
+
*/
|
|
10405
|
+
async listMcpTools() {
|
|
10406
|
+
const directTools = this.getTools().filter((tool) => !isApiToolDefinition(tool)).map((tool) => ({
|
|
10407
|
+
name: tool.name,
|
|
10408
|
+
description: tool.description,
|
|
10409
|
+
inputSchema: tool.inputSchema
|
|
10410
|
+
}));
|
|
10411
|
+
const apiTools = await this.apiToolRegistry.listMappedTools(this.tools);
|
|
10412
|
+
const mappedApiTools = apiTools.map((tool) => ({
|
|
10413
|
+
name: tool.mcpToolDefinition.name,
|
|
10414
|
+
description: tool.mcpToolDefinition.description,
|
|
10415
|
+
inputSchema: tool.mcpToolDefinition.inputSchema
|
|
10416
|
+
}));
|
|
10417
|
+
return [...directTools, ...mappedApiTools];
|
|
10418
|
+
}
|
|
9540
10419
|
/**
|
|
9541
10420
|
* Apply JavaScript transform to output
|
|
9542
10421
|
*/
|
|
@@ -9564,7 +10443,7 @@ var init_custom_tool_executor = __esm({
|
|
|
9564
10443
|
* Convert custom tools to MCP tool format
|
|
9565
10444
|
*/
|
|
9566
10445
|
toMcpTools() {
|
|
9567
|
-
return Array.from(this.tools.values()).map((tool) => ({
|
|
10446
|
+
return Array.from(this.tools.values()).filter((tool) => !isApiToolDefinition(tool)).map((tool) => ({
|
|
9568
10447
|
name: tool.name,
|
|
9569
10448
|
description: tool.description,
|
|
9570
10449
|
inputSchema: tool.inputSchema,
|
|
@@ -9582,12 +10461,12 @@ var workflow_registry_exports = {};
|
|
|
9582
10461
|
__export(workflow_registry_exports, {
|
|
9583
10462
|
WorkflowRegistry: () => WorkflowRegistry
|
|
9584
10463
|
});
|
|
9585
|
-
var import_fs3,
|
|
10464
|
+
var import_fs3, path9, yaml, import_ajv2, import_ajv_formats, WorkflowRegistry;
|
|
9586
10465
|
var init_workflow_registry = __esm({
|
|
9587
10466
|
"src/workflow-registry.ts"() {
|
|
9588
10467
|
"use strict";
|
|
9589
10468
|
import_fs3 = require("fs");
|
|
9590
|
-
|
|
10469
|
+
path9 = __toESM(require("path"));
|
|
9591
10470
|
yaml = __toESM(require("js-yaml"));
|
|
9592
10471
|
init_logger();
|
|
9593
10472
|
init_dependency_resolver();
|
|
@@ -9923,9 +10802,9 @@ var init_workflow_registry = __esm({
|
|
|
9923
10802
|
const importBasePath = new URL(".", resolvedUrl).toString();
|
|
9924
10803
|
return { content: await response.text(), resolvedSource: resolvedUrl, importBasePath };
|
|
9925
10804
|
}
|
|
9926
|
-
const filePath =
|
|
10805
|
+
const filePath = path9.isAbsolute(source) ? source : path9.resolve(basePath || process.cwd(), source);
|
|
9927
10806
|
const content = await import_fs3.promises.readFile(filePath, "utf-8");
|
|
9928
|
-
return { content, resolvedSource: filePath, importBasePath:
|
|
10807
|
+
return { content, resolvedSource: filePath, importBasePath: path9.dirname(filePath) };
|
|
9929
10808
|
}
|
|
9930
10809
|
/**
|
|
9931
10810
|
* Parse workflow content (YAML or JSON)
|
|
@@ -10682,12 +11561,12 @@ var init_config_merger = __esm({
|
|
|
10682
11561
|
});
|
|
10683
11562
|
|
|
10684
11563
|
// src/utils/config-loader.ts
|
|
10685
|
-
var
|
|
11564
|
+
var fs8, path10, yaml2, ConfigLoader;
|
|
10686
11565
|
var init_config_loader = __esm({
|
|
10687
11566
|
"src/utils/config-loader.ts"() {
|
|
10688
11567
|
"use strict";
|
|
10689
|
-
|
|
10690
|
-
|
|
11568
|
+
fs8 = __toESM(require("fs"));
|
|
11569
|
+
path10 = __toESM(require("path"));
|
|
10691
11570
|
yaml2 = __toESM(require("js-yaml"));
|
|
10692
11571
|
ConfigLoader = class {
|
|
10693
11572
|
constructor(options = {}) {
|
|
@@ -10707,6 +11586,19 @@ var init_config_loader = __esm({
|
|
|
10707
11586
|
}
|
|
10708
11587
|
cache = /* @__PURE__ */ new Map();
|
|
10709
11588
|
loadedConfigs = /* @__PURE__ */ new Set();
|
|
11589
|
+
/**
|
|
11590
|
+
* Annotate tool definitions with their source base directory.
|
|
11591
|
+
* This is used at runtime to resolve relative tool assets (e.g., API specs).
|
|
11592
|
+
*/
|
|
11593
|
+
annotateToolsBaseDir(config, baseDir) {
|
|
11594
|
+
if (!config.tools || typeof config.tools !== "object") {
|
|
11595
|
+
return;
|
|
11596
|
+
}
|
|
11597
|
+
for (const tool of Object.values(config.tools)) {
|
|
11598
|
+
if (!tool || typeof tool !== "object") continue;
|
|
11599
|
+
tool.__baseDir = tool.__baseDir || baseDir;
|
|
11600
|
+
}
|
|
11601
|
+
}
|
|
10710
11602
|
/**
|
|
10711
11603
|
* Determine the source type from a string
|
|
10712
11604
|
*/
|
|
@@ -10768,7 +11660,7 @@ var init_config_loader = __esm({
|
|
|
10768
11660
|
return source.toLowerCase();
|
|
10769
11661
|
case "local" /* LOCAL */:
|
|
10770
11662
|
const basePath = this.options.baseDir || process.cwd();
|
|
10771
|
-
return
|
|
11663
|
+
return path10.resolve(basePath, source);
|
|
10772
11664
|
default:
|
|
10773
11665
|
return source;
|
|
10774
11666
|
}
|
|
@@ -10778,10 +11670,10 @@ var init_config_loader = __esm({
|
|
|
10778
11670
|
*/
|
|
10779
11671
|
async fetchLocalConfig(filePath) {
|
|
10780
11672
|
const basePath = this.options.baseDir || process.cwd();
|
|
10781
|
-
const resolvedPath =
|
|
11673
|
+
const resolvedPath = path10.resolve(basePath, filePath);
|
|
10782
11674
|
this.validateLocalPath(resolvedPath);
|
|
10783
11675
|
try {
|
|
10784
|
-
const content =
|
|
11676
|
+
const content = fs8.readFileSync(resolvedPath, "utf8");
|
|
10785
11677
|
const config = yaml2.load(content);
|
|
10786
11678
|
if (!config || typeof config !== "object") {
|
|
10787
11679
|
throw new Error(`Invalid YAML in configuration file: ${resolvedPath}`);
|
|
@@ -10791,8 +11683,9 @@ var init_config_loader = __esm({
|
|
|
10791
11683
|
config.extends = Array.isArray(inc) ? inc : [inc];
|
|
10792
11684
|
delete config.include;
|
|
10793
11685
|
}
|
|
11686
|
+
this.annotateToolsBaseDir(config, path10.dirname(resolvedPath));
|
|
10794
11687
|
const previousBaseDir = this.options.baseDir;
|
|
10795
|
-
this.options.baseDir =
|
|
11688
|
+
this.options.baseDir = path10.dirname(resolvedPath);
|
|
10796
11689
|
try {
|
|
10797
11690
|
if (config.extends) {
|
|
10798
11691
|
const processedConfig = await this.processExtends(config);
|
|
@@ -10848,6 +11741,12 @@ var init_config_loader = __esm({
|
|
|
10848
11741
|
if (!config || typeof config !== "object") {
|
|
10849
11742
|
throw new Error(`Invalid YAML in remote configuration: ${url}`);
|
|
10850
11743
|
}
|
|
11744
|
+
try {
|
|
11745
|
+
const parsed = new URL(url);
|
|
11746
|
+
const baseUrl = new URL(".", parsed).toString();
|
|
11747
|
+
this.annotateToolsBaseDir(config, baseUrl);
|
|
11748
|
+
} catch {
|
|
11749
|
+
}
|
|
10851
11750
|
this.cache.set(url, {
|
|
10852
11751
|
config,
|
|
10853
11752
|
timestamp: Date.now(),
|
|
@@ -10875,25 +11774,25 @@ var init_config_loader = __esm({
|
|
|
10875
11774
|
async fetchDefaultConfig() {
|
|
10876
11775
|
const possiblePaths = [
|
|
10877
11776
|
// Only support new non-dot filename
|
|
10878
|
-
|
|
11777
|
+
path10.join(__dirname, "defaults", "visor.yaml"),
|
|
10879
11778
|
// When running from source
|
|
10880
|
-
|
|
11779
|
+
path10.join(__dirname, "..", "..", "defaults", "visor.yaml"),
|
|
10881
11780
|
// Try via package root
|
|
10882
|
-
this.findPackageRoot() ?
|
|
11781
|
+
this.findPackageRoot() ? path10.join(this.findPackageRoot(), "defaults", "visor.yaml") : "",
|
|
10883
11782
|
// GitHub Action environment variable
|
|
10884
|
-
process.env.GITHUB_ACTION_PATH ?
|
|
10885
|
-
process.env.GITHUB_ACTION_PATH ?
|
|
11783
|
+
process.env.GITHUB_ACTION_PATH ? path10.join(process.env.GITHUB_ACTION_PATH, "defaults", "visor.yaml") : "",
|
|
11784
|
+
process.env.GITHUB_ACTION_PATH ? path10.join(process.env.GITHUB_ACTION_PATH, "dist", "defaults", "visor.yaml") : ""
|
|
10886
11785
|
].filter((p) => p);
|
|
10887
11786
|
let defaultConfigPath;
|
|
10888
11787
|
for (const possiblePath of possiblePaths) {
|
|
10889
|
-
if (
|
|
11788
|
+
if (fs8.existsSync(possiblePath)) {
|
|
10890
11789
|
defaultConfigPath = possiblePath;
|
|
10891
11790
|
break;
|
|
10892
11791
|
}
|
|
10893
11792
|
}
|
|
10894
11793
|
if (defaultConfigPath) {
|
|
10895
11794
|
console.error(`\u{1F4E6} Loading bundled default configuration from ${defaultConfigPath}`);
|
|
10896
|
-
const content =
|
|
11795
|
+
const content = fs8.readFileSync(defaultConfigPath, "utf8");
|
|
10897
11796
|
let config = yaml2.load(content);
|
|
10898
11797
|
if (!config || typeof config !== "object") {
|
|
10899
11798
|
throw new Error("Invalid default configuration");
|
|
@@ -10907,7 +11806,7 @@ var init_config_loader = __esm({
|
|
|
10907
11806
|
if (config.extends) {
|
|
10908
11807
|
const previousBaseDir = this.options.baseDir;
|
|
10909
11808
|
try {
|
|
10910
|
-
this.options.baseDir =
|
|
11809
|
+
this.options.baseDir = path10.dirname(defaultConfigPath);
|
|
10911
11810
|
return await this.processExtends(config);
|
|
10912
11811
|
} finally {
|
|
10913
11812
|
this.options.baseDir = previousBaseDir;
|
|
@@ -10984,9 +11883,17 @@ var init_config_loader = __esm({
|
|
|
10984
11883
|
*/
|
|
10985
11884
|
validateLocalPath(resolvedPath) {
|
|
10986
11885
|
const projectRoot = this.options.projectRoot || process.cwd();
|
|
10987
|
-
const
|
|
10988
|
-
|
|
10989
|
-
|
|
11886
|
+
const canonicalize = (p) => {
|
|
11887
|
+
const resolved = path10.resolve(p);
|
|
11888
|
+
try {
|
|
11889
|
+
return path10.normalize(fs8.realpathSync.native(resolved));
|
|
11890
|
+
} catch {
|
|
11891
|
+
return path10.normalize(resolved);
|
|
11892
|
+
}
|
|
11893
|
+
};
|
|
11894
|
+
const normalizedPath = canonicalize(resolvedPath);
|
|
11895
|
+
const normalizedRoot = canonicalize(projectRoot);
|
|
11896
|
+
if (normalizedPath !== normalizedRoot && !normalizedPath.startsWith(`${normalizedRoot}${path10.sep}`)) {
|
|
10990
11897
|
throw new Error(
|
|
10991
11898
|
`Security error: Path traversal detected. Cannot access files outside project root: ${projectRoot}`
|
|
10992
11899
|
);
|
|
@@ -10997,7 +11904,7 @@ var init_config_loader = __esm({
|
|
|
10997
11904
|
"/.ssh/",
|
|
10998
11905
|
"/.aws/",
|
|
10999
11906
|
"/.env",
|
|
11000
|
-
"/private/"
|
|
11907
|
+
"/private/etc/"
|
|
11001
11908
|
];
|
|
11002
11909
|
const lowerPath = normalizedPath.toLowerCase();
|
|
11003
11910
|
for (const pattern of sensitivePatterns) {
|
|
@@ -11011,19 +11918,19 @@ var init_config_loader = __esm({
|
|
|
11011
11918
|
*/
|
|
11012
11919
|
findPackageRoot() {
|
|
11013
11920
|
let currentDir = __dirname;
|
|
11014
|
-
const root =
|
|
11921
|
+
const root = path10.parse(currentDir).root;
|
|
11015
11922
|
while (currentDir !== root) {
|
|
11016
|
-
const packageJsonPath =
|
|
11017
|
-
if (
|
|
11923
|
+
const packageJsonPath = path10.join(currentDir, "package.json");
|
|
11924
|
+
if (fs8.existsSync(packageJsonPath)) {
|
|
11018
11925
|
try {
|
|
11019
|
-
const packageJson = JSON.parse(
|
|
11926
|
+
const packageJson = JSON.parse(fs8.readFileSync(packageJsonPath, "utf8"));
|
|
11020
11927
|
if (packageJson.name === "@probelabs/visor") {
|
|
11021
11928
|
return currentDir;
|
|
11022
11929
|
}
|
|
11023
11930
|
} catch {
|
|
11024
11931
|
}
|
|
11025
11932
|
}
|
|
11026
|
-
currentDir =
|
|
11933
|
+
currentDir = path10.dirname(currentDir);
|
|
11027
11934
|
}
|
|
11028
11935
|
return null;
|
|
11029
11936
|
}
|
|
@@ -11271,6 +12178,11 @@ var init_config_schema = __esm({
|
|
|
11271
12178
|
CustomToolDefinition: {
|
|
11272
12179
|
type: "object",
|
|
11273
12180
|
properties: {
|
|
12181
|
+
type: {
|
|
12182
|
+
type: "string",
|
|
12183
|
+
enum: ["command", "api"],
|
|
12184
|
+
description: "Tool implementation type (defaults to 'command')"
|
|
12185
|
+
},
|
|
11274
12186
|
name: {
|
|
11275
12187
|
type: "string",
|
|
11276
12188
|
description: "Tool name - used to reference the tool in MCP blocks"
|
|
@@ -11308,7 +12220,7 @@ var init_config_schema = __esm({
|
|
|
11308
12220
|
},
|
|
11309
12221
|
exec: {
|
|
11310
12222
|
type: "string",
|
|
11311
|
-
description: "Command to execute - supports Liquid template"
|
|
12223
|
+
description: "Command to execute - supports Liquid template (required for type: 'command')"
|
|
11312
12224
|
},
|
|
11313
12225
|
stdin: {
|
|
11314
12226
|
type: "string",
|
|
@@ -11341,9 +12253,132 @@ var init_config_schema = __esm({
|
|
|
11341
12253
|
outputSchema: {
|
|
11342
12254
|
$ref: "#/definitions/Record%3Cstring%2Cunknown%3E",
|
|
11343
12255
|
description: "Expected output schema for validation"
|
|
12256
|
+
},
|
|
12257
|
+
spec: {
|
|
12258
|
+
anyOf: [
|
|
12259
|
+
{
|
|
12260
|
+
type: "string"
|
|
12261
|
+
},
|
|
12262
|
+
{
|
|
12263
|
+
$ref: "#/definitions/Record%3Cstring%2Cunknown%3E"
|
|
12264
|
+
}
|
|
12265
|
+
],
|
|
12266
|
+
description: "OpenAPI specification path/URL or inline object (required for type: 'api')"
|
|
12267
|
+
},
|
|
12268
|
+
overlays: {
|
|
12269
|
+
anyOf: [
|
|
12270
|
+
{
|
|
12271
|
+
type: "string"
|
|
12272
|
+
},
|
|
12273
|
+
{
|
|
12274
|
+
$ref: "#/definitions/Record%3Cstring%2Cunknown%3E"
|
|
12275
|
+
},
|
|
12276
|
+
{
|
|
12277
|
+
type: "array",
|
|
12278
|
+
items: {
|
|
12279
|
+
anyOf: [
|
|
12280
|
+
{
|
|
12281
|
+
type: "string"
|
|
12282
|
+
},
|
|
12283
|
+
{
|
|
12284
|
+
$ref: "#/definitions/Record%3Cstring%2Cunknown%3E"
|
|
12285
|
+
}
|
|
12286
|
+
]
|
|
12287
|
+
}
|
|
12288
|
+
}
|
|
12289
|
+
],
|
|
12290
|
+
description: "Overlay path/URL, inline object, or a mixed array applied in order"
|
|
12291
|
+
},
|
|
12292
|
+
targetUrl: {
|
|
12293
|
+
type: "string",
|
|
12294
|
+
description: "Override API base URL instead of OpenAPI servers"
|
|
12295
|
+
},
|
|
12296
|
+
target_url: {
|
|
12297
|
+
type: "string",
|
|
12298
|
+
description: "Alias for targetUrl (snake_case)"
|
|
12299
|
+
},
|
|
12300
|
+
whitelist: {
|
|
12301
|
+
anyOf: [
|
|
12302
|
+
{
|
|
12303
|
+
type: "array",
|
|
12304
|
+
items: {
|
|
12305
|
+
type: "string"
|
|
12306
|
+
}
|
|
12307
|
+
},
|
|
12308
|
+
{
|
|
12309
|
+
type: "string"
|
|
12310
|
+
}
|
|
12311
|
+
],
|
|
12312
|
+
description: "Include only operations matching these glob patterns (operationId or METHOD:/path)"
|
|
12313
|
+
},
|
|
12314
|
+
blacklist: {
|
|
12315
|
+
anyOf: [
|
|
12316
|
+
{
|
|
12317
|
+
type: "array",
|
|
12318
|
+
items: {
|
|
12319
|
+
type: "string"
|
|
12320
|
+
}
|
|
12321
|
+
},
|
|
12322
|
+
{
|
|
12323
|
+
type: "string"
|
|
12324
|
+
}
|
|
12325
|
+
],
|
|
12326
|
+
description: "Exclude operations matching these glob patterns (ignored when whitelist is set)"
|
|
12327
|
+
},
|
|
12328
|
+
headers: {
|
|
12329
|
+
$ref: "#/definitions/Record%3Cstring%2Cstring%3E",
|
|
12330
|
+
description: "Extra headers added to all API requests"
|
|
12331
|
+
},
|
|
12332
|
+
disableXMcp: {
|
|
12333
|
+
type: "boolean",
|
|
12334
|
+
description: "Disable X-MCP: 1 request header"
|
|
12335
|
+
},
|
|
12336
|
+
disable_x_mcp: {
|
|
12337
|
+
type: "boolean",
|
|
12338
|
+
description: "Alias for disableXMcp (snake_case)"
|
|
12339
|
+
},
|
|
12340
|
+
apiKey: {
|
|
12341
|
+
type: "string",
|
|
12342
|
+
description: "API key fallback credential used by security schemes"
|
|
12343
|
+
},
|
|
12344
|
+
api_key: {
|
|
12345
|
+
type: "string",
|
|
12346
|
+
description: "Alias for apiKey (snake_case)"
|
|
12347
|
+
},
|
|
12348
|
+
securitySchemeName: {
|
|
12349
|
+
type: "string",
|
|
12350
|
+
description: "Preferred security scheme name (optional hint)"
|
|
12351
|
+
},
|
|
12352
|
+
security_scheme_name: {
|
|
12353
|
+
type: "string",
|
|
12354
|
+
description: "Alias for securitySchemeName (snake_case)"
|
|
12355
|
+
},
|
|
12356
|
+
securityCredentials: {
|
|
12357
|
+
$ref: "#/definitions/Record%3Cstring%2Cstring%3E",
|
|
12358
|
+
description: "Credentials by OpenAPI security scheme name"
|
|
12359
|
+
},
|
|
12360
|
+
security_credentials: {
|
|
12361
|
+
$ref: "#/definitions/Record%3Cstring%2Cstring%3E",
|
|
12362
|
+
description: "Alias for securityCredentials (snake_case)"
|
|
12363
|
+
},
|
|
12364
|
+
namePrefix: {
|
|
12365
|
+
type: "string",
|
|
12366
|
+
description: "Optional prefix prepended to generated operation tool names"
|
|
12367
|
+
},
|
|
12368
|
+
name_prefix: {
|
|
12369
|
+
type: "string",
|
|
12370
|
+
description: "Alias for namePrefix (snake_case)"
|
|
12371
|
+
},
|
|
12372
|
+
requestTimeoutMs: {
|
|
12373
|
+
type: "number",
|
|
12374
|
+
description: "Request timeout in milliseconds for API calls"
|
|
12375
|
+
},
|
|
12376
|
+
request_timeout_ms: {
|
|
12377
|
+
type: "number",
|
|
12378
|
+
description: "Alias for requestTimeoutMs (snake_case)"
|
|
11344
12379
|
}
|
|
11345
12380
|
},
|
|
11346
|
-
required: ["name"
|
|
12381
|
+
required: ["name"],
|
|
11347
12382
|
additionalProperties: false,
|
|
11348
12383
|
description: "Custom tool definition for use in MCP blocks",
|
|
11349
12384
|
patternProperties: {
|
|
@@ -11474,6 +12509,46 @@ var init_config_schema = __esm({
|
|
|
11474
12509
|
type: "string",
|
|
11475
12510
|
description: "Script content to execute for script checks"
|
|
11476
12511
|
},
|
|
12512
|
+
tools: {
|
|
12513
|
+
type: "array",
|
|
12514
|
+
items: {
|
|
12515
|
+
anyOf: [
|
|
12516
|
+
{
|
|
12517
|
+
type: "string"
|
|
12518
|
+
},
|
|
12519
|
+
{
|
|
12520
|
+
type: "object",
|
|
12521
|
+
properties: {
|
|
12522
|
+
workflow: {
|
|
12523
|
+
type: "string"
|
|
12524
|
+
},
|
|
12525
|
+
args: {
|
|
12526
|
+
$ref: "#/definitions/Record%3Cstring%2Cunknown%3E"
|
|
12527
|
+
}
|
|
12528
|
+
},
|
|
12529
|
+
required: ["workflow"],
|
|
12530
|
+
additionalProperties: false
|
|
12531
|
+
}
|
|
12532
|
+
]
|
|
12533
|
+
},
|
|
12534
|
+
description: "Tool names to expose inside script checks (string names or workflow references)"
|
|
12535
|
+
},
|
|
12536
|
+
tools_js: {
|
|
12537
|
+
type: "string",
|
|
12538
|
+
description: "JavaScript expression to dynamically compute tools for script checks"
|
|
12539
|
+
},
|
|
12540
|
+
mcp_servers: {
|
|
12541
|
+
$ref: "#/definitions/Record%3Cstring%2CMcpServerConfig%3E",
|
|
12542
|
+
description: "MCP servers whose tools are exposed inside script checks"
|
|
12543
|
+
},
|
|
12544
|
+
enable_fetch: {
|
|
12545
|
+
type: "boolean",
|
|
12546
|
+
description: "Enable fetch() function in script checks (default: false)"
|
|
12547
|
+
},
|
|
12548
|
+
enable_bash: {
|
|
12549
|
+
type: "boolean",
|
|
12550
|
+
description: "Enable bash() function in script checks (default: false)"
|
|
12551
|
+
},
|
|
11477
12552
|
schedule: {
|
|
11478
12553
|
type: "string",
|
|
11479
12554
|
description: 'Cron schedule expression (e.g., "0 2 * * *") - optional for any check type'
|
|
@@ -11819,7 +12894,7 @@ var init_config_schema = __esm({
|
|
|
11819
12894
|
description: "Arguments/inputs for the workflow"
|
|
11820
12895
|
},
|
|
11821
12896
|
overrides: {
|
|
11822
|
-
$ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-13489-
|
|
12897
|
+
$ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-13489-28083-src_types_config.ts-0-53867%3E%3E",
|
|
11823
12898
|
description: "Override specific step configurations in the workflow"
|
|
11824
12899
|
},
|
|
11825
12900
|
output_mapping: {
|
|
@@ -11835,7 +12910,7 @@ var init_config_schema = __esm({
|
|
|
11835
12910
|
description: "Config file path - alternative to workflow ID (loads a Visor config file as workflow)"
|
|
11836
12911
|
},
|
|
11837
12912
|
workflow_overrides: {
|
|
11838
|
-
$ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-13489-
|
|
12913
|
+
$ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-13489-28083-src_types_config.ts-0-53867%3E%3E",
|
|
11839
12914
|
description: "Alias for overrides - workflow step overrides (backward compatibility)"
|
|
11840
12915
|
},
|
|
11841
12916
|
ref: {
|
|
@@ -11941,6 +13016,72 @@ var init_config_schema = __esm({
|
|
|
11941
13016
|
],
|
|
11942
13017
|
description: "Valid check types in configuration"
|
|
11943
13018
|
},
|
|
13019
|
+
"Record<string,McpServerConfig>": {
|
|
13020
|
+
type: "object",
|
|
13021
|
+
additionalProperties: {
|
|
13022
|
+
$ref: "#/definitions/McpServerConfig"
|
|
13023
|
+
}
|
|
13024
|
+
},
|
|
13025
|
+
McpServerConfig: {
|
|
13026
|
+
type: "object",
|
|
13027
|
+
properties: {
|
|
13028
|
+
command: {
|
|
13029
|
+
type: "string",
|
|
13030
|
+
description: "Command to execute (presence indicates stdio server)"
|
|
13031
|
+
},
|
|
13032
|
+
args: {
|
|
13033
|
+
type: "array",
|
|
13034
|
+
items: {
|
|
13035
|
+
type: "string"
|
|
13036
|
+
},
|
|
13037
|
+
description: "Arguments to pass to the command"
|
|
13038
|
+
},
|
|
13039
|
+
env: {
|
|
13040
|
+
$ref: "#/definitions/Record%3Cstring%2Cstring%3E",
|
|
13041
|
+
description: "Environment variables for the MCP server"
|
|
13042
|
+
},
|
|
13043
|
+
url: {
|
|
13044
|
+
type: "string",
|
|
13045
|
+
description: "URL endpoint (presence indicates external server)"
|
|
13046
|
+
},
|
|
13047
|
+
transport: {
|
|
13048
|
+
type: "string",
|
|
13049
|
+
enum: ["stdio", "sse", "http"],
|
|
13050
|
+
description: "Transport type"
|
|
13051
|
+
},
|
|
13052
|
+
workflow: {
|
|
13053
|
+
type: "string",
|
|
13054
|
+
description: "Workflow ID or path (presence indicates workflow tool)"
|
|
13055
|
+
},
|
|
13056
|
+
inputs: {
|
|
13057
|
+
$ref: "#/definitions/Record%3Cstring%2Cunknown%3E",
|
|
13058
|
+
description: "Inputs to pass to workflow"
|
|
13059
|
+
},
|
|
13060
|
+
description: {
|
|
13061
|
+
type: "string",
|
|
13062
|
+
description: "Tool description for AI"
|
|
13063
|
+
},
|
|
13064
|
+
allowedMethods: {
|
|
13065
|
+
type: "array",
|
|
13066
|
+
items: {
|
|
13067
|
+
type: "string"
|
|
13068
|
+
},
|
|
13069
|
+
description: 'Whitelist specific methods from this MCP server (supports wildcards like "search_*")'
|
|
13070
|
+
},
|
|
13071
|
+
blockedMethods: {
|
|
13072
|
+
type: "array",
|
|
13073
|
+
items: {
|
|
13074
|
+
type: "string"
|
|
13075
|
+
},
|
|
13076
|
+
description: 'Block specific methods from this MCP server (supports wildcards like "*_delete")'
|
|
13077
|
+
}
|
|
13078
|
+
},
|
|
13079
|
+
additionalProperties: false,
|
|
13080
|
+
description: "Unified MCP server/tool entry - type detected by which properties are present\n\nDetection logic (priority order): 1. Has `command` \u2192 stdio MCP server (external process) 2. Has `url` \u2192 SSE/HTTP MCP server (external endpoint) 3. Has `workflow` \u2192 workflow tool reference 4. Empty `{}` or just key \u2192 auto-detect from `tools:` section",
|
|
13081
|
+
patternProperties: {
|
|
13082
|
+
"^x-": {}
|
|
13083
|
+
}
|
|
13084
|
+
},
|
|
11944
13085
|
EventTrigger: {
|
|
11945
13086
|
type: "string",
|
|
11946
13087
|
enum: [
|
|
@@ -12069,72 +13210,6 @@ var init_config_schema = __esm({
|
|
|
12069
13210
|
"^x-": {}
|
|
12070
13211
|
}
|
|
12071
13212
|
},
|
|
12072
|
-
"Record<string,McpServerConfig>": {
|
|
12073
|
-
type: "object",
|
|
12074
|
-
additionalProperties: {
|
|
12075
|
-
$ref: "#/definitions/McpServerConfig"
|
|
12076
|
-
}
|
|
12077
|
-
},
|
|
12078
|
-
McpServerConfig: {
|
|
12079
|
-
type: "object",
|
|
12080
|
-
properties: {
|
|
12081
|
-
command: {
|
|
12082
|
-
type: "string",
|
|
12083
|
-
description: "Command to execute (presence indicates stdio server)"
|
|
12084
|
-
},
|
|
12085
|
-
args: {
|
|
12086
|
-
type: "array",
|
|
12087
|
-
items: {
|
|
12088
|
-
type: "string"
|
|
12089
|
-
},
|
|
12090
|
-
description: "Arguments to pass to the command"
|
|
12091
|
-
},
|
|
12092
|
-
env: {
|
|
12093
|
-
$ref: "#/definitions/Record%3Cstring%2Cstring%3E",
|
|
12094
|
-
description: "Environment variables for the MCP server"
|
|
12095
|
-
},
|
|
12096
|
-
url: {
|
|
12097
|
-
type: "string",
|
|
12098
|
-
description: "URL endpoint (presence indicates external server)"
|
|
12099
|
-
},
|
|
12100
|
-
transport: {
|
|
12101
|
-
type: "string",
|
|
12102
|
-
enum: ["stdio", "sse", "http"],
|
|
12103
|
-
description: "Transport type"
|
|
12104
|
-
},
|
|
12105
|
-
workflow: {
|
|
12106
|
-
type: "string",
|
|
12107
|
-
description: "Workflow ID or path (presence indicates workflow tool)"
|
|
12108
|
-
},
|
|
12109
|
-
inputs: {
|
|
12110
|
-
$ref: "#/definitions/Record%3Cstring%2Cunknown%3E",
|
|
12111
|
-
description: "Inputs to pass to workflow"
|
|
12112
|
-
},
|
|
12113
|
-
description: {
|
|
12114
|
-
type: "string",
|
|
12115
|
-
description: "Tool description for AI"
|
|
12116
|
-
},
|
|
12117
|
-
allowedMethods: {
|
|
12118
|
-
type: "array",
|
|
12119
|
-
items: {
|
|
12120
|
-
type: "string"
|
|
12121
|
-
},
|
|
12122
|
-
description: 'Whitelist specific methods from this MCP server (supports wildcards like "search_*")'
|
|
12123
|
-
},
|
|
12124
|
-
blockedMethods: {
|
|
12125
|
-
type: "array",
|
|
12126
|
-
items: {
|
|
12127
|
-
type: "string"
|
|
12128
|
-
},
|
|
12129
|
-
description: 'Block specific methods from this MCP server (supports wildcards like "*_delete")'
|
|
12130
|
-
}
|
|
12131
|
-
},
|
|
12132
|
-
additionalProperties: false,
|
|
12133
|
-
description: "Unified MCP server/tool entry - type detected by which properties are present\n\nDetection logic (priority order): 1. Has `command` \u2192 stdio MCP server (external process) 2. Has `url` \u2192 SSE/HTTP MCP server (external endpoint) 3. Has `workflow` \u2192 workflow tool reference 4. Empty `{}` or just key \u2192 auto-detect from `tools:` section",
|
|
12134
|
-
patternProperties: {
|
|
12135
|
-
"^x-": {}
|
|
12136
|
-
}
|
|
12137
|
-
},
|
|
12138
13213
|
AIRetryConfig: {
|
|
12139
13214
|
type: "object",
|
|
12140
13215
|
properties: {
|
|
@@ -12523,7 +13598,7 @@ var init_config_schema = __esm({
|
|
|
12523
13598
|
description: "Custom output name (defaults to workflow name)"
|
|
12524
13599
|
},
|
|
12525
13600
|
overrides: {
|
|
12526
|
-
$ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-13489-
|
|
13601
|
+
$ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-13489-28083-src_types_config.ts-0-53867%3E%3E",
|
|
12527
13602
|
description: "Step overrides"
|
|
12528
13603
|
},
|
|
12529
13604
|
output_mapping: {
|
|
@@ -12538,13 +13613,13 @@ var init_config_schema = __esm({
|
|
|
12538
13613
|
"^x-": {}
|
|
12539
13614
|
}
|
|
12540
13615
|
},
|
|
12541
|
-
"Record<string,Partial<interface-src_types_config.ts-13489-
|
|
13616
|
+
"Record<string,Partial<interface-src_types_config.ts-13489-28083-src_types_config.ts-0-53867>>": {
|
|
12542
13617
|
type: "object",
|
|
12543
13618
|
additionalProperties: {
|
|
12544
|
-
$ref: "#/definitions/Partial%3Cinterface-src_types_config.ts-13489-
|
|
13619
|
+
$ref: "#/definitions/Partial%3Cinterface-src_types_config.ts-13489-28083-src_types_config.ts-0-53867%3E"
|
|
12545
13620
|
}
|
|
12546
13621
|
},
|
|
12547
|
-
"Partial<interface-src_types_config.ts-13489-
|
|
13622
|
+
"Partial<interface-src_types_config.ts-13489-28083-src_types_config.ts-0-53867>": {
|
|
12548
13623
|
type: "object",
|
|
12549
13624
|
additionalProperties: false
|
|
12550
13625
|
},
|
|
@@ -13792,13 +14867,13 @@ __export(config_exports, {
|
|
|
13792
14867
|
ConfigManager: () => ConfigManager,
|
|
13793
14868
|
VALID_EVENT_TRIGGERS: () => VALID_EVENT_TRIGGERS
|
|
13794
14869
|
});
|
|
13795
|
-
var yaml3,
|
|
14870
|
+
var yaml3, fs9, path11, import_simple_git, import_ajv3, import_ajv_formats2, VALID_EVENT_TRIGGERS, ConfigManager, __ajvValidate, __ajvErrors;
|
|
13796
14871
|
var init_config = __esm({
|
|
13797
14872
|
"src/config.ts"() {
|
|
13798
14873
|
"use strict";
|
|
13799
14874
|
yaml3 = __toESM(require("js-yaml"));
|
|
13800
|
-
|
|
13801
|
-
|
|
14875
|
+
fs9 = __toESM(require("fs"));
|
|
14876
|
+
path11 = __toESM(require("path"));
|
|
13802
14877
|
init_logger();
|
|
13803
14878
|
import_simple_git = __toESM(require("simple-git"));
|
|
13804
14879
|
init_config_loader();
|
|
@@ -13837,16 +14912,28 @@ var init_config = __esm({
|
|
|
13837
14912
|
validEventTriggers = [...VALID_EVENT_TRIGGERS];
|
|
13838
14913
|
validOutputFormats = ["table", "json", "markdown", "sarif"];
|
|
13839
14914
|
validGroupByOptions = ["check", "file", "severity", "group"];
|
|
14915
|
+
/**
|
|
14916
|
+
* Annotate tools with the originating config directory for relative asset resolution.
|
|
14917
|
+
*/
|
|
14918
|
+
annotateToolBaseDirs(config, baseDir) {
|
|
14919
|
+
if (!config.tools || typeof config.tools !== "object") {
|
|
14920
|
+
return;
|
|
14921
|
+
}
|
|
14922
|
+
for (const tool of Object.values(config.tools)) {
|
|
14923
|
+
if (!tool || typeof tool !== "object") continue;
|
|
14924
|
+
tool.__baseDir = tool.__baseDir || baseDir;
|
|
14925
|
+
}
|
|
14926
|
+
}
|
|
13840
14927
|
/**
|
|
13841
14928
|
* Load configuration from a file
|
|
13842
14929
|
*/
|
|
13843
14930
|
async loadConfig(configPath, options = {}) {
|
|
13844
14931
|
const { validate = true, mergeDefaults = true, allowedRemotePatterns } = options;
|
|
13845
|
-
const resolvedPath =
|
|
14932
|
+
const resolvedPath = path11.isAbsolute(configPath) ? configPath : path11.resolve(process.cwd(), configPath);
|
|
13846
14933
|
try {
|
|
13847
14934
|
let configContent;
|
|
13848
14935
|
try {
|
|
13849
|
-
configContent =
|
|
14936
|
+
configContent = fs9.readFileSync(resolvedPath, "utf8");
|
|
13850
14937
|
} catch (readErr) {
|
|
13851
14938
|
if (readErr && (readErr.code === "ENOENT" || readErr.code === "ENOTDIR")) {
|
|
13852
14939
|
throw new Error(`Configuration file not found: ${resolvedPath}`);
|
|
@@ -13868,7 +14955,7 @@ var init_config = __esm({
|
|
|
13868
14955
|
const extendsValue = parsedConfig.extends || parsedConfig.include;
|
|
13869
14956
|
if (extendsValue) {
|
|
13870
14957
|
const loaderOptions = {
|
|
13871
|
-
baseDir:
|
|
14958
|
+
baseDir: path11.dirname(resolvedPath),
|
|
13872
14959
|
allowRemote: this.isRemoteExtendsAllowed(),
|
|
13873
14960
|
maxDepth: 10,
|
|
13874
14961
|
allowedRemotePatterns
|
|
@@ -13887,10 +14974,11 @@ var init_config = __esm({
|
|
|
13887
14974
|
parsedConfig = merger.removeDisabledChecks(parsedConfig);
|
|
13888
14975
|
}
|
|
13889
14976
|
if (parsedConfig.id && typeof parsedConfig.id === "string") {
|
|
13890
|
-
parsedConfig = await this.convertWorkflowToConfig(parsedConfig,
|
|
14977
|
+
parsedConfig = await this.convertWorkflowToConfig(parsedConfig, path11.dirname(resolvedPath));
|
|
13891
14978
|
}
|
|
14979
|
+
this.annotateToolBaseDirs(parsedConfig, path11.dirname(resolvedPath));
|
|
13892
14980
|
parsedConfig = this.normalizeStepsAndChecks(parsedConfig, !!extendsValue);
|
|
13893
|
-
await this.loadWorkflows(parsedConfig,
|
|
14981
|
+
await this.loadWorkflows(parsedConfig, path11.dirname(resolvedPath));
|
|
13894
14982
|
if (validate) {
|
|
13895
14983
|
this.validateConfig(parsedConfig);
|
|
13896
14984
|
}
|
|
@@ -13949,6 +15037,7 @@ var init_config = __esm({
|
|
|
13949
15037
|
if (parsedConfig.id && typeof parsedConfig.id === "string") {
|
|
13950
15038
|
parsedConfig = await this.convertWorkflowToConfig(parsedConfig, baseDir || process.cwd());
|
|
13951
15039
|
}
|
|
15040
|
+
this.annotateToolBaseDirs(parsedConfig, baseDir || process.cwd());
|
|
13952
15041
|
parsedConfig = this.normalizeStepsAndChecks(parsedConfig, !!extendsValue);
|
|
13953
15042
|
await this.loadWorkflows(parsedConfig, baseDir || process.cwd());
|
|
13954
15043
|
if (validate) this.validateConfig(parsedConfig);
|
|
@@ -13968,16 +15057,16 @@ var init_config = __esm({
|
|
|
13968
15057
|
const searchDirs = [gitRoot, process.cwd()].filter(Boolean);
|
|
13969
15058
|
for (const baseDir of searchDirs) {
|
|
13970
15059
|
const candidates = ["visor.yaml", "visor.yml", ".visor.yaml", ".visor.yml"].map(
|
|
13971
|
-
(p) =>
|
|
15060
|
+
(p) => path11.join(baseDir, p)
|
|
13972
15061
|
);
|
|
13973
15062
|
for (const p of candidates) {
|
|
13974
15063
|
try {
|
|
13975
|
-
const st =
|
|
15064
|
+
const st = fs9.statSync(p);
|
|
13976
15065
|
if (!st.isFile()) continue;
|
|
13977
|
-
const isLegacy =
|
|
15066
|
+
const isLegacy = path11.basename(p).startsWith(".");
|
|
13978
15067
|
if (isLegacy) {
|
|
13979
15068
|
if (process.env.VISOR_STRICT_CONFIG_NAME === "true") {
|
|
13980
|
-
const rel =
|
|
15069
|
+
const rel = path11.relative(baseDir, p);
|
|
13981
15070
|
throw new Error(
|
|
13982
15071
|
`Legacy config detected: ${rel}. Please rename to visor.yaml (or visor.yml).`
|
|
13983
15072
|
);
|
|
@@ -14040,23 +15129,23 @@ var init_config = __esm({
|
|
|
14040
15129
|
const possiblePaths = [];
|
|
14041
15130
|
if (typeof __dirname !== "undefined") {
|
|
14042
15131
|
possiblePaths.push(
|
|
14043
|
-
|
|
14044
|
-
|
|
15132
|
+
path11.join(__dirname, "defaults", "visor.yaml"),
|
|
15133
|
+
path11.join(__dirname, "..", "defaults", "visor.yaml")
|
|
14045
15134
|
);
|
|
14046
15135
|
}
|
|
14047
15136
|
const pkgRoot = this.findPackageRoot();
|
|
14048
15137
|
if (pkgRoot) {
|
|
14049
|
-
possiblePaths.push(
|
|
15138
|
+
possiblePaths.push(path11.join(pkgRoot, "defaults", "visor.yaml"));
|
|
14050
15139
|
}
|
|
14051
15140
|
if (process.env.GITHUB_ACTION_PATH) {
|
|
14052
15141
|
possiblePaths.push(
|
|
14053
|
-
|
|
14054
|
-
|
|
15142
|
+
path11.join(process.env.GITHUB_ACTION_PATH, "defaults", "visor.yaml"),
|
|
15143
|
+
path11.join(process.env.GITHUB_ACTION_PATH, "dist", "defaults", "visor.yaml")
|
|
14055
15144
|
);
|
|
14056
15145
|
}
|
|
14057
15146
|
let bundledConfigPath;
|
|
14058
15147
|
for (const possiblePath of possiblePaths) {
|
|
14059
|
-
if (
|
|
15148
|
+
if (fs9.existsSync(possiblePath)) {
|
|
14060
15149
|
bundledConfigPath = possiblePath;
|
|
14061
15150
|
break;
|
|
14062
15151
|
}
|
|
@@ -14064,7 +15153,7 @@ var init_config = __esm({
|
|
|
14064
15153
|
if (bundledConfigPath) {
|
|
14065
15154
|
console.error(`\u{1F4E6} Loading bundled default configuration from ${bundledConfigPath}`);
|
|
14066
15155
|
const readAndParse = (p) => {
|
|
14067
|
-
const raw =
|
|
15156
|
+
const raw = fs9.readFileSync(p, "utf8");
|
|
14068
15157
|
const obj = yaml3.load(raw);
|
|
14069
15158
|
if (!obj || typeof obj !== "object") return {};
|
|
14070
15159
|
if (obj.include && !obj.extends) {
|
|
@@ -14074,7 +15163,7 @@ var init_config = __esm({
|
|
|
14074
15163
|
}
|
|
14075
15164
|
return obj;
|
|
14076
15165
|
};
|
|
14077
|
-
const baseDir =
|
|
15166
|
+
const baseDir = path11.dirname(bundledConfigPath);
|
|
14078
15167
|
const merger = new (init_config_merger(), __toCommonJS(config_merger_exports)).ConfigMerger();
|
|
14079
15168
|
const loadWithExtendsSync = (p) => {
|
|
14080
15169
|
const current = readAndParse(p);
|
|
@@ -14086,7 +15175,7 @@ var init_config = __esm({
|
|
|
14086
15175
|
let acc = {};
|
|
14087
15176
|
for (const src of list) {
|
|
14088
15177
|
const rel = typeof src === "string" ? src : String(src);
|
|
14089
|
-
const abs =
|
|
15178
|
+
const abs = path11.isAbsolute(rel) ? rel : path11.resolve(baseDir, rel);
|
|
14090
15179
|
const parentCfg = loadWithExtendsSync(abs);
|
|
14091
15180
|
acc = merger.merge(acc, parentCfg);
|
|
14092
15181
|
}
|
|
@@ -14110,18 +15199,18 @@ var init_config = __esm({
|
|
|
14110
15199
|
*/
|
|
14111
15200
|
findPackageRoot() {
|
|
14112
15201
|
let currentDir = __dirname;
|
|
14113
|
-
while (currentDir !==
|
|
14114
|
-
const packageJsonPath =
|
|
14115
|
-
if (
|
|
15202
|
+
while (currentDir !== path11.dirname(currentDir)) {
|
|
15203
|
+
const packageJsonPath = path11.join(currentDir, "package.json");
|
|
15204
|
+
if (fs9.existsSync(packageJsonPath)) {
|
|
14116
15205
|
try {
|
|
14117
|
-
const packageJson = JSON.parse(
|
|
15206
|
+
const packageJson = JSON.parse(fs9.readFileSync(packageJsonPath, "utf8"));
|
|
14118
15207
|
if (packageJson.name === "@probelabs/visor") {
|
|
14119
15208
|
return currentDir;
|
|
14120
15209
|
}
|
|
14121
15210
|
} catch {
|
|
14122
15211
|
}
|
|
14123
15212
|
}
|
|
14124
|
-
currentDir =
|
|
15213
|
+
currentDir = path11.dirname(currentDir);
|
|
14125
15214
|
}
|
|
14126
15215
|
return null;
|
|
14127
15216
|
}
|
|
@@ -14461,6 +15550,42 @@ ${errors}`);
|
|
|
14461
15550
|
if (config.ai_mcp_servers) {
|
|
14462
15551
|
this.validateMcpServersObject(config.ai_mcp_servers, "ai_mcp_servers", errors, warnings);
|
|
14463
15552
|
}
|
|
15553
|
+
if (config.tools) {
|
|
15554
|
+
for (const [toolName, toolDef] of Object.entries(config.tools)) {
|
|
15555
|
+
const type = toolDef.type || "command";
|
|
15556
|
+
if (type === "api") {
|
|
15557
|
+
const spec = toolDef.spec;
|
|
15558
|
+
const hasStringSpec = typeof spec === "string" && spec.trim().length > 0;
|
|
15559
|
+
const hasInlineSpec = !!spec && typeof spec === "object" && !Array.isArray(spec);
|
|
15560
|
+
if (!hasStringSpec && !hasInlineSpec) {
|
|
15561
|
+
errors.push({
|
|
15562
|
+
field: `tools.${toolName}.spec`,
|
|
15563
|
+
message: `Invalid tool configuration for "${toolName}": missing spec field (required for type: api)`
|
|
15564
|
+
});
|
|
15565
|
+
}
|
|
15566
|
+
const overlays = toolDef.overlays;
|
|
15567
|
+
if (overlays !== void 0) {
|
|
15568
|
+
const isInlineOverlay = !!overlays && typeof overlays === "object" && !Array.isArray(overlays);
|
|
15569
|
+
const isStringOverlay = typeof overlays === "string";
|
|
15570
|
+
const isMixedArrayOverlay = Array.isArray(overlays) && overlays.every(
|
|
15571
|
+
(item) => typeof item === "string" || !!item && typeof item === "object" && !Array.isArray(item)
|
|
15572
|
+
);
|
|
15573
|
+
if (!isInlineOverlay && !isStringOverlay && !isMixedArrayOverlay) {
|
|
15574
|
+
errors.push({
|
|
15575
|
+
field: `tools.${toolName}.overlays`,
|
|
15576
|
+
message: `Invalid tool configuration for "${toolName}": overlays must be a string, object, or array of strings/objects`,
|
|
15577
|
+
value: overlays
|
|
15578
|
+
});
|
|
15579
|
+
}
|
|
15580
|
+
}
|
|
15581
|
+
} else if (!toolDef.exec || typeof toolDef.exec !== "string") {
|
|
15582
|
+
errors.push({
|
|
15583
|
+
field: `tools.${toolName}.exec`,
|
|
15584
|
+
message: `Invalid tool configuration for "${toolName}": missing exec field (required for command tools)`
|
|
15585
|
+
});
|
|
15586
|
+
}
|
|
15587
|
+
}
|
|
15588
|
+
}
|
|
14464
15589
|
if (config.output) {
|
|
14465
15590
|
this.validateOutputConfig(config.output, errors);
|
|
14466
15591
|
}
|
|
@@ -14878,7 +16003,7 @@ ${errors}`);
|
|
|
14878
16003
|
if (policy.engine === "local" && policy.rules) {
|
|
14879
16004
|
const rulesPath = Array.isArray(policy.rules) ? policy.rules : [policy.rules];
|
|
14880
16005
|
for (const rp of rulesPath) {
|
|
14881
|
-
if (typeof rp === "string" && !
|
|
16006
|
+
if (typeof rp === "string" && !fs9.existsSync(path11.resolve(rp))) {
|
|
14882
16007
|
warnings.push({
|
|
14883
16008
|
field: "policy.rules",
|
|
14884
16009
|
message: `Policy rules path does not exist: ${rp}. It will be resolved at runtime.`,
|
|
@@ -14928,7 +16053,7 @@ ${errors}`);
|
|
|
14928
16053
|
});
|
|
14929
16054
|
}
|
|
14930
16055
|
}
|
|
14931
|
-
if (policy.data && typeof policy.data === "string" && !
|
|
16056
|
+
if (policy.data && typeof policy.data === "string" && !fs9.existsSync(path11.resolve(policy.data))) {
|
|
14932
16057
|
warnings.push({
|
|
14933
16058
|
field: "policy.data",
|
|
14934
16059
|
message: `Policy data file does not exist: ${policy.data}. It will be resolved at runtime.`,
|
|
@@ -15078,7 +16203,7 @@ ${errors}`);
|
|
|
15078
16203
|
try {
|
|
15079
16204
|
if (!__ajvValidate) {
|
|
15080
16205
|
try {
|
|
15081
|
-
const jsonPath =
|
|
16206
|
+
const jsonPath = path11.resolve(__dirname, "generated", "config-schema.json");
|
|
15082
16207
|
const jsonSchema = require(jsonPath);
|
|
15083
16208
|
if (jsonSchema) {
|
|
15084
16209
|
const ajv = new import_ajv3.default({ allErrors: true, allowUnionTypes: true, strict: false });
|
|
@@ -15128,6 +16253,9 @@ ${errors}`);
|
|
|
15128
16253
|
if (topLevel && allowedTopLevelKeys.has(addl)) {
|
|
15129
16254
|
continue;
|
|
15130
16255
|
}
|
|
16256
|
+
if (!topLevel && addl === "__baseDir" && pathStr.startsWith("tools.")) {
|
|
16257
|
+
continue;
|
|
16258
|
+
}
|
|
15131
16259
|
if (!topLevel && addl === "sandbox" && pathStr.match(/^(checks|steps)\.[^.]+$/)) {
|
|
15132
16260
|
continue;
|
|
15133
16261
|
}
|
|
@@ -15332,7 +16460,7 @@ var workflow_check_provider_exports = {};
|
|
|
15332
16460
|
__export(workflow_check_provider_exports, {
|
|
15333
16461
|
WorkflowCheckProvider: () => WorkflowCheckProvider
|
|
15334
16462
|
});
|
|
15335
|
-
var
|
|
16463
|
+
var path12, yaml4, WorkflowCheckProvider;
|
|
15336
16464
|
var init_workflow_check_provider = __esm({
|
|
15337
16465
|
"src/providers/workflow-check-provider.ts"() {
|
|
15338
16466
|
"use strict";
|
|
@@ -15343,7 +16471,7 @@ var init_workflow_check_provider = __esm({
|
|
|
15343
16471
|
init_sandbox();
|
|
15344
16472
|
init_human_id();
|
|
15345
16473
|
init_liquid_extensions();
|
|
15346
|
-
|
|
16474
|
+
path12 = __toESM(require("path"));
|
|
15347
16475
|
yaml4 = __toESM(require("js-yaml"));
|
|
15348
16476
|
WorkflowCheckProvider = class extends CheckProvider {
|
|
15349
16477
|
registry;
|
|
@@ -15552,13 +16680,13 @@ var init_workflow_check_provider = __esm({
|
|
|
15552
16680
|
const loadConfigLiquid = createExtendedLiquid();
|
|
15553
16681
|
const loadConfig2 = (filePath) => {
|
|
15554
16682
|
try {
|
|
15555
|
-
const normalizedBasePath =
|
|
15556
|
-
const resolvedPath =
|
|
15557
|
-
const basePathWithSep = normalizedBasePath.endsWith(
|
|
16683
|
+
const normalizedBasePath = path12.normalize(basePath);
|
|
16684
|
+
const resolvedPath = path12.isAbsolute(filePath) ? path12.normalize(filePath) : path12.normalize(path12.resolve(basePath, filePath));
|
|
16685
|
+
const basePathWithSep = normalizedBasePath.endsWith(path12.sep) ? normalizedBasePath : normalizedBasePath + path12.sep;
|
|
15558
16686
|
if (!resolvedPath.startsWith(basePathWithSep) && resolvedPath !== normalizedBasePath) {
|
|
15559
16687
|
throw new Error(`Path '${filePath}' escapes base directory`);
|
|
15560
16688
|
}
|
|
15561
|
-
const configDir =
|
|
16689
|
+
const configDir = path12.dirname(resolvedPath);
|
|
15562
16690
|
const rawContent = require("fs").readFileSync(resolvedPath, "utf-8");
|
|
15563
16691
|
const renderedContent = loadConfigLiquid.parseAndRenderSync(rawContent, {
|
|
15564
16692
|
basePath: configDir
|
|
@@ -16009,17 +17137,17 @@ var init_workflow_check_provider = __esm({
|
|
|
16009
17137
|
* so it can be executed by the state machine as a nested workflow.
|
|
16010
17138
|
*/
|
|
16011
17139
|
async loadWorkflowFromConfigPath(sourcePath, baseDir) {
|
|
16012
|
-
const
|
|
16013
|
-
const
|
|
17140
|
+
const path30 = require("path");
|
|
17141
|
+
const fs26 = require("fs");
|
|
16014
17142
|
const yaml5 = require("js-yaml");
|
|
16015
|
-
const resolved =
|
|
16016
|
-
if (!
|
|
17143
|
+
const resolved = path30.isAbsolute(sourcePath) ? sourcePath : path30.resolve(baseDir, sourcePath);
|
|
17144
|
+
if (!fs26.existsSync(resolved)) {
|
|
16017
17145
|
throw new Error(`Workflow config not found at: ${resolved}`);
|
|
16018
17146
|
}
|
|
16019
|
-
const rawContent =
|
|
17147
|
+
const rawContent = fs26.readFileSync(resolved, "utf8");
|
|
16020
17148
|
const rawData = yaml5.load(rawContent);
|
|
16021
17149
|
if (rawData.imports && Array.isArray(rawData.imports)) {
|
|
16022
|
-
const configDir =
|
|
17150
|
+
const configDir = path30.dirname(resolved);
|
|
16023
17151
|
for (const source of rawData.imports) {
|
|
16024
17152
|
const results = await this.registry.import(source, {
|
|
16025
17153
|
basePath: configDir,
|
|
@@ -16049,8 +17177,8 @@ ${errors}`);
|
|
|
16049
17177
|
if (!steps || Object.keys(steps).length === 0) {
|
|
16050
17178
|
throw new Error(`Config '${resolved}' does not contain any steps to execute as a workflow`);
|
|
16051
17179
|
}
|
|
16052
|
-
const id =
|
|
16053
|
-
const name = loaded.name || `Workflow from ${
|
|
17180
|
+
const id = path30.basename(resolved).replace(/\.(ya?ml)$/i, "");
|
|
17181
|
+
const name = loaded.name || `Workflow from ${path30.basename(resolved)}`;
|
|
16054
17182
|
const workflowDef = {
|
|
16055
17183
|
id,
|
|
16056
17184
|
name,
|
|
@@ -16289,11 +17417,11 @@ function fromDbRow(row) {
|
|
|
16289
17417
|
previousResponse: row.previous_response ?? void 0
|
|
16290
17418
|
};
|
|
16291
17419
|
}
|
|
16292
|
-
var
|
|
17420
|
+
var import_path5, import_fs4, import_uuid, SqliteStoreBackend;
|
|
16293
17421
|
var init_sqlite_store = __esm({
|
|
16294
17422
|
"src/scheduler/store/sqlite-store.ts"() {
|
|
16295
17423
|
"use strict";
|
|
16296
|
-
|
|
17424
|
+
import_path5 = __toESM(require("path"));
|
|
16297
17425
|
import_fs4 = __toESM(require("fs"));
|
|
16298
17426
|
import_uuid = require("uuid");
|
|
16299
17427
|
init_logger();
|
|
@@ -16306,8 +17434,8 @@ var init_sqlite_store = __esm({
|
|
|
16306
17434
|
this.dbPath = filename || ".visor/schedules.db";
|
|
16307
17435
|
}
|
|
16308
17436
|
async initialize() {
|
|
16309
|
-
const resolvedPath =
|
|
16310
|
-
const dir =
|
|
17437
|
+
const resolvedPath = import_path5.default.resolve(process.cwd(), this.dbPath);
|
|
17438
|
+
const dir = import_path5.default.dirname(resolvedPath);
|
|
16311
17439
|
import_fs4.default.mkdirSync(dir, { recursive: true });
|
|
16312
17440
|
const { createRequire } = require("module");
|
|
16313
17441
|
const runtimeRequire = createRequire(__filename);
|
|
@@ -16703,10 +17831,10 @@ var init_store = __esm({
|
|
|
16703
17831
|
|
|
16704
17832
|
// src/scheduler/store/json-migrator.ts
|
|
16705
17833
|
async function migrateJsonToBackend(jsonPath, backend) {
|
|
16706
|
-
const resolvedPath =
|
|
17834
|
+
const resolvedPath = import_path6.default.resolve(process.cwd(), jsonPath);
|
|
16707
17835
|
let content;
|
|
16708
17836
|
try {
|
|
16709
|
-
content = await
|
|
17837
|
+
content = await import_promises3.default.readFile(resolvedPath, "utf-8");
|
|
16710
17838
|
} catch (err) {
|
|
16711
17839
|
if (err.code === "ENOENT") {
|
|
16712
17840
|
return 0;
|
|
@@ -16753,7 +17881,7 @@ async function migrateJsonToBackend(jsonPath, backend) {
|
|
|
16753
17881
|
async function renameToMigrated(resolvedPath) {
|
|
16754
17882
|
const migratedPath = `${resolvedPath}.migrated`;
|
|
16755
17883
|
try {
|
|
16756
|
-
await
|
|
17884
|
+
await import_promises3.default.rename(resolvedPath, migratedPath);
|
|
16757
17885
|
logger.info(`[JsonMigrator] Backed up ${resolvedPath} \u2192 ${migratedPath}`);
|
|
16758
17886
|
} catch (err) {
|
|
16759
17887
|
logger.warn(
|
|
@@ -16761,12 +17889,12 @@ async function renameToMigrated(resolvedPath) {
|
|
|
16761
17889
|
);
|
|
16762
17890
|
}
|
|
16763
17891
|
}
|
|
16764
|
-
var
|
|
17892
|
+
var import_promises3, import_path6;
|
|
16765
17893
|
var init_json_migrator = __esm({
|
|
16766
17894
|
"src/scheduler/store/json-migrator.ts"() {
|
|
16767
17895
|
"use strict";
|
|
16768
|
-
|
|
16769
|
-
|
|
17896
|
+
import_promises3 = __toESM(require("fs/promises"));
|
|
17897
|
+
import_path6 = __toESM(require("path"));
|
|
16770
17898
|
init_logger();
|
|
16771
17899
|
}
|
|
16772
17900
|
});
|
|
@@ -17044,6 +18172,13 @@ var init_schedule_parser = __esm({
|
|
|
17044
18172
|
});
|
|
17045
18173
|
|
|
17046
18174
|
// src/scheduler/schedule-tool.ts
|
|
18175
|
+
var schedule_tool_exports = {};
|
|
18176
|
+
__export(schedule_tool_exports, {
|
|
18177
|
+
buildScheduleToolContext: () => buildScheduleToolContext,
|
|
18178
|
+
getScheduleToolDefinition: () => getScheduleToolDefinition,
|
|
18179
|
+
handleScheduleAction: () => handleScheduleAction,
|
|
18180
|
+
isScheduleTool: () => isScheduleTool
|
|
18181
|
+
});
|
|
17047
18182
|
function matchGlobPattern(pattern, value) {
|
|
17048
18183
|
const regexPattern = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
17049
18184
|
return new RegExp(`^${regexPattern}$`).test(value);
|
|
@@ -18374,7 +19509,23 @@ var init_mcp_custom_sse_server = __esm({
|
|
|
18374
19509
|
* Handle tools/list MCP request
|
|
18375
19510
|
*/
|
|
18376
19511
|
async handleToolsList(id) {
|
|
18377
|
-
const
|
|
19512
|
+
const normalizeInputSchema = (schema) => {
|
|
19513
|
+
if (schema && schema.type === "object") {
|
|
19514
|
+
return schema;
|
|
19515
|
+
}
|
|
19516
|
+
return {
|
|
19517
|
+
type: "object",
|
|
19518
|
+
properties: {},
|
|
19519
|
+
required: []
|
|
19520
|
+
};
|
|
19521
|
+
};
|
|
19522
|
+
const regularTools = await this.toolExecutor.listMcpTools();
|
|
19523
|
+
const workflowTools = Array.from(this.tools.values()).filter(isWorkflowTool).map((tool) => ({
|
|
19524
|
+
name: tool.name,
|
|
19525
|
+
description: tool.description || `Execute ${tool.name}`,
|
|
19526
|
+
inputSchema: normalizeInputSchema(tool.inputSchema)
|
|
19527
|
+
}));
|
|
19528
|
+
const allTools = [...regularTools, ...workflowTools];
|
|
18378
19529
|
if (this.debug) {
|
|
18379
19530
|
logger.debug(
|
|
18380
19531
|
`[CustomToolsSSEServer:${this.sessionId}] Listing ${allTools.length} tools: ${allTools.map((t) => t.name).join(", ")}`
|
|
@@ -18387,11 +19538,7 @@ var init_mcp_custom_sse_server = __esm({
|
|
|
18387
19538
|
tools: allTools.map((tool) => ({
|
|
18388
19539
|
name: tool.name,
|
|
18389
19540
|
description: tool.description || `Execute ${tool.name}`,
|
|
18390
|
-
inputSchema: tool.inputSchema
|
|
18391
|
-
type: "object",
|
|
18392
|
-
properties: {},
|
|
18393
|
-
required: []
|
|
18394
|
-
}
|
|
19541
|
+
inputSchema: normalizeInputSchema(tool.inputSchema)
|
|
18395
19542
|
}))
|
|
18396
19543
|
}
|
|
18397
19544
|
};
|
|
@@ -18581,8 +19728,45 @@ var init_mcp_custom_sse_server = __esm({
|
|
|
18581
19728
|
}
|
|
18582
19729
|
});
|
|
18583
19730
|
|
|
19731
|
+
// src/utils/tool-resolver.ts
|
|
19732
|
+
function resolveTools(toolItems, globalTools, logPrefix = "[ToolResolver]") {
|
|
19733
|
+
const tools = /* @__PURE__ */ new Map();
|
|
19734
|
+
for (const item of toolItems) {
|
|
19735
|
+
const workflowTool = resolveWorkflowToolFromItem(item);
|
|
19736
|
+
if (workflowTool) {
|
|
19737
|
+
logger.debug(`${logPrefix} Loaded workflow '${workflowTool.name}' as tool`);
|
|
19738
|
+
tools.set(workflowTool.name, workflowTool);
|
|
19739
|
+
continue;
|
|
19740
|
+
}
|
|
19741
|
+
if (typeof item === "string") {
|
|
19742
|
+
if (globalTools && globalTools[item]) {
|
|
19743
|
+
const tool = globalTools[item];
|
|
19744
|
+
tool.name = tool.name || item;
|
|
19745
|
+
tools.set(item, tool);
|
|
19746
|
+
continue;
|
|
19747
|
+
}
|
|
19748
|
+
logger.warn(`${logPrefix} Tool '${item}' not found in global tools or workflow registry`);
|
|
19749
|
+
} else if (isWorkflowToolReference(item)) {
|
|
19750
|
+
logger.warn(`${logPrefix} Workflow '${item.workflow}' referenced but not found in registry`);
|
|
19751
|
+
}
|
|
19752
|
+
}
|
|
19753
|
+
if (tools.size === 0 && toolItems.length > 0 && !globalTools) {
|
|
19754
|
+
logger.warn(
|
|
19755
|
+
`${logPrefix} Tools specified but no global tools found in configuration and no workflows matched`
|
|
19756
|
+
);
|
|
19757
|
+
}
|
|
19758
|
+
return tools;
|
|
19759
|
+
}
|
|
19760
|
+
var init_tool_resolver = __esm({
|
|
19761
|
+
"src/utils/tool-resolver.ts"() {
|
|
19762
|
+
"use strict";
|
|
19763
|
+
init_workflow_tool_executor();
|
|
19764
|
+
init_logger();
|
|
19765
|
+
}
|
|
19766
|
+
});
|
|
19767
|
+
|
|
18584
19768
|
// src/providers/ai-check-provider.ts
|
|
18585
|
-
var
|
|
19769
|
+
var import_promises4, import_path7, AICheckProvider;
|
|
18586
19770
|
var init_ai_check_provider = __esm({
|
|
18587
19771
|
"src/providers/ai-check-provider.ts"() {
|
|
18588
19772
|
"use strict";
|
|
@@ -18591,13 +19775,14 @@ var init_ai_check_provider = __esm({
|
|
|
18591
19775
|
init_env_resolver();
|
|
18592
19776
|
init_issue_filter();
|
|
18593
19777
|
init_liquid_extensions();
|
|
18594
|
-
|
|
18595
|
-
|
|
19778
|
+
import_promises4 = __toESM(require("fs/promises"));
|
|
19779
|
+
import_path7 = __toESM(require("path"));
|
|
18596
19780
|
init_lazy_otel();
|
|
18597
19781
|
init_state_capture();
|
|
18598
19782
|
init_mcp_custom_sse_server();
|
|
18599
19783
|
init_logger();
|
|
18600
19784
|
init_workflow_tool_executor();
|
|
19785
|
+
init_tool_resolver();
|
|
18601
19786
|
init_sandbox();
|
|
18602
19787
|
init_schedule_tool();
|
|
18603
19788
|
init_schedule_tool_handler();
|
|
@@ -18756,7 +19941,7 @@ var init_ai_check_provider = __esm({
|
|
|
18756
19941
|
const hasFileExtension = /\.[a-zA-Z0-9]{1,10}$/i.test(str);
|
|
18757
19942
|
const hasPathSeparators = /[\/\\]/.test(str);
|
|
18758
19943
|
const isRelativePath = /^\.{1,2}\//.test(str);
|
|
18759
|
-
const isAbsolutePath =
|
|
19944
|
+
const isAbsolutePath = import_path7.default.isAbsolute(str);
|
|
18760
19945
|
const hasTypicalFileChars = /^[a-zA-Z0-9._\-\/\\:~]+$/.test(str);
|
|
18761
19946
|
if (!(hasFileExtension || isRelativePath || isAbsolutePath || hasPathSeparators)) {
|
|
18762
19947
|
return false;
|
|
@@ -18766,14 +19951,14 @@ var init_ai_check_provider = __esm({
|
|
|
18766
19951
|
}
|
|
18767
19952
|
try {
|
|
18768
19953
|
let resolvedPath;
|
|
18769
|
-
if (
|
|
18770
|
-
resolvedPath =
|
|
19954
|
+
if (import_path7.default.isAbsolute(str)) {
|
|
19955
|
+
resolvedPath = import_path7.default.normalize(str);
|
|
18771
19956
|
} else {
|
|
18772
|
-
resolvedPath =
|
|
19957
|
+
resolvedPath = import_path7.default.resolve(process.cwd(), str);
|
|
18773
19958
|
}
|
|
18774
|
-
const
|
|
19959
|
+
const fs26 = require("fs").promises;
|
|
18775
19960
|
try {
|
|
18776
|
-
const stat = await
|
|
19961
|
+
const stat = await fs26.stat(resolvedPath);
|
|
18777
19962
|
return stat.isFile();
|
|
18778
19963
|
} catch {
|
|
18779
19964
|
return hasFileExtension && (isRelativePath || isAbsolutePath || hasPathSeparators);
|
|
@@ -18790,14 +19975,14 @@ var init_ai_check_provider = __esm({
|
|
|
18790
19975
|
throw new Error("Prompt file must have .liquid extension");
|
|
18791
19976
|
}
|
|
18792
19977
|
let resolvedPath;
|
|
18793
|
-
if (
|
|
19978
|
+
if (import_path7.default.isAbsolute(promptPath)) {
|
|
18794
19979
|
resolvedPath = promptPath;
|
|
18795
19980
|
} else {
|
|
18796
|
-
resolvedPath =
|
|
19981
|
+
resolvedPath = import_path7.default.resolve(process.cwd(), promptPath);
|
|
18797
19982
|
}
|
|
18798
|
-
if (!
|
|
18799
|
-
const normalizedPath =
|
|
18800
|
-
const currentDir =
|
|
19983
|
+
if (!import_path7.default.isAbsolute(promptPath)) {
|
|
19984
|
+
const normalizedPath = import_path7.default.normalize(resolvedPath);
|
|
19985
|
+
const currentDir = import_path7.default.resolve(process.cwd());
|
|
18801
19986
|
if (!normalizedPath.startsWith(currentDir)) {
|
|
18802
19987
|
throw new Error("Invalid prompt file path: path traversal detected");
|
|
18803
19988
|
}
|
|
@@ -18806,7 +19991,7 @@ var init_ai_check_provider = __esm({
|
|
|
18806
19991
|
throw new Error("Invalid prompt file path: path traversal detected");
|
|
18807
19992
|
}
|
|
18808
19993
|
try {
|
|
18809
|
-
const promptContent = await
|
|
19994
|
+
const promptContent = await import_promises4.default.readFile(resolvedPath, "utf-8");
|
|
18810
19995
|
return promptContent;
|
|
18811
19996
|
} catch (error) {
|
|
18812
19997
|
throw new Error(
|
|
@@ -20209,37 +21394,8 @@ ${processedPrompt}` : processedPrompt;
|
|
|
20209
21394
|
* Supports both traditional custom tools and workflow-as-tool references
|
|
20210
21395
|
*/
|
|
20211
21396
|
loadCustomTools(toolItems, config) {
|
|
20212
|
-
const tools = /* @__PURE__ */ new Map();
|
|
20213
21397
|
const globalTools = config.__globalTools;
|
|
20214
|
-
|
|
20215
|
-
const workflowTool = resolveWorkflowToolFromItem(item);
|
|
20216
|
-
if (workflowTool) {
|
|
20217
|
-
logger.debug(`[AICheckProvider] Loaded workflow '${workflowTool.name}' as custom tool`);
|
|
20218
|
-
tools.set(workflowTool.name, workflowTool);
|
|
20219
|
-
continue;
|
|
20220
|
-
}
|
|
20221
|
-
if (typeof item === "string") {
|
|
20222
|
-
if (globalTools && globalTools[item]) {
|
|
20223
|
-
const tool = globalTools[item];
|
|
20224
|
-
tool.name = tool.name || item;
|
|
20225
|
-
tools.set(item, tool);
|
|
20226
|
-
continue;
|
|
20227
|
-
}
|
|
20228
|
-
logger.warn(
|
|
20229
|
-
`[AICheckProvider] Custom tool '${item}' not found in global tools or workflow registry`
|
|
20230
|
-
);
|
|
20231
|
-
} else if (isWorkflowToolReference(item)) {
|
|
20232
|
-
logger.warn(
|
|
20233
|
-
`[AICheckProvider] Workflow '${item.workflow}' referenced but not found in registry`
|
|
20234
|
-
);
|
|
20235
|
-
}
|
|
20236
|
-
}
|
|
20237
|
-
if (tools.size === 0 && toolItems.length > 0 && !globalTools) {
|
|
20238
|
-
logger.warn(
|
|
20239
|
-
`[AICheckProvider] ai_custom_tools specified but no global tools found in configuration and no workflows matched`
|
|
20240
|
-
);
|
|
20241
|
-
}
|
|
20242
|
-
return tools;
|
|
21398
|
+
return resolveTools(toolItems, globalTools, "[AICheckProvider]");
|
|
20243
21399
|
}
|
|
20244
21400
|
/**
|
|
20245
21401
|
* Intersect config-level allowedTools with policy-level allowedTools.
|
|
@@ -20789,7 +21945,7 @@ var init_template_context = __esm({
|
|
|
20789
21945
|
});
|
|
20790
21946
|
|
|
20791
21947
|
// src/providers/http-client-provider.ts
|
|
20792
|
-
var
|
|
21948
|
+
var fs13, path16, HttpClientProvider;
|
|
20793
21949
|
var init_http_client_provider = __esm({
|
|
20794
21950
|
"src/providers/http-client-provider.ts"() {
|
|
20795
21951
|
"use strict";
|
|
@@ -20799,8 +21955,8 @@ var init_http_client_provider = __esm({
|
|
|
20799
21955
|
init_sandbox();
|
|
20800
21956
|
init_template_context();
|
|
20801
21957
|
init_logger();
|
|
20802
|
-
|
|
20803
|
-
|
|
21958
|
+
fs13 = __toESM(require("fs"));
|
|
21959
|
+
path16 = __toESM(require("path"));
|
|
20804
21960
|
HttpClientProvider = class extends CheckProvider {
|
|
20805
21961
|
liquid;
|
|
20806
21962
|
sandbox;
|
|
@@ -20895,14 +22051,14 @@ var init_http_client_provider = __esm({
|
|
|
20895
22051
|
const parentContext = context2?._parentContext;
|
|
20896
22052
|
const workingDirectory = parentContext?.workingDirectory;
|
|
20897
22053
|
const workspaceEnabled = parentContext?.workspace?.isEnabled?.();
|
|
20898
|
-
if (workspaceEnabled && workingDirectory && !
|
|
20899
|
-
resolvedOutputFile =
|
|
22054
|
+
if (workspaceEnabled && workingDirectory && !path16.isAbsolute(resolvedOutputFile)) {
|
|
22055
|
+
resolvedOutputFile = path16.join(workingDirectory, resolvedOutputFile);
|
|
20900
22056
|
logger.debug(
|
|
20901
22057
|
`[http_client] Resolved relative output_file to workspace: ${resolvedOutputFile}`
|
|
20902
22058
|
);
|
|
20903
22059
|
}
|
|
20904
|
-
if (skipIfExists &&
|
|
20905
|
-
const stats =
|
|
22060
|
+
if (skipIfExists && fs13.existsSync(resolvedOutputFile)) {
|
|
22061
|
+
const stats = fs13.statSync(resolvedOutputFile);
|
|
20906
22062
|
logger.verbose(`[http_client] File cached: ${resolvedOutputFile} (${stats.size} bytes)`);
|
|
20907
22063
|
return {
|
|
20908
22064
|
issues: [],
|
|
@@ -21113,13 +22269,13 @@ var init_http_client_provider = __esm({
|
|
|
21113
22269
|
]
|
|
21114
22270
|
};
|
|
21115
22271
|
}
|
|
21116
|
-
const parentDir =
|
|
21117
|
-
if (parentDir && !
|
|
21118
|
-
|
|
22272
|
+
const parentDir = path16.dirname(outputFile);
|
|
22273
|
+
if (parentDir && !fs13.existsSync(parentDir)) {
|
|
22274
|
+
fs13.mkdirSync(parentDir, { recursive: true });
|
|
21119
22275
|
}
|
|
21120
22276
|
const arrayBuffer = await response.arrayBuffer();
|
|
21121
22277
|
const buffer = Buffer.from(arrayBuffer);
|
|
21122
|
-
|
|
22278
|
+
fs13.writeFileSync(outputFile, buffer);
|
|
21123
22279
|
const contentType = response.headers.get("content-type") || "application/octet-stream";
|
|
21124
22280
|
logger.verbose(`[http_client] Downloaded: ${outputFile} (${buffer.length} bytes)`);
|
|
21125
22281
|
return {
|
|
@@ -21878,7 +23034,7 @@ var init_claude_code_types = __esm({
|
|
|
21878
23034
|
function isClaudeCodeConstructor(value) {
|
|
21879
23035
|
return typeof value === "function";
|
|
21880
23036
|
}
|
|
21881
|
-
var
|
|
23037
|
+
var import_promises5, import_path8, ClaudeCodeSDKNotInstalledError, ClaudeCodeAPIKeyMissingError, ClaudeCodeCheckProvider;
|
|
21882
23038
|
var init_claude_code_check_provider = __esm({
|
|
21883
23039
|
"src/providers/claude-code-check-provider.ts"() {
|
|
21884
23040
|
"use strict";
|
|
@@ -21886,8 +23042,8 @@ var init_claude_code_check_provider = __esm({
|
|
|
21886
23042
|
init_env_resolver();
|
|
21887
23043
|
init_issue_filter();
|
|
21888
23044
|
init_liquid_extensions();
|
|
21889
|
-
|
|
21890
|
-
|
|
23045
|
+
import_promises5 = __toESM(require("fs/promises"));
|
|
23046
|
+
import_path8 = __toESM(require("path"));
|
|
21891
23047
|
init_claude_code_types();
|
|
21892
23048
|
ClaudeCodeSDKNotInstalledError = class extends Error {
|
|
21893
23049
|
constructor() {
|
|
@@ -22035,7 +23191,7 @@ var init_claude_code_check_provider = __esm({
|
|
|
22035
23191
|
const hasFileExtension = /\.[a-zA-Z0-9]{1,10}$/i.test(str);
|
|
22036
23192
|
const hasPathSeparators = /[\/\\]/.test(str);
|
|
22037
23193
|
const isRelativePath = /^\.{1,2}\//.test(str);
|
|
22038
|
-
const isAbsolutePath =
|
|
23194
|
+
const isAbsolutePath = import_path8.default.isAbsolute(str);
|
|
22039
23195
|
const hasTypicalFileChars = /^[a-zA-Z0-9._\-\/\\:~]+$/.test(str);
|
|
22040
23196
|
if (!(hasFileExtension || isRelativePath || isAbsolutePath || hasPathSeparators)) {
|
|
22041
23197
|
return false;
|
|
@@ -22045,13 +23201,13 @@ var init_claude_code_check_provider = __esm({
|
|
|
22045
23201
|
}
|
|
22046
23202
|
try {
|
|
22047
23203
|
let resolvedPath;
|
|
22048
|
-
if (
|
|
22049
|
-
resolvedPath =
|
|
23204
|
+
if (import_path8.default.isAbsolute(str)) {
|
|
23205
|
+
resolvedPath = import_path8.default.normalize(str);
|
|
22050
23206
|
} else {
|
|
22051
|
-
resolvedPath =
|
|
23207
|
+
resolvedPath = import_path8.default.resolve(process.cwd(), str);
|
|
22052
23208
|
}
|
|
22053
23209
|
try {
|
|
22054
|
-
const stat = await
|
|
23210
|
+
const stat = await import_promises5.default.stat(resolvedPath);
|
|
22055
23211
|
return stat.isFile();
|
|
22056
23212
|
} catch {
|
|
22057
23213
|
return hasFileExtension && (isRelativePath || isAbsolutePath || hasPathSeparators);
|
|
@@ -22068,14 +23224,14 @@ var init_claude_code_check_provider = __esm({
|
|
|
22068
23224
|
throw new Error("Prompt file must have .liquid extension");
|
|
22069
23225
|
}
|
|
22070
23226
|
let resolvedPath;
|
|
22071
|
-
if (
|
|
23227
|
+
if (import_path8.default.isAbsolute(promptPath)) {
|
|
22072
23228
|
resolvedPath = promptPath;
|
|
22073
23229
|
} else {
|
|
22074
|
-
resolvedPath =
|
|
23230
|
+
resolvedPath = import_path8.default.resolve(process.cwd(), promptPath);
|
|
22075
23231
|
}
|
|
22076
|
-
if (!
|
|
22077
|
-
const normalizedPath =
|
|
22078
|
-
const currentDir =
|
|
23232
|
+
if (!import_path8.default.isAbsolute(promptPath)) {
|
|
23233
|
+
const normalizedPath = import_path8.default.normalize(resolvedPath);
|
|
23234
|
+
const currentDir = import_path8.default.resolve(process.cwd());
|
|
22079
23235
|
if (!normalizedPath.startsWith(currentDir)) {
|
|
22080
23236
|
throw new Error("Invalid prompt file path: path traversal detected");
|
|
22081
23237
|
}
|
|
@@ -22084,7 +23240,7 @@ var init_claude_code_check_provider = __esm({
|
|
|
22084
23240
|
throw new Error("Invalid prompt file path: path traversal detected");
|
|
22085
23241
|
}
|
|
22086
23242
|
try {
|
|
22087
|
-
const promptContent = await
|
|
23243
|
+
const promptContent = await import_promises5.default.readFile(resolvedPath, "utf-8");
|
|
22088
23244
|
return promptContent;
|
|
22089
23245
|
} catch (error) {
|
|
22090
23246
|
throw new Error(
|
|
@@ -22436,6 +23592,7 @@ var init_command_check_provider = __esm({
|
|
|
22436
23592
|
init_sandbox();
|
|
22437
23593
|
init_liquid_extensions();
|
|
22438
23594
|
init_logger();
|
|
23595
|
+
init_env_resolver();
|
|
22439
23596
|
init_command_executor();
|
|
22440
23597
|
init_author_permissions();
|
|
22441
23598
|
init_lazy_otel();
|
|
@@ -22637,7 +23794,7 @@ var init_command_check_provider = __esm({
|
|
|
22637
23794
|
if (config.env) {
|
|
22638
23795
|
for (const [key, value] of Object.entries(config.env)) {
|
|
22639
23796
|
if (value !== void 0 && value !== null) {
|
|
22640
|
-
scriptEnv[key] = String(value);
|
|
23797
|
+
scriptEnv[key] = String(EnvironmentResolver.resolveValue(value));
|
|
22641
23798
|
}
|
|
22642
23799
|
}
|
|
22643
23800
|
}
|
|
@@ -24653,14 +25810,14 @@ var require_util = __commonJS({
|
|
|
24653
25810
|
}
|
|
24654
25811
|
const port = url.port != null ? url.port : url.protocol === "https:" ? 443 : 80;
|
|
24655
25812
|
let origin = url.origin != null ? url.origin : `${url.protocol}//${url.hostname}:${port}`;
|
|
24656
|
-
let
|
|
25813
|
+
let path30 = url.path != null ? url.path : `${url.pathname || ""}${url.search || ""}`;
|
|
24657
25814
|
if (origin.endsWith("/")) {
|
|
24658
25815
|
origin = origin.substring(0, origin.length - 1);
|
|
24659
25816
|
}
|
|
24660
|
-
if (
|
|
24661
|
-
|
|
25817
|
+
if (path30 && !path30.startsWith("/")) {
|
|
25818
|
+
path30 = `/${path30}`;
|
|
24662
25819
|
}
|
|
24663
|
-
url = new URL(origin +
|
|
25820
|
+
url = new URL(origin + path30);
|
|
24664
25821
|
}
|
|
24665
25822
|
return url;
|
|
24666
25823
|
}
|
|
@@ -26274,20 +27431,20 @@ var require_parseParams = __commonJS({
|
|
|
26274
27431
|
var require_basename = __commonJS({
|
|
26275
27432
|
"node_modules/@fastify/busboy/lib/utils/basename.js"(exports2, module2) {
|
|
26276
27433
|
"use strict";
|
|
26277
|
-
module2.exports = function basename4(
|
|
26278
|
-
if (typeof
|
|
27434
|
+
module2.exports = function basename4(path30) {
|
|
27435
|
+
if (typeof path30 !== "string") {
|
|
26279
27436
|
return "";
|
|
26280
27437
|
}
|
|
26281
|
-
for (var i =
|
|
26282
|
-
switch (
|
|
27438
|
+
for (var i = path30.length - 1; i >= 0; --i) {
|
|
27439
|
+
switch (path30.charCodeAt(i)) {
|
|
26283
27440
|
case 47:
|
|
26284
27441
|
// '/'
|
|
26285
27442
|
case 92:
|
|
26286
|
-
|
|
26287
|
-
return
|
|
27443
|
+
path30 = path30.slice(i + 1);
|
|
27444
|
+
return path30 === ".." || path30 === "." ? "" : path30;
|
|
26288
27445
|
}
|
|
26289
27446
|
}
|
|
26290
|
-
return
|
|
27447
|
+
return path30 === ".." || path30 === "." ? "" : path30;
|
|
26291
27448
|
};
|
|
26292
27449
|
}
|
|
26293
27450
|
});
|
|
@@ -29318,7 +30475,7 @@ var require_request = __commonJS({
|
|
|
29318
30475
|
}
|
|
29319
30476
|
var Request = class _Request {
|
|
29320
30477
|
constructor(origin, {
|
|
29321
|
-
path:
|
|
30478
|
+
path: path30,
|
|
29322
30479
|
method,
|
|
29323
30480
|
body,
|
|
29324
30481
|
headers,
|
|
@@ -29332,11 +30489,11 @@ var require_request = __commonJS({
|
|
|
29332
30489
|
throwOnError,
|
|
29333
30490
|
expectContinue
|
|
29334
30491
|
}, handler) {
|
|
29335
|
-
if (typeof
|
|
30492
|
+
if (typeof path30 !== "string") {
|
|
29336
30493
|
throw new InvalidArgumentError("path must be a string");
|
|
29337
|
-
} else if (
|
|
30494
|
+
} else if (path30[0] !== "/" && !(path30.startsWith("http://") || path30.startsWith("https://")) && method !== "CONNECT") {
|
|
29338
30495
|
throw new InvalidArgumentError("path must be an absolute URL or start with a slash");
|
|
29339
|
-
} else if (invalidPathRegex.exec(
|
|
30496
|
+
} else if (invalidPathRegex.exec(path30) !== null) {
|
|
29340
30497
|
throw new InvalidArgumentError("invalid request path");
|
|
29341
30498
|
}
|
|
29342
30499
|
if (typeof method !== "string") {
|
|
@@ -29399,7 +30556,7 @@ var require_request = __commonJS({
|
|
|
29399
30556
|
this.completed = false;
|
|
29400
30557
|
this.aborted = false;
|
|
29401
30558
|
this.upgrade = upgrade || null;
|
|
29402
|
-
this.path = query ? util.buildURL(
|
|
30559
|
+
this.path = query ? util.buildURL(path30, query) : path30;
|
|
29403
30560
|
this.origin = origin;
|
|
29404
30561
|
this.idempotent = idempotent == null ? method === "HEAD" || method === "GET" : idempotent;
|
|
29405
30562
|
this.blocking = blocking == null ? false : blocking;
|
|
@@ -30407,9 +31564,9 @@ var require_RedirectHandler = __commonJS({
|
|
|
30407
31564
|
return this.handler.onHeaders(statusCode, headers, resume, statusText);
|
|
30408
31565
|
}
|
|
30409
31566
|
const { origin, pathname, search } = util.parseURL(new URL(this.location, this.opts.origin && new URL(this.opts.path, this.opts.origin)));
|
|
30410
|
-
const
|
|
31567
|
+
const path30 = search ? `${pathname}${search}` : pathname;
|
|
30411
31568
|
this.opts.headers = cleanRequestHeaders(this.opts.headers, statusCode === 303, this.opts.origin !== origin);
|
|
30412
|
-
this.opts.path =
|
|
31569
|
+
this.opts.path = path30;
|
|
30413
31570
|
this.opts.origin = origin;
|
|
30414
31571
|
this.opts.maxRedirections = 0;
|
|
30415
31572
|
this.opts.query = null;
|
|
@@ -31651,7 +32808,7 @@ var require_client = __commonJS({
|
|
|
31651
32808
|
writeH2(client, client[kHTTP2Session], request);
|
|
31652
32809
|
return;
|
|
31653
32810
|
}
|
|
31654
|
-
const { body, method, path:
|
|
32811
|
+
const { body, method, path: path30, host, upgrade, headers, blocking, reset } = request;
|
|
31655
32812
|
const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH";
|
|
31656
32813
|
if (body && typeof body.read === "function") {
|
|
31657
32814
|
body.read(0);
|
|
@@ -31701,7 +32858,7 @@ var require_client = __commonJS({
|
|
|
31701
32858
|
if (blocking) {
|
|
31702
32859
|
socket[kBlocking] = true;
|
|
31703
32860
|
}
|
|
31704
|
-
let header = `${method} ${
|
|
32861
|
+
let header = `${method} ${path30} HTTP/1.1\r
|
|
31705
32862
|
`;
|
|
31706
32863
|
if (typeof host === "string") {
|
|
31707
32864
|
header += `host: ${host}\r
|
|
@@ -31764,7 +32921,7 @@ upgrade: ${upgrade}\r
|
|
|
31764
32921
|
return true;
|
|
31765
32922
|
}
|
|
31766
32923
|
function writeH2(client, session, request) {
|
|
31767
|
-
const { body, method, path:
|
|
32924
|
+
const { body, method, path: path30, host, upgrade, expectContinue, signal, headers: reqHeaders } = request;
|
|
31768
32925
|
let headers;
|
|
31769
32926
|
if (typeof reqHeaders === "string") headers = Request[kHTTP2CopyHeaders](reqHeaders.trim());
|
|
31770
32927
|
else headers = reqHeaders;
|
|
@@ -31807,7 +32964,7 @@ upgrade: ${upgrade}\r
|
|
|
31807
32964
|
});
|
|
31808
32965
|
return true;
|
|
31809
32966
|
}
|
|
31810
|
-
headers[HTTP2_HEADER_PATH] =
|
|
32967
|
+
headers[HTTP2_HEADER_PATH] = path30;
|
|
31811
32968
|
headers[HTTP2_HEADER_SCHEME] = "https";
|
|
31812
32969
|
const expectsPayload = method === "PUT" || method === "POST" || method === "PATCH";
|
|
31813
32970
|
if (body && typeof body.read === "function") {
|
|
@@ -34050,20 +35207,20 @@ var require_mock_utils = __commonJS({
|
|
|
34050
35207
|
}
|
|
34051
35208
|
return true;
|
|
34052
35209
|
}
|
|
34053
|
-
function safeUrl(
|
|
34054
|
-
if (typeof
|
|
34055
|
-
return
|
|
35210
|
+
function safeUrl(path30) {
|
|
35211
|
+
if (typeof path30 !== "string") {
|
|
35212
|
+
return path30;
|
|
34056
35213
|
}
|
|
34057
|
-
const pathSegments =
|
|
35214
|
+
const pathSegments = path30.split("?");
|
|
34058
35215
|
if (pathSegments.length !== 2) {
|
|
34059
|
-
return
|
|
35216
|
+
return path30;
|
|
34060
35217
|
}
|
|
34061
35218
|
const qp = new URLSearchParams(pathSegments.pop());
|
|
34062
35219
|
qp.sort();
|
|
34063
35220
|
return [...pathSegments, qp.toString()].join("?");
|
|
34064
35221
|
}
|
|
34065
|
-
function matchKey(mockDispatch2, { path:
|
|
34066
|
-
const pathMatch = matchValue(mockDispatch2.path,
|
|
35222
|
+
function matchKey(mockDispatch2, { path: path30, method, body, headers }) {
|
|
35223
|
+
const pathMatch = matchValue(mockDispatch2.path, path30);
|
|
34067
35224
|
const methodMatch = matchValue(mockDispatch2.method, method);
|
|
34068
35225
|
const bodyMatch = typeof mockDispatch2.body !== "undefined" ? matchValue(mockDispatch2.body, body) : true;
|
|
34069
35226
|
const headersMatch = matchHeaders(mockDispatch2, headers);
|
|
@@ -34081,7 +35238,7 @@ var require_mock_utils = __commonJS({
|
|
|
34081
35238
|
function getMockDispatch(mockDispatches, key) {
|
|
34082
35239
|
const basePath = key.query ? buildURL(key.path, key.query) : key.path;
|
|
34083
35240
|
const resolvedPath = typeof basePath === "string" ? safeUrl(basePath) : basePath;
|
|
34084
|
-
let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path:
|
|
35241
|
+
let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path: path30 }) => matchValue(safeUrl(path30), resolvedPath));
|
|
34085
35242
|
if (matchedMockDispatches.length === 0) {
|
|
34086
35243
|
throw new MockNotMatchedError(`Mock dispatch not matched for path '${resolvedPath}'`);
|
|
34087
35244
|
}
|
|
@@ -34118,9 +35275,9 @@ var require_mock_utils = __commonJS({
|
|
|
34118
35275
|
}
|
|
34119
35276
|
}
|
|
34120
35277
|
function buildKey(opts) {
|
|
34121
|
-
const { path:
|
|
35278
|
+
const { path: path30, method, body, headers, query } = opts;
|
|
34122
35279
|
return {
|
|
34123
|
-
path:
|
|
35280
|
+
path: path30,
|
|
34124
35281
|
method,
|
|
34125
35282
|
body,
|
|
34126
35283
|
headers,
|
|
@@ -34569,10 +35726,10 @@ var require_pending_interceptors_formatter = __commonJS({
|
|
|
34569
35726
|
}
|
|
34570
35727
|
format(pendingInterceptors) {
|
|
34571
35728
|
const withPrettyHeaders = pendingInterceptors.map(
|
|
34572
|
-
({ method, path:
|
|
35729
|
+
({ method, path: path30, data: { statusCode }, persist, times, timesInvoked, origin }) => ({
|
|
34573
35730
|
Method: method,
|
|
34574
35731
|
Origin: origin,
|
|
34575
|
-
Path:
|
|
35732
|
+
Path: path30,
|
|
34576
35733
|
"Status code": statusCode,
|
|
34577
35734
|
Persistent: persist ? "\u2705" : "\u274C",
|
|
34578
35735
|
Invocations: timesInvoked,
|
|
@@ -39193,8 +40350,8 @@ var require_util6 = __commonJS({
|
|
|
39193
40350
|
}
|
|
39194
40351
|
}
|
|
39195
40352
|
}
|
|
39196
|
-
function validateCookiePath(
|
|
39197
|
-
for (const char of
|
|
40353
|
+
function validateCookiePath(path30) {
|
|
40354
|
+
for (const char of path30) {
|
|
39198
40355
|
const code = char.charCodeAt(0);
|
|
39199
40356
|
if (code < 33 || char === ";") {
|
|
39200
40357
|
throw new Error("Invalid cookie path");
|
|
@@ -40874,11 +42031,11 @@ var require_undici = __commonJS({
|
|
|
40874
42031
|
if (typeof opts.path !== "string") {
|
|
40875
42032
|
throw new InvalidArgumentError("invalid opts.path");
|
|
40876
42033
|
}
|
|
40877
|
-
let
|
|
42034
|
+
let path30 = opts.path;
|
|
40878
42035
|
if (!opts.path.startsWith("/")) {
|
|
40879
|
-
|
|
42036
|
+
path30 = `/${path30}`;
|
|
40880
42037
|
}
|
|
40881
|
-
url = new URL(util.parseOrigin(url).origin +
|
|
42038
|
+
url = new URL(util.parseOrigin(url).origin + path30);
|
|
40882
42039
|
} else {
|
|
40883
42040
|
if (!opts) {
|
|
40884
42041
|
opts = typeof url === "object" ? url : {};
|
|
@@ -41306,10 +42463,11 @@ var init_mcp_check_provider = __esm({
|
|
|
41306
42463
|
'No custom tools available. Define tools in the "tools" section of your configuration.'
|
|
41307
42464
|
);
|
|
41308
42465
|
}
|
|
41309
|
-
const
|
|
41310
|
-
if (!
|
|
42466
|
+
const hasTool = await this.customToolExecutor.hasTool(config.method);
|
|
42467
|
+
if (!hasTool) {
|
|
42468
|
+
const availableToolNames = await this.customToolExecutor.getToolNames();
|
|
41311
42469
|
throw new Error(
|
|
41312
|
-
`Custom tool not found: ${config.method}. Available tools: ${
|
|
42470
|
+
`Custom tool not found: ${config.method}. Available tools: ${availableToolNames.join(", ")}`
|
|
41313
42471
|
);
|
|
41314
42472
|
}
|
|
41315
42473
|
const context2 = {
|
|
@@ -41420,11 +42578,24 @@ var init_mcp_check_provider = __esm({
|
|
|
41420
42578
|
* Execute MCP method using stdio transport
|
|
41421
42579
|
*/
|
|
41422
42580
|
async executeStdioMethod(config, methodArgs, timeout) {
|
|
42581
|
+
const env = {};
|
|
42582
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
42583
|
+
if (value !== void 0) {
|
|
42584
|
+
env[key] = value;
|
|
42585
|
+
}
|
|
42586
|
+
}
|
|
42587
|
+
if (config.env) {
|
|
42588
|
+
for (const [key, value] of Object.entries(config.env)) {
|
|
42589
|
+
if (value !== void 0 && value !== null) {
|
|
42590
|
+
env[key] = String(EnvironmentResolver.resolveValue(value));
|
|
42591
|
+
}
|
|
42592
|
+
}
|
|
42593
|
+
}
|
|
41423
42594
|
return this.executeWithTransport(
|
|
41424
42595
|
() => new import_stdio.StdioClientTransport({
|
|
41425
42596
|
command: config.command,
|
|
41426
42597
|
args: config.command_args,
|
|
41427
|
-
env
|
|
42598
|
+
env,
|
|
41428
42599
|
cwd: config.workingDirectory,
|
|
41429
42600
|
stderr: "pipe"
|
|
41430
42601
|
// Prevent child stderr from corrupting TUI
|
|
@@ -42149,7 +43320,7 @@ var init_stdin_reader = __esm({
|
|
|
42149
43320
|
});
|
|
42150
43321
|
|
|
42151
43322
|
// src/providers/human-input-check-provider.ts
|
|
42152
|
-
var
|
|
43323
|
+
var fs15, path18, HumanInputCheckProvider;
|
|
42153
43324
|
var init_human_input_check_provider = __esm({
|
|
42154
43325
|
"src/providers/human-input-check-provider.ts"() {
|
|
42155
43326
|
"use strict";
|
|
@@ -42158,8 +43329,8 @@ var init_human_input_check_provider = __esm({
|
|
|
42158
43329
|
init_prompt_state();
|
|
42159
43330
|
init_liquid_extensions();
|
|
42160
43331
|
init_stdin_reader();
|
|
42161
|
-
|
|
42162
|
-
|
|
43332
|
+
fs15 = __toESM(require("fs"));
|
|
43333
|
+
path18 = __toESM(require("path"));
|
|
42163
43334
|
HumanInputCheckProvider = class _HumanInputCheckProvider extends CheckProvider {
|
|
42164
43335
|
liquid;
|
|
42165
43336
|
/**
|
|
@@ -42333,19 +43504,19 @@ var init_human_input_check_provider = __esm({
|
|
|
42333
43504
|
*/
|
|
42334
43505
|
async tryReadFile(filePath) {
|
|
42335
43506
|
try {
|
|
42336
|
-
const absolutePath =
|
|
42337
|
-
const normalizedPath =
|
|
43507
|
+
const absolutePath = path18.isAbsolute(filePath) ? filePath : path18.resolve(process.cwd(), filePath);
|
|
43508
|
+
const normalizedPath = path18.normalize(absolutePath);
|
|
42338
43509
|
const cwd = process.cwd();
|
|
42339
|
-
if (!normalizedPath.startsWith(cwd +
|
|
43510
|
+
if (!normalizedPath.startsWith(cwd + path18.sep) && normalizedPath !== cwd) {
|
|
42340
43511
|
return null;
|
|
42341
43512
|
}
|
|
42342
43513
|
try {
|
|
42343
|
-
await
|
|
42344
|
-
const stats = await
|
|
43514
|
+
await fs15.promises.access(normalizedPath, fs15.constants.R_OK);
|
|
43515
|
+
const stats = await fs15.promises.stat(normalizedPath);
|
|
42345
43516
|
if (!stats.isFile()) {
|
|
42346
43517
|
return null;
|
|
42347
43518
|
}
|
|
42348
|
-
const content = await
|
|
43519
|
+
const content = await fs15.promises.readFile(normalizedPath, "utf-8");
|
|
42349
43520
|
return content.trim();
|
|
42350
43521
|
} catch {
|
|
42351
43522
|
return null;
|
|
@@ -42630,6 +43801,494 @@ ${snippet}`
|
|
|
42630
43801
|
}
|
|
42631
43802
|
});
|
|
42632
43803
|
|
|
43804
|
+
// src/utils/script-tool-environment.ts
|
|
43805
|
+
function formatSyntaxError(code, err) {
|
|
43806
|
+
const line = err.loc?.line ?? 0;
|
|
43807
|
+
const col = err.loc?.column ?? 0;
|
|
43808
|
+
const baseMsg = err.message?.replace(/\s*\(\d+:\d+\)$/, "") || "Syntax error";
|
|
43809
|
+
if (!line) return `Syntax error: ${baseMsg}`;
|
|
43810
|
+
const lines = code.split("\n");
|
|
43811
|
+
const snippetLines = [];
|
|
43812
|
+
const start = Math.max(0, line - 2);
|
|
43813
|
+
const end = Math.min(lines.length, line + 1);
|
|
43814
|
+
for (let i = start; i < end; i++) {
|
|
43815
|
+
const lineNum = String(i + 1).padStart(3, " ");
|
|
43816
|
+
if (i === line - 1) {
|
|
43817
|
+
snippetLines.push(` > ${lineNum} | ${lines[i]}`);
|
|
43818
|
+
snippetLines.push(` ${" ".repeat(lineNum.length)} | ${" ".repeat(col)}^`);
|
|
43819
|
+
} else {
|
|
43820
|
+
snippetLines.push(` ${lineNum} | ${lines[i]}`);
|
|
43821
|
+
}
|
|
43822
|
+
}
|
|
43823
|
+
return `Syntax error at line ${line}, column ${col}: ${baseMsg}
|
|
43824
|
+
|
|
43825
|
+
${snippetLines.join("\n")}`;
|
|
43826
|
+
}
|
|
43827
|
+
function levenshtein(a, b) {
|
|
43828
|
+
const m = a.length;
|
|
43829
|
+
const n = b.length;
|
|
43830
|
+
const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
|
|
43831
|
+
for (let i = 0; i <= m; i++) dp[i][0] = i;
|
|
43832
|
+
for (let j = 0; j <= n; j++) dp[0][j] = j;
|
|
43833
|
+
for (let i = 1; i <= m; i++) {
|
|
43834
|
+
for (let j = 1; j <= n; j++) {
|
|
43835
|
+
dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
|
|
43836
|
+
}
|
|
43837
|
+
}
|
|
43838
|
+
return dp[m][n];
|
|
43839
|
+
}
|
|
43840
|
+
function transformScriptForAsync(code, asyncFunctionNames, opts) {
|
|
43841
|
+
if (asyncFunctionNames.size === 0) {
|
|
43842
|
+
return `return (() => {
|
|
43843
|
+
${code}
|
|
43844
|
+
})()`;
|
|
43845
|
+
}
|
|
43846
|
+
let ast;
|
|
43847
|
+
try {
|
|
43848
|
+
ast = acorn.parse(code, {
|
|
43849
|
+
ecmaVersion: 2022,
|
|
43850
|
+
sourceType: "script",
|
|
43851
|
+
allowReturnOutsideFunction: true,
|
|
43852
|
+
locations: true
|
|
43853
|
+
});
|
|
43854
|
+
} catch (e) {
|
|
43855
|
+
throw new Error(formatSyntaxError(code, e));
|
|
43856
|
+
}
|
|
43857
|
+
if (opts?.knownGlobals) {
|
|
43858
|
+
lintUnknownCalls(code, ast, opts.knownGlobals, opts.disabledBuiltins);
|
|
43859
|
+
}
|
|
43860
|
+
const insertions = [];
|
|
43861
|
+
const functionsNeedingAsync = /* @__PURE__ */ new Set();
|
|
43862
|
+
const functionScopes = [];
|
|
43863
|
+
walk.full(ast, (node) => {
|
|
43864
|
+
if (node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression") {
|
|
43865
|
+
functionScopes.push(node);
|
|
43866
|
+
}
|
|
43867
|
+
});
|
|
43868
|
+
walk.full(ast, (node) => {
|
|
43869
|
+
if (node.type !== "CallExpression") return;
|
|
43870
|
+
const calleeName = getCalleeName(node);
|
|
43871
|
+
if (!calleeName || !asyncFunctionNames.has(calleeName)) return;
|
|
43872
|
+
insertions.push({ offset: node.start, text: "await " });
|
|
43873
|
+
for (const fn of functionScopes) {
|
|
43874
|
+
const body2 = fn.body;
|
|
43875
|
+
if (body2 && body2.start <= node.start && body2.end >= node.end) {
|
|
43876
|
+
functionsNeedingAsync.add(fn);
|
|
43877
|
+
}
|
|
43878
|
+
}
|
|
43879
|
+
});
|
|
43880
|
+
walk.full(ast, (node) => {
|
|
43881
|
+
if (node.type !== "CallExpression") return;
|
|
43882
|
+
const callNode = node;
|
|
43883
|
+
const calleeName = getCalleeName(callNode);
|
|
43884
|
+
if (calleeName !== "map" || !callNode.arguments || callNode.arguments.length < 2) return;
|
|
43885
|
+
const callback = callNode.arguments[1];
|
|
43886
|
+
if (callback.type === "ArrowFunctionExpression" || callback.type === "FunctionExpression") {
|
|
43887
|
+
let hasAsyncCall = false;
|
|
43888
|
+
walk.full(callback, (inner) => {
|
|
43889
|
+
if (inner.type === "CallExpression") {
|
|
43890
|
+
const innerName = getCalleeName(inner);
|
|
43891
|
+
if (innerName && asyncFunctionNames.has(innerName)) {
|
|
43892
|
+
hasAsyncCall = true;
|
|
43893
|
+
}
|
|
43894
|
+
}
|
|
43895
|
+
});
|
|
43896
|
+
if (hasAsyncCall) {
|
|
43897
|
+
functionsNeedingAsync.add(callback);
|
|
43898
|
+
}
|
|
43899
|
+
}
|
|
43900
|
+
});
|
|
43901
|
+
walk.full(ast, (node) => {
|
|
43902
|
+
if (node.type === "WhileStatement" || node.type === "ForStatement" || node.type === "ForOfStatement" || node.type === "ForInStatement") {
|
|
43903
|
+
const body2 = node.body;
|
|
43904
|
+
if (body2 && body2.type === "BlockStatement" && body2.body && body2.body.length > 0) {
|
|
43905
|
+
insertions.push({ offset: body2.start + 1, text: " __checkLoop();" });
|
|
43906
|
+
}
|
|
43907
|
+
}
|
|
43908
|
+
});
|
|
43909
|
+
for (const fn of functionsNeedingAsync) {
|
|
43910
|
+
insertions.push({ offset: fn.start, text: "async " });
|
|
43911
|
+
}
|
|
43912
|
+
const body = ast.body;
|
|
43913
|
+
if (body && body.length > 0) {
|
|
43914
|
+
const lastStmt = body[body.length - 1];
|
|
43915
|
+
if (lastStmt.type === "ExpressionStatement") {
|
|
43916
|
+
insertions.push({ offset: lastStmt.start, text: "return " });
|
|
43917
|
+
}
|
|
43918
|
+
}
|
|
43919
|
+
insertions.sort((a, b) => b.offset - a.offset);
|
|
43920
|
+
let transformed = code;
|
|
43921
|
+
for (const ins of insertions) {
|
|
43922
|
+
transformed = transformed.slice(0, ins.offset) + ins.text + transformed.slice(ins.offset);
|
|
43923
|
+
}
|
|
43924
|
+
return `return (async () => {
|
|
43925
|
+
${transformed}
|
|
43926
|
+
})()`;
|
|
43927
|
+
}
|
|
43928
|
+
function getCalleeName(callExpr) {
|
|
43929
|
+
const callee = callExpr.callee;
|
|
43930
|
+
if (callee.type === "Identifier" && callee.name) {
|
|
43931
|
+
return callee.name;
|
|
43932
|
+
}
|
|
43933
|
+
return null;
|
|
43934
|
+
}
|
|
43935
|
+
function lintUnknownCalls(_code, ast, knownGlobals, disabledBuiltins) {
|
|
43936
|
+
const declaredFunctions = /* @__PURE__ */ new Set();
|
|
43937
|
+
walk.full(ast, (node) => {
|
|
43938
|
+
if (node.type === "FunctionDeclaration" && node.id?.name) {
|
|
43939
|
+
declaredFunctions.add(node.id.name);
|
|
43940
|
+
}
|
|
43941
|
+
if (node.type === "VariableDeclarator" && node.id?.name) {
|
|
43942
|
+
const init = node.init;
|
|
43943
|
+
if (init && (init.type === "ArrowFunctionExpression" || init.type === "FunctionExpression")) {
|
|
43944
|
+
declaredFunctions.add(node.id.name);
|
|
43945
|
+
}
|
|
43946
|
+
}
|
|
43947
|
+
});
|
|
43948
|
+
const warnings = [];
|
|
43949
|
+
walk.full(ast, (node) => {
|
|
43950
|
+
if (node.type !== "CallExpression") return;
|
|
43951
|
+
const name = getCalleeName(node);
|
|
43952
|
+
if (!name) return;
|
|
43953
|
+
if (knownGlobals.has(name) || JS_BUILTINS.has(name) || declaredFunctions.has(name)) return;
|
|
43954
|
+
const loc = node.loc?.start;
|
|
43955
|
+
const lineInfo = loc ? ` (line ${loc.line}, column ${loc.column})` : "";
|
|
43956
|
+
if (disabledBuiltins?.has(name)) {
|
|
43957
|
+
const hint = disabledBuiltins.get(name);
|
|
43958
|
+
warnings.push(`'${name}()' is not enabled${lineInfo}. ${hint}`);
|
|
43959
|
+
return;
|
|
43960
|
+
}
|
|
43961
|
+
const allNames = [...knownGlobals];
|
|
43962
|
+
let bestMatch = "";
|
|
43963
|
+
let bestDist = Infinity;
|
|
43964
|
+
for (const candidate of allNames) {
|
|
43965
|
+
const dist = levenshtein(name.toLowerCase(), candidate.toLowerCase());
|
|
43966
|
+
if (dist < bestDist) {
|
|
43967
|
+
bestDist = dist;
|
|
43968
|
+
bestMatch = candidate;
|
|
43969
|
+
}
|
|
43970
|
+
}
|
|
43971
|
+
const maxLen = Math.max(name.length, bestMatch.length);
|
|
43972
|
+
const suggestion = bestDist <= Math.ceil(maxLen * 0.4) ? ` Did you mean '${bestMatch}'?` : "";
|
|
43973
|
+
warnings.push(`Unknown function '${name}()'${lineInfo}.${suggestion}`);
|
|
43974
|
+
});
|
|
43975
|
+
if (warnings.length > 0) {
|
|
43976
|
+
throw new Error(`Script lint errors:
|
|
43977
|
+
${warnings.map((w) => ` - ${w}`).join("\n")}`);
|
|
43978
|
+
}
|
|
43979
|
+
}
|
|
43980
|
+
function tryParseJSON(text) {
|
|
43981
|
+
if (typeof text !== "string") return text;
|
|
43982
|
+
const firstChar = text.trimStart()[0];
|
|
43983
|
+
if (firstChar === "{" || firstChar === "[") {
|
|
43984
|
+
try {
|
|
43985
|
+
return JSON.parse(text);
|
|
43986
|
+
} catch {
|
|
43987
|
+
}
|
|
43988
|
+
}
|
|
43989
|
+
return text;
|
|
43990
|
+
}
|
|
43991
|
+
function buildToolGlobals(opts) {
|
|
43992
|
+
const { resolvedTools, mcpClients, toolContext, workflowContext } = opts;
|
|
43993
|
+
const globals = {};
|
|
43994
|
+
const asyncFunctionNames = /* @__PURE__ */ new Set();
|
|
43995
|
+
const commandTools = {};
|
|
43996
|
+
for (const [name, tool] of resolvedTools) {
|
|
43997
|
+
if (!isWorkflowTool(tool)) {
|
|
43998
|
+
commandTools[name] = tool;
|
|
43999
|
+
}
|
|
44000
|
+
}
|
|
44001
|
+
const toolExecutor = new CustomToolExecutor(commandTools);
|
|
44002
|
+
const allToolInfo = [];
|
|
44003
|
+
for (const [name, tool] of resolvedTools) {
|
|
44004
|
+
const toolFn = async (args = {}) => {
|
|
44005
|
+
try {
|
|
44006
|
+
if (isWorkflowTool(tool)) {
|
|
44007
|
+
if (!workflowContext) {
|
|
44008
|
+
return `ERROR: Workflow context not available for tool '${name}'`;
|
|
44009
|
+
}
|
|
44010
|
+
return await executeWorkflowAsTool(
|
|
44011
|
+
tool.__workflowId,
|
|
44012
|
+
args,
|
|
44013
|
+
workflowContext,
|
|
44014
|
+
tool.__argsOverrides
|
|
44015
|
+
);
|
|
44016
|
+
}
|
|
44017
|
+
return await toolExecutor.execute(name, args, toolContext);
|
|
44018
|
+
} catch (e) {
|
|
44019
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
44020
|
+
logger.warn(`[script:${name}] Tool error: ${msg}`);
|
|
44021
|
+
return `ERROR: ${msg}`;
|
|
44022
|
+
}
|
|
44023
|
+
};
|
|
44024
|
+
globals[name] = toolFn;
|
|
44025
|
+
asyncFunctionNames.add(name);
|
|
44026
|
+
allToolInfo.push({ name, description: tool.description });
|
|
44027
|
+
}
|
|
44028
|
+
if (mcpClients) {
|
|
44029
|
+
for (const entry of mcpClients) {
|
|
44030
|
+
for (const mcpTool of entry.tools) {
|
|
44031
|
+
const globalName = `${entry.serverName}_${mcpTool.name}`;
|
|
44032
|
+
const mcpToolFn = async (args = {}) => {
|
|
44033
|
+
try {
|
|
44034
|
+
const result = await entry.client.callTool({
|
|
44035
|
+
name: mcpTool.name,
|
|
44036
|
+
arguments: args
|
|
44037
|
+
});
|
|
44038
|
+
const content = result?.content;
|
|
44039
|
+
if (Array.isArray(content) && content.length > 0) {
|
|
44040
|
+
const text = content[0]?.text;
|
|
44041
|
+
if (text !== void 0) {
|
|
44042
|
+
return tryParseJSON(text);
|
|
44043
|
+
}
|
|
44044
|
+
}
|
|
44045
|
+
return result;
|
|
44046
|
+
} catch (e) {
|
|
44047
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
44048
|
+
logger.warn(`[script:${globalName}] MCP tool error: ${msg}`);
|
|
44049
|
+
return `ERROR: ${msg}`;
|
|
44050
|
+
}
|
|
44051
|
+
};
|
|
44052
|
+
globals[globalName] = mcpToolFn;
|
|
44053
|
+
asyncFunctionNames.add(globalName);
|
|
44054
|
+
allToolInfo.push({ name: globalName, description: mcpTool.description });
|
|
44055
|
+
}
|
|
44056
|
+
}
|
|
44057
|
+
}
|
|
44058
|
+
const callToolFn = async (name, args = {}) => {
|
|
44059
|
+
const fn = globals[name];
|
|
44060
|
+
if (!fn || typeof fn !== "function") {
|
|
44061
|
+
const available = Array.from(asyncFunctionNames).join(", ");
|
|
44062
|
+
return `ERROR: Tool '${name}' not found. Available: ${available}`;
|
|
44063
|
+
}
|
|
44064
|
+
return fn(args);
|
|
44065
|
+
};
|
|
44066
|
+
globals.callTool = callToolFn;
|
|
44067
|
+
asyncFunctionNames.add("callTool");
|
|
44068
|
+
globals.listTools = () => {
|
|
44069
|
+
return [...allToolInfo];
|
|
44070
|
+
};
|
|
44071
|
+
return { globals, asyncFunctionNames };
|
|
44072
|
+
}
|
|
44073
|
+
function buildBuiltinGlobals(opts) {
|
|
44074
|
+
const globals = {};
|
|
44075
|
+
const asyncFunctionNames = /* @__PURE__ */ new Set();
|
|
44076
|
+
const scheduleFn = async (args = {}) => {
|
|
44077
|
+
try {
|
|
44078
|
+
const { handleScheduleAction: handleScheduleAction2, buildScheduleToolContext: buildScheduleToolContext2 } = await Promise.resolve().then(() => (init_schedule_tool(), schedule_tool_exports));
|
|
44079
|
+
const { extractSlackContext: extractSlackContext2 } = await Promise.resolve().then(() => (init_schedule_tool_handler(), schedule_tool_handler_exports));
|
|
44080
|
+
const parentCtx = opts.sessionInfo?._parentContext;
|
|
44081
|
+
const webhookData = parentCtx?.prInfo?.eventContext?.webhookData;
|
|
44082
|
+
const visorCfg = parentCtx?.config;
|
|
44083
|
+
const slackContext = webhookData ? extractSlackContext2(webhookData) : null;
|
|
44084
|
+
const availableWorkflows = visorCfg?.checks ? Object.keys(visorCfg.checks) : void 0;
|
|
44085
|
+
const permissions = visorCfg?.scheduler?.permissions;
|
|
44086
|
+
const context2 = buildScheduleToolContext2(
|
|
44087
|
+
{
|
|
44088
|
+
slackContext: slackContext || void 0,
|
|
44089
|
+
cliContext: slackContext ? void 0 : { userId: "script" }
|
|
44090
|
+
},
|
|
44091
|
+
availableWorkflows,
|
|
44092
|
+
permissions
|
|
44093
|
+
);
|
|
44094
|
+
return await handleScheduleAction2(args, context2);
|
|
44095
|
+
} catch (e) {
|
|
44096
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
44097
|
+
logger.warn(`[script:schedule] Error: ${msg}`);
|
|
44098
|
+
return `ERROR: ${msg}`;
|
|
44099
|
+
}
|
|
44100
|
+
};
|
|
44101
|
+
globals.schedule = scheduleFn;
|
|
44102
|
+
asyncFunctionNames.add("schedule");
|
|
44103
|
+
if (opts.config.enable_fetch === true) {
|
|
44104
|
+
const fetchFn = async (args = {}) => {
|
|
44105
|
+
try {
|
|
44106
|
+
const url = String(args.url || "");
|
|
44107
|
+
if (!url) return "ERROR: url is required";
|
|
44108
|
+
const method = String(args.method || "GET");
|
|
44109
|
+
const headers = args.headers || {};
|
|
44110
|
+
const body = args.body != null ? String(args.body) : void 0;
|
|
44111
|
+
const timeout = Number(args.timeout) || 3e4;
|
|
44112
|
+
const controller = new AbortController();
|
|
44113
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
44114
|
+
try {
|
|
44115
|
+
const resp = await globalThis.fetch(url, {
|
|
44116
|
+
method,
|
|
44117
|
+
headers,
|
|
44118
|
+
body: method !== "GET" ? body : void 0,
|
|
44119
|
+
signal: controller.signal
|
|
44120
|
+
});
|
|
44121
|
+
clearTimeout(timeoutId);
|
|
44122
|
+
const contentType = resp.headers.get("content-type") || "";
|
|
44123
|
+
if (contentType.includes("json")) return await resp.json();
|
|
44124
|
+
const text = await resp.text();
|
|
44125
|
+
try {
|
|
44126
|
+
return JSON.parse(text);
|
|
44127
|
+
} catch {
|
|
44128
|
+
return text;
|
|
44129
|
+
}
|
|
44130
|
+
} finally {
|
|
44131
|
+
clearTimeout(timeoutId);
|
|
44132
|
+
}
|
|
44133
|
+
} catch (e) {
|
|
44134
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
44135
|
+
logger.warn(`[script:fetch] Error: ${msg}`);
|
|
44136
|
+
return `ERROR: ${msg}`;
|
|
44137
|
+
}
|
|
44138
|
+
};
|
|
44139
|
+
globals.fetch = fetchFn;
|
|
44140
|
+
asyncFunctionNames.add("fetch");
|
|
44141
|
+
}
|
|
44142
|
+
const octokit = opts.config.eventContext?.octokit;
|
|
44143
|
+
if (octokit) {
|
|
44144
|
+
const githubFn = async (args = {}) => {
|
|
44145
|
+
try {
|
|
44146
|
+
const op = String(args.op || "");
|
|
44147
|
+
const repoEnv = process.env.GITHUB_REPOSITORY || "";
|
|
44148
|
+
let owner = "";
|
|
44149
|
+
let repo = "";
|
|
44150
|
+
if (repoEnv.includes("/")) {
|
|
44151
|
+
[owner, repo] = repoEnv.split("/");
|
|
44152
|
+
}
|
|
44153
|
+
if (!owner || !repo) {
|
|
44154
|
+
const ec = opts.config.eventContext || {};
|
|
44155
|
+
owner = owner || ec.repository?.owner?.login || "";
|
|
44156
|
+
repo = repo || ec.repository?.name || "";
|
|
44157
|
+
}
|
|
44158
|
+
const prNumber = opts.prInfo?.number;
|
|
44159
|
+
if (!owner || !repo || !prNumber) {
|
|
44160
|
+
return "ERROR: Missing GitHub repo/PR context";
|
|
44161
|
+
}
|
|
44162
|
+
const values = Array.isArray(args.values) ? args.values.map(String) : typeof args.value === "string" ? [args.value] : typeof args.values === "string" ? [args.values] : [];
|
|
44163
|
+
switch (op) {
|
|
44164
|
+
case "labels.add":
|
|
44165
|
+
await octokit.rest.issues.addLabels({
|
|
44166
|
+
owner,
|
|
44167
|
+
repo,
|
|
44168
|
+
issue_number: prNumber,
|
|
44169
|
+
labels: values
|
|
44170
|
+
});
|
|
44171
|
+
return { success: true, op };
|
|
44172
|
+
case "labels.remove":
|
|
44173
|
+
for (const name of values) {
|
|
44174
|
+
try {
|
|
44175
|
+
await octokit.rest.issues.removeLabel({
|
|
44176
|
+
owner,
|
|
44177
|
+
repo,
|
|
44178
|
+
issue_number: prNumber,
|
|
44179
|
+
name
|
|
44180
|
+
});
|
|
44181
|
+
} catch {
|
|
44182
|
+
}
|
|
44183
|
+
}
|
|
44184
|
+
return { success: true, op };
|
|
44185
|
+
case "comment.create":
|
|
44186
|
+
await octokit.rest.issues.createComment({
|
|
44187
|
+
owner,
|
|
44188
|
+
repo,
|
|
44189
|
+
issue_number: prNumber,
|
|
44190
|
+
body: values[0] || ""
|
|
44191
|
+
});
|
|
44192
|
+
return { success: true, op };
|
|
44193
|
+
default:
|
|
44194
|
+
return `ERROR: Unknown github op '${op}'. Supported: labels.add, labels.remove, comment.create`;
|
|
44195
|
+
}
|
|
44196
|
+
} catch (e) {
|
|
44197
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
44198
|
+
logger.warn(`[script:github] Error: ${msg}`);
|
|
44199
|
+
return `ERROR: ${msg}`;
|
|
44200
|
+
}
|
|
44201
|
+
};
|
|
44202
|
+
globals.github = githubFn;
|
|
44203
|
+
asyncFunctionNames.add("github");
|
|
44204
|
+
}
|
|
44205
|
+
if (opts.config.enable_bash === true) {
|
|
44206
|
+
const bashFn = async (args = {}) => {
|
|
44207
|
+
try {
|
|
44208
|
+
const { CommandExecutor: CommandExecutor2 } = await Promise.resolve().then(() => (init_command_executor(), command_executor_exports));
|
|
44209
|
+
const executor = CommandExecutor2.getInstance();
|
|
44210
|
+
const command = String(args.command || "");
|
|
44211
|
+
if (!command) return "ERROR: command is required";
|
|
44212
|
+
return await executor.execute(command, {
|
|
44213
|
+
cwd: args.cwd ? String(args.cwd) : void 0,
|
|
44214
|
+
env: args.env,
|
|
44215
|
+
timeout: Number(args.timeout) || 3e4
|
|
44216
|
+
});
|
|
44217
|
+
} catch (e) {
|
|
44218
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
44219
|
+
logger.warn(`[script:bash] Error: ${msg}`);
|
|
44220
|
+
return `ERROR: ${msg}`;
|
|
44221
|
+
}
|
|
44222
|
+
};
|
|
44223
|
+
globals.bash = bashFn;
|
|
44224
|
+
asyncFunctionNames.add("bash");
|
|
44225
|
+
}
|
|
44226
|
+
return { globals, asyncFunctionNames };
|
|
44227
|
+
}
|
|
44228
|
+
var acorn, walk, JS_BUILTINS;
|
|
44229
|
+
var init_script_tool_environment = __esm({
|
|
44230
|
+
"src/utils/script-tool-environment.ts"() {
|
|
44231
|
+
"use strict";
|
|
44232
|
+
acorn = __toESM(require("acorn"));
|
|
44233
|
+
walk = __toESM(require("acorn-walk"));
|
|
44234
|
+
init_custom_tool_executor();
|
|
44235
|
+
init_workflow_tool_executor();
|
|
44236
|
+
init_logger();
|
|
44237
|
+
JS_BUILTINS = /* @__PURE__ */ new Set([
|
|
44238
|
+
// Constructors & types
|
|
44239
|
+
"Array",
|
|
44240
|
+
"Object",
|
|
44241
|
+
"String",
|
|
44242
|
+
"Number",
|
|
44243
|
+
"Boolean",
|
|
44244
|
+
"Date",
|
|
44245
|
+
"RegExp",
|
|
44246
|
+
"Error",
|
|
44247
|
+
"TypeError",
|
|
44248
|
+
"RangeError",
|
|
44249
|
+
"Map",
|
|
44250
|
+
"Set",
|
|
44251
|
+
"WeakMap",
|
|
44252
|
+
"WeakSet",
|
|
44253
|
+
"Promise",
|
|
44254
|
+
"Symbol",
|
|
44255
|
+
"BigInt",
|
|
44256
|
+
"Proxy",
|
|
44257
|
+
"Reflect",
|
|
44258
|
+
// Static methods commonly called as functions
|
|
44259
|
+
"parseInt",
|
|
44260
|
+
"parseFloat",
|
|
44261
|
+
"isNaN",
|
|
44262
|
+
"isFinite",
|
|
44263
|
+
"encodeURIComponent",
|
|
44264
|
+
"decodeURIComponent",
|
|
44265
|
+
"encodeURI",
|
|
44266
|
+
"decodeURI",
|
|
44267
|
+
// Timers
|
|
44268
|
+
"setTimeout",
|
|
44269
|
+
"clearTimeout",
|
|
44270
|
+
"setInterval",
|
|
44271
|
+
"clearInterval",
|
|
44272
|
+
// JSON/Math accessed via method calls are on objects, but just in case
|
|
44273
|
+
"JSON",
|
|
44274
|
+
"Math",
|
|
44275
|
+
"console",
|
|
44276
|
+
// Node.js globals — sandbox blocks these at runtime with a better error
|
|
44277
|
+
"require",
|
|
44278
|
+
"process",
|
|
44279
|
+
"Buffer",
|
|
44280
|
+
"global",
|
|
44281
|
+
"globalThis",
|
|
44282
|
+
// Common patterns
|
|
44283
|
+
"eval",
|
|
44284
|
+
"Function",
|
|
44285
|
+
"alert",
|
|
44286
|
+
"confirm",
|
|
44287
|
+
"prompt"
|
|
44288
|
+
]);
|
|
44289
|
+
}
|
|
44290
|
+
});
|
|
44291
|
+
|
|
42633
44292
|
// src/providers/script-check-provider.ts
|
|
42634
44293
|
var ScriptCheckProvider;
|
|
42635
44294
|
var init_script_check_provider = __esm({
|
|
@@ -42642,6 +44301,10 @@ var init_script_check_provider = __esm({
|
|
|
42642
44301
|
init_sandbox();
|
|
42643
44302
|
init_template_context();
|
|
42644
44303
|
init_script_memory_ops();
|
|
44304
|
+
init_tool_resolver();
|
|
44305
|
+
init_script_tool_environment();
|
|
44306
|
+
init_workflow_tool_executor();
|
|
44307
|
+
init_env_resolver();
|
|
42645
44308
|
ScriptCheckProvider = class extends CheckProvider {
|
|
42646
44309
|
liquid;
|
|
42647
44310
|
constructor() {
|
|
@@ -42658,7 +44321,7 @@ var init_script_check_provider = __esm({
|
|
|
42658
44321
|
return "script";
|
|
42659
44322
|
}
|
|
42660
44323
|
getDescription() {
|
|
42661
|
-
return "Execute JavaScript with access to PR context, dependency outputs, and
|
|
44324
|
+
return "Execute JavaScript with access to PR context, dependency outputs, memory, and tools.";
|
|
42662
44325
|
}
|
|
42663
44326
|
async validateConfig(config) {
|
|
42664
44327
|
if (!config || typeof config !== "object") return false;
|
|
@@ -42708,16 +44371,77 @@ var init_script_check_provider = __esm({
|
|
|
42708
44371
|
ctx.atob = (str) => {
|
|
42709
44372
|
return Buffer.from(String(str), "base64").toString("binary");
|
|
42710
44373
|
};
|
|
44374
|
+
const { globals: builtinGlobals, asyncFunctionNames: builtinAsyncNames } = buildBuiltinGlobals({
|
|
44375
|
+
config,
|
|
44376
|
+
prInfo,
|
|
44377
|
+
sessionInfo: _sessionInfo
|
|
44378
|
+
});
|
|
44379
|
+
Object.assign(ctx, builtinGlobals);
|
|
44380
|
+
const hasTools = Array.isArray(config.tools) || config.tools_js || config.mcp_servers;
|
|
42711
44381
|
const sandbox = this.createSecureSandbox();
|
|
42712
44382
|
let result;
|
|
42713
|
-
|
|
42714
|
-
|
|
44383
|
+
let mcpClients = [];
|
|
44384
|
+
try {
|
|
44385
|
+
const asyncFunctionNames = new Set(builtinAsyncNames);
|
|
44386
|
+
if (hasTools) {
|
|
44387
|
+
const toolItems = this.resolveToolItems(config, prInfo, dependencyResults, ctx);
|
|
44388
|
+
const globalTools = config.__globalTools;
|
|
44389
|
+
const resolvedTools = resolveTools(toolItems, globalTools, "[script]");
|
|
44390
|
+
mcpClients = await this.connectMcpServers(config.mcp_servers);
|
|
44391
|
+
const toolContext = {
|
|
44392
|
+
pr: ctx.pr,
|
|
44393
|
+
files: ctx.files || prInfo.files,
|
|
44394
|
+
outputs: ctx.outputs,
|
|
44395
|
+
env: process.env
|
|
44396
|
+
};
|
|
44397
|
+
const parentCtx = _sessionInfo?._parentContext;
|
|
44398
|
+
const workflowContext = {
|
|
44399
|
+
prInfo,
|
|
44400
|
+
outputs: dependencyResults,
|
|
44401
|
+
executionContext: _sessionInfo,
|
|
44402
|
+
workspace: parentCtx?.workspace
|
|
44403
|
+
};
|
|
44404
|
+
const { globals: toolGlobals, asyncFunctionNames: toolAsyncNames } = buildToolGlobals({
|
|
44405
|
+
resolvedTools,
|
|
44406
|
+
mcpClients,
|
|
44407
|
+
toolContext,
|
|
44408
|
+
workflowContext
|
|
44409
|
+
});
|
|
44410
|
+
Object.assign(ctx, toolGlobals);
|
|
44411
|
+
for (const name of toolAsyncNames) asyncFunctionNames.add(name);
|
|
44412
|
+
}
|
|
44413
|
+
let loopIterations = 0;
|
|
44414
|
+
const maxLoopIterations = 1e4;
|
|
44415
|
+
ctx.__checkLoop = () => {
|
|
44416
|
+
loopIterations++;
|
|
44417
|
+
if (loopIterations > maxLoopIterations) {
|
|
44418
|
+
throw new Error(`Loop exceeded maximum of ${maxLoopIterations} iterations`);
|
|
44419
|
+
}
|
|
44420
|
+
};
|
|
44421
|
+
const knownGlobals = new Set(Object.keys(ctx));
|
|
44422
|
+
for (const name of asyncFunctionNames) knownGlobals.add(name);
|
|
44423
|
+
knownGlobals.add("__checkLoop");
|
|
44424
|
+
knownGlobals.add("log");
|
|
44425
|
+
const disabledBuiltins = /* @__PURE__ */ new Map();
|
|
44426
|
+
if (!config.enable_bash) {
|
|
44427
|
+
disabledBuiltins.set("bash", "Add 'enable_bash: true' to your check config to enable it.");
|
|
44428
|
+
}
|
|
44429
|
+
if (!config.enable_fetch) {
|
|
44430
|
+
disabledBuiltins.set(
|
|
44431
|
+
"fetch",
|
|
44432
|
+
"Add 'enable_fetch: true' to your check config to enable it."
|
|
44433
|
+
);
|
|
44434
|
+
}
|
|
44435
|
+
const transformed = transformScriptForAsync(script, asyncFunctionNames, {
|
|
44436
|
+
knownGlobals,
|
|
44437
|
+
disabledBuiltins
|
|
44438
|
+
});
|
|
44439
|
+
result = await compileAndRunAsync(
|
|
42715
44440
|
sandbox,
|
|
42716
|
-
|
|
44441
|
+
transformed,
|
|
42717
44442
|
{ ...ctx },
|
|
42718
44443
|
{
|
|
42719
44444
|
injectLog: true,
|
|
42720
|
-
wrapFunction: true,
|
|
42721
44445
|
logPrefix: "[script]"
|
|
42722
44446
|
}
|
|
42723
44447
|
);
|
|
@@ -42737,6 +44461,8 @@ var init_script_check_provider = __esm({
|
|
|
42737
44461
|
],
|
|
42738
44462
|
output: null
|
|
42739
44463
|
};
|
|
44464
|
+
} finally {
|
|
44465
|
+
await this.disconnectMcpClients(mcpClients);
|
|
42740
44466
|
}
|
|
42741
44467
|
try {
|
|
42742
44468
|
if (needsSave() && memoryStore.getConfig().storage === "file" && memoryStore.getConfig().auto_save) {
|
|
@@ -42762,17 +44488,167 @@ var init_script_check_provider = __esm({
|
|
|
42762
44488
|
}
|
|
42763
44489
|
return out;
|
|
42764
44490
|
}
|
|
44491
|
+
/**
|
|
44492
|
+
* Resolve tool items from static config and optional JS expression.
|
|
44493
|
+
*/
|
|
44494
|
+
resolveToolItems(config, prInfo, dependencyResults, ctx) {
|
|
44495
|
+
let items = [];
|
|
44496
|
+
const staticTools = config.tools;
|
|
44497
|
+
if (Array.isArray(staticTools)) {
|
|
44498
|
+
items = staticTools.filter(
|
|
44499
|
+
(item) => typeof item === "string" || isWorkflowToolReference(item)
|
|
44500
|
+
);
|
|
44501
|
+
}
|
|
44502
|
+
const toolsJsExpr = config.tools_js;
|
|
44503
|
+
if (toolsJsExpr && dependencyResults) {
|
|
44504
|
+
try {
|
|
44505
|
+
const jsSandbox = this.createSecureSandbox();
|
|
44506
|
+
const jsCtx = ctx || buildProviderTemplateContext(prInfo, dependencyResults);
|
|
44507
|
+
jsCtx.env = process.env;
|
|
44508
|
+
jsCtx.inputs = config.workflowInputs || {};
|
|
44509
|
+
const evalResult = compileAndRun(jsSandbox, toolsJsExpr, jsCtx, {
|
|
44510
|
+
injectLog: true,
|
|
44511
|
+
wrapFunction: true,
|
|
44512
|
+
logPrefix: "[tools_js]"
|
|
44513
|
+
});
|
|
44514
|
+
if (Array.isArray(evalResult)) {
|
|
44515
|
+
const dynamic = evalResult.filter(
|
|
44516
|
+
(item) => typeof item === "string" || isWorkflowToolReference(item)
|
|
44517
|
+
);
|
|
44518
|
+
const existingNames = new Set(items.map((i) => typeof i === "string" ? i : i.workflow));
|
|
44519
|
+
for (const tool of dynamic) {
|
|
44520
|
+
const name = typeof tool === "string" ? tool : tool.workflow;
|
|
44521
|
+
if (!existingNames.has(name)) {
|
|
44522
|
+
items.push(tool);
|
|
44523
|
+
}
|
|
44524
|
+
}
|
|
44525
|
+
}
|
|
44526
|
+
} catch (error) {
|
|
44527
|
+
logger.error(
|
|
44528
|
+
`[script] Failed to evaluate tools_js: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
44529
|
+
);
|
|
44530
|
+
}
|
|
44531
|
+
}
|
|
44532
|
+
return items;
|
|
44533
|
+
}
|
|
44534
|
+
/**
|
|
44535
|
+
* Connect to MCP servers and discover their tools.
|
|
44536
|
+
*/
|
|
44537
|
+
async connectMcpServers(mcpServersConfig) {
|
|
44538
|
+
if (!mcpServersConfig || Object.keys(mcpServersConfig).length === 0) {
|
|
44539
|
+
return [];
|
|
44540
|
+
}
|
|
44541
|
+
const entries = [];
|
|
44542
|
+
for (const [serverName, serverConfig] of Object.entries(mcpServersConfig)) {
|
|
44543
|
+
try {
|
|
44544
|
+
const { Client: Client2 } = await import("@modelcontextprotocol/sdk/client/index.js");
|
|
44545
|
+
const client = new Client2(
|
|
44546
|
+
{ name: "visor-script-client", version: "1.0.0" },
|
|
44547
|
+
{ capabilities: {} }
|
|
44548
|
+
);
|
|
44549
|
+
const env = {};
|
|
44550
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
44551
|
+
if (value !== void 0) env[key] = value;
|
|
44552
|
+
}
|
|
44553
|
+
if (serverConfig.env) {
|
|
44554
|
+
for (const [key, value] of Object.entries(serverConfig.env)) {
|
|
44555
|
+
env[key] = String(EnvironmentResolver.resolveValue(String(value)));
|
|
44556
|
+
}
|
|
44557
|
+
}
|
|
44558
|
+
const timeout = (serverConfig.timeout || 60) * 1e3;
|
|
44559
|
+
if (serverConfig.command) {
|
|
44560
|
+
const { StdioClientTransport: StdioClientTransport2 } = await import("@modelcontextprotocol/sdk/client/stdio.js");
|
|
44561
|
+
const transport = new StdioClientTransport2({
|
|
44562
|
+
command: serverConfig.command,
|
|
44563
|
+
args: serverConfig.args,
|
|
44564
|
+
env,
|
|
44565
|
+
stderr: "pipe"
|
|
44566
|
+
});
|
|
44567
|
+
await Promise.race([
|
|
44568
|
+
client.connect(transport),
|
|
44569
|
+
new Promise(
|
|
44570
|
+
(_, reject) => setTimeout(() => reject(new Error("MCP connection timeout")), timeout)
|
|
44571
|
+
)
|
|
44572
|
+
]);
|
|
44573
|
+
} else if (serverConfig.url) {
|
|
44574
|
+
const transportType = serverConfig.transport || "sse";
|
|
44575
|
+
if (transportType === "sse") {
|
|
44576
|
+
const { SSEClientTransport: SSEClientTransport2 } = await import("@modelcontextprotocol/sdk/client/sse.js");
|
|
44577
|
+
await Promise.race([
|
|
44578
|
+
client.connect(new SSEClientTransport2(new URL(serverConfig.url))),
|
|
44579
|
+
new Promise(
|
|
44580
|
+
(_, reject) => setTimeout(() => reject(new Error("MCP connection timeout")), timeout)
|
|
44581
|
+
)
|
|
44582
|
+
]);
|
|
44583
|
+
} else {
|
|
44584
|
+
const { StreamableHTTPClientTransport: StreamableHTTPClientTransport2 } = await import("@modelcontextprotocol/sdk/client/streamableHttp.js");
|
|
44585
|
+
await Promise.race([
|
|
44586
|
+
client.connect(new StreamableHTTPClientTransport2(new URL(serverConfig.url))),
|
|
44587
|
+
new Promise(
|
|
44588
|
+
(_, reject) => setTimeout(() => reject(new Error("MCP connection timeout")), timeout)
|
|
44589
|
+
)
|
|
44590
|
+
]);
|
|
44591
|
+
}
|
|
44592
|
+
} else {
|
|
44593
|
+
logger.warn(`[script] MCP server '${serverName}' has no command or url, skipping`);
|
|
44594
|
+
continue;
|
|
44595
|
+
}
|
|
44596
|
+
let tools = [];
|
|
44597
|
+
try {
|
|
44598
|
+
const listResult = await client.listTools();
|
|
44599
|
+
tools = (listResult?.tools || []).map((t) => ({
|
|
44600
|
+
name: t.name,
|
|
44601
|
+
description: t.description,
|
|
44602
|
+
inputSchema: t.inputSchema
|
|
44603
|
+
}));
|
|
44604
|
+
logger.debug(
|
|
44605
|
+
`[script] MCP '${serverName}': ${tools.length} tools [${tools.map((t) => t.name).join(", ")}]`
|
|
44606
|
+
);
|
|
44607
|
+
} catch (err) {
|
|
44608
|
+
logger.warn(
|
|
44609
|
+
`[script] Could not list tools from MCP '${serverName}': ${err instanceof Error ? err.message : String(err)}`
|
|
44610
|
+
);
|
|
44611
|
+
}
|
|
44612
|
+
entries.push({ client, serverName, tools });
|
|
44613
|
+
} catch (err) {
|
|
44614
|
+
logger.error(
|
|
44615
|
+
`[script] Failed to connect MCP '${serverName}': ${err instanceof Error ? err.message : String(err)}`
|
|
44616
|
+
);
|
|
44617
|
+
}
|
|
44618
|
+
}
|
|
44619
|
+
return entries;
|
|
44620
|
+
}
|
|
44621
|
+
/**
|
|
44622
|
+
* Disconnect all MCP clients.
|
|
44623
|
+
*/
|
|
44624
|
+
async disconnectMcpClients(clients) {
|
|
44625
|
+
for (const entry of clients) {
|
|
44626
|
+
try {
|
|
44627
|
+
await entry.client.close();
|
|
44628
|
+
} catch (err) {
|
|
44629
|
+
logger.debug(
|
|
44630
|
+
`[script] Error closing MCP '${entry.serverName}': ${err instanceof Error ? err.message : String(err)}`
|
|
44631
|
+
);
|
|
44632
|
+
}
|
|
44633
|
+
}
|
|
44634
|
+
}
|
|
42765
44635
|
getSupportedConfigKeys() {
|
|
42766
44636
|
return [
|
|
42767
44637
|
"type",
|
|
42768
44638
|
"content",
|
|
44639
|
+
"tools",
|
|
44640
|
+
"tools_js",
|
|
44641
|
+
"mcp_servers",
|
|
44642
|
+
"enable_fetch",
|
|
44643
|
+
"enable_bash",
|
|
42769
44644
|
"depends_on",
|
|
42770
44645
|
"group",
|
|
42771
44646
|
"on",
|
|
42772
44647
|
"if",
|
|
42773
44648
|
"fail_if",
|
|
42774
44649
|
"on_fail",
|
|
42775
|
-
"on_success"
|
|
44650
|
+
"on_success",
|
|
44651
|
+
"timeout"
|
|
42776
44652
|
];
|
|
42777
44653
|
}
|
|
42778
44654
|
async isAvailable() {
|
|
@@ -42781,19 +44657,18 @@ var init_script_check_provider = __esm({
|
|
|
42781
44657
|
getRequirements() {
|
|
42782
44658
|
return ["No external dependencies required"];
|
|
42783
44659
|
}
|
|
42784
|
-
// No local buildTemplateContext; uses shared builder above
|
|
42785
44660
|
};
|
|
42786
44661
|
}
|
|
42787
44662
|
});
|
|
42788
44663
|
|
|
42789
44664
|
// src/utils/worktree-manager.ts
|
|
42790
|
-
var
|
|
44665
|
+
var fs16, fsp, path19, crypto, WorktreeManager, worktreeManager;
|
|
42791
44666
|
var init_worktree_manager = __esm({
|
|
42792
44667
|
"src/utils/worktree-manager.ts"() {
|
|
42793
44668
|
"use strict";
|
|
42794
|
-
|
|
44669
|
+
fs16 = __toESM(require("fs"));
|
|
42795
44670
|
fsp = __toESM(require("fs/promises"));
|
|
42796
|
-
|
|
44671
|
+
path19 = __toESM(require("path"));
|
|
42797
44672
|
crypto = __toESM(require("crypto"));
|
|
42798
44673
|
init_command_executor();
|
|
42799
44674
|
init_logger();
|
|
@@ -42809,7 +44684,7 @@ var init_worktree_manager = __esm({
|
|
|
42809
44684
|
} catch {
|
|
42810
44685
|
cwd = "/tmp";
|
|
42811
44686
|
}
|
|
42812
|
-
const defaultBasePath = process.env.VISOR_WORKTREE_PATH ||
|
|
44687
|
+
const defaultBasePath = process.env.VISOR_WORKTREE_PATH || path19.join(cwd, ".visor", "worktrees");
|
|
42813
44688
|
this.config = {
|
|
42814
44689
|
enabled: true,
|
|
42815
44690
|
base_path: defaultBasePath,
|
|
@@ -42846,20 +44721,20 @@ var init_worktree_manager = __esm({
|
|
|
42846
44721
|
}
|
|
42847
44722
|
const reposDir = this.getReposDir();
|
|
42848
44723
|
const worktreesDir = this.getWorktreesDir();
|
|
42849
|
-
if (!
|
|
42850
|
-
|
|
44724
|
+
if (!fs16.existsSync(reposDir)) {
|
|
44725
|
+
fs16.mkdirSync(reposDir, { recursive: true });
|
|
42851
44726
|
logger.debug(`Created repos directory: ${reposDir}`);
|
|
42852
44727
|
}
|
|
42853
|
-
if (!
|
|
42854
|
-
|
|
44728
|
+
if (!fs16.existsSync(worktreesDir)) {
|
|
44729
|
+
fs16.mkdirSync(worktreesDir, { recursive: true });
|
|
42855
44730
|
logger.debug(`Created worktrees directory: ${worktreesDir}`);
|
|
42856
44731
|
}
|
|
42857
44732
|
}
|
|
42858
44733
|
getReposDir() {
|
|
42859
|
-
return
|
|
44734
|
+
return path19.join(this.config.base_path, "repos");
|
|
42860
44735
|
}
|
|
42861
44736
|
getWorktreesDir() {
|
|
42862
|
-
return
|
|
44737
|
+
return path19.join(this.config.base_path, "worktrees");
|
|
42863
44738
|
}
|
|
42864
44739
|
/**
|
|
42865
44740
|
* Generate a deterministic worktree ID based on repository and ref.
|
|
@@ -42877,8 +44752,8 @@ var init_worktree_manager = __esm({
|
|
|
42877
44752
|
async getOrCreateBareRepo(repository, repoUrl, token, fetchDepth, cloneTimeoutMs) {
|
|
42878
44753
|
const reposDir = this.getReposDir();
|
|
42879
44754
|
const repoName = repository.replace(/\//g, "-");
|
|
42880
|
-
const bareRepoPath =
|
|
42881
|
-
if (
|
|
44755
|
+
const bareRepoPath = path19.join(reposDir, `${repoName}.git`);
|
|
44756
|
+
if (fs16.existsSync(bareRepoPath)) {
|
|
42882
44757
|
logger.debug(`Bare repository already exists: ${bareRepoPath}`);
|
|
42883
44758
|
const verifyResult = await this.verifyBareRepoRemote(bareRepoPath, repoUrl);
|
|
42884
44759
|
if (verifyResult === "timeout") {
|
|
@@ -42997,11 +44872,11 @@ var init_worktree_manager = __esm({
|
|
|
42997
44872
|
options.cloneTimeoutMs
|
|
42998
44873
|
);
|
|
42999
44874
|
const worktreeId = this.generateWorktreeId(repository, ref);
|
|
43000
|
-
let worktreePath = options.workingDirectory ||
|
|
44875
|
+
let worktreePath = options.workingDirectory || path19.join(this.getWorktreesDir(), worktreeId);
|
|
43001
44876
|
if (options.workingDirectory) {
|
|
43002
44877
|
worktreePath = this.validatePath(options.workingDirectory);
|
|
43003
44878
|
}
|
|
43004
|
-
if (
|
|
44879
|
+
if (fs16.existsSync(worktreePath)) {
|
|
43005
44880
|
logger.debug(`Worktree already exists: ${worktreePath}`);
|
|
43006
44881
|
const metadata2 = await this.loadMetadata(worktreePath);
|
|
43007
44882
|
if (metadata2) {
|
|
@@ -43242,9 +45117,9 @@ var init_worktree_manager = __esm({
|
|
|
43242
45117
|
const result = await this.executeGitCommand(removeCmd, { timeout: 3e4 });
|
|
43243
45118
|
if (result.exitCode !== 0) {
|
|
43244
45119
|
logger.warn(`Failed to remove worktree via git: ${result.stderr}`);
|
|
43245
|
-
if (
|
|
45120
|
+
if (fs16.existsSync(worktree_path)) {
|
|
43246
45121
|
logger.debug(`Manually removing worktree directory`);
|
|
43247
|
-
|
|
45122
|
+
fs16.rmSync(worktree_path, { recursive: true, force: true });
|
|
43248
45123
|
}
|
|
43249
45124
|
}
|
|
43250
45125
|
this.activeWorktrees.delete(worktreeId);
|
|
@@ -43254,19 +45129,19 @@ var init_worktree_manager = __esm({
|
|
|
43254
45129
|
* Save worktree metadata
|
|
43255
45130
|
*/
|
|
43256
45131
|
async saveMetadata(worktreePath, metadata) {
|
|
43257
|
-
const metadataPath =
|
|
43258
|
-
|
|
45132
|
+
const metadataPath = path19.join(worktreePath, ".visor-metadata.json");
|
|
45133
|
+
fs16.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2), "utf8");
|
|
43259
45134
|
}
|
|
43260
45135
|
/**
|
|
43261
45136
|
* Load worktree metadata
|
|
43262
45137
|
*/
|
|
43263
45138
|
async loadMetadata(worktreePath) {
|
|
43264
|
-
const metadataPath =
|
|
43265
|
-
if (!
|
|
45139
|
+
const metadataPath = path19.join(worktreePath, ".visor-metadata.json");
|
|
45140
|
+
if (!fs16.existsSync(metadataPath)) {
|
|
43266
45141
|
return null;
|
|
43267
45142
|
}
|
|
43268
45143
|
try {
|
|
43269
|
-
const content =
|
|
45144
|
+
const content = fs16.readFileSync(metadataPath, "utf8");
|
|
43270
45145
|
return JSON.parse(content);
|
|
43271
45146
|
} catch (error) {
|
|
43272
45147
|
logger.warn(`Failed to load metadata: ${error}`);
|
|
@@ -43278,14 +45153,14 @@ var init_worktree_manager = __esm({
|
|
|
43278
45153
|
*/
|
|
43279
45154
|
async listWorktrees() {
|
|
43280
45155
|
const worktreesDir = this.getWorktreesDir();
|
|
43281
|
-
if (!
|
|
45156
|
+
if (!fs16.existsSync(worktreesDir)) {
|
|
43282
45157
|
return [];
|
|
43283
45158
|
}
|
|
43284
|
-
const entries =
|
|
45159
|
+
const entries = fs16.readdirSync(worktreesDir, { withFileTypes: true });
|
|
43285
45160
|
const worktrees = [];
|
|
43286
45161
|
for (const entry of entries) {
|
|
43287
45162
|
if (!entry.isDirectory()) continue;
|
|
43288
|
-
const worktreePath =
|
|
45163
|
+
const worktreePath = path19.join(worktreesDir, entry.name);
|
|
43289
45164
|
const metadata = await this.loadMetadata(worktreePath);
|
|
43290
45165
|
if (metadata) {
|
|
43291
45166
|
worktrees.push({
|
|
@@ -43417,8 +45292,8 @@ var init_worktree_manager = __esm({
|
|
|
43417
45292
|
* Validate path to prevent directory traversal
|
|
43418
45293
|
*/
|
|
43419
45294
|
validatePath(userPath) {
|
|
43420
|
-
const resolvedPath =
|
|
43421
|
-
if (!
|
|
45295
|
+
const resolvedPath = path19.resolve(userPath);
|
|
45296
|
+
if (!path19.isAbsolute(resolvedPath)) {
|
|
43422
45297
|
throw new Error("Path must be absolute");
|
|
43423
45298
|
}
|
|
43424
45299
|
const sensitivePatterns = [
|
|
@@ -45127,6 +47002,7 @@ async function executeSingleCheck(checkId, context2, state, emitEvent, transitio
|
|
|
45127
47002
|
...checkConfig,
|
|
45128
47003
|
eventContext: context2.prInfo?.eventContext || {},
|
|
45129
47004
|
__outputHistory: outputHistory,
|
|
47005
|
+
__globalTools: context2.config.tools || {},
|
|
45130
47006
|
// Propagate workflow inputs for template access via {{ inputs.* }}
|
|
45131
47007
|
workflowInputs,
|
|
45132
47008
|
ai: {
|
|
@@ -45444,23 +47320,23 @@ __export(renderer_schema_exports, {
|
|
|
45444
47320
|
});
|
|
45445
47321
|
async function loadRendererSchema(name) {
|
|
45446
47322
|
try {
|
|
45447
|
-
const
|
|
45448
|
-
const
|
|
47323
|
+
const fs26 = await import("fs/promises");
|
|
47324
|
+
const path30 = await import("path");
|
|
45449
47325
|
const sanitized = String(name).replace(/[^a-zA-Z0-9-]/g, "");
|
|
45450
47326
|
if (!sanitized) return void 0;
|
|
45451
47327
|
const candidates = [
|
|
45452
47328
|
// When bundled with ncc, __dirname is dist/ and output/ is at dist/output/
|
|
45453
|
-
|
|
47329
|
+
path30.join(__dirname, "output", sanitized, "schema.json"),
|
|
45454
47330
|
// When running from source, __dirname is src/state-machine/dispatch/ and output/ is at output/
|
|
45455
|
-
|
|
47331
|
+
path30.join(__dirname, "..", "..", "output", sanitized, "schema.json"),
|
|
45456
47332
|
// When running from a checkout with output/ folder copied to CWD
|
|
45457
|
-
|
|
47333
|
+
path30.join(process.cwd(), "output", sanitized, "schema.json"),
|
|
45458
47334
|
// Fallback: cwd/dist/output/
|
|
45459
|
-
|
|
47335
|
+
path30.join(process.cwd(), "dist", "output", sanitized, "schema.json")
|
|
45460
47336
|
];
|
|
45461
47337
|
for (const p of candidates) {
|
|
45462
47338
|
try {
|
|
45463
|
-
const raw = await
|
|
47339
|
+
const raw = await fs26.readFile(p, "utf-8");
|
|
45464
47340
|
return JSON.parse(raw);
|
|
45465
47341
|
} catch {
|
|
45466
47342
|
}
|
|
@@ -47879,8 +49755,8 @@ function updateStats2(results, state, isForEachIteration = false) {
|
|
|
47879
49755
|
async function renderTemplateContent2(checkId, checkConfig, reviewSummary) {
|
|
47880
49756
|
try {
|
|
47881
49757
|
const { createExtendedLiquid: createExtendedLiquid2 } = await Promise.resolve().then(() => (init_liquid_extensions(), liquid_extensions_exports));
|
|
47882
|
-
const
|
|
47883
|
-
const
|
|
49758
|
+
const fs26 = await import("fs/promises");
|
|
49759
|
+
const path30 = await import("path");
|
|
47884
49760
|
const schemaRaw = checkConfig.schema || "plain";
|
|
47885
49761
|
const schema = typeof schemaRaw === "string" && !schemaRaw.includes("{{") && !schemaRaw.includes("{%") ? schemaRaw : typeof schemaRaw === "object" ? "code-review" : "plain";
|
|
47886
49762
|
let templateContent;
|
|
@@ -47889,27 +49765,27 @@ async function renderTemplateContent2(checkId, checkConfig, reviewSummary) {
|
|
|
47889
49765
|
logger.debug(`[LevelDispatch] Using inline template for ${checkId}`);
|
|
47890
49766
|
} else if (checkConfig.template && checkConfig.template.file) {
|
|
47891
49767
|
const file = String(checkConfig.template.file);
|
|
47892
|
-
const resolved =
|
|
47893
|
-
templateContent = await
|
|
49768
|
+
const resolved = path30.resolve(process.cwd(), file);
|
|
49769
|
+
templateContent = await fs26.readFile(resolved, "utf-8");
|
|
47894
49770
|
logger.debug(`[LevelDispatch] Using template file for ${checkId}: ${resolved}`);
|
|
47895
49771
|
} else if (schema && schema !== "plain") {
|
|
47896
49772
|
const sanitized = String(schema).replace(/[^a-zA-Z0-9-]/g, "");
|
|
47897
49773
|
if (sanitized) {
|
|
47898
49774
|
const candidatePaths = [
|
|
47899
|
-
|
|
49775
|
+
path30.join(__dirname, "output", sanitized, "template.liquid"),
|
|
47900
49776
|
// bundled: dist/output/
|
|
47901
|
-
|
|
49777
|
+
path30.join(__dirname, "..", "..", "output", sanitized, "template.liquid"),
|
|
47902
49778
|
// source (from state-machine/states)
|
|
47903
|
-
|
|
49779
|
+
path30.join(__dirname, "..", "..", "..", "output", sanitized, "template.liquid"),
|
|
47904
49780
|
// source (alternate)
|
|
47905
|
-
|
|
49781
|
+
path30.join(process.cwd(), "output", sanitized, "template.liquid"),
|
|
47906
49782
|
// fallback: cwd/output/
|
|
47907
|
-
|
|
49783
|
+
path30.join(process.cwd(), "dist", "output", sanitized, "template.liquid")
|
|
47908
49784
|
// fallback: cwd/dist/output/
|
|
47909
49785
|
];
|
|
47910
49786
|
for (const p of candidatePaths) {
|
|
47911
49787
|
try {
|
|
47912
|
-
templateContent = await
|
|
49788
|
+
templateContent = await fs26.readFile(p, "utf-8");
|
|
47913
49789
|
if (templateContent) {
|
|
47914
49790
|
logger.debug(`[LevelDispatch] Using schema template for ${checkId}: ${p}`);
|
|
47915
49791
|
break;
|
|
@@ -48516,14 +50392,14 @@ var init_runner = __esm({
|
|
|
48516
50392
|
});
|
|
48517
50393
|
|
|
48518
50394
|
// src/sandbox/docker-image-sandbox.ts
|
|
48519
|
-
var import_util2, import_child_process2, import_fs5,
|
|
50395
|
+
var import_util2, import_child_process2, import_fs5, import_path9, import_os, import_crypto2, execFileAsync, EXEC_MAX_BUFFER, DockerImageSandbox;
|
|
48520
50396
|
var init_docker_image_sandbox = __esm({
|
|
48521
50397
|
"src/sandbox/docker-image-sandbox.ts"() {
|
|
48522
50398
|
"use strict";
|
|
48523
50399
|
import_util2 = require("util");
|
|
48524
50400
|
import_child_process2 = require("child_process");
|
|
48525
50401
|
import_fs5 = require("fs");
|
|
48526
|
-
|
|
50402
|
+
import_path9 = require("path");
|
|
48527
50403
|
import_os = require("os");
|
|
48528
50404
|
import_crypto2 = require("crypto");
|
|
48529
50405
|
init_logger();
|
|
@@ -48568,8 +50444,8 @@ var init_docker_image_sandbox = __esm({
|
|
|
48568
50444
|
`Sandbox '${this.name}' has invalid dockerfile_inline: must contain a FROM instruction`
|
|
48569
50445
|
);
|
|
48570
50446
|
}
|
|
48571
|
-
const tmpDir = (0, import_fs5.mkdtempSync)((0,
|
|
48572
|
-
const dockerfilePath = (0,
|
|
50447
|
+
const tmpDir = (0, import_fs5.mkdtempSync)((0, import_path9.join)((0, import_os.tmpdir)(), "visor-build-"));
|
|
50448
|
+
const dockerfilePath = (0, import_path9.join)(tmpDir, "Dockerfile");
|
|
48573
50449
|
(0, import_fs5.writeFileSync)(dockerfilePath, this.config.dockerfile_inline, "utf8");
|
|
48574
50450
|
try {
|
|
48575
50451
|
logger.info(`Building sandbox image '${imageName}' from inline Dockerfile`);
|
|
@@ -49006,11 +50882,11 @@ var init_cache_volume_manager = __esm({
|
|
|
49006
50882
|
});
|
|
49007
50883
|
|
|
49008
50884
|
// src/sandbox/sandbox-manager.ts
|
|
49009
|
-
var
|
|
50885
|
+
var import_path10, import_fs6, SandboxManager;
|
|
49010
50886
|
var init_sandbox_manager = __esm({
|
|
49011
50887
|
"src/sandbox/sandbox-manager.ts"() {
|
|
49012
50888
|
"use strict";
|
|
49013
|
-
|
|
50889
|
+
import_path10 = require("path");
|
|
49014
50890
|
import_fs6 = require("fs");
|
|
49015
50891
|
init_docker_image_sandbox();
|
|
49016
50892
|
init_docker_compose_sandbox();
|
|
@@ -49030,10 +50906,10 @@ var init_sandbox_manager = __esm({
|
|
|
49030
50906
|
}
|
|
49031
50907
|
constructor(sandboxDefs, repoPath, gitBranch) {
|
|
49032
50908
|
this.sandboxDefs = sandboxDefs;
|
|
49033
|
-
this.repoPath = (0,
|
|
50909
|
+
this.repoPath = (0, import_path10.resolve)(repoPath);
|
|
49034
50910
|
this.gitBranch = gitBranch;
|
|
49035
50911
|
this.cacheManager = new CacheVolumeManager();
|
|
49036
|
-
this.visorDistPath = (0, import_fs6.existsSync)((0,
|
|
50912
|
+
this.visorDistPath = (0, import_fs6.existsSync)((0, import_path10.join)(__dirname, "index.js")) ? __dirname : (0, import_path10.resolve)((0, import_path10.dirname)(__dirname));
|
|
49037
50913
|
}
|
|
49038
50914
|
/**
|
|
49039
50915
|
* Resolve which sandbox a check should use.
|
|
@@ -49150,13 +51026,13 @@ var init_sandbox_manager = __esm({
|
|
|
49150
51026
|
});
|
|
49151
51027
|
|
|
49152
51028
|
// src/utils/file-exclusion.ts
|
|
49153
|
-
var import_ignore,
|
|
51029
|
+
var import_ignore, fs17, path20, DEFAULT_EXCLUSION_PATTERNS, FileExclusionHelper;
|
|
49154
51030
|
var init_file_exclusion = __esm({
|
|
49155
51031
|
"src/utils/file-exclusion.ts"() {
|
|
49156
51032
|
"use strict";
|
|
49157
51033
|
import_ignore = __toESM(require("ignore"));
|
|
49158
|
-
|
|
49159
|
-
|
|
51034
|
+
fs17 = __toESM(require("fs"));
|
|
51035
|
+
path20 = __toESM(require("path"));
|
|
49160
51036
|
DEFAULT_EXCLUSION_PATTERNS = [
|
|
49161
51037
|
"dist/",
|
|
49162
51038
|
"build/",
|
|
@@ -49175,7 +51051,7 @@ var init_file_exclusion = __esm({
|
|
|
49175
51051
|
* @param additionalPatterns - Additional patterns to include (optional, defaults to common build artifacts)
|
|
49176
51052
|
*/
|
|
49177
51053
|
constructor(workingDirectory = process.cwd(), additionalPatterns = DEFAULT_EXCLUSION_PATTERNS) {
|
|
49178
|
-
const normalizedPath =
|
|
51054
|
+
const normalizedPath = path20.resolve(workingDirectory);
|
|
49179
51055
|
if (normalizedPath.includes("\0")) {
|
|
49180
51056
|
throw new Error("Invalid workingDirectory: contains null bytes");
|
|
49181
51057
|
}
|
|
@@ -49187,11 +51063,11 @@ var init_file_exclusion = __esm({
|
|
|
49187
51063
|
* @param additionalPatterns - Additional patterns to add to gitignore rules
|
|
49188
51064
|
*/
|
|
49189
51065
|
loadGitignore(additionalPatterns) {
|
|
49190
|
-
const gitignorePath =
|
|
49191
|
-
const resolvedWorkingDir =
|
|
51066
|
+
const gitignorePath = path20.resolve(this.workingDirectory, ".gitignore");
|
|
51067
|
+
const resolvedWorkingDir = path20.resolve(this.workingDirectory);
|
|
49192
51068
|
try {
|
|
49193
|
-
const relativePath =
|
|
49194
|
-
if (relativePath.startsWith("..") ||
|
|
51069
|
+
const relativePath = path20.relative(resolvedWorkingDir, gitignorePath);
|
|
51070
|
+
if (relativePath.startsWith("..") || path20.isAbsolute(relativePath)) {
|
|
49195
51071
|
throw new Error("Invalid gitignore path: path traversal detected");
|
|
49196
51072
|
}
|
|
49197
51073
|
if (relativePath !== ".gitignore") {
|
|
@@ -49201,8 +51077,8 @@ var init_file_exclusion = __esm({
|
|
|
49201
51077
|
if (additionalPatterns && additionalPatterns.length > 0) {
|
|
49202
51078
|
this.gitignore.add(additionalPatterns);
|
|
49203
51079
|
}
|
|
49204
|
-
if (
|
|
49205
|
-
const rawContent =
|
|
51080
|
+
if (fs17.existsSync(gitignorePath)) {
|
|
51081
|
+
const rawContent = fs17.readFileSync(gitignorePath, "utf8");
|
|
49206
51082
|
const gitignoreContent = rawContent.replace(/[\r\n]+/g, "\n").replace(/[\x00-\x09\x0B-\x1F\x7F]/g, "").split("\n").filter((line) => line.length < 1e3).join("\n").trim();
|
|
49207
51083
|
this.gitignore.add(gitignoreContent);
|
|
49208
51084
|
if (process.env.VISOR_DEBUG === "true") {
|
|
@@ -49234,13 +51110,13 @@ var git_repository_analyzer_exports = {};
|
|
|
49234
51110
|
__export(git_repository_analyzer_exports, {
|
|
49235
51111
|
GitRepositoryAnalyzer: () => GitRepositoryAnalyzer
|
|
49236
51112
|
});
|
|
49237
|
-
var import_simple_git2,
|
|
51113
|
+
var import_simple_git2, path21, fs18, MAX_PATCH_SIZE, GitRepositoryAnalyzer;
|
|
49238
51114
|
var init_git_repository_analyzer = __esm({
|
|
49239
51115
|
"src/git-repository-analyzer.ts"() {
|
|
49240
51116
|
"use strict";
|
|
49241
51117
|
import_simple_git2 = require("simple-git");
|
|
49242
|
-
|
|
49243
|
-
|
|
51118
|
+
path21 = __toESM(require("path"));
|
|
51119
|
+
fs18 = __toESM(require("fs"));
|
|
49244
51120
|
init_file_exclusion();
|
|
49245
51121
|
MAX_PATCH_SIZE = 50 * 1024;
|
|
49246
51122
|
GitRepositoryAnalyzer = class {
|
|
@@ -49429,7 +51305,7 @@ ${file.patch}`).join("\n\n");
|
|
|
49429
51305
|
console.error(`\u23ED\uFE0F Skipping excluded file: ${file}`);
|
|
49430
51306
|
continue;
|
|
49431
51307
|
}
|
|
49432
|
-
const filePath =
|
|
51308
|
+
const filePath = path21.join(this.cwd, file);
|
|
49433
51309
|
const fileChange = await this.analyzeFileChange(file, status2, filePath, includeContext);
|
|
49434
51310
|
changes.push(fileChange);
|
|
49435
51311
|
}
|
|
@@ -49505,7 +51381,7 @@ ${file.patch}`).join("\n\n");
|
|
|
49505
51381
|
let content;
|
|
49506
51382
|
let truncated = false;
|
|
49507
51383
|
try {
|
|
49508
|
-
if (includeContext && status !== "added" &&
|
|
51384
|
+
if (includeContext && status !== "added" && fs18.existsSync(filePath)) {
|
|
49509
51385
|
const diff = await this.git.diff(["--", filename]).catch(() => "");
|
|
49510
51386
|
if (diff) {
|
|
49511
51387
|
const result = this.truncatePatch(diff, filename);
|
|
@@ -49515,7 +51391,7 @@ ${file.patch}`).join("\n\n");
|
|
|
49515
51391
|
additions = lines.filter((line) => line.startsWith("+")).length;
|
|
49516
51392
|
deletions = lines.filter((line) => line.startsWith("-")).length;
|
|
49517
51393
|
}
|
|
49518
|
-
} else if (status !== "added" &&
|
|
51394
|
+
} else if (status !== "added" && fs18.existsSync(filePath)) {
|
|
49519
51395
|
const diff = await this.git.diff(["--", filename]).catch(() => "");
|
|
49520
51396
|
if (diff) {
|
|
49521
51397
|
const lines = diff.split("\n");
|
|
@@ -49523,17 +51399,17 @@ ${file.patch}`).join("\n\n");
|
|
|
49523
51399
|
deletions = lines.filter((line) => line.startsWith("-")).length;
|
|
49524
51400
|
}
|
|
49525
51401
|
}
|
|
49526
|
-
if (status === "added" &&
|
|
51402
|
+
if (status === "added" && fs18.existsSync(filePath)) {
|
|
49527
51403
|
try {
|
|
49528
|
-
const stats =
|
|
51404
|
+
const stats = fs18.statSync(filePath);
|
|
49529
51405
|
if (stats.isFile() && stats.size < 1024 * 1024) {
|
|
49530
51406
|
if (includeContext) {
|
|
49531
|
-
content =
|
|
51407
|
+
content = fs18.readFileSync(filePath, "utf8");
|
|
49532
51408
|
const result = this.truncatePatch(content, filename);
|
|
49533
51409
|
patch = result.patch;
|
|
49534
51410
|
truncated = result.truncated;
|
|
49535
51411
|
}
|
|
49536
|
-
const fileContent = includeContext ? content :
|
|
51412
|
+
const fileContent = includeContext ? content : fs18.readFileSync(filePath, "utf8");
|
|
49537
51413
|
additions = fileContent.split("\n").length;
|
|
49538
51414
|
}
|
|
49539
51415
|
} catch {
|
|
@@ -49624,12 +51500,12 @@ function shellEscape(str) {
|
|
|
49624
51500
|
function sanitizePathComponent(name) {
|
|
49625
51501
|
return name.replace(/\.\./g, "").replace(/[\/\\]/g, "-").replace(/^\.+/, "").trim() || "unnamed";
|
|
49626
51502
|
}
|
|
49627
|
-
var fsp2,
|
|
51503
|
+
var fsp2, path22, WorkspaceManager;
|
|
49628
51504
|
var init_workspace_manager = __esm({
|
|
49629
51505
|
"src/utils/workspace-manager.ts"() {
|
|
49630
51506
|
"use strict";
|
|
49631
51507
|
fsp2 = __toESM(require("fs/promises"));
|
|
49632
|
-
|
|
51508
|
+
path22 = __toESM(require("path"));
|
|
49633
51509
|
init_command_executor();
|
|
49634
51510
|
init_logger();
|
|
49635
51511
|
WorkspaceManager = class _WorkspaceManager {
|
|
@@ -49663,7 +51539,7 @@ var init_workspace_manager = __esm({
|
|
|
49663
51539
|
};
|
|
49664
51540
|
this.basePath = this.config.basePath;
|
|
49665
51541
|
const workspaceDirName = sanitizePathComponent(this.config.name || this.sessionId);
|
|
49666
|
-
this.workspacePath =
|
|
51542
|
+
this.workspacePath = path22.join(this.basePath, workspaceDirName);
|
|
49667
51543
|
}
|
|
49668
51544
|
/**
|
|
49669
51545
|
* Get or create a WorkspaceManager instance for a session
|
|
@@ -49758,7 +51634,7 @@ var init_workspace_manager = __esm({
|
|
|
49758
51634
|
configuredMainProjectName || this.extractProjectName(this.originalPath)
|
|
49759
51635
|
);
|
|
49760
51636
|
this.usedNames.add(mainProjectName);
|
|
49761
|
-
const mainProjectPath =
|
|
51637
|
+
const mainProjectPath = path22.join(this.workspacePath, mainProjectName);
|
|
49762
51638
|
const isGitRepo = await this.isGitRepository(this.originalPath);
|
|
49763
51639
|
if (isGitRepo) {
|
|
49764
51640
|
await this.createMainProjectWorktree(mainProjectPath);
|
|
@@ -49799,7 +51675,7 @@ var init_workspace_manager = __esm({
|
|
|
49799
51675
|
let projectName = sanitizePathComponent(description || this.extractRepoName(repository));
|
|
49800
51676
|
projectName = this.getUniqueName(projectName);
|
|
49801
51677
|
this.usedNames.add(projectName);
|
|
49802
|
-
const workspacePath =
|
|
51678
|
+
const workspacePath = path22.join(this.workspacePath, projectName);
|
|
49803
51679
|
await fsp2.rm(workspacePath, { recursive: true, force: true });
|
|
49804
51680
|
try {
|
|
49805
51681
|
await fsp2.symlink(worktreePath, workspacePath);
|
|
@@ -49938,7 +51814,7 @@ var init_workspace_manager = __esm({
|
|
|
49938
51814
|
* Extract project name from path
|
|
49939
51815
|
*/
|
|
49940
51816
|
extractProjectName(dirPath) {
|
|
49941
|
-
return
|
|
51817
|
+
return path22.basename(dirPath);
|
|
49942
51818
|
}
|
|
49943
51819
|
/**
|
|
49944
51820
|
* Extract repository name from owner/repo format
|
|
@@ -50155,13 +52031,13 @@ var validator_exports = {};
|
|
|
50155
52031
|
__export(validator_exports, {
|
|
50156
52032
|
LicenseValidator: () => LicenseValidator
|
|
50157
52033
|
});
|
|
50158
|
-
var crypto2,
|
|
52034
|
+
var crypto2, fs19, path23, LicenseValidator;
|
|
50159
52035
|
var init_validator = __esm({
|
|
50160
52036
|
"src/enterprise/license/validator.ts"() {
|
|
50161
52037
|
"use strict";
|
|
50162
52038
|
crypto2 = __toESM(require("crypto"));
|
|
50163
|
-
|
|
50164
|
-
|
|
52039
|
+
fs19 = __toESM(require("fs"));
|
|
52040
|
+
path23 = __toESM(require("path"));
|
|
50165
52041
|
LicenseValidator = class _LicenseValidator {
|
|
50166
52042
|
/** Ed25519 public key for license verification (PEM format). */
|
|
50167
52043
|
static PUBLIC_KEY = "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAI/Zd08EFmgIdrDm/HXd0l3/5GBt7R1PrdvhdmEXhJlU=\n-----END PUBLIC KEY-----\n";
|
|
@@ -50214,28 +52090,28 @@ var init_validator = __esm({
|
|
|
50214
52090
|
return process.env.VISOR_LICENSE.trim();
|
|
50215
52091
|
}
|
|
50216
52092
|
if (process.env.VISOR_LICENSE_FILE) {
|
|
50217
|
-
const resolved =
|
|
52093
|
+
const resolved = path23.resolve(process.env.VISOR_LICENSE_FILE);
|
|
50218
52094
|
const home2 = process.env.HOME || process.env.USERPROFILE || "";
|
|
50219
|
-
const allowedPrefixes = [
|
|
50220
|
-
if (home2) allowedPrefixes.push(
|
|
52095
|
+
const allowedPrefixes = [path23.normalize(process.cwd())];
|
|
52096
|
+
if (home2) allowedPrefixes.push(path23.normalize(path23.join(home2, ".config", "visor")));
|
|
50221
52097
|
let realPath;
|
|
50222
52098
|
try {
|
|
50223
|
-
realPath =
|
|
52099
|
+
realPath = fs19.realpathSync(resolved);
|
|
50224
52100
|
} catch {
|
|
50225
52101
|
return null;
|
|
50226
52102
|
}
|
|
50227
52103
|
const isSafe = allowedPrefixes.some(
|
|
50228
|
-
(prefix) => realPath === prefix || realPath.startsWith(prefix +
|
|
52104
|
+
(prefix) => realPath === prefix || realPath.startsWith(prefix + path23.sep)
|
|
50229
52105
|
);
|
|
50230
52106
|
if (!isSafe) return null;
|
|
50231
52107
|
return this.readFile(realPath);
|
|
50232
52108
|
}
|
|
50233
|
-
const cwdPath =
|
|
52109
|
+
const cwdPath = path23.join(process.cwd(), ".visor-license");
|
|
50234
52110
|
const cwdToken = this.readFile(cwdPath);
|
|
50235
52111
|
if (cwdToken) return cwdToken;
|
|
50236
52112
|
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
50237
52113
|
if (home) {
|
|
50238
|
-
const configPath =
|
|
52114
|
+
const configPath = path23.join(home, ".config", "visor", ".visor-license");
|
|
50239
52115
|
const configToken = this.readFile(configPath);
|
|
50240
52116
|
if (configToken) return configToken;
|
|
50241
52117
|
}
|
|
@@ -50243,7 +52119,7 @@ var init_validator = __esm({
|
|
|
50243
52119
|
}
|
|
50244
52120
|
readFile(filePath) {
|
|
50245
52121
|
try {
|
|
50246
|
-
return
|
|
52122
|
+
return fs19.readFileSync(filePath, "utf-8").trim();
|
|
50247
52123
|
} catch {
|
|
50248
52124
|
return null;
|
|
50249
52125
|
}
|
|
@@ -50282,17 +52158,17 @@ var init_validator = __esm({
|
|
|
50282
52158
|
});
|
|
50283
52159
|
|
|
50284
52160
|
// src/enterprise/policy/opa-compiler.ts
|
|
50285
|
-
var
|
|
52161
|
+
var fs20, path24, os, crypto3, import_child_process5, OpaCompiler;
|
|
50286
52162
|
var init_opa_compiler = __esm({
|
|
50287
52163
|
"src/enterprise/policy/opa-compiler.ts"() {
|
|
50288
52164
|
"use strict";
|
|
50289
|
-
|
|
50290
|
-
|
|
52165
|
+
fs20 = __toESM(require("fs"));
|
|
52166
|
+
path24 = __toESM(require("path"));
|
|
50291
52167
|
os = __toESM(require("os"));
|
|
50292
52168
|
crypto3 = __toESM(require("crypto"));
|
|
50293
52169
|
import_child_process5 = require("child_process");
|
|
50294
52170
|
OpaCompiler = class _OpaCompiler {
|
|
50295
|
-
static CACHE_DIR =
|
|
52171
|
+
static CACHE_DIR = path24.join(os.tmpdir(), "visor-opa-cache");
|
|
50296
52172
|
/**
|
|
50297
52173
|
* Resolve the input paths to WASM bytes.
|
|
50298
52174
|
*
|
|
@@ -50304,24 +52180,24 @@ var init_opa_compiler = __esm({
|
|
|
50304
52180
|
async resolveWasmBytes(paths) {
|
|
50305
52181
|
const regoFiles = [];
|
|
50306
52182
|
for (const p of paths) {
|
|
50307
|
-
const resolved =
|
|
50308
|
-
if (
|
|
52183
|
+
const resolved = path24.resolve(p);
|
|
52184
|
+
if (path24.normalize(resolved).includes("..")) {
|
|
50309
52185
|
throw new Error(`Policy path contains traversal sequences: ${p}`);
|
|
50310
52186
|
}
|
|
50311
|
-
if (resolved.endsWith(".wasm") &&
|
|
50312
|
-
return
|
|
52187
|
+
if (resolved.endsWith(".wasm") && fs20.existsSync(resolved)) {
|
|
52188
|
+
return fs20.readFileSync(resolved);
|
|
50313
52189
|
}
|
|
50314
|
-
if (!
|
|
50315
|
-
const stat =
|
|
52190
|
+
if (!fs20.existsSync(resolved)) continue;
|
|
52191
|
+
const stat = fs20.statSync(resolved);
|
|
50316
52192
|
if (stat.isDirectory()) {
|
|
50317
|
-
const wasmCandidate =
|
|
50318
|
-
if (
|
|
50319
|
-
return
|
|
52193
|
+
const wasmCandidate = path24.join(resolved, "policy.wasm");
|
|
52194
|
+
if (fs20.existsSync(wasmCandidate)) {
|
|
52195
|
+
return fs20.readFileSync(wasmCandidate);
|
|
50320
52196
|
}
|
|
50321
|
-
const files =
|
|
52197
|
+
const files = fs20.readdirSync(resolved);
|
|
50322
52198
|
for (const f of files) {
|
|
50323
52199
|
if (f.endsWith(".rego")) {
|
|
50324
|
-
regoFiles.push(
|
|
52200
|
+
regoFiles.push(path24.join(resolved, f));
|
|
50325
52201
|
}
|
|
50326
52202
|
}
|
|
50327
52203
|
} else if (resolved.endsWith(".rego")) {
|
|
@@ -50351,17 +52227,17 @@ var init_opa_compiler = __esm({
|
|
|
50351
52227
|
}
|
|
50352
52228
|
const hash = crypto3.createHash("sha256");
|
|
50353
52229
|
for (const f of regoFiles.sort()) {
|
|
50354
|
-
hash.update(
|
|
52230
|
+
hash.update(fs20.readFileSync(f));
|
|
50355
52231
|
hash.update(f);
|
|
50356
52232
|
}
|
|
50357
52233
|
const cacheKey = hash.digest("hex").slice(0, 16);
|
|
50358
52234
|
const cacheDir = _OpaCompiler.CACHE_DIR;
|
|
50359
|
-
const cachedWasm =
|
|
50360
|
-
if (
|
|
50361
|
-
return
|
|
52235
|
+
const cachedWasm = path24.join(cacheDir, `${cacheKey}.wasm`);
|
|
52236
|
+
if (fs20.existsSync(cachedWasm)) {
|
|
52237
|
+
return fs20.readFileSync(cachedWasm);
|
|
50362
52238
|
}
|
|
50363
|
-
|
|
50364
|
-
const bundleTar =
|
|
52239
|
+
fs20.mkdirSync(cacheDir, { recursive: true });
|
|
52240
|
+
const bundleTar = path24.join(cacheDir, `${cacheKey}-bundle.tar.gz`);
|
|
50365
52241
|
try {
|
|
50366
52242
|
const args = [
|
|
50367
52243
|
"build",
|
|
@@ -50390,43 +52266,43 @@ Ensure your .rego files are valid and the \`opa\` CLI is installed.`
|
|
|
50390
52266
|
(0, import_child_process5.execFileSync)("tar", ["-xzf", bundleTar, "-C", cacheDir, "/policy.wasm"], {
|
|
50391
52267
|
stdio: "pipe"
|
|
50392
52268
|
});
|
|
50393
|
-
const extractedWasm =
|
|
50394
|
-
if (
|
|
50395
|
-
|
|
52269
|
+
const extractedWasm = path24.join(cacheDir, "policy.wasm");
|
|
52270
|
+
if (fs20.existsSync(extractedWasm)) {
|
|
52271
|
+
fs20.renameSync(extractedWasm, cachedWasm);
|
|
50396
52272
|
}
|
|
50397
52273
|
} catch {
|
|
50398
52274
|
try {
|
|
50399
52275
|
(0, import_child_process5.execFileSync)("tar", ["-xzf", bundleTar, "-C", cacheDir, "policy.wasm"], {
|
|
50400
52276
|
stdio: "pipe"
|
|
50401
52277
|
});
|
|
50402
|
-
const extractedWasm =
|
|
50403
|
-
if (
|
|
50404
|
-
|
|
52278
|
+
const extractedWasm = path24.join(cacheDir, "policy.wasm");
|
|
52279
|
+
if (fs20.existsSync(extractedWasm)) {
|
|
52280
|
+
fs20.renameSync(extractedWasm, cachedWasm);
|
|
50405
52281
|
}
|
|
50406
52282
|
} catch (err2) {
|
|
50407
52283
|
throw new Error(`Failed to extract policy.wasm from OPA bundle: ${err2?.message || err2}`);
|
|
50408
52284
|
}
|
|
50409
52285
|
}
|
|
50410
52286
|
try {
|
|
50411
|
-
|
|
52287
|
+
fs20.unlinkSync(bundleTar);
|
|
50412
52288
|
} catch {
|
|
50413
52289
|
}
|
|
50414
|
-
if (!
|
|
52290
|
+
if (!fs20.existsSync(cachedWasm)) {
|
|
50415
52291
|
throw new Error("OPA build succeeded but policy.wasm was not found in the bundle");
|
|
50416
52292
|
}
|
|
50417
|
-
return
|
|
52293
|
+
return fs20.readFileSync(cachedWasm);
|
|
50418
52294
|
}
|
|
50419
52295
|
};
|
|
50420
52296
|
}
|
|
50421
52297
|
});
|
|
50422
52298
|
|
|
50423
52299
|
// src/enterprise/policy/opa-wasm-evaluator.ts
|
|
50424
|
-
var
|
|
52300
|
+
var fs21, path25, OpaWasmEvaluator;
|
|
50425
52301
|
var init_opa_wasm_evaluator = __esm({
|
|
50426
52302
|
"src/enterprise/policy/opa-wasm-evaluator.ts"() {
|
|
50427
52303
|
"use strict";
|
|
50428
|
-
|
|
50429
|
-
|
|
52304
|
+
fs21 = __toESM(require("fs"));
|
|
52305
|
+
path25 = __toESM(require("path"));
|
|
50430
52306
|
init_opa_compiler();
|
|
50431
52307
|
OpaWasmEvaluator = class {
|
|
50432
52308
|
policy = null;
|
|
@@ -50459,18 +52335,18 @@ var init_opa_wasm_evaluator = __esm({
|
|
|
50459
52335
|
* making it available in Rego via `data.<key>`.
|
|
50460
52336
|
*/
|
|
50461
52337
|
loadData(dataPath) {
|
|
50462
|
-
const resolved =
|
|
50463
|
-
if (
|
|
52338
|
+
const resolved = path25.resolve(dataPath);
|
|
52339
|
+
if (path25.normalize(resolved).includes("..")) {
|
|
50464
52340
|
throw new Error(`Data path contains traversal sequences: ${dataPath}`);
|
|
50465
52341
|
}
|
|
50466
|
-
if (!
|
|
52342
|
+
if (!fs21.existsSync(resolved)) {
|
|
50467
52343
|
throw new Error(`OPA data file not found: ${resolved}`);
|
|
50468
52344
|
}
|
|
50469
|
-
const stat =
|
|
52345
|
+
const stat = fs21.statSync(resolved);
|
|
50470
52346
|
if (stat.size > 10 * 1024 * 1024) {
|
|
50471
52347
|
throw new Error(`OPA data file exceeds 10MB limit: ${resolved} (${stat.size} bytes)`);
|
|
50472
52348
|
}
|
|
50473
|
-
const raw =
|
|
52349
|
+
const raw = fs21.readFileSync(resolved, "utf-8");
|
|
50474
52350
|
try {
|
|
50475
52351
|
const parsed = JSON.parse(raw);
|
|
50476
52352
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
@@ -50991,12 +52867,12 @@ function toInsertRow(schedule) {
|
|
|
50991
52867
|
previous_response: schedule.previousResponse ?? null
|
|
50992
52868
|
};
|
|
50993
52869
|
}
|
|
50994
|
-
var
|
|
52870
|
+
var fs22, path26, import_uuid2, KnexStoreBackend;
|
|
50995
52871
|
var init_knex_store = __esm({
|
|
50996
52872
|
"src/enterprise/scheduler/knex-store.ts"() {
|
|
50997
52873
|
"use strict";
|
|
50998
|
-
|
|
50999
|
-
|
|
52874
|
+
fs22 = __toESM(require("fs"));
|
|
52875
|
+
path26 = __toESM(require("path"));
|
|
51000
52876
|
import_uuid2 = require("uuid");
|
|
51001
52877
|
init_logger();
|
|
51002
52878
|
KnexStoreBackend = class {
|
|
@@ -51082,24 +52958,24 @@ var init_knex_store = __esm({
|
|
|
51082
52958
|
};
|
|
51083
52959
|
if (ssl.ca) {
|
|
51084
52960
|
const caPath = this.validateSslPath(ssl.ca, "CA certificate");
|
|
51085
|
-
result.ca =
|
|
52961
|
+
result.ca = fs22.readFileSync(caPath, "utf8");
|
|
51086
52962
|
}
|
|
51087
52963
|
if (ssl.cert) {
|
|
51088
52964
|
const certPath = this.validateSslPath(ssl.cert, "client certificate");
|
|
51089
|
-
result.cert =
|
|
52965
|
+
result.cert = fs22.readFileSync(certPath, "utf8");
|
|
51090
52966
|
}
|
|
51091
52967
|
if (ssl.key) {
|
|
51092
52968
|
const keyPath = this.validateSslPath(ssl.key, "client key");
|
|
51093
|
-
result.key =
|
|
52969
|
+
result.key = fs22.readFileSync(keyPath, "utf8");
|
|
51094
52970
|
}
|
|
51095
52971
|
return result;
|
|
51096
52972
|
}
|
|
51097
52973
|
validateSslPath(filePath, label) {
|
|
51098
|
-
const resolved =
|
|
51099
|
-
if (resolved !==
|
|
52974
|
+
const resolved = path26.resolve(filePath);
|
|
52975
|
+
if (resolved !== path26.normalize(resolved)) {
|
|
51100
52976
|
throw new Error(`SSL ${label} path contains invalid sequences: ${filePath}`);
|
|
51101
52977
|
}
|
|
51102
|
-
if (!
|
|
52978
|
+
if (!fs22.existsSync(resolved)) {
|
|
51103
52979
|
throw new Error(`SSL ${label} not found: ${filePath}`);
|
|
51104
52980
|
}
|
|
51105
52981
|
return resolved;
|
|
@@ -51430,12 +53306,12 @@ var ndjson_sink_exports = {};
|
|
|
51430
53306
|
__export(ndjson_sink_exports, {
|
|
51431
53307
|
NdjsonSink: () => NdjsonSink
|
|
51432
53308
|
});
|
|
51433
|
-
var import_fs7,
|
|
53309
|
+
var import_fs7, import_path11, NdjsonSink;
|
|
51434
53310
|
var init_ndjson_sink = __esm({
|
|
51435
53311
|
"src/frontends/ndjson-sink.ts"() {
|
|
51436
53312
|
"use strict";
|
|
51437
53313
|
import_fs7 = __toESM(require("fs"));
|
|
51438
|
-
|
|
53314
|
+
import_path11 = __toESM(require("path"));
|
|
51439
53315
|
NdjsonSink = class {
|
|
51440
53316
|
name = "ndjson-sink";
|
|
51441
53317
|
cfg;
|
|
@@ -51467,8 +53343,8 @@ var init_ndjson_sink = __esm({
|
|
|
51467
53343
|
this.unsub = void 0;
|
|
51468
53344
|
}
|
|
51469
53345
|
resolveFile(p) {
|
|
51470
|
-
if (
|
|
51471
|
-
return
|
|
53346
|
+
if (import_path11.default.isAbsolute(p)) return p;
|
|
53347
|
+
return import_path11.default.join(process.cwd(), p);
|
|
51472
53348
|
}
|
|
51473
53349
|
};
|
|
51474
53350
|
}
|
|
@@ -53195,16 +55071,16 @@ function extractMermaidDiagrams(text) {
|
|
|
53195
55071
|
}
|
|
53196
55072
|
async function renderMermaidToPng(mermaidCode) {
|
|
53197
55073
|
const tmpDir = os2.tmpdir();
|
|
53198
|
-
const inputFile =
|
|
55074
|
+
const inputFile = path28.join(
|
|
53199
55075
|
tmpDir,
|
|
53200
55076
|
`mermaid-${Date.now()}-${Math.random().toString(36).slice(2)}.mmd`
|
|
53201
55077
|
);
|
|
53202
|
-
const outputFile =
|
|
55078
|
+
const outputFile = path28.join(
|
|
53203
55079
|
tmpDir,
|
|
53204
55080
|
`mermaid-${Date.now()}-${Math.random().toString(36).slice(2)}.png`
|
|
53205
55081
|
);
|
|
53206
55082
|
try {
|
|
53207
|
-
|
|
55083
|
+
fs24.writeFileSync(inputFile, mermaidCode, "utf-8");
|
|
53208
55084
|
const chromiumPaths = [
|
|
53209
55085
|
"/usr/bin/chromium",
|
|
53210
55086
|
"/usr/bin/chromium-browser",
|
|
@@ -53213,7 +55089,7 @@ async function renderMermaidToPng(mermaidCode) {
|
|
|
53213
55089
|
];
|
|
53214
55090
|
let chromiumPath;
|
|
53215
55091
|
for (const p of chromiumPaths) {
|
|
53216
|
-
if (
|
|
55092
|
+
if (fs24.existsSync(p)) {
|
|
53217
55093
|
chromiumPath = p;
|
|
53218
55094
|
break;
|
|
53219
55095
|
}
|
|
@@ -53265,19 +55141,19 @@ async function renderMermaidToPng(mermaidCode) {
|
|
|
53265
55141
|
console.warn(`Mermaid rendering failed: ${result.error}`);
|
|
53266
55142
|
return null;
|
|
53267
55143
|
}
|
|
53268
|
-
if (!
|
|
55144
|
+
if (!fs24.existsSync(outputFile)) {
|
|
53269
55145
|
console.warn("Mermaid output file not created");
|
|
53270
55146
|
return null;
|
|
53271
55147
|
}
|
|
53272
|
-
const pngBuffer =
|
|
55148
|
+
const pngBuffer = fs24.readFileSync(outputFile);
|
|
53273
55149
|
return pngBuffer;
|
|
53274
55150
|
} catch (e) {
|
|
53275
55151
|
console.warn(`Mermaid rendering error: ${e instanceof Error ? e.message : String(e)}`);
|
|
53276
55152
|
return null;
|
|
53277
55153
|
} finally {
|
|
53278
55154
|
try {
|
|
53279
|
-
if (
|
|
53280
|
-
if (
|
|
55155
|
+
if (fs24.existsSync(inputFile)) fs24.unlinkSync(inputFile);
|
|
55156
|
+
if (fs24.existsSync(outputFile)) fs24.unlinkSync(outputFile);
|
|
53281
55157
|
} catch {
|
|
53282
55158
|
}
|
|
53283
55159
|
}
|
|
@@ -53382,13 +55258,13 @@ function replaceFileSections(text, sections, replacement = (idx) => `_(See file:
|
|
|
53382
55258
|
function formatSlackText(text) {
|
|
53383
55259
|
return markdownToSlack(text);
|
|
53384
55260
|
}
|
|
53385
|
-
var import_child_process6,
|
|
55261
|
+
var import_child_process6, fs24, path28, os2;
|
|
53386
55262
|
var init_markdown = __esm({
|
|
53387
55263
|
"src/slack/markdown.ts"() {
|
|
53388
55264
|
"use strict";
|
|
53389
55265
|
import_child_process6 = require("child_process");
|
|
53390
|
-
|
|
53391
|
-
|
|
55266
|
+
fs24 = __toESM(require("fs"));
|
|
55267
|
+
path28 = __toESM(require("path"));
|
|
53392
55268
|
os2 = __toESM(require("os"));
|
|
53393
55269
|
}
|
|
53394
55270
|
});
|
|
@@ -54365,15 +56241,15 @@ function serializeRunState(state) {
|
|
|
54365
56241
|
])
|
|
54366
56242
|
};
|
|
54367
56243
|
}
|
|
54368
|
-
var
|
|
56244
|
+
var path29, fs25, StateMachineExecutionEngine;
|
|
54369
56245
|
var init_state_machine_execution_engine = __esm({
|
|
54370
56246
|
"src/state-machine-execution-engine.ts"() {
|
|
54371
56247
|
"use strict";
|
|
54372
56248
|
init_runner();
|
|
54373
56249
|
init_logger();
|
|
54374
56250
|
init_sandbox_manager();
|
|
54375
|
-
|
|
54376
|
-
|
|
56251
|
+
path29 = __toESM(require("path"));
|
|
56252
|
+
fs25 = __toESM(require("fs"));
|
|
54377
56253
|
StateMachineExecutionEngine = class _StateMachineExecutionEngine {
|
|
54378
56254
|
workingDirectory;
|
|
54379
56255
|
executionContext;
|
|
@@ -54563,6 +56439,15 @@ var init_state_machine_execution_engine = __esm({
|
|
|
54563
56439
|
...config,
|
|
54564
56440
|
tag_filter: tagFilter
|
|
54565
56441
|
} : config;
|
|
56442
|
+
try {
|
|
56443
|
+
const { CheckProviderRegistry: CheckProviderRegistry2 } = await Promise.resolve().then(() => (init_check_provider_registry(), check_provider_registry_exports));
|
|
56444
|
+
const registry = CheckProviderRegistry2.getInstance();
|
|
56445
|
+
registry.setCustomTools(configWithTagFilter.tools || {});
|
|
56446
|
+
} catch (error) {
|
|
56447
|
+
logger.warn(
|
|
56448
|
+
`[StateMachine] Failed to register custom tools: ${error instanceof Error ? error.message : String(error)}`
|
|
56449
|
+
);
|
|
56450
|
+
}
|
|
54566
56451
|
const context2 = this.buildEngineContext(
|
|
54567
56452
|
configWithTagFilter,
|
|
54568
56453
|
prInfo,
|
|
@@ -54749,9 +56634,9 @@ var init_state_machine_execution_engine = __esm({
|
|
|
54749
56634
|
}
|
|
54750
56635
|
const checkId = String(ev?.checkId || "unknown");
|
|
54751
56636
|
const threadKey = ev?.threadKey || (channel && threadTs ? `${channel}:${threadTs}` : "session");
|
|
54752
|
-
const baseDir = process.env.VISOR_SNAPSHOT_DIR ||
|
|
54753
|
-
|
|
54754
|
-
const filePath =
|
|
56637
|
+
const baseDir = process.env.VISOR_SNAPSHOT_DIR || path29.resolve(process.cwd(), ".visor", "snapshots");
|
|
56638
|
+
fs25.mkdirSync(baseDir, { recursive: true });
|
|
56639
|
+
const filePath = path29.join(baseDir, `${threadKey}-${checkId}.json`);
|
|
54755
56640
|
await this.saveSnapshotToFile(filePath);
|
|
54756
56641
|
logger.info(`[Snapshot] Saved run snapshot: ${filePath}`);
|
|
54757
56642
|
try {
|
|
@@ -54892,7 +56777,7 @@ var init_state_machine_execution_engine = __esm({
|
|
|
54892
56777
|
* Does not include secrets. Intended for debugging and future resume support.
|
|
54893
56778
|
*/
|
|
54894
56779
|
async saveSnapshotToFile(filePath) {
|
|
54895
|
-
const
|
|
56780
|
+
const fs26 = await import("fs/promises");
|
|
54896
56781
|
const ctx = this._lastContext;
|
|
54897
56782
|
const runner = this._lastRunner;
|
|
54898
56783
|
if (!ctx || !runner) {
|
|
@@ -54912,14 +56797,14 @@ var init_state_machine_execution_engine = __esm({
|
|
|
54912
56797
|
journal: entries,
|
|
54913
56798
|
requestedChecks: ctx.requestedChecks || []
|
|
54914
56799
|
};
|
|
54915
|
-
await
|
|
56800
|
+
await fs26.writeFile(filePath, JSON.stringify(payload, null, 2), "utf8");
|
|
54916
56801
|
}
|
|
54917
56802
|
/**
|
|
54918
56803
|
* Load a snapshot JSON from file and return it. Resume support can build on this.
|
|
54919
56804
|
*/
|
|
54920
56805
|
async loadSnapshotFromFile(filePath) {
|
|
54921
|
-
const
|
|
54922
|
-
const raw = await
|
|
56806
|
+
const fs26 = await import("fs/promises");
|
|
56807
|
+
const raw = await fs26.readFile(filePath, "utf8");
|
|
54923
56808
|
return JSON.parse(raw);
|
|
54924
56809
|
}
|
|
54925
56810
|
/**
|