@probelabs/visor 0.1.165 → 0.1.166-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/index.js +2363 -141
- package/dist/pr-analyzer.d.ts +2 -1
- package/dist/pr-analyzer.d.ts.map +1 -1
- package/dist/providers/http-client-provider.d.ts.map +1 -1
- package/dist/sdk/{check-provider-registry-6P2KJ423.mjs → check-provider-registry-PU67PWTU.mjs} +5 -5
- package/dist/sdk/{check-provider-registry-TTVN3V2O.mjs → check-provider-registry-TGPICTHD.mjs} +5 -5
- package/dist/sdk/{chunk-EO4IJNM7.mjs → chunk-E7NRUDWL.mjs} +2 -2
- package/dist/sdk/{chunk-G5JBWW3O.mjs → chunk-P5P6BOO7.mjs} +161 -21
- package/dist/sdk/chunk-P5P6BOO7.mjs.map +1 -0
- package/dist/sdk/{chunk-GMHSXC5K.mjs → chunk-RV5SK4FZ.mjs} +3 -3
- package/dist/sdk/{chunk-S47KBQQK.mjs → chunk-T5USZCCM.mjs} +2 -2
- package/dist/sdk/{chunk-S47KBQQK.mjs.map → chunk-T5USZCCM.mjs.map} +1 -1
- package/dist/sdk/{chunk-GOJRNYTV.mjs → chunk-WSYVK6ML.mjs} +188 -22
- package/dist/sdk/chunk-WSYVK6ML.mjs.map +1 -0
- package/dist/sdk/{failure-condition-evaluator-N3VNLWZD.mjs → failure-condition-evaluator-GPANOHP2.mjs} +3 -3
- package/dist/sdk/{github-frontend-ATORHHF6.mjs → github-frontend-P274ISBJ.mjs} +3 -3
- package/dist/sdk/{host-JROON6IT.mjs → host-AIMRV5YL.mjs} +2 -2
- package/dist/sdk/{host-OBXKDFT7.mjs → host-QYPOS4R6.mjs} +2 -2
- package/dist/sdk/knex-store-CRORFJE6.mjs +527 -0
- package/dist/sdk/knex-store-CRORFJE6.mjs.map +1 -0
- package/dist/sdk/loader-NJCF7DUS.mjs +89 -0
- package/dist/sdk/loader-NJCF7DUS.mjs.map +1 -0
- package/dist/sdk/opa-policy-engine-S2S2ULEI.mjs +655 -0
- package/dist/sdk/opa-policy-engine-S2S2ULEI.mjs.map +1 -0
- package/dist/sdk/{routing-QCDX43XD.mjs → routing-BXHP2E62.mjs} +4 -4
- package/dist/sdk/{schedule-tool-D5TSTGP2.mjs → schedule-tool-5FVFYH2A.mjs} +5 -5
- package/dist/sdk/{schedule-tool-XCGJI2VB.mjs → schedule-tool-MQHISNJ6.mjs} +5 -5
- package/dist/sdk/{schedule-tool-handler-DKHHPZAG.mjs → schedule-tool-handler-4TCT2P7A.mjs} +5 -5
- package/dist/sdk/{schedule-tool-handler-OKZ53WMC.mjs → schedule-tool-handler-TZYXM664.mjs} +5 -5
- package/dist/sdk/sdk.js +1779 -265
- package/dist/sdk/sdk.js.map +1 -1
- package/dist/sdk/sdk.mjs +4 -4
- package/dist/sdk/{trace-helpers-J5CJ4PUN.mjs → trace-helpers-UG6FOWVV.mjs} +2 -2
- package/dist/sdk/validator-XTZJZZJH.mjs +134 -0
- package/dist/sdk/validator-XTZJZZJH.mjs.map +1 -0
- package/dist/sdk/{workflow-check-provider-T6WFK4RB.mjs → workflow-check-provider-BE2SVYWW.mjs} +5 -5
- package/dist/sdk/{workflow-check-provider-WLUAJPAS.mjs → workflow-check-provider-QKHL6AFT.mjs} +5 -5
- package/dist/slack/socket-runner.d.ts +14 -0
- package/dist/slack/socket-runner.d.ts.map +1 -1
- package/dist/utils/oauth2-token-cache.d.ts +44 -0
- package/dist/utils/oauth2-token-cache.d.ts.map +1 -0
- package/package.json +2 -2
- package/dist/output/traces/run-2026-03-06T06-08-10-897Z.ndjson +0 -138
- package/dist/output/traces/run-2026-03-06T06-08-55-016Z.ndjson +0 -2235
- package/dist/sdk/check-provider-registry-4SHN3GSH.mjs +0 -29
- package/dist/sdk/chunk-G5JBWW3O.mjs.map +0 -1
- package/dist/sdk/chunk-GOJRNYTV.mjs.map +0 -1
- package/dist/sdk/chunk-J236ZVYX.mjs +0 -1502
- package/dist/sdk/chunk-J236ZVYX.mjs.map +0 -1
- package/dist/sdk/chunk-LDE33FGE.mjs +0 -443
- package/dist/sdk/chunk-LDE33FGE.mjs.map +0 -1
- package/dist/sdk/chunk-MYROK4LB.mjs +0 -43917
- package/dist/sdk/chunk-MYROK4LB.mjs.map +0 -1
- package/dist/sdk/chunk-XDIBL7QB.mjs +0 -739
- package/dist/sdk/chunk-XDIBL7QB.mjs.map +0 -1
- package/dist/sdk/failure-condition-evaluator-M6SIUQF4.mjs +0 -17
- package/dist/sdk/github-frontend-MHXL2Q2V.mjs +0 -1368
- package/dist/sdk/github-frontend-MHXL2Q2V.mjs.map +0 -1
- package/dist/sdk/routing-TGJD66Q5.mjs +0 -25
- package/dist/sdk/schedule-tool-C5QN5OQU.mjs +0 -35
- package/dist/sdk/schedule-tool-handler-OKZ53WMC.mjs.map +0 -1
- package/dist/sdk/schedule-tool-handler-ZUMPNAVY.mjs +0 -39
- package/dist/sdk/schedule-tool-handler-ZUMPNAVY.mjs.map +0 -1
- package/dist/sdk/trace-helpers-J5CJ4PUN.mjs.map +0 -1
- package/dist/sdk/trace-helpers-KFQJ7IAG.mjs +0 -25
- package/dist/sdk/trace-helpers-KFQJ7IAG.mjs.map +0 -1
- package/dist/sdk/workflow-check-provider-RBYA6ZGU.mjs +0 -29
- package/dist/sdk/workflow-check-provider-RBYA6ZGU.mjs.map +0 -1
- package/dist/sdk/workflow-check-provider-T6WFK4RB.mjs.map +0 -1
- package/dist/sdk/workflow-check-provider-WLUAJPAS.mjs.map +0 -1
- package/dist/traces/run-2026-03-06T06-08-10-897Z.ndjson +0 -138
- package/dist/traces/run-2026-03-06T06-08-55-016Z.ndjson +0 -2235
- /package/dist/sdk/{check-provider-registry-4SHN3GSH.mjs.map → check-provider-registry-PU67PWTU.mjs.map} +0 -0
- /package/dist/sdk/{check-provider-registry-6P2KJ423.mjs.map → check-provider-registry-TGPICTHD.mjs.map} +0 -0
- /package/dist/sdk/{chunk-EO4IJNM7.mjs.map → chunk-E7NRUDWL.mjs.map} +0 -0
- /package/dist/sdk/{chunk-GMHSXC5K.mjs.map → chunk-RV5SK4FZ.mjs.map} +0 -0
- /package/dist/sdk/{check-provider-registry-TTVN3V2O.mjs.map → failure-condition-evaluator-GPANOHP2.mjs.map} +0 -0
- /package/dist/sdk/{github-frontend-ATORHHF6.mjs.map → github-frontend-P274ISBJ.mjs.map} +0 -0
- /package/dist/sdk/{host-JROON6IT.mjs.map → host-AIMRV5YL.mjs.map} +0 -0
- /package/dist/sdk/{host-OBXKDFT7.mjs.map → host-QYPOS4R6.mjs.map} +0 -0
- /package/dist/sdk/{failure-condition-evaluator-M6SIUQF4.mjs.map → routing-BXHP2E62.mjs.map} +0 -0
- /package/dist/sdk/{failure-condition-evaluator-N3VNLWZD.mjs.map → schedule-tool-5FVFYH2A.mjs.map} +0 -0
- /package/dist/sdk/{routing-QCDX43XD.mjs.map → schedule-tool-MQHISNJ6.mjs.map} +0 -0
- /package/dist/sdk/{routing-TGJD66Q5.mjs.map → schedule-tool-handler-4TCT2P7A.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-C5QN5OQU.mjs.map → schedule-tool-handler-TZYXM664.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-D5TSTGP2.mjs.map → trace-helpers-UG6FOWVV.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-XCGJI2VB.mjs.map → workflow-check-provider-BE2SVYWW.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-handler-DKHHPZAG.mjs.map → workflow-check-provider-QKHL6AFT.mjs.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
process.env.VISOR_VERSION = '0.1.
|
|
3
|
-
process.env.PROBE_VERSION = '0.6.0-
|
|
4
|
-
process.env.VISOR_COMMIT_SHA = '
|
|
5
|
-
process.env.VISOR_COMMIT_SHORT = '
|
|
2
|
+
process.env.VISOR_VERSION = '0.1.166';
|
|
3
|
+
process.env.PROBE_VERSION = '0.6.0-rc280';
|
|
4
|
+
process.env.VISOR_COMMIT_SHA = 'eff5ed2df07d89e22e1c88d7d96b97572b0fd908';
|
|
5
|
+
process.env.VISOR_COMMIT_SHORT = 'eff5ed2';
|
|
6
6
|
/******/ (() => { // webpackBootstrap
|
|
7
7
|
/******/ var __webpack_modules__ = ({
|
|
8
8
|
|
|
@@ -161217,7 +161217,7 @@ async function handleDumpPolicyInput(checkId, argv) {
|
|
|
161217
161217
|
let PolicyInputBuilder;
|
|
161218
161218
|
try {
|
|
161219
161219
|
// @ts-ignore — enterprise/ may not exist in OSS builds (caught at runtime)
|
|
161220
|
-
const mod = await Promise.resolve().then(() => __importStar(__nccwpck_require__(
|
|
161220
|
+
const mod = await Promise.resolve().then(() => __importStar(__nccwpck_require__(17117)));
|
|
161221
161221
|
PolicyInputBuilder = mod.PolicyInputBuilder;
|
|
161222
161222
|
}
|
|
161223
161223
|
catch {
|
|
@@ -167104,6 +167104,1810 @@ class DependencyResolver {
|
|
|
167104
167104
|
exports.DependencyResolver = DependencyResolver;
|
|
167105
167105
|
|
|
167106
167106
|
|
|
167107
|
+
/***/ }),
|
|
167108
|
+
|
|
167109
|
+
/***/ 50069:
|
|
167110
|
+
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
|
|
167111
|
+
|
|
167112
|
+
"use strict";
|
|
167113
|
+
|
|
167114
|
+
/**
|
|
167115
|
+
* Copyright (c) ProbeLabs. All rights reserved.
|
|
167116
|
+
* Licensed under the Elastic License 2.0; you may not use this file except
|
|
167117
|
+
* in compliance with the Elastic License 2.0.
|
|
167118
|
+
*/
|
|
167119
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
167120
|
+
if (k2 === undefined) k2 = k;
|
|
167121
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
167122
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
167123
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
167124
|
+
}
|
|
167125
|
+
Object.defineProperty(o, k2, desc);
|
|
167126
|
+
}) : (function(o, m, k, k2) {
|
|
167127
|
+
if (k2 === undefined) k2 = k;
|
|
167128
|
+
o[k2] = m[k];
|
|
167129
|
+
}));
|
|
167130
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
167131
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
167132
|
+
}) : function(o, v) {
|
|
167133
|
+
o["default"] = v;
|
|
167134
|
+
});
|
|
167135
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
167136
|
+
var ownKeys = function(o) {
|
|
167137
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
167138
|
+
var ar = [];
|
|
167139
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
167140
|
+
return ar;
|
|
167141
|
+
};
|
|
167142
|
+
return ownKeys(o);
|
|
167143
|
+
};
|
|
167144
|
+
return function (mod) {
|
|
167145
|
+
if (mod && mod.__esModule) return mod;
|
|
167146
|
+
var result = {};
|
|
167147
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
167148
|
+
__setModuleDefault(result, mod);
|
|
167149
|
+
return result;
|
|
167150
|
+
};
|
|
167151
|
+
})();
|
|
167152
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
167153
|
+
exports.LicenseValidator = void 0;
|
|
167154
|
+
const crypto = __importStar(__nccwpck_require__(76982));
|
|
167155
|
+
const fs = __importStar(__nccwpck_require__(79896));
|
|
167156
|
+
const path = __importStar(__nccwpck_require__(16928));
|
|
167157
|
+
class LicenseValidator {
|
|
167158
|
+
/** Ed25519 public key for license verification (PEM format). */
|
|
167159
|
+
static PUBLIC_KEY = '-----BEGIN PUBLIC KEY-----\n' +
|
|
167160
|
+
'MCowBQYDK2VwAyEAI/Zd08EFmgIdrDm/HXd0l3/5GBt7R1PrdvhdmEXhJlU=\n' +
|
|
167161
|
+
'-----END PUBLIC KEY-----\n';
|
|
167162
|
+
cache = null;
|
|
167163
|
+
static CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
|
167164
|
+
static GRACE_PERIOD = 72 * 3600 * 1000; // 72 hours after expiry
|
|
167165
|
+
/**
|
|
167166
|
+
* Load and validate license from environment or file.
|
|
167167
|
+
*
|
|
167168
|
+
* Resolution order:
|
|
167169
|
+
* 1. VISOR_LICENSE env var (JWT string)
|
|
167170
|
+
* 2. VISOR_LICENSE_FILE env var (path to file)
|
|
167171
|
+
* 3. .visor-license in project root (cwd)
|
|
167172
|
+
* 4. .visor-license in ~/.config/visor/
|
|
167173
|
+
*/
|
|
167174
|
+
async loadAndValidate() {
|
|
167175
|
+
// Return cached result if still fresh
|
|
167176
|
+
if (this.cache && Date.now() - this.cache.validatedAt < LicenseValidator.CACHE_TTL) {
|
|
167177
|
+
return this.cache.payload;
|
|
167178
|
+
}
|
|
167179
|
+
const token = this.resolveToken();
|
|
167180
|
+
if (!token)
|
|
167181
|
+
return null;
|
|
167182
|
+
const payload = this.verifyAndDecode(token);
|
|
167183
|
+
if (!payload)
|
|
167184
|
+
return null;
|
|
167185
|
+
this.cache = { payload, validatedAt: Date.now() };
|
|
167186
|
+
return payload;
|
|
167187
|
+
}
|
|
167188
|
+
/** Check if a specific feature is licensed */
|
|
167189
|
+
hasFeature(feature) {
|
|
167190
|
+
if (!this.cache)
|
|
167191
|
+
return false;
|
|
167192
|
+
return this.cache.payload.features.includes(feature);
|
|
167193
|
+
}
|
|
167194
|
+
/** Check if license is valid (with grace period) */
|
|
167195
|
+
isValid() {
|
|
167196
|
+
if (!this.cache)
|
|
167197
|
+
return false;
|
|
167198
|
+
const now = Date.now();
|
|
167199
|
+
const expiryMs = this.cache.payload.exp * 1000;
|
|
167200
|
+
return now < expiryMs + LicenseValidator.GRACE_PERIOD;
|
|
167201
|
+
}
|
|
167202
|
+
/** Check if the license is within its grace period (expired but still valid) */
|
|
167203
|
+
isInGracePeriod() {
|
|
167204
|
+
if (!this.cache)
|
|
167205
|
+
return false;
|
|
167206
|
+
const now = Date.now();
|
|
167207
|
+
const expiryMs = this.cache.payload.exp * 1000;
|
|
167208
|
+
return now >= expiryMs && now < expiryMs + LicenseValidator.GRACE_PERIOD;
|
|
167209
|
+
}
|
|
167210
|
+
resolveToken() {
|
|
167211
|
+
// 1. Direct env var
|
|
167212
|
+
if (process.env.VISOR_LICENSE) {
|
|
167213
|
+
return process.env.VISOR_LICENSE.trim();
|
|
167214
|
+
}
|
|
167215
|
+
// 2. File path from env (validate against path traversal)
|
|
167216
|
+
if (process.env.VISOR_LICENSE_FILE) {
|
|
167217
|
+
// path.resolve() produces an absolute path with all '..' segments resolved,
|
|
167218
|
+
// so a separate resolved.includes('..') check is unnecessary.
|
|
167219
|
+
const resolved = path.resolve(process.env.VISOR_LICENSE_FILE);
|
|
167220
|
+
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
167221
|
+
const allowedPrefixes = [path.normalize(process.cwd())];
|
|
167222
|
+
if (home)
|
|
167223
|
+
allowedPrefixes.push(path.normalize(path.join(home, '.config', 'visor')));
|
|
167224
|
+
// Resolve symlinks so an attacker cannot create a symlink inside an
|
|
167225
|
+
// allowed prefix that points to an arbitrary file outside it.
|
|
167226
|
+
let realPath;
|
|
167227
|
+
try {
|
|
167228
|
+
realPath = fs.realpathSync(resolved);
|
|
167229
|
+
}
|
|
167230
|
+
catch {
|
|
167231
|
+
return null; // File doesn't exist or isn't accessible
|
|
167232
|
+
}
|
|
167233
|
+
const isSafe = allowedPrefixes.some(prefix => realPath === prefix || realPath.startsWith(prefix + path.sep));
|
|
167234
|
+
if (!isSafe)
|
|
167235
|
+
return null;
|
|
167236
|
+
return this.readFile(realPath);
|
|
167237
|
+
}
|
|
167238
|
+
// 3. .visor-license in cwd
|
|
167239
|
+
const cwdPath = path.join(process.cwd(), '.visor-license');
|
|
167240
|
+
const cwdToken = this.readFile(cwdPath);
|
|
167241
|
+
if (cwdToken)
|
|
167242
|
+
return cwdToken;
|
|
167243
|
+
// 4. ~/.config/visor/.visor-license
|
|
167244
|
+
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
167245
|
+
if (home) {
|
|
167246
|
+
const configPath = path.join(home, '.config', 'visor', '.visor-license');
|
|
167247
|
+
const configToken = this.readFile(configPath);
|
|
167248
|
+
if (configToken)
|
|
167249
|
+
return configToken;
|
|
167250
|
+
}
|
|
167251
|
+
return null;
|
|
167252
|
+
}
|
|
167253
|
+
readFile(filePath) {
|
|
167254
|
+
try {
|
|
167255
|
+
return fs.readFileSync(filePath, 'utf-8').trim();
|
|
167256
|
+
}
|
|
167257
|
+
catch {
|
|
167258
|
+
return null;
|
|
167259
|
+
}
|
|
167260
|
+
}
|
|
167261
|
+
verifyAndDecode(token) {
|
|
167262
|
+
try {
|
|
167263
|
+
const parts = token.split('.');
|
|
167264
|
+
if (parts.length !== 3)
|
|
167265
|
+
return null;
|
|
167266
|
+
const [headerB64, payloadB64, signatureB64] = parts;
|
|
167267
|
+
// Decode header to verify algorithm
|
|
167268
|
+
const header = JSON.parse(Buffer.from(headerB64, 'base64url').toString());
|
|
167269
|
+
if (header.alg !== 'EdDSA')
|
|
167270
|
+
return null;
|
|
167271
|
+
// Verify signature
|
|
167272
|
+
const data = `${headerB64}.${payloadB64}`;
|
|
167273
|
+
const signature = Buffer.from(signatureB64, 'base64url');
|
|
167274
|
+
const publicKey = crypto.createPublicKey(LicenseValidator.PUBLIC_KEY);
|
|
167275
|
+
// Validate that the loaded public key is actually Ed25519 (OID 1.3.101.112).
|
|
167276
|
+
// This prevents algorithm-confusion attacks if the embedded key were ever
|
|
167277
|
+
// swapped to a different type.
|
|
167278
|
+
if (publicKey.asymmetricKeyType !== 'ed25519') {
|
|
167279
|
+
return null;
|
|
167280
|
+
}
|
|
167281
|
+
// Ed25519 verification: algorithm must be null because EdDSA performs its
|
|
167282
|
+
// own internal hashing (SHA-512) — passing a digest algorithm here would
|
|
167283
|
+
// cause Node.js to throw. The key type is validated above.
|
|
167284
|
+
const isValid = crypto.verify(null, Buffer.from(data), publicKey, signature);
|
|
167285
|
+
if (!isValid)
|
|
167286
|
+
return null;
|
|
167287
|
+
// Decode payload
|
|
167288
|
+
const payload = JSON.parse(Buffer.from(payloadB64, 'base64url').toString());
|
|
167289
|
+
// Validate required fields
|
|
167290
|
+
if (!payload.org ||
|
|
167291
|
+
!Array.isArray(payload.features) ||
|
|
167292
|
+
typeof payload.exp !== 'number' ||
|
|
167293
|
+
typeof payload.iat !== 'number' ||
|
|
167294
|
+
!payload.sub) {
|
|
167295
|
+
return null;
|
|
167296
|
+
}
|
|
167297
|
+
// Check expiry (with grace period)
|
|
167298
|
+
const now = Date.now();
|
|
167299
|
+
const expiryMs = payload.exp * 1000;
|
|
167300
|
+
if (now >= expiryMs + LicenseValidator.GRACE_PERIOD) {
|
|
167301
|
+
return null;
|
|
167302
|
+
}
|
|
167303
|
+
return payload;
|
|
167304
|
+
}
|
|
167305
|
+
catch {
|
|
167306
|
+
return null;
|
|
167307
|
+
}
|
|
167308
|
+
}
|
|
167309
|
+
}
|
|
167310
|
+
exports.LicenseValidator = LicenseValidator;
|
|
167311
|
+
|
|
167312
|
+
|
|
167313
|
+
/***/ }),
|
|
167314
|
+
|
|
167315
|
+
/***/ 87068:
|
|
167316
|
+
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
|
|
167317
|
+
|
|
167318
|
+
"use strict";
|
|
167319
|
+
|
|
167320
|
+
/**
|
|
167321
|
+
* Copyright (c) ProbeLabs. All rights reserved.
|
|
167322
|
+
* Licensed under the Elastic License 2.0; you may not use this file except
|
|
167323
|
+
* in compliance with the Elastic License 2.0.
|
|
167324
|
+
*/
|
|
167325
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
167326
|
+
if (k2 === undefined) k2 = k;
|
|
167327
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
167328
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
167329
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
167330
|
+
}
|
|
167331
|
+
Object.defineProperty(o, k2, desc);
|
|
167332
|
+
}) : (function(o, m, k, k2) {
|
|
167333
|
+
if (k2 === undefined) k2 = k;
|
|
167334
|
+
o[k2] = m[k];
|
|
167335
|
+
}));
|
|
167336
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
167337
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
167338
|
+
}) : function(o, v) {
|
|
167339
|
+
o["default"] = v;
|
|
167340
|
+
});
|
|
167341
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
167342
|
+
var ownKeys = function(o) {
|
|
167343
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
167344
|
+
var ar = [];
|
|
167345
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
167346
|
+
return ar;
|
|
167347
|
+
};
|
|
167348
|
+
return ownKeys(o);
|
|
167349
|
+
};
|
|
167350
|
+
return function (mod) {
|
|
167351
|
+
if (mod && mod.__esModule) return mod;
|
|
167352
|
+
var result = {};
|
|
167353
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
167354
|
+
__setModuleDefault(result, mod);
|
|
167355
|
+
return result;
|
|
167356
|
+
};
|
|
167357
|
+
})();
|
|
167358
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
167359
|
+
exports.loadEnterprisePolicyEngine = loadEnterprisePolicyEngine;
|
|
167360
|
+
exports.loadEnterpriseStoreBackend = loadEnterpriseStoreBackend;
|
|
167361
|
+
const default_engine_1 = __nccwpck_require__(93866);
|
|
167362
|
+
/**
|
|
167363
|
+
* Load the enterprise policy engine if licensed, otherwise return the default no-op engine.
|
|
167364
|
+
*
|
|
167365
|
+
* This is the sole import boundary between OSS and enterprise code. Core code
|
|
167366
|
+
* must only import from this module (via dynamic `await import()`), never from
|
|
167367
|
+
* individual enterprise submodules.
|
|
167368
|
+
*/
|
|
167369
|
+
async function loadEnterprisePolicyEngine(config) {
|
|
167370
|
+
try {
|
|
167371
|
+
const { LicenseValidator } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(50069)));
|
|
167372
|
+
const validator = new LicenseValidator();
|
|
167373
|
+
const license = await validator.loadAndValidate();
|
|
167374
|
+
if (!license || !validator.hasFeature('policy')) {
|
|
167375
|
+
return new default_engine_1.DefaultPolicyEngine();
|
|
167376
|
+
}
|
|
167377
|
+
if (validator.isInGracePeriod()) {
|
|
167378
|
+
// eslint-disable-next-line no-console
|
|
167379
|
+
console.warn('[visor:enterprise] License has expired but is within the 72-hour grace period. ' +
|
|
167380
|
+
'Please renew your license.');
|
|
167381
|
+
}
|
|
167382
|
+
const { OpaPolicyEngine } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(39530)));
|
|
167383
|
+
const engine = new OpaPolicyEngine(config);
|
|
167384
|
+
await engine.initialize(config);
|
|
167385
|
+
return engine;
|
|
167386
|
+
}
|
|
167387
|
+
catch (err) {
|
|
167388
|
+
// Enterprise code not available or initialization failed
|
|
167389
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
167390
|
+
try {
|
|
167391
|
+
const { logger } = __nccwpck_require__(86999);
|
|
167392
|
+
logger.warn(`[PolicyEngine] Enterprise policy init failed, falling back to default: ${msg}`);
|
|
167393
|
+
}
|
|
167394
|
+
catch {
|
|
167395
|
+
// silent
|
|
167396
|
+
}
|
|
167397
|
+
return new default_engine_1.DefaultPolicyEngine();
|
|
167398
|
+
}
|
|
167399
|
+
}
|
|
167400
|
+
/**
|
|
167401
|
+
* Load the enterprise schedule store backend if licensed.
|
|
167402
|
+
*
|
|
167403
|
+
* @param driver Database driver ('postgresql', 'mysql', or 'mssql')
|
|
167404
|
+
* @param storageConfig Storage configuration with connection details
|
|
167405
|
+
* @param haConfig Optional HA configuration
|
|
167406
|
+
* @throws Error if enterprise license is not available or missing 'scheduler-sql' feature
|
|
167407
|
+
*/
|
|
167408
|
+
async function loadEnterpriseStoreBackend(driver, storageConfig, haConfig) {
|
|
167409
|
+
const { LicenseValidator } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(50069)));
|
|
167410
|
+
const validator = new LicenseValidator();
|
|
167411
|
+
const license = await validator.loadAndValidate();
|
|
167412
|
+
if (!license || !validator.hasFeature('scheduler-sql')) {
|
|
167413
|
+
throw new Error(`The ${driver} schedule storage driver requires a Visor Enterprise license ` +
|
|
167414
|
+
`with the 'scheduler-sql' feature. Please upgrade or use driver: 'sqlite' (default).`);
|
|
167415
|
+
}
|
|
167416
|
+
if (validator.isInGracePeriod()) {
|
|
167417
|
+
// eslint-disable-next-line no-console
|
|
167418
|
+
console.warn('[visor:enterprise] License has expired but is within the 72-hour grace period. ' +
|
|
167419
|
+
'Please renew your license.');
|
|
167420
|
+
}
|
|
167421
|
+
const { KnexStoreBackend } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(63737)));
|
|
167422
|
+
return new KnexStoreBackend(driver, storageConfig, haConfig);
|
|
167423
|
+
}
|
|
167424
|
+
|
|
167425
|
+
|
|
167426
|
+
/***/ }),
|
|
167427
|
+
|
|
167428
|
+
/***/ 628:
|
|
167429
|
+
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
|
|
167430
|
+
|
|
167431
|
+
"use strict";
|
|
167432
|
+
|
|
167433
|
+
/**
|
|
167434
|
+
* Copyright (c) ProbeLabs. All rights reserved.
|
|
167435
|
+
* Licensed under the Elastic License 2.0; you may not use this file except
|
|
167436
|
+
* in compliance with the Elastic License 2.0.
|
|
167437
|
+
*/
|
|
167438
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
167439
|
+
if (k2 === undefined) k2 = k;
|
|
167440
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
167441
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
167442
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
167443
|
+
}
|
|
167444
|
+
Object.defineProperty(o, k2, desc);
|
|
167445
|
+
}) : (function(o, m, k, k2) {
|
|
167446
|
+
if (k2 === undefined) k2 = k;
|
|
167447
|
+
o[k2] = m[k];
|
|
167448
|
+
}));
|
|
167449
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
167450
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
167451
|
+
}) : function(o, v) {
|
|
167452
|
+
o["default"] = v;
|
|
167453
|
+
});
|
|
167454
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
167455
|
+
var ownKeys = function(o) {
|
|
167456
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
167457
|
+
var ar = [];
|
|
167458
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
167459
|
+
return ar;
|
|
167460
|
+
};
|
|
167461
|
+
return ownKeys(o);
|
|
167462
|
+
};
|
|
167463
|
+
return function (mod) {
|
|
167464
|
+
if (mod && mod.__esModule) return mod;
|
|
167465
|
+
var result = {};
|
|
167466
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
167467
|
+
__setModuleDefault(result, mod);
|
|
167468
|
+
return result;
|
|
167469
|
+
};
|
|
167470
|
+
})();
|
|
167471
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
167472
|
+
exports.OpaCompiler = void 0;
|
|
167473
|
+
const fs = __importStar(__nccwpck_require__(79896));
|
|
167474
|
+
const path = __importStar(__nccwpck_require__(16928));
|
|
167475
|
+
const os = __importStar(__nccwpck_require__(70857));
|
|
167476
|
+
const crypto = __importStar(__nccwpck_require__(76982));
|
|
167477
|
+
const child_process_1 = __nccwpck_require__(35317);
|
|
167478
|
+
/**
|
|
167479
|
+
* OPA Rego Compiler - compiles .rego policy files to WASM bundles using the `opa` CLI.
|
|
167480
|
+
*
|
|
167481
|
+
* Handles:
|
|
167482
|
+
* - Resolving input paths to WASM bytes (direct .wasm, directory with policy.wasm, or .rego files)
|
|
167483
|
+
* - Compiling .rego files to WASM via `opa build`
|
|
167484
|
+
* - Caching compiled bundles based on content hashes
|
|
167485
|
+
* - Extracting policy.wasm from OPA tar.gz bundles
|
|
167486
|
+
*
|
|
167487
|
+
* Requires:
|
|
167488
|
+
* - `opa` CLI on PATH (only when auto-compiling .rego files)
|
|
167489
|
+
*/
|
|
167490
|
+
class OpaCompiler {
|
|
167491
|
+
static CACHE_DIR = path.join(os.tmpdir(), 'visor-opa-cache');
|
|
167492
|
+
/**
|
|
167493
|
+
* Resolve the input paths to WASM bytes.
|
|
167494
|
+
*
|
|
167495
|
+
* Strategy:
|
|
167496
|
+
* 1. If any path is a .wasm file, read it directly
|
|
167497
|
+
* 2. If a directory contains policy.wasm, read it
|
|
167498
|
+
* 3. Otherwise, collect all .rego files and auto-compile via `opa build`
|
|
167499
|
+
*/
|
|
167500
|
+
async resolveWasmBytes(paths) {
|
|
167501
|
+
// Collect .rego files and check for existing .wasm
|
|
167502
|
+
const regoFiles = [];
|
|
167503
|
+
for (const p of paths) {
|
|
167504
|
+
const resolved = path.resolve(p);
|
|
167505
|
+
// Reject paths containing '..' after resolution (path traversal)
|
|
167506
|
+
if (path.normalize(resolved).includes('..')) {
|
|
167507
|
+
throw new Error(`Policy path contains traversal sequences: ${p}`);
|
|
167508
|
+
}
|
|
167509
|
+
// Direct .wasm file
|
|
167510
|
+
if (resolved.endsWith('.wasm') && fs.existsSync(resolved)) {
|
|
167511
|
+
return fs.readFileSync(resolved);
|
|
167512
|
+
}
|
|
167513
|
+
if (!fs.existsSync(resolved))
|
|
167514
|
+
continue;
|
|
167515
|
+
const stat = fs.statSync(resolved);
|
|
167516
|
+
if (stat.isDirectory()) {
|
|
167517
|
+
// Check for pre-compiled policy.wasm in directory
|
|
167518
|
+
const wasmCandidate = path.join(resolved, 'policy.wasm');
|
|
167519
|
+
if (fs.existsSync(wasmCandidate)) {
|
|
167520
|
+
return fs.readFileSync(wasmCandidate);
|
|
167521
|
+
}
|
|
167522
|
+
// Collect all .rego files from directory
|
|
167523
|
+
const files = fs.readdirSync(resolved);
|
|
167524
|
+
for (const f of files) {
|
|
167525
|
+
if (f.endsWith('.rego')) {
|
|
167526
|
+
regoFiles.push(path.join(resolved, f));
|
|
167527
|
+
}
|
|
167528
|
+
}
|
|
167529
|
+
}
|
|
167530
|
+
else if (resolved.endsWith('.rego')) {
|
|
167531
|
+
regoFiles.push(resolved);
|
|
167532
|
+
}
|
|
167533
|
+
}
|
|
167534
|
+
if (regoFiles.length === 0) {
|
|
167535
|
+
throw new Error(`OPA WASM evaluator: no .wasm bundle or .rego files found in: ${paths.join(', ')}`);
|
|
167536
|
+
}
|
|
167537
|
+
// Auto-compile .rego -> .wasm
|
|
167538
|
+
return this.compileRego(regoFiles);
|
|
167539
|
+
}
|
|
167540
|
+
/**
|
|
167541
|
+
* Auto-compile .rego files to a WASM bundle using the `opa` CLI.
|
|
167542
|
+
*
|
|
167543
|
+
* Caches the compiled bundle based on a content hash of all input .rego files
|
|
167544
|
+
* so subsequent runs skip compilation if policies haven't changed.
|
|
167545
|
+
*/
|
|
167546
|
+
compileRego(regoFiles) {
|
|
167547
|
+
// Check that `opa` CLI is available
|
|
167548
|
+
try {
|
|
167549
|
+
(0, child_process_1.execFileSync)('opa', ['version'], { stdio: 'pipe' });
|
|
167550
|
+
}
|
|
167551
|
+
catch {
|
|
167552
|
+
throw new Error('OPA CLI (`opa`) not found on PATH. Install it from https://www.openpolicyagent.org/docs/latest/#running-opa\n' +
|
|
167553
|
+
'Or pre-compile your .rego files: opa build -t wasm -e visor -o bundle.tar.gz ' +
|
|
167554
|
+
regoFiles.join(' '));
|
|
167555
|
+
}
|
|
167556
|
+
// Compute content hash for cache key
|
|
167557
|
+
const hash = crypto.createHash('sha256');
|
|
167558
|
+
for (const f of regoFiles.sort()) {
|
|
167559
|
+
hash.update(fs.readFileSync(f));
|
|
167560
|
+
hash.update(f); // include filename for disambiguation
|
|
167561
|
+
}
|
|
167562
|
+
const cacheKey = hash.digest('hex').slice(0, 16);
|
|
167563
|
+
const cacheDir = OpaCompiler.CACHE_DIR;
|
|
167564
|
+
const cachedWasm = path.join(cacheDir, `${cacheKey}.wasm`);
|
|
167565
|
+
// Return cached bundle if still valid
|
|
167566
|
+
if (fs.existsSync(cachedWasm)) {
|
|
167567
|
+
return fs.readFileSync(cachedWasm);
|
|
167568
|
+
}
|
|
167569
|
+
// Compile to WASM via opa build
|
|
167570
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
167571
|
+
const bundleTar = path.join(cacheDir, `${cacheKey}-bundle.tar.gz`);
|
|
167572
|
+
try {
|
|
167573
|
+
const args = [
|
|
167574
|
+
'build',
|
|
167575
|
+
'-t',
|
|
167576
|
+
'wasm',
|
|
167577
|
+
'-e',
|
|
167578
|
+
'visor', // entrypoint: the visor package tree
|
|
167579
|
+
'-o',
|
|
167580
|
+
bundleTar,
|
|
167581
|
+
...regoFiles,
|
|
167582
|
+
];
|
|
167583
|
+
(0, child_process_1.execFileSync)('opa', args, {
|
|
167584
|
+
stdio: 'pipe',
|
|
167585
|
+
timeout: 30000,
|
|
167586
|
+
});
|
|
167587
|
+
}
|
|
167588
|
+
catch (err) {
|
|
167589
|
+
const stderr = err?.stderr?.toString() || '';
|
|
167590
|
+
throw new Error(`Failed to compile .rego files to WASM:\n${stderr}\n` +
|
|
167591
|
+
'Ensure your .rego files are valid and the `opa` CLI is installed.');
|
|
167592
|
+
}
|
|
167593
|
+
// Extract policy.wasm from the tar.gz bundle
|
|
167594
|
+
// OPA bundles are tar.gz with /policy.wasm inside
|
|
167595
|
+
try {
|
|
167596
|
+
(0, child_process_1.execFileSync)('tar', ['-xzf', bundleTar, '-C', cacheDir, '/policy.wasm'], {
|
|
167597
|
+
stdio: 'pipe',
|
|
167598
|
+
});
|
|
167599
|
+
const extractedWasm = path.join(cacheDir, 'policy.wasm');
|
|
167600
|
+
if (fs.existsSync(extractedWasm)) {
|
|
167601
|
+
// Move to cache-key named file
|
|
167602
|
+
fs.renameSync(extractedWasm, cachedWasm);
|
|
167603
|
+
}
|
|
167604
|
+
}
|
|
167605
|
+
catch {
|
|
167606
|
+
// Some tar implementations don't like leading /
|
|
167607
|
+
try {
|
|
167608
|
+
(0, child_process_1.execFileSync)('tar', ['-xzf', bundleTar, '-C', cacheDir, 'policy.wasm'], {
|
|
167609
|
+
stdio: 'pipe',
|
|
167610
|
+
});
|
|
167611
|
+
const extractedWasm = path.join(cacheDir, 'policy.wasm');
|
|
167612
|
+
if (fs.existsSync(extractedWasm)) {
|
|
167613
|
+
fs.renameSync(extractedWasm, cachedWasm);
|
|
167614
|
+
}
|
|
167615
|
+
}
|
|
167616
|
+
catch (err2) {
|
|
167617
|
+
throw new Error(`Failed to extract policy.wasm from OPA bundle: ${err2?.message || err2}`);
|
|
167618
|
+
}
|
|
167619
|
+
}
|
|
167620
|
+
// Clean up tar
|
|
167621
|
+
try {
|
|
167622
|
+
fs.unlinkSync(bundleTar);
|
|
167623
|
+
}
|
|
167624
|
+
catch { }
|
|
167625
|
+
if (!fs.existsSync(cachedWasm)) {
|
|
167626
|
+
throw new Error('OPA build succeeded but policy.wasm was not found in the bundle');
|
|
167627
|
+
}
|
|
167628
|
+
return fs.readFileSync(cachedWasm);
|
|
167629
|
+
}
|
|
167630
|
+
}
|
|
167631
|
+
exports.OpaCompiler = OpaCompiler;
|
|
167632
|
+
|
|
167633
|
+
|
|
167634
|
+
/***/ }),
|
|
167635
|
+
|
|
167636
|
+
/***/ 44693:
|
|
167637
|
+
/***/ ((__unused_webpack_module, exports) => {
|
|
167638
|
+
|
|
167639
|
+
"use strict";
|
|
167640
|
+
|
|
167641
|
+
/**
|
|
167642
|
+
* Copyright (c) ProbeLabs. All rights reserved.
|
|
167643
|
+
* Licensed under the Elastic License 2.0; you may not use this file except
|
|
167644
|
+
* in compliance with the Elastic License 2.0.
|
|
167645
|
+
*/
|
|
167646
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
167647
|
+
exports.OpaHttpEvaluator = void 0;
|
|
167648
|
+
/**
|
|
167649
|
+
* OPA HTTP Evaluator - evaluates policies via an external OPA server's REST API.
|
|
167650
|
+
*
|
|
167651
|
+
* Uses the built-in `fetch` API (Node 18+), so no extra dependencies are needed.
|
|
167652
|
+
*/
|
|
167653
|
+
class OpaHttpEvaluator {
|
|
167654
|
+
baseUrl;
|
|
167655
|
+
timeout;
|
|
167656
|
+
constructor(baseUrl, timeout = 5000) {
|
|
167657
|
+
// Validate URL format and protocol
|
|
167658
|
+
let parsed;
|
|
167659
|
+
try {
|
|
167660
|
+
parsed = new URL(baseUrl);
|
|
167661
|
+
}
|
|
167662
|
+
catch {
|
|
167663
|
+
throw new Error(`OPA HTTP evaluator: invalid URL: ${baseUrl}`);
|
|
167664
|
+
}
|
|
167665
|
+
if (!['http:', 'https:'].includes(parsed.protocol)) {
|
|
167666
|
+
throw new Error(`OPA HTTP evaluator: url must use http:// or https:// protocol, got: ${baseUrl}`);
|
|
167667
|
+
}
|
|
167668
|
+
// Block cloud metadata, loopback, link-local, and private network addresses
|
|
167669
|
+
const hostname = parsed.hostname;
|
|
167670
|
+
if (this.isBlockedHostname(hostname)) {
|
|
167671
|
+
throw new Error(`OPA HTTP evaluator: url must not point to internal, loopback, or private network addresses`);
|
|
167672
|
+
}
|
|
167673
|
+
// Normalize: strip trailing slash
|
|
167674
|
+
this.baseUrl = baseUrl.replace(/\/+$/, '');
|
|
167675
|
+
this.timeout = timeout;
|
|
167676
|
+
}
|
|
167677
|
+
/**
|
|
167678
|
+
* Check if a hostname is blocked due to SSRF concerns.
|
|
167679
|
+
*
|
|
167680
|
+
* Blocks:
|
|
167681
|
+
* - Loopback addresses (127.x.x.x, localhost, 0.0.0.0, ::1)
|
|
167682
|
+
* - Link-local addresses (169.254.x.x)
|
|
167683
|
+
* - Private networks (10.x.x.x, 172.16-31.x.x, 192.168.x.x)
|
|
167684
|
+
* - IPv6 unique local addresses (fd00::/8)
|
|
167685
|
+
* - Cloud metadata services (*.internal)
|
|
167686
|
+
*/
|
|
167687
|
+
isBlockedHostname(hostname) {
|
|
167688
|
+
if (!hostname)
|
|
167689
|
+
return true; // block empty hostnames
|
|
167690
|
+
// Normalize hostname: lowercase and remove brackets for IPv6
|
|
167691
|
+
const normalized = hostname.toLowerCase().replace(/^\[|\]$/g, '');
|
|
167692
|
+
// Block .internal domains (cloud metadata services)
|
|
167693
|
+
if (normalized === 'metadata.google.internal' || normalized.endsWith('.internal')) {
|
|
167694
|
+
return true;
|
|
167695
|
+
}
|
|
167696
|
+
// Block localhost variants
|
|
167697
|
+
if (normalized === 'localhost' || normalized === 'localhost.localdomain') {
|
|
167698
|
+
return true;
|
|
167699
|
+
}
|
|
167700
|
+
// Block IPv6 loopback
|
|
167701
|
+
if (normalized === '::1' || normalized === '0:0:0:0:0:0:0:1') {
|
|
167702
|
+
return true;
|
|
167703
|
+
}
|
|
167704
|
+
// Check IPv4 patterns
|
|
167705
|
+
const ipv4Pattern = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
|
|
167706
|
+
const ipv4Match = normalized.match(ipv4Pattern);
|
|
167707
|
+
if (ipv4Match) {
|
|
167708
|
+
const octets = ipv4Match.slice(1, 5).map(Number);
|
|
167709
|
+
// Validate octets are in range [0, 255]
|
|
167710
|
+
if (octets.some(octet => octet > 255)) {
|
|
167711
|
+
return false;
|
|
167712
|
+
}
|
|
167713
|
+
const [a, b] = octets;
|
|
167714
|
+
// Block loopback: 127.0.0.0/8
|
|
167715
|
+
if (a === 127) {
|
|
167716
|
+
return true;
|
|
167717
|
+
}
|
|
167718
|
+
// Block 0.0.0.0/8 (this host)
|
|
167719
|
+
if (a === 0) {
|
|
167720
|
+
return true;
|
|
167721
|
+
}
|
|
167722
|
+
// Block link-local: 169.254.0.0/16
|
|
167723
|
+
if (a === 169 && b === 254) {
|
|
167724
|
+
return true;
|
|
167725
|
+
}
|
|
167726
|
+
// Block private networks
|
|
167727
|
+
// 10.0.0.0/8
|
|
167728
|
+
if (a === 10) {
|
|
167729
|
+
return true;
|
|
167730
|
+
}
|
|
167731
|
+
// 172.16.0.0/12 (172.16.x.x through 172.31.x.x)
|
|
167732
|
+
if (a === 172 && b >= 16 && b <= 31) {
|
|
167733
|
+
return true;
|
|
167734
|
+
}
|
|
167735
|
+
// 192.168.0.0/16
|
|
167736
|
+
if (a === 192 && b === 168) {
|
|
167737
|
+
return true;
|
|
167738
|
+
}
|
|
167739
|
+
}
|
|
167740
|
+
// Check IPv6 patterns
|
|
167741
|
+
// Block unique local addresses: fd00::/8
|
|
167742
|
+
if (normalized.startsWith('fd') || normalized.startsWith('fc')) {
|
|
167743
|
+
return true;
|
|
167744
|
+
}
|
|
167745
|
+
// Block link-local: fe80::/10
|
|
167746
|
+
if (normalized.startsWith('fe80:')) {
|
|
167747
|
+
return true;
|
|
167748
|
+
}
|
|
167749
|
+
return false;
|
|
167750
|
+
}
|
|
167751
|
+
/**
|
|
167752
|
+
* Evaluate a policy rule against an input document via OPA REST API.
|
|
167753
|
+
*
|
|
167754
|
+
* @param input - The input document to evaluate
|
|
167755
|
+
* @param rulePath - OPA rule path (e.g., 'visor/check/execute')
|
|
167756
|
+
* @returns The result object from OPA, or undefined on error
|
|
167757
|
+
*/
|
|
167758
|
+
async evaluate(input, rulePath) {
|
|
167759
|
+
// OPA Data API: POST /v1/data/<path>
|
|
167760
|
+
const encodedPath = rulePath
|
|
167761
|
+
.split('/')
|
|
167762
|
+
.map(s => encodeURIComponent(s))
|
|
167763
|
+
.join('/');
|
|
167764
|
+
const url = `${this.baseUrl}/v1/data/${encodedPath}`;
|
|
167765
|
+
const controller = new AbortController();
|
|
167766
|
+
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
167767
|
+
try {
|
|
167768
|
+
const response = await fetch(url, {
|
|
167769
|
+
method: 'POST',
|
|
167770
|
+
headers: { 'Content-Type': 'application/json' },
|
|
167771
|
+
body: JSON.stringify({ input }),
|
|
167772
|
+
signal: controller.signal,
|
|
167773
|
+
});
|
|
167774
|
+
if (!response.ok) {
|
|
167775
|
+
throw new Error(`OPA HTTP ${response.status}: ${response.statusText}`);
|
|
167776
|
+
}
|
|
167777
|
+
let body;
|
|
167778
|
+
try {
|
|
167779
|
+
body = await response.json();
|
|
167780
|
+
}
|
|
167781
|
+
catch (jsonErr) {
|
|
167782
|
+
throw new Error(`OPA HTTP evaluator: failed to parse JSON response: ${jsonErr instanceof Error ? jsonErr.message : String(jsonErr)}`);
|
|
167783
|
+
}
|
|
167784
|
+
// OPA returns { result: { ... } }
|
|
167785
|
+
return body?.result;
|
|
167786
|
+
}
|
|
167787
|
+
finally {
|
|
167788
|
+
clearTimeout(timer);
|
|
167789
|
+
}
|
|
167790
|
+
}
|
|
167791
|
+
async shutdown() {
|
|
167792
|
+
// No persistent connections to close
|
|
167793
|
+
}
|
|
167794
|
+
}
|
|
167795
|
+
exports.OpaHttpEvaluator = OpaHttpEvaluator;
|
|
167796
|
+
|
|
167797
|
+
|
|
167798
|
+
/***/ }),
|
|
167799
|
+
|
|
167800
|
+
/***/ 39530:
|
|
167801
|
+
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
|
167802
|
+
|
|
167803
|
+
"use strict";
|
|
167804
|
+
|
|
167805
|
+
/**
|
|
167806
|
+
* Copyright (c) ProbeLabs. All rights reserved.
|
|
167807
|
+
* Licensed under the Elastic License 2.0; you may not use this file except
|
|
167808
|
+
* in compliance with the Elastic License 2.0.
|
|
167809
|
+
*/
|
|
167810
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
167811
|
+
exports.OpaPolicyEngine = void 0;
|
|
167812
|
+
const opa_wasm_evaluator_1 = __nccwpck_require__(8613);
|
|
167813
|
+
const opa_http_evaluator_1 = __nccwpck_require__(44693);
|
|
167814
|
+
const policy_input_builder_1 = __nccwpck_require__(17117);
|
|
167815
|
+
/**
|
|
167816
|
+
* Enterprise OPA Policy Engine.
|
|
167817
|
+
*
|
|
167818
|
+
* Wraps both WASM (local) and HTTP (remote) OPA evaluators behind the
|
|
167819
|
+
* OSS PolicyEngine interface. All OPA input building and role resolution
|
|
167820
|
+
* is handled internally — the OSS call sites pass only plain types.
|
|
167821
|
+
*/
|
|
167822
|
+
class OpaPolicyEngine {
|
|
167823
|
+
evaluator = null;
|
|
167824
|
+
fallback;
|
|
167825
|
+
timeout;
|
|
167826
|
+
config;
|
|
167827
|
+
inputBuilder = null;
|
|
167828
|
+
logger = null;
|
|
167829
|
+
constructor(config) {
|
|
167830
|
+
this.config = config;
|
|
167831
|
+
this.fallback = config.fallback || 'deny';
|
|
167832
|
+
this.timeout = config.timeout || 5000;
|
|
167833
|
+
}
|
|
167834
|
+
async initialize(config) {
|
|
167835
|
+
// Resolve logger once at initialization
|
|
167836
|
+
try {
|
|
167837
|
+
this.logger = (__nccwpck_require__(86999).logger);
|
|
167838
|
+
}
|
|
167839
|
+
catch {
|
|
167840
|
+
// logger not available in this context
|
|
167841
|
+
}
|
|
167842
|
+
// Build actor/repo context from environment (available at engine init time)
|
|
167843
|
+
const actor = {
|
|
167844
|
+
authorAssociation: process.env.VISOR_AUTHOR_ASSOCIATION,
|
|
167845
|
+
login: process.env.VISOR_AUTHOR_LOGIN || process.env.GITHUB_ACTOR,
|
|
167846
|
+
isLocalMode: !process.env.GITHUB_ACTIONS,
|
|
167847
|
+
};
|
|
167848
|
+
const repo = {
|
|
167849
|
+
owner: process.env.GITHUB_REPOSITORY_OWNER,
|
|
167850
|
+
name: process.env.GITHUB_REPOSITORY?.split('/')[1],
|
|
167851
|
+
branch: process.env.GITHUB_HEAD_REF,
|
|
167852
|
+
baseBranch: process.env.GITHUB_BASE_REF,
|
|
167853
|
+
event: process.env.GITHUB_EVENT_NAME,
|
|
167854
|
+
};
|
|
167855
|
+
const prNum = process.env.GITHUB_PR_NUMBER
|
|
167856
|
+
? parseInt(process.env.GITHUB_PR_NUMBER, 10)
|
|
167857
|
+
: undefined;
|
|
167858
|
+
const pullRequest = {
|
|
167859
|
+
number: prNum !== undefined && Number.isFinite(prNum) ? prNum : undefined,
|
|
167860
|
+
};
|
|
167861
|
+
this.inputBuilder = new policy_input_builder_1.PolicyInputBuilder(config, actor, repo, pullRequest);
|
|
167862
|
+
if (config.engine === 'local') {
|
|
167863
|
+
if (!config.rules) {
|
|
167864
|
+
throw new Error('OPA local mode requires `policy.rules` path to .wasm or .rego files');
|
|
167865
|
+
}
|
|
167866
|
+
const wasm = new opa_wasm_evaluator_1.OpaWasmEvaluator();
|
|
167867
|
+
await wasm.initialize(config.rules);
|
|
167868
|
+
if (config.data) {
|
|
167869
|
+
wasm.loadData(config.data);
|
|
167870
|
+
}
|
|
167871
|
+
this.evaluator = wasm;
|
|
167872
|
+
}
|
|
167873
|
+
else if (config.engine === 'remote') {
|
|
167874
|
+
if (!config.url) {
|
|
167875
|
+
throw new Error('OPA remote mode requires `policy.url` pointing to OPA server');
|
|
167876
|
+
}
|
|
167877
|
+
this.evaluator = new opa_http_evaluator_1.OpaHttpEvaluator(config.url, this.timeout);
|
|
167878
|
+
}
|
|
167879
|
+
else {
|
|
167880
|
+
this.evaluator = null;
|
|
167881
|
+
}
|
|
167882
|
+
}
|
|
167883
|
+
/**
|
|
167884
|
+
* Update actor/repo/PR context (e.g., after PR info becomes available).
|
|
167885
|
+
* Called by the enterprise loader when engine context is enriched.
|
|
167886
|
+
*/
|
|
167887
|
+
setActorContext(actor, repo, pullRequest) {
|
|
167888
|
+
this.inputBuilder = new policy_input_builder_1.PolicyInputBuilder(this.config, actor, repo, pullRequest);
|
|
167889
|
+
}
|
|
167890
|
+
async evaluateCheckExecution(checkId, checkConfig) {
|
|
167891
|
+
if (!this.evaluator || !this.inputBuilder)
|
|
167892
|
+
return { allowed: true };
|
|
167893
|
+
const cfg = checkConfig && typeof checkConfig === 'object'
|
|
167894
|
+
? checkConfig
|
|
167895
|
+
: {};
|
|
167896
|
+
const policyOverride = cfg.policy;
|
|
167897
|
+
const input = this.inputBuilder.forCheckExecution({
|
|
167898
|
+
id: checkId,
|
|
167899
|
+
type: cfg.type || 'ai',
|
|
167900
|
+
group: cfg.group,
|
|
167901
|
+
tags: cfg.tags,
|
|
167902
|
+
criticality: cfg.criticality,
|
|
167903
|
+
sandbox: cfg.sandbox,
|
|
167904
|
+
policy: policyOverride,
|
|
167905
|
+
});
|
|
167906
|
+
return this.doEvaluate(input, this.resolveRulePath('check.execute', policyOverride?.rule));
|
|
167907
|
+
}
|
|
167908
|
+
async evaluateToolInvocation(serverName, methodName, transport) {
|
|
167909
|
+
if (!this.evaluator || !this.inputBuilder)
|
|
167910
|
+
return { allowed: true };
|
|
167911
|
+
const input = this.inputBuilder.forToolInvocation(serverName, methodName, transport);
|
|
167912
|
+
return this.doEvaluate(input, 'visor/tool/invoke');
|
|
167913
|
+
}
|
|
167914
|
+
async evaluateCapabilities(checkId, capabilities) {
|
|
167915
|
+
if (!this.evaluator || !this.inputBuilder)
|
|
167916
|
+
return { allowed: true };
|
|
167917
|
+
const input = this.inputBuilder.forCapabilityResolve(checkId, capabilities);
|
|
167918
|
+
return this.doEvaluate(input, 'visor/capability/resolve');
|
|
167919
|
+
}
|
|
167920
|
+
async shutdown() {
|
|
167921
|
+
if (this.evaluator && 'shutdown' in this.evaluator) {
|
|
167922
|
+
await this.evaluator.shutdown();
|
|
167923
|
+
}
|
|
167924
|
+
this.evaluator = null;
|
|
167925
|
+
this.inputBuilder = null;
|
|
167926
|
+
}
|
|
167927
|
+
resolveRulePath(defaultScope, override) {
|
|
167928
|
+
if (override) {
|
|
167929
|
+
return override.startsWith('visor/') ? override : `visor/${override}`;
|
|
167930
|
+
}
|
|
167931
|
+
return `visor/${defaultScope.replace(/\./g, '/')}`;
|
|
167932
|
+
}
|
|
167933
|
+
async doEvaluate(input, rulePath) {
|
|
167934
|
+
try {
|
|
167935
|
+
this.logger?.debug(`[PolicyEngine] Evaluating ${rulePath}`, JSON.stringify(input));
|
|
167936
|
+
let timer;
|
|
167937
|
+
const timeoutPromise = new Promise((_resolve, reject) => {
|
|
167938
|
+
timer = setTimeout(() => reject(new Error('policy evaluation timeout')), this.timeout);
|
|
167939
|
+
});
|
|
167940
|
+
try {
|
|
167941
|
+
const result = await Promise.race([this.rawEvaluate(input, rulePath), timeoutPromise]);
|
|
167942
|
+
const decision = this.parseDecision(result);
|
|
167943
|
+
// In warn mode, override denied decisions to allowed but flag as warn
|
|
167944
|
+
if (!decision.allowed && this.fallback === 'warn') {
|
|
167945
|
+
decision.allowed = true;
|
|
167946
|
+
decision.warn = true;
|
|
167947
|
+
decision.reason = `audit: ${decision.reason || 'policy denied'}`;
|
|
167948
|
+
}
|
|
167949
|
+
this.logger?.debug(`[PolicyEngine] Decision for ${rulePath}: allowed=${decision.allowed}, warn=${decision.warn || false}, reason=${decision.reason || 'none'}`);
|
|
167950
|
+
return decision;
|
|
167951
|
+
}
|
|
167952
|
+
finally {
|
|
167953
|
+
if (timer)
|
|
167954
|
+
clearTimeout(timer);
|
|
167955
|
+
}
|
|
167956
|
+
}
|
|
167957
|
+
catch (err) {
|
|
167958
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
167959
|
+
this.logger?.warn(`[PolicyEngine] Evaluation failed for ${rulePath}: ${msg}`);
|
|
167960
|
+
return {
|
|
167961
|
+
allowed: this.fallback === 'allow' || this.fallback === 'warn',
|
|
167962
|
+
warn: this.fallback === 'warn' ? true : undefined,
|
|
167963
|
+
reason: `policy evaluation failed, fallback=${this.fallback}`,
|
|
167964
|
+
};
|
|
167965
|
+
}
|
|
167966
|
+
}
|
|
167967
|
+
async rawEvaluate(input, rulePath) {
|
|
167968
|
+
if (this.evaluator instanceof opa_wasm_evaluator_1.OpaWasmEvaluator) {
|
|
167969
|
+
const result = await this.evaluator.evaluate(input);
|
|
167970
|
+
// WASM compiled with `-e visor` entrypoint returns the full visor package tree.
|
|
167971
|
+
// Navigate to the specific rule subtree using rulePath segments.
|
|
167972
|
+
// e.g., 'visor/check/execute' → result.check.execute
|
|
167973
|
+
return this.navigateWasmResult(result, rulePath);
|
|
167974
|
+
}
|
|
167975
|
+
return this.evaluator.evaluate(input, rulePath);
|
|
167976
|
+
}
|
|
167977
|
+
/**
|
|
167978
|
+
* Navigate nested OPA WASM result tree to reach the specific rule's output.
|
|
167979
|
+
* The WASM entrypoint `-e visor` means the result root IS the visor package,
|
|
167980
|
+
* so we strip the `visor/` prefix and walk the remaining segments.
|
|
167981
|
+
*/
|
|
167982
|
+
navigateWasmResult(result, rulePath) {
|
|
167983
|
+
if (!result || typeof result !== 'object')
|
|
167984
|
+
return result;
|
|
167985
|
+
// Strip the 'visor/' prefix (matches our compilation entrypoint)
|
|
167986
|
+
const segments = rulePath.replace(/^visor\//, '').split('/');
|
|
167987
|
+
let current = result;
|
|
167988
|
+
for (const seg of segments) {
|
|
167989
|
+
if (current && typeof current === 'object' && seg in current) {
|
|
167990
|
+
current = current[seg];
|
|
167991
|
+
}
|
|
167992
|
+
else {
|
|
167993
|
+
return undefined; // path not found in result tree
|
|
167994
|
+
}
|
|
167995
|
+
}
|
|
167996
|
+
return current;
|
|
167997
|
+
}
|
|
167998
|
+
parseDecision(result) {
|
|
167999
|
+
if (result === undefined || result === null) {
|
|
168000
|
+
return {
|
|
168001
|
+
allowed: this.fallback === 'allow' || this.fallback === 'warn',
|
|
168002
|
+
warn: this.fallback === 'warn' ? true : undefined,
|
|
168003
|
+
reason: this.fallback === 'warn' ? 'audit: no policy result' : 'no policy result',
|
|
168004
|
+
};
|
|
168005
|
+
}
|
|
168006
|
+
const allowed = result.allowed !== false;
|
|
168007
|
+
const decision = {
|
|
168008
|
+
allowed,
|
|
168009
|
+
reason: result.reason,
|
|
168010
|
+
};
|
|
168011
|
+
if (result.capabilities) {
|
|
168012
|
+
decision.capabilities = result.capabilities;
|
|
168013
|
+
}
|
|
168014
|
+
return decision;
|
|
168015
|
+
}
|
|
168016
|
+
}
|
|
168017
|
+
exports.OpaPolicyEngine = OpaPolicyEngine;
|
|
168018
|
+
|
|
168019
|
+
|
|
168020
|
+
/***/ }),
|
|
168021
|
+
|
|
168022
|
+
/***/ 8613:
|
|
168023
|
+
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
|
|
168024
|
+
|
|
168025
|
+
"use strict";
|
|
168026
|
+
|
|
168027
|
+
/**
|
|
168028
|
+
* Copyright (c) ProbeLabs. All rights reserved.
|
|
168029
|
+
* Licensed under the Elastic License 2.0; you may not use this file except
|
|
168030
|
+
* in compliance with the Elastic License 2.0.
|
|
168031
|
+
*/
|
|
168032
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
168033
|
+
if (k2 === undefined) k2 = k;
|
|
168034
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
168035
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
168036
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
168037
|
+
}
|
|
168038
|
+
Object.defineProperty(o, k2, desc);
|
|
168039
|
+
}) : (function(o, m, k, k2) {
|
|
168040
|
+
if (k2 === undefined) k2 = k;
|
|
168041
|
+
o[k2] = m[k];
|
|
168042
|
+
}));
|
|
168043
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
168044
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
168045
|
+
}) : function(o, v) {
|
|
168046
|
+
o["default"] = v;
|
|
168047
|
+
});
|
|
168048
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
168049
|
+
var ownKeys = function(o) {
|
|
168050
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
168051
|
+
var ar = [];
|
|
168052
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
168053
|
+
return ar;
|
|
168054
|
+
};
|
|
168055
|
+
return ownKeys(o);
|
|
168056
|
+
};
|
|
168057
|
+
return function (mod) {
|
|
168058
|
+
if (mod && mod.__esModule) return mod;
|
|
168059
|
+
var result = {};
|
|
168060
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
168061
|
+
__setModuleDefault(result, mod);
|
|
168062
|
+
return result;
|
|
168063
|
+
};
|
|
168064
|
+
})();
|
|
168065
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
168066
|
+
exports.OpaWasmEvaluator = void 0;
|
|
168067
|
+
const fs = __importStar(__nccwpck_require__(79896));
|
|
168068
|
+
const path = __importStar(__nccwpck_require__(16928));
|
|
168069
|
+
const opa_compiler_1 = __nccwpck_require__(628);
|
|
168070
|
+
/**
|
|
168071
|
+
* OPA WASM Evaluator - loads and evaluates OPA policies locally.
|
|
168072
|
+
*
|
|
168073
|
+
* Supports three input formats:
|
|
168074
|
+
* 1. Pre-compiled `.wasm` bundle — loaded directly (fastest startup)
|
|
168075
|
+
* 2. `.rego` files or directory — auto-compiled to WASM via `opa build` CLI
|
|
168076
|
+
* 3. Directory with `policy.wasm` inside — loaded directly
|
|
168077
|
+
*
|
|
168078
|
+
* Compilation and caching of .rego files is delegated to {@link OpaCompiler}.
|
|
168079
|
+
*
|
|
168080
|
+
* Requires:
|
|
168081
|
+
* - `@open-policy-agent/opa-wasm` npm package (optional dep)
|
|
168082
|
+
* - `opa` CLI on PATH (only when auto-compiling .rego files)
|
|
168083
|
+
*/
|
|
168084
|
+
class OpaWasmEvaluator {
|
|
168085
|
+
policy = null;
|
|
168086
|
+
dataDocument = {};
|
|
168087
|
+
compiler = new opa_compiler_1.OpaCompiler();
|
|
168088
|
+
async initialize(rulesPath) {
|
|
168089
|
+
const paths = Array.isArray(rulesPath) ? rulesPath : [rulesPath];
|
|
168090
|
+
const wasmBytes = await this.compiler.resolveWasmBytes(paths);
|
|
168091
|
+
try {
|
|
168092
|
+
// Use createRequire to load the optional dep at runtime without ncc bundling it.
|
|
168093
|
+
// `new Function('id', 'return require(id)')` fails in ncc bundles because
|
|
168094
|
+
// `require` is not in the `new Function` scope. `createRequire` works correctly
|
|
168095
|
+
// because it creates a real Node.js require rooted at the given path.
|
|
168096
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
168097
|
+
const { createRequire } = __nccwpck_require__(73339);
|
|
168098
|
+
const runtimeRequire = createRequire(__filename);
|
|
168099
|
+
const opaWasm = runtimeRequire('@open-policy-agent/opa-wasm');
|
|
168100
|
+
const loadPolicy = opaWasm.loadPolicy || opaWasm.default?.loadPolicy;
|
|
168101
|
+
if (!loadPolicy) {
|
|
168102
|
+
throw new Error('loadPolicy not found in @open-policy-agent/opa-wasm');
|
|
168103
|
+
}
|
|
168104
|
+
this.policy = await loadPolicy(wasmBytes);
|
|
168105
|
+
}
|
|
168106
|
+
catch (err) {
|
|
168107
|
+
if (err?.code === 'MODULE_NOT_FOUND' || err?.code === 'ERR_MODULE_NOT_FOUND') {
|
|
168108
|
+
throw new Error('OPA WASM evaluator requires @open-policy-agent/opa-wasm. ' +
|
|
168109
|
+
'Install it with: npm install @open-policy-agent/opa-wasm');
|
|
168110
|
+
}
|
|
168111
|
+
throw err;
|
|
168112
|
+
}
|
|
168113
|
+
}
|
|
168114
|
+
/**
|
|
168115
|
+
* Load external data from a JSON file to use as the OPA data document.
|
|
168116
|
+
* The loaded data will be passed to `policy.setData()` during evaluation,
|
|
168117
|
+
* making it available in Rego via `data.<key>`.
|
|
168118
|
+
*/
|
|
168119
|
+
loadData(dataPath) {
|
|
168120
|
+
const resolved = path.resolve(dataPath);
|
|
168121
|
+
if (path.normalize(resolved).includes('..')) {
|
|
168122
|
+
throw new Error(`Data path contains traversal sequences: ${dataPath}`);
|
|
168123
|
+
}
|
|
168124
|
+
if (!fs.existsSync(resolved)) {
|
|
168125
|
+
throw new Error(`OPA data file not found: ${resolved}`);
|
|
168126
|
+
}
|
|
168127
|
+
const stat = fs.statSync(resolved);
|
|
168128
|
+
if (stat.size > 10 * 1024 * 1024) {
|
|
168129
|
+
throw new Error(`OPA data file exceeds 10MB limit: ${resolved} (${stat.size} bytes)`);
|
|
168130
|
+
}
|
|
168131
|
+
const raw = fs.readFileSync(resolved, 'utf-8');
|
|
168132
|
+
try {
|
|
168133
|
+
const parsed = JSON.parse(raw);
|
|
168134
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
168135
|
+
throw new Error('OPA data file must contain a JSON object (not an array or primitive)');
|
|
168136
|
+
}
|
|
168137
|
+
this.dataDocument = parsed;
|
|
168138
|
+
}
|
|
168139
|
+
catch (err) {
|
|
168140
|
+
if (err.message.startsWith('OPA data file must')) {
|
|
168141
|
+
throw err;
|
|
168142
|
+
}
|
|
168143
|
+
throw new Error(`Failed to parse OPA data file ${resolved}: ${err.message}`);
|
|
168144
|
+
}
|
|
168145
|
+
}
|
|
168146
|
+
async evaluate(input) {
|
|
168147
|
+
if (!this.policy) {
|
|
168148
|
+
throw new Error('OPA WASM evaluator not initialized');
|
|
168149
|
+
}
|
|
168150
|
+
this.policy.setData(this.dataDocument);
|
|
168151
|
+
const resultSet = this.policy.evaluate(input);
|
|
168152
|
+
if (Array.isArray(resultSet) && resultSet.length > 0) {
|
|
168153
|
+
return resultSet[0].result;
|
|
168154
|
+
}
|
|
168155
|
+
return undefined;
|
|
168156
|
+
}
|
|
168157
|
+
async shutdown() {
|
|
168158
|
+
if (this.policy) {
|
|
168159
|
+
// opa-wasm policy objects may have a close/free method for WASM cleanup
|
|
168160
|
+
if (typeof this.policy.close === 'function') {
|
|
168161
|
+
try {
|
|
168162
|
+
this.policy.close();
|
|
168163
|
+
}
|
|
168164
|
+
catch { }
|
|
168165
|
+
}
|
|
168166
|
+
else if (typeof this.policy.free === 'function') {
|
|
168167
|
+
try {
|
|
168168
|
+
this.policy.free();
|
|
168169
|
+
}
|
|
168170
|
+
catch { }
|
|
168171
|
+
}
|
|
168172
|
+
}
|
|
168173
|
+
this.policy = null;
|
|
168174
|
+
}
|
|
168175
|
+
}
|
|
168176
|
+
exports.OpaWasmEvaluator = OpaWasmEvaluator;
|
|
168177
|
+
|
|
168178
|
+
|
|
168179
|
+
/***/ }),
|
|
168180
|
+
|
|
168181
|
+
/***/ 17117:
|
|
168182
|
+
/***/ ((__unused_webpack_module, exports) => {
|
|
168183
|
+
|
|
168184
|
+
"use strict";
|
|
168185
|
+
|
|
168186
|
+
/**
|
|
168187
|
+
* Copyright (c) ProbeLabs. All rights reserved.
|
|
168188
|
+
* Licensed under the Elastic License 2.0; you may not use this file except
|
|
168189
|
+
* in compliance with the Elastic License 2.0.
|
|
168190
|
+
*/
|
|
168191
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
168192
|
+
exports.PolicyInputBuilder = void 0;
|
|
168193
|
+
/**
|
|
168194
|
+
* Builds OPA-compatible input documents from engine context.
|
|
168195
|
+
*
|
|
168196
|
+
* Resolves actor roles from the `policy.roles` config section by matching
|
|
168197
|
+
* the actor's authorAssociation and login against role definitions.
|
|
168198
|
+
*/
|
|
168199
|
+
class PolicyInputBuilder {
|
|
168200
|
+
roles;
|
|
168201
|
+
actor;
|
|
168202
|
+
repository;
|
|
168203
|
+
pullRequest;
|
|
168204
|
+
constructor(policyConfig, actor, repository, pullRequest) {
|
|
168205
|
+
this.roles = policyConfig.roles || {};
|
|
168206
|
+
this.actor = actor;
|
|
168207
|
+
this.repository = repository;
|
|
168208
|
+
this.pullRequest = pullRequest;
|
|
168209
|
+
}
|
|
168210
|
+
/** Resolve which roles apply to the current actor. */
|
|
168211
|
+
resolveRoles() {
|
|
168212
|
+
const matched = [];
|
|
168213
|
+
for (const [roleName, roleConfig] of Object.entries(this.roles)) {
|
|
168214
|
+
let identityMatch = false;
|
|
168215
|
+
if (roleConfig.author_association &&
|
|
168216
|
+
this.actor.authorAssociation &&
|
|
168217
|
+
roleConfig.author_association.includes(this.actor.authorAssociation)) {
|
|
168218
|
+
identityMatch = true;
|
|
168219
|
+
}
|
|
168220
|
+
if (!identityMatch &&
|
|
168221
|
+
roleConfig.users &&
|
|
168222
|
+
this.actor.login &&
|
|
168223
|
+
roleConfig.users.includes(this.actor.login)) {
|
|
168224
|
+
identityMatch = true;
|
|
168225
|
+
}
|
|
168226
|
+
// Slack user ID match
|
|
168227
|
+
if (!identityMatch &&
|
|
168228
|
+
roleConfig.slack_users &&
|
|
168229
|
+
this.actor.slack?.userId &&
|
|
168230
|
+
roleConfig.slack_users.includes(this.actor.slack.userId)) {
|
|
168231
|
+
identityMatch = true;
|
|
168232
|
+
}
|
|
168233
|
+
// Email match (case-insensitive)
|
|
168234
|
+
if (!identityMatch && roleConfig.emails && this.actor.slack?.email) {
|
|
168235
|
+
const actorEmail = this.actor.slack.email.toLowerCase();
|
|
168236
|
+
if (roleConfig.emails.some(e => e.toLowerCase() === actorEmail)) {
|
|
168237
|
+
identityMatch = true;
|
|
168238
|
+
}
|
|
168239
|
+
}
|
|
168240
|
+
// Note: teams-based role resolution requires GitHub API access (read:org scope)
|
|
168241
|
+
// and is not yet implemented. If configured, the role will not match via teams.
|
|
168242
|
+
if (!identityMatch)
|
|
168243
|
+
continue;
|
|
168244
|
+
// slack_channels gate: if set, the role only applies when triggered from one of these channels
|
|
168245
|
+
if (roleConfig.slack_channels && roleConfig.slack_channels.length > 0) {
|
|
168246
|
+
if (!this.actor.slack?.channelId ||
|
|
168247
|
+
!roleConfig.slack_channels.includes(this.actor.slack.channelId)) {
|
|
168248
|
+
continue;
|
|
168249
|
+
}
|
|
168250
|
+
}
|
|
168251
|
+
matched.push(roleName);
|
|
168252
|
+
}
|
|
168253
|
+
return matched;
|
|
168254
|
+
}
|
|
168255
|
+
buildActor() {
|
|
168256
|
+
return {
|
|
168257
|
+
authorAssociation: this.actor.authorAssociation,
|
|
168258
|
+
login: this.actor.login,
|
|
168259
|
+
roles: this.resolveRoles(),
|
|
168260
|
+
isLocalMode: this.actor.isLocalMode,
|
|
168261
|
+
...(this.actor.slack && { slack: this.actor.slack }),
|
|
168262
|
+
};
|
|
168263
|
+
}
|
|
168264
|
+
forCheckExecution(check) {
|
|
168265
|
+
return {
|
|
168266
|
+
scope: 'check.execute',
|
|
168267
|
+
check: {
|
|
168268
|
+
id: check.id,
|
|
168269
|
+
type: check.type,
|
|
168270
|
+
group: check.group,
|
|
168271
|
+
tags: check.tags,
|
|
168272
|
+
criticality: check.criticality,
|
|
168273
|
+
sandbox: check.sandbox,
|
|
168274
|
+
policy: check.policy,
|
|
168275
|
+
},
|
|
168276
|
+
actor: this.buildActor(),
|
|
168277
|
+
repository: this.repository,
|
|
168278
|
+
pullRequest: this.pullRequest,
|
|
168279
|
+
};
|
|
168280
|
+
}
|
|
168281
|
+
forToolInvocation(serverName, methodName, transport) {
|
|
168282
|
+
return {
|
|
168283
|
+
scope: 'tool.invoke',
|
|
168284
|
+
tool: { serverName, methodName, transport },
|
|
168285
|
+
actor: this.buildActor(),
|
|
168286
|
+
repository: this.repository,
|
|
168287
|
+
pullRequest: this.pullRequest,
|
|
168288
|
+
};
|
|
168289
|
+
}
|
|
168290
|
+
forCapabilityResolve(checkId, capabilities) {
|
|
168291
|
+
return {
|
|
168292
|
+
scope: 'capability.resolve',
|
|
168293
|
+
check: { id: checkId, type: 'ai' },
|
|
168294
|
+
capability: capabilities,
|
|
168295
|
+
actor: this.buildActor(),
|
|
168296
|
+
repository: this.repository,
|
|
168297
|
+
pullRequest: this.pullRequest,
|
|
168298
|
+
};
|
|
168299
|
+
}
|
|
168300
|
+
}
|
|
168301
|
+
exports.PolicyInputBuilder = PolicyInputBuilder;
|
|
168302
|
+
|
|
168303
|
+
|
|
168304
|
+
/***/ }),
|
|
168305
|
+
|
|
168306
|
+
/***/ 63737:
|
|
168307
|
+
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {
|
|
168308
|
+
|
|
168309
|
+
"use strict";
|
|
168310
|
+
|
|
168311
|
+
/**
|
|
168312
|
+
* Copyright (c) ProbeLabs. All rights reserved.
|
|
168313
|
+
* Licensed under the Elastic License 2.0; you may not use this file except
|
|
168314
|
+
* in compliance with the Elastic License 2.0.
|
|
168315
|
+
*/
|
|
168316
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
168317
|
+
if (k2 === undefined) k2 = k;
|
|
168318
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
168319
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
168320
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
168321
|
+
}
|
|
168322
|
+
Object.defineProperty(o, k2, desc);
|
|
168323
|
+
}) : (function(o, m, k, k2) {
|
|
168324
|
+
if (k2 === undefined) k2 = k;
|
|
168325
|
+
o[k2] = m[k];
|
|
168326
|
+
}));
|
|
168327
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
168328
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
168329
|
+
}) : function(o, v) {
|
|
168330
|
+
o["default"] = v;
|
|
168331
|
+
});
|
|
168332
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
168333
|
+
var ownKeys = function(o) {
|
|
168334
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
168335
|
+
var ar = [];
|
|
168336
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
168337
|
+
return ar;
|
|
168338
|
+
};
|
|
168339
|
+
return ownKeys(o);
|
|
168340
|
+
};
|
|
168341
|
+
return function (mod) {
|
|
168342
|
+
if (mod && mod.__esModule) return mod;
|
|
168343
|
+
var result = {};
|
|
168344
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
168345
|
+
__setModuleDefault(result, mod);
|
|
168346
|
+
return result;
|
|
168347
|
+
};
|
|
168348
|
+
})();
|
|
168349
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
168350
|
+
exports.KnexStoreBackend = void 0;
|
|
168351
|
+
/**
|
|
168352
|
+
* Knex-backed schedule store for PostgreSQL, MySQL, and MSSQL (Enterprise)
|
|
168353
|
+
*
|
|
168354
|
+
* Uses Knex query builder for database-agnostic SQL. Same schema as SQLite backend
|
|
168355
|
+
* but with real distributed locking via row-level claims (claimed_by/claimed_at/lock_token).
|
|
168356
|
+
*/
|
|
168357
|
+
const fs = __importStar(__nccwpck_require__(79896));
|
|
168358
|
+
const path = __importStar(__nccwpck_require__(16928));
|
|
168359
|
+
const uuid_1 = __nccwpck_require__(31914);
|
|
168360
|
+
const logger_1 = __nccwpck_require__(86999);
|
|
168361
|
+
function toNum(val) {
|
|
168362
|
+
if (val === null || val === undefined)
|
|
168363
|
+
return undefined;
|
|
168364
|
+
return typeof val === 'string' ? parseInt(val, 10) : val;
|
|
168365
|
+
}
|
|
168366
|
+
function safeJsonParse(value) {
|
|
168367
|
+
if (!value)
|
|
168368
|
+
return undefined;
|
|
168369
|
+
try {
|
|
168370
|
+
return JSON.parse(value);
|
|
168371
|
+
}
|
|
168372
|
+
catch {
|
|
168373
|
+
return undefined;
|
|
168374
|
+
}
|
|
168375
|
+
}
|
|
168376
|
+
function fromTriggerRow(row) {
|
|
168377
|
+
return {
|
|
168378
|
+
id: row.id,
|
|
168379
|
+
creatorId: row.creator_id,
|
|
168380
|
+
creatorContext: row.creator_context ?? undefined,
|
|
168381
|
+
creatorName: row.creator_name ?? undefined,
|
|
168382
|
+
description: row.description ?? undefined,
|
|
168383
|
+
channels: safeJsonParse(row.channels),
|
|
168384
|
+
fromUsers: safeJsonParse(row.from_users),
|
|
168385
|
+
fromBots: row.from_bots === true || row.from_bots === 1,
|
|
168386
|
+
contains: safeJsonParse(row.contains),
|
|
168387
|
+
matchPattern: row.match_pattern ?? undefined,
|
|
168388
|
+
threads: row.threads,
|
|
168389
|
+
workflow: row.workflow,
|
|
168390
|
+
inputs: safeJsonParse(row.inputs),
|
|
168391
|
+
outputContext: safeJsonParse(row.output_context),
|
|
168392
|
+
status: row.status,
|
|
168393
|
+
enabled: row.enabled === true || row.enabled === 1,
|
|
168394
|
+
createdAt: toNum(row.created_at),
|
|
168395
|
+
};
|
|
168396
|
+
}
|
|
168397
|
+
function toTriggerInsertRow(trigger) {
|
|
168398
|
+
return {
|
|
168399
|
+
id: trigger.id,
|
|
168400
|
+
creator_id: trigger.creatorId,
|
|
168401
|
+
creator_context: trigger.creatorContext ?? null,
|
|
168402
|
+
creator_name: trigger.creatorName ?? null,
|
|
168403
|
+
description: trigger.description ?? null,
|
|
168404
|
+
channels: trigger.channels ? JSON.stringify(trigger.channels) : null,
|
|
168405
|
+
from_users: trigger.fromUsers ? JSON.stringify(trigger.fromUsers) : null,
|
|
168406
|
+
from_bots: trigger.fromBots,
|
|
168407
|
+
contains: trigger.contains ? JSON.stringify(trigger.contains) : null,
|
|
168408
|
+
match_pattern: trigger.matchPattern ?? null,
|
|
168409
|
+
threads: trigger.threads,
|
|
168410
|
+
workflow: trigger.workflow,
|
|
168411
|
+
inputs: trigger.inputs ? JSON.stringify(trigger.inputs) : null,
|
|
168412
|
+
output_context: trigger.outputContext ? JSON.stringify(trigger.outputContext) : null,
|
|
168413
|
+
status: trigger.status,
|
|
168414
|
+
enabled: trigger.enabled,
|
|
168415
|
+
created_at: trigger.createdAt,
|
|
168416
|
+
};
|
|
168417
|
+
}
|
|
168418
|
+
function fromDbRow(row) {
|
|
168419
|
+
return {
|
|
168420
|
+
id: row.id,
|
|
168421
|
+
creatorId: row.creator_id,
|
|
168422
|
+
creatorContext: row.creator_context ?? undefined,
|
|
168423
|
+
creatorName: row.creator_name ?? undefined,
|
|
168424
|
+
timezone: row.timezone,
|
|
168425
|
+
schedule: row.schedule_expr,
|
|
168426
|
+
runAt: toNum(row.run_at),
|
|
168427
|
+
isRecurring: row.is_recurring === true || row.is_recurring === 1,
|
|
168428
|
+
originalExpression: row.original_expression,
|
|
168429
|
+
workflow: row.workflow ?? undefined,
|
|
168430
|
+
workflowInputs: safeJsonParse(row.workflow_inputs),
|
|
168431
|
+
outputContext: safeJsonParse(row.output_context),
|
|
168432
|
+
status: row.status,
|
|
168433
|
+
createdAt: toNum(row.created_at),
|
|
168434
|
+
lastRunAt: toNum(row.last_run_at),
|
|
168435
|
+
nextRunAt: toNum(row.next_run_at),
|
|
168436
|
+
runCount: row.run_count,
|
|
168437
|
+
failureCount: row.failure_count,
|
|
168438
|
+
lastError: row.last_error ?? undefined,
|
|
168439
|
+
previousResponse: row.previous_response ?? undefined,
|
|
168440
|
+
};
|
|
168441
|
+
}
|
|
168442
|
+
function toInsertRow(schedule) {
|
|
168443
|
+
return {
|
|
168444
|
+
id: schedule.id,
|
|
168445
|
+
creator_id: schedule.creatorId,
|
|
168446
|
+
creator_context: schedule.creatorContext ?? null,
|
|
168447
|
+
creator_name: schedule.creatorName ?? null,
|
|
168448
|
+
timezone: schedule.timezone,
|
|
168449
|
+
schedule_expr: schedule.schedule,
|
|
168450
|
+
run_at: schedule.runAt ?? null,
|
|
168451
|
+
is_recurring: schedule.isRecurring,
|
|
168452
|
+
original_expression: schedule.originalExpression,
|
|
168453
|
+
workflow: schedule.workflow ?? null,
|
|
168454
|
+
workflow_inputs: schedule.workflowInputs ? JSON.stringify(schedule.workflowInputs) : null,
|
|
168455
|
+
output_context: schedule.outputContext ? JSON.stringify(schedule.outputContext) : null,
|
|
168456
|
+
status: schedule.status,
|
|
168457
|
+
created_at: schedule.createdAt,
|
|
168458
|
+
last_run_at: schedule.lastRunAt ?? null,
|
|
168459
|
+
next_run_at: schedule.nextRunAt ?? null,
|
|
168460
|
+
run_count: schedule.runCount,
|
|
168461
|
+
failure_count: schedule.failureCount,
|
|
168462
|
+
last_error: schedule.lastError ?? null,
|
|
168463
|
+
previous_response: schedule.previousResponse ?? null,
|
|
168464
|
+
};
|
|
168465
|
+
}
|
|
168466
|
+
/**
|
|
168467
|
+
* Enterprise Knex-backed store for PostgreSQL, MySQL, and MSSQL
|
|
168468
|
+
*/
|
|
168469
|
+
class KnexStoreBackend {
|
|
168470
|
+
knex = null;
|
|
168471
|
+
driver;
|
|
168472
|
+
connection;
|
|
168473
|
+
constructor(driver, storageConfig, _haConfig) {
|
|
168474
|
+
this.driver = driver;
|
|
168475
|
+
this.connection = (storageConfig.connection || {});
|
|
168476
|
+
}
|
|
168477
|
+
async initialize() {
|
|
168478
|
+
// Load knex dynamically
|
|
168479
|
+
const { createRequire } = __nccwpck_require__(73339);
|
|
168480
|
+
const runtimeRequire = createRequire(__filename);
|
|
168481
|
+
let knexFactory;
|
|
168482
|
+
try {
|
|
168483
|
+
knexFactory = runtimeRequire('knex');
|
|
168484
|
+
}
|
|
168485
|
+
catch (err) {
|
|
168486
|
+
const code = err?.code;
|
|
168487
|
+
if (code === 'MODULE_NOT_FOUND' || code === 'ERR_MODULE_NOT_FOUND') {
|
|
168488
|
+
throw new Error('knex is required for PostgreSQL/MySQL/MSSQL schedule storage. ' +
|
|
168489
|
+
'Install it with: npm install knex');
|
|
168490
|
+
}
|
|
168491
|
+
throw err;
|
|
168492
|
+
}
|
|
168493
|
+
const clientMap = {
|
|
168494
|
+
postgresql: 'pg',
|
|
168495
|
+
mysql: 'mysql2',
|
|
168496
|
+
mssql: 'tedious',
|
|
168497
|
+
};
|
|
168498
|
+
const client = clientMap[this.driver];
|
|
168499
|
+
// Build connection config
|
|
168500
|
+
let connection;
|
|
168501
|
+
if (this.connection.connection_string) {
|
|
168502
|
+
connection = this.connection.connection_string;
|
|
168503
|
+
}
|
|
168504
|
+
else if (this.driver === 'mssql') {
|
|
168505
|
+
connection = this.buildMssqlConnection();
|
|
168506
|
+
}
|
|
168507
|
+
else {
|
|
168508
|
+
connection = this.buildStandardConnection();
|
|
168509
|
+
}
|
|
168510
|
+
this.knex = knexFactory({
|
|
168511
|
+
client,
|
|
168512
|
+
connection,
|
|
168513
|
+
pool: {
|
|
168514
|
+
min: this.connection.pool?.min ?? 0,
|
|
168515
|
+
max: this.connection.pool?.max ?? 10,
|
|
168516
|
+
},
|
|
168517
|
+
});
|
|
168518
|
+
// Run schema migration
|
|
168519
|
+
await this.migrateSchema();
|
|
168520
|
+
logger_1.logger.info(`[KnexStore] Initialized (${this.driver})`);
|
|
168521
|
+
}
|
|
168522
|
+
buildStandardConnection() {
|
|
168523
|
+
return {
|
|
168524
|
+
host: this.connection.host || 'localhost',
|
|
168525
|
+
port: this.connection.port,
|
|
168526
|
+
database: this.connection.database || 'visor',
|
|
168527
|
+
user: this.connection.user,
|
|
168528
|
+
password: this.connection.password,
|
|
168529
|
+
ssl: this.resolveSslConfig(),
|
|
168530
|
+
};
|
|
168531
|
+
}
|
|
168532
|
+
buildMssqlConnection() {
|
|
168533
|
+
const ssl = this.connection.ssl;
|
|
168534
|
+
const sslEnabled = ssl === true || (typeof ssl === 'object' && ssl.enabled !== false);
|
|
168535
|
+
return {
|
|
168536
|
+
server: this.connection.host || 'localhost',
|
|
168537
|
+
port: this.connection.port,
|
|
168538
|
+
database: this.connection.database || 'visor',
|
|
168539
|
+
user: this.connection.user,
|
|
168540
|
+
password: this.connection.password,
|
|
168541
|
+
options: {
|
|
168542
|
+
encrypt: sslEnabled,
|
|
168543
|
+
trustServerCertificate: typeof ssl === 'object' ? ssl.reject_unauthorized === false : !sslEnabled,
|
|
168544
|
+
},
|
|
168545
|
+
};
|
|
168546
|
+
}
|
|
168547
|
+
resolveSslConfig() {
|
|
168548
|
+
const ssl = this.connection.ssl;
|
|
168549
|
+
if (ssl === false || ssl === undefined)
|
|
168550
|
+
return false;
|
|
168551
|
+
if (ssl === true)
|
|
168552
|
+
return { rejectUnauthorized: true };
|
|
168553
|
+
// Object config
|
|
168554
|
+
if (ssl.enabled === false)
|
|
168555
|
+
return false;
|
|
168556
|
+
const result = {
|
|
168557
|
+
rejectUnauthorized: ssl.reject_unauthorized !== false,
|
|
168558
|
+
};
|
|
168559
|
+
if (ssl.ca) {
|
|
168560
|
+
const caPath = this.validateSslPath(ssl.ca, 'CA certificate');
|
|
168561
|
+
result.ca = fs.readFileSync(caPath, 'utf8');
|
|
168562
|
+
}
|
|
168563
|
+
if (ssl.cert) {
|
|
168564
|
+
const certPath = this.validateSslPath(ssl.cert, 'client certificate');
|
|
168565
|
+
result.cert = fs.readFileSync(certPath, 'utf8');
|
|
168566
|
+
}
|
|
168567
|
+
if (ssl.key) {
|
|
168568
|
+
const keyPath = this.validateSslPath(ssl.key, 'client key');
|
|
168569
|
+
result.key = fs.readFileSync(keyPath, 'utf8');
|
|
168570
|
+
}
|
|
168571
|
+
return result;
|
|
168572
|
+
}
|
|
168573
|
+
validateSslPath(filePath, label) {
|
|
168574
|
+
const resolved = path.resolve(filePath);
|
|
168575
|
+
if (resolved !== path.normalize(resolved)) {
|
|
168576
|
+
throw new Error(`SSL ${label} path contains invalid sequences: ${filePath}`);
|
|
168577
|
+
}
|
|
168578
|
+
if (!fs.existsSync(resolved)) {
|
|
168579
|
+
throw new Error(`SSL ${label} not found: ${filePath}`);
|
|
168580
|
+
}
|
|
168581
|
+
return resolved;
|
|
168582
|
+
}
|
|
168583
|
+
async shutdown() {
|
|
168584
|
+
if (this.knex) {
|
|
168585
|
+
await this.knex.destroy();
|
|
168586
|
+
this.knex = null;
|
|
168587
|
+
}
|
|
168588
|
+
}
|
|
168589
|
+
async migrateSchema() {
|
|
168590
|
+
const knex = this.getKnex();
|
|
168591
|
+
const exists = await knex.schema.hasTable('schedules');
|
|
168592
|
+
if (!exists) {
|
|
168593
|
+
await knex.schema.createTable('schedules', table => {
|
|
168594
|
+
table.string('id', 36).primary();
|
|
168595
|
+
table.string('creator_id', 255).notNullable().index();
|
|
168596
|
+
table.string('creator_context', 255);
|
|
168597
|
+
table.string('creator_name', 255);
|
|
168598
|
+
table.string('timezone', 64).notNullable().defaultTo('UTC');
|
|
168599
|
+
table.string('schedule_expr', 255);
|
|
168600
|
+
table.bigInteger('run_at');
|
|
168601
|
+
table.boolean('is_recurring').notNullable();
|
|
168602
|
+
table.text('original_expression');
|
|
168603
|
+
table.string('workflow', 255);
|
|
168604
|
+
table.text('workflow_inputs');
|
|
168605
|
+
table.text('output_context');
|
|
168606
|
+
table.string('status', 20).notNullable().index();
|
|
168607
|
+
table.bigInteger('created_at').notNullable();
|
|
168608
|
+
table.bigInteger('last_run_at');
|
|
168609
|
+
table.bigInteger('next_run_at');
|
|
168610
|
+
table.integer('run_count').notNullable().defaultTo(0);
|
|
168611
|
+
table.integer('failure_count').notNullable().defaultTo(0);
|
|
168612
|
+
table.text('last_error');
|
|
168613
|
+
table.text('previous_response');
|
|
168614
|
+
table.index(['status', 'next_run_at']);
|
|
168615
|
+
});
|
|
168616
|
+
}
|
|
168617
|
+
// Create message_triggers table
|
|
168618
|
+
const triggersExist = await knex.schema.hasTable('message_triggers');
|
|
168619
|
+
if (!triggersExist) {
|
|
168620
|
+
await knex.schema.createTable('message_triggers', table => {
|
|
168621
|
+
table.string('id', 36).primary();
|
|
168622
|
+
table.string('creator_id', 255).notNullable().index();
|
|
168623
|
+
table.string('creator_context', 255);
|
|
168624
|
+
table.string('creator_name', 255);
|
|
168625
|
+
table.text('description');
|
|
168626
|
+
table.text('channels'); // JSON array
|
|
168627
|
+
table.text('from_users'); // JSON array
|
|
168628
|
+
table.boolean('from_bots').notNullable().defaultTo(false);
|
|
168629
|
+
table.text('contains'); // JSON array
|
|
168630
|
+
table.text('match_pattern');
|
|
168631
|
+
table.string('threads', 20).notNullable().defaultTo('any');
|
|
168632
|
+
table.string('workflow', 255).notNullable();
|
|
168633
|
+
table.text('inputs'); // JSON
|
|
168634
|
+
table.text('output_context'); // JSON
|
|
168635
|
+
table.string('status', 20).notNullable().defaultTo('active').index();
|
|
168636
|
+
table.boolean('enabled').notNullable().defaultTo(true);
|
|
168637
|
+
table.bigInteger('created_at').notNullable();
|
|
168638
|
+
});
|
|
168639
|
+
}
|
|
168640
|
+
// Create scheduler_locks table for distributed locking
|
|
168641
|
+
const locksExist = await knex.schema.hasTable('scheduler_locks');
|
|
168642
|
+
if (!locksExist) {
|
|
168643
|
+
await knex.schema.createTable('scheduler_locks', table => {
|
|
168644
|
+
table.string('lock_id', 255).primary();
|
|
168645
|
+
table.string('node_id', 255).notNullable();
|
|
168646
|
+
table.string('lock_token', 36).notNullable();
|
|
168647
|
+
table.bigInteger('acquired_at').notNullable();
|
|
168648
|
+
table.bigInteger('expires_at').notNullable();
|
|
168649
|
+
});
|
|
168650
|
+
}
|
|
168651
|
+
}
|
|
168652
|
+
getKnex() {
|
|
168653
|
+
if (!this.knex) {
|
|
168654
|
+
throw new Error('[KnexStore] Not initialized. Call initialize() first.');
|
|
168655
|
+
}
|
|
168656
|
+
return this.knex;
|
|
168657
|
+
}
|
|
168658
|
+
// --- CRUD ---
|
|
168659
|
+
async create(schedule) {
|
|
168660
|
+
const knex = this.getKnex();
|
|
168661
|
+
const newSchedule = {
|
|
168662
|
+
...schedule,
|
|
168663
|
+
id: (0, uuid_1.v4)(),
|
|
168664
|
+
createdAt: Date.now(),
|
|
168665
|
+
runCount: 0,
|
|
168666
|
+
failureCount: 0,
|
|
168667
|
+
status: 'active',
|
|
168668
|
+
};
|
|
168669
|
+
await knex('schedules').insert(toInsertRow(newSchedule));
|
|
168670
|
+
logger_1.logger.info(`[KnexStore] Created schedule ${newSchedule.id} for user ${newSchedule.creatorId}`);
|
|
168671
|
+
return newSchedule;
|
|
168672
|
+
}
|
|
168673
|
+
async importSchedule(schedule) {
|
|
168674
|
+
const knex = this.getKnex();
|
|
168675
|
+
const existing = await knex('schedules').where('id', schedule.id).first();
|
|
168676
|
+
if (existing)
|
|
168677
|
+
return; // Already imported (idempotent)
|
|
168678
|
+
await knex('schedules').insert(toInsertRow(schedule));
|
|
168679
|
+
}
|
|
168680
|
+
async get(id) {
|
|
168681
|
+
const knex = this.getKnex();
|
|
168682
|
+
const row = await knex('schedules').where('id', id).first();
|
|
168683
|
+
return row ? fromDbRow(row) : undefined;
|
|
168684
|
+
}
|
|
168685
|
+
async update(id, patch) {
|
|
168686
|
+
const knex = this.getKnex();
|
|
168687
|
+
const existing = await knex('schedules').where('id', id).first();
|
|
168688
|
+
if (!existing)
|
|
168689
|
+
return undefined;
|
|
168690
|
+
const current = fromDbRow(existing);
|
|
168691
|
+
const updated = { ...current, ...patch, id: current.id };
|
|
168692
|
+
const row = toInsertRow(updated);
|
|
168693
|
+
// Remove id from update (PK cannot change)
|
|
168694
|
+
delete row.id;
|
|
168695
|
+
await knex('schedules').where('id', id).update(row);
|
|
168696
|
+
return updated;
|
|
168697
|
+
}
|
|
168698
|
+
async delete(id) {
|
|
168699
|
+
const knex = this.getKnex();
|
|
168700
|
+
const deleted = await knex('schedules').where('id', id).del();
|
|
168701
|
+
if (deleted > 0) {
|
|
168702
|
+
logger_1.logger.info(`[KnexStore] Deleted schedule ${id}`);
|
|
168703
|
+
return true;
|
|
168704
|
+
}
|
|
168705
|
+
return false;
|
|
168706
|
+
}
|
|
168707
|
+
// --- Queries ---
|
|
168708
|
+
async getByCreator(creatorId) {
|
|
168709
|
+
const knex = this.getKnex();
|
|
168710
|
+
const rows = await knex('schedules').where('creator_id', creatorId);
|
|
168711
|
+
return rows.map((r) => fromDbRow(r));
|
|
168712
|
+
}
|
|
168713
|
+
async getActiveSchedules() {
|
|
168714
|
+
const knex = this.getKnex();
|
|
168715
|
+
const rows = await knex('schedules').where('status', 'active');
|
|
168716
|
+
return rows.map((r) => fromDbRow(r));
|
|
168717
|
+
}
|
|
168718
|
+
async getDueSchedules(now) {
|
|
168719
|
+
const ts = now ?? Date.now();
|
|
168720
|
+
const knex = this.getKnex();
|
|
168721
|
+
// MSSQL uses 1/0 for booleans
|
|
168722
|
+
const bFalse = this.driver === 'mssql' ? 0 : false;
|
|
168723
|
+
const bTrue = this.driver === 'mssql' ? 1 : true;
|
|
168724
|
+
const rows = await knex('schedules')
|
|
168725
|
+
.where('status', 'active')
|
|
168726
|
+
.andWhere(function () {
|
|
168727
|
+
this.where(function () {
|
|
168728
|
+
this.where('is_recurring', bFalse)
|
|
168729
|
+
.whereNotNull('run_at')
|
|
168730
|
+
.where('run_at', '<=', ts);
|
|
168731
|
+
}).orWhere(function () {
|
|
168732
|
+
this.where('is_recurring', bTrue)
|
|
168733
|
+
.whereNotNull('next_run_at')
|
|
168734
|
+
.where('next_run_at', '<=', ts);
|
|
168735
|
+
});
|
|
168736
|
+
});
|
|
168737
|
+
return rows.map((r) => fromDbRow(r));
|
|
168738
|
+
}
|
|
168739
|
+
async findByWorkflow(creatorId, workflowName) {
|
|
168740
|
+
const knex = this.getKnex();
|
|
168741
|
+
const escaped = workflowName.toLowerCase().replace(/[%_\\]/g, '\\$&');
|
|
168742
|
+
const pattern = `%${escaped}%`;
|
|
168743
|
+
const rows = await knex('schedules')
|
|
168744
|
+
.where('creator_id', creatorId)
|
|
168745
|
+
.where('status', 'active')
|
|
168746
|
+
.whereRaw("LOWER(workflow) LIKE ? ESCAPE '\\'", [pattern]);
|
|
168747
|
+
return rows.map((r) => fromDbRow(r));
|
|
168748
|
+
}
|
|
168749
|
+
async getAll() {
|
|
168750
|
+
const knex = this.getKnex();
|
|
168751
|
+
const rows = await knex('schedules');
|
|
168752
|
+
return rows.map((r) => fromDbRow(r));
|
|
168753
|
+
}
|
|
168754
|
+
async getStats() {
|
|
168755
|
+
const knex = this.getKnex();
|
|
168756
|
+
// MSSQL uses 1/0 for booleans; PostgreSQL/MySQL accept both true/1
|
|
168757
|
+
const boolTrue = this.driver === 'mssql' ? '1' : 'true';
|
|
168758
|
+
const boolFalse = this.driver === 'mssql' ? '0' : 'false';
|
|
168759
|
+
const result = await knex('schedules')
|
|
168760
|
+
.select(knex.raw('COUNT(*) as total'), knex.raw("SUM(CASE WHEN status = 'active' THEN 1 ELSE 0 END) as active"), knex.raw("SUM(CASE WHEN status = 'paused' THEN 1 ELSE 0 END) as paused"), knex.raw("SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed"), knex.raw("SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed"), knex.raw(`SUM(CASE WHEN is_recurring = ${boolTrue} THEN 1 ELSE 0 END) as recurring`), knex.raw(`SUM(CASE WHEN is_recurring = ${boolFalse} THEN 1 ELSE 0 END) as one_time`))
|
|
168761
|
+
.first();
|
|
168762
|
+
return {
|
|
168763
|
+
total: Number(result.total) || 0,
|
|
168764
|
+
active: Number(result.active) || 0,
|
|
168765
|
+
paused: Number(result.paused) || 0,
|
|
168766
|
+
completed: Number(result.completed) || 0,
|
|
168767
|
+
failed: Number(result.failed) || 0,
|
|
168768
|
+
recurring: Number(result.recurring) || 0,
|
|
168769
|
+
oneTime: Number(result.one_time) || 0,
|
|
168770
|
+
};
|
|
168771
|
+
}
|
|
168772
|
+
async validateLimits(creatorId, isRecurring, limits) {
|
|
168773
|
+
const knex = this.getKnex();
|
|
168774
|
+
if (limits.maxGlobal) {
|
|
168775
|
+
const result = await knex('schedules').count('* as cnt').first();
|
|
168776
|
+
if (Number(result?.cnt) >= limits.maxGlobal) {
|
|
168777
|
+
throw new Error(`Global schedule limit reached (${limits.maxGlobal})`);
|
|
168778
|
+
}
|
|
168779
|
+
}
|
|
168780
|
+
if (limits.maxPerUser) {
|
|
168781
|
+
const result = await knex('schedules')
|
|
168782
|
+
.where('creator_id', creatorId)
|
|
168783
|
+
.count('* as cnt')
|
|
168784
|
+
.first();
|
|
168785
|
+
if (Number(result?.cnt) >= limits.maxPerUser) {
|
|
168786
|
+
throw new Error(`You have reached the maximum number of schedules (${limits.maxPerUser})`);
|
|
168787
|
+
}
|
|
168788
|
+
}
|
|
168789
|
+
if (isRecurring && limits.maxRecurringPerUser) {
|
|
168790
|
+
const bTrue = this.driver === 'mssql' ? 1 : true;
|
|
168791
|
+
const result = await knex('schedules')
|
|
168792
|
+
.where('creator_id', creatorId)
|
|
168793
|
+
.where('is_recurring', bTrue)
|
|
168794
|
+
.count('* as cnt')
|
|
168795
|
+
.first();
|
|
168796
|
+
if (Number(result?.cnt) >= limits.maxRecurringPerUser) {
|
|
168797
|
+
throw new Error(`You have reached the maximum number of recurring schedules (${limits.maxRecurringPerUser})`);
|
|
168798
|
+
}
|
|
168799
|
+
}
|
|
168800
|
+
}
|
|
168801
|
+
// --- HA Distributed Locking (via scheduler_locks table) ---
|
|
168802
|
+
async tryAcquireLock(lockId, nodeId, ttlSeconds) {
|
|
168803
|
+
const knex = this.getKnex();
|
|
168804
|
+
const now = Date.now();
|
|
168805
|
+
const expiresAt = now + ttlSeconds * 1000;
|
|
168806
|
+
const token = (0, uuid_1.v4)();
|
|
168807
|
+
// Step 1: Try to claim an existing expired lock
|
|
168808
|
+
const updated = await knex('scheduler_locks')
|
|
168809
|
+
.where('lock_id', lockId)
|
|
168810
|
+
.where('expires_at', '<', now)
|
|
168811
|
+
.update({
|
|
168812
|
+
node_id: nodeId,
|
|
168813
|
+
lock_token: token,
|
|
168814
|
+
acquired_at: now,
|
|
168815
|
+
expires_at: expiresAt,
|
|
168816
|
+
});
|
|
168817
|
+
if (updated > 0)
|
|
168818
|
+
return token;
|
|
168819
|
+
// Step 2: Try to INSERT a new lock row
|
|
168820
|
+
try {
|
|
168821
|
+
await knex('scheduler_locks').insert({
|
|
168822
|
+
lock_id: lockId,
|
|
168823
|
+
node_id: nodeId,
|
|
168824
|
+
lock_token: token,
|
|
168825
|
+
acquired_at: now,
|
|
168826
|
+
expires_at: expiresAt,
|
|
168827
|
+
});
|
|
168828
|
+
return token;
|
|
168829
|
+
}
|
|
168830
|
+
catch {
|
|
168831
|
+
// Unique constraint violation — another node holds the lock
|
|
168832
|
+
return null;
|
|
168833
|
+
}
|
|
168834
|
+
}
|
|
168835
|
+
async releaseLock(lockId, lockToken) {
|
|
168836
|
+
const knex = this.getKnex();
|
|
168837
|
+
await knex('scheduler_locks').where('lock_id', lockId).where('lock_token', lockToken).del();
|
|
168838
|
+
}
|
|
168839
|
+
async renewLock(lockId, lockToken, ttlSeconds) {
|
|
168840
|
+
const knex = this.getKnex();
|
|
168841
|
+
const now = Date.now();
|
|
168842
|
+
const expiresAt = now + ttlSeconds * 1000;
|
|
168843
|
+
const updated = await knex('scheduler_locks')
|
|
168844
|
+
.where('lock_id', lockId)
|
|
168845
|
+
.where('lock_token', lockToken)
|
|
168846
|
+
.update({ acquired_at: now, expires_at: expiresAt });
|
|
168847
|
+
return updated > 0;
|
|
168848
|
+
}
|
|
168849
|
+
async flush() {
|
|
168850
|
+
// No-op for server-based backends
|
|
168851
|
+
}
|
|
168852
|
+
// --- Message Trigger CRUD ---
|
|
168853
|
+
async createTrigger(trigger) {
|
|
168854
|
+
const knex = this.getKnex();
|
|
168855
|
+
const newTrigger = {
|
|
168856
|
+
...trigger,
|
|
168857
|
+
id: (0, uuid_1.v4)(),
|
|
168858
|
+
createdAt: Date.now(),
|
|
168859
|
+
};
|
|
168860
|
+
await knex('message_triggers').insert(toTriggerInsertRow(newTrigger));
|
|
168861
|
+
logger_1.logger.info(`[KnexStore] Created trigger ${newTrigger.id} for user ${newTrigger.creatorId}`);
|
|
168862
|
+
return newTrigger;
|
|
168863
|
+
}
|
|
168864
|
+
async getTrigger(id) {
|
|
168865
|
+
const knex = this.getKnex();
|
|
168866
|
+
const row = await knex('message_triggers').where('id', id).first();
|
|
168867
|
+
return row ? fromTriggerRow(row) : undefined;
|
|
168868
|
+
}
|
|
168869
|
+
async updateTrigger(id, patch) {
|
|
168870
|
+
const knex = this.getKnex();
|
|
168871
|
+
const existing = await knex('message_triggers').where('id', id).first();
|
|
168872
|
+
if (!existing)
|
|
168873
|
+
return undefined;
|
|
168874
|
+
const current = fromTriggerRow(existing);
|
|
168875
|
+
const updated = {
|
|
168876
|
+
...current,
|
|
168877
|
+
...patch,
|
|
168878
|
+
id: current.id,
|
|
168879
|
+
createdAt: current.createdAt,
|
|
168880
|
+
};
|
|
168881
|
+
const row = toTriggerInsertRow(updated);
|
|
168882
|
+
delete row.id;
|
|
168883
|
+
await knex('message_triggers').where('id', id).update(row);
|
|
168884
|
+
return updated;
|
|
168885
|
+
}
|
|
168886
|
+
async deleteTrigger(id) {
|
|
168887
|
+
const knex = this.getKnex();
|
|
168888
|
+
const deleted = await knex('message_triggers').where('id', id).del();
|
|
168889
|
+
if (deleted > 0) {
|
|
168890
|
+
logger_1.logger.info(`[KnexStore] Deleted trigger ${id}`);
|
|
168891
|
+
return true;
|
|
168892
|
+
}
|
|
168893
|
+
return false;
|
|
168894
|
+
}
|
|
168895
|
+
async getTriggersByCreator(creatorId) {
|
|
168896
|
+
const knex = this.getKnex();
|
|
168897
|
+
const rows = await knex('message_triggers').where('creator_id', creatorId);
|
|
168898
|
+
return rows.map((r) => fromTriggerRow(r));
|
|
168899
|
+
}
|
|
168900
|
+
async getActiveTriggers() {
|
|
168901
|
+
const knex = this.getKnex();
|
|
168902
|
+
const rows = await knex('message_triggers')
|
|
168903
|
+
.where('status', 'active')
|
|
168904
|
+
.where('enabled', this.driver === 'mssql' ? 1 : true);
|
|
168905
|
+
return rows.map((r) => fromTriggerRow(r));
|
|
168906
|
+
}
|
|
168907
|
+
}
|
|
168908
|
+
exports.KnexStoreBackend = KnexStoreBackend;
|
|
168909
|
+
|
|
168910
|
+
|
|
167107
168911
|
/***/ }),
|
|
167108
168912
|
|
|
167109
168913
|
/***/ 83864:
|
|
@@ -178117,6 +179921,35 @@ class OutputFormatters {
|
|
|
178117
179921
|
exports.OutputFormatters = OutputFormatters;
|
|
178118
179922
|
|
|
178119
179923
|
|
|
179924
|
+
/***/ }),
|
|
179925
|
+
|
|
179926
|
+
/***/ 93866:
|
|
179927
|
+
/***/ ((__unused_webpack_module, exports) => {
|
|
179928
|
+
|
|
179929
|
+
"use strict";
|
|
179930
|
+
|
|
179931
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
179932
|
+
exports.DefaultPolicyEngine = void 0;
|
|
179933
|
+
/**
|
|
179934
|
+
* Default (no-op) policy engine — always allows everything.
|
|
179935
|
+
* Used when no enterprise license is present or policy is disabled.
|
|
179936
|
+
*/
|
|
179937
|
+
class DefaultPolicyEngine {
|
|
179938
|
+
async initialize(_config) { }
|
|
179939
|
+
async evaluateCheckExecution(_checkId, _checkConfig) {
|
|
179940
|
+
return { allowed: true };
|
|
179941
|
+
}
|
|
179942
|
+
async evaluateToolInvocation(_serverName, _methodName, _transport) {
|
|
179943
|
+
return { allowed: true };
|
|
179944
|
+
}
|
|
179945
|
+
async evaluateCapabilities(_checkId, _capabilities) {
|
|
179946
|
+
return { allowed: true };
|
|
179947
|
+
}
|
|
179948
|
+
async shutdown() { }
|
|
179949
|
+
}
|
|
179950
|
+
exports.DefaultPolicyEngine = DefaultPolicyEngine;
|
|
179951
|
+
|
|
179952
|
+
|
|
178120
179953
|
/***/ }),
|
|
178121
179954
|
|
|
178122
179955
|
/***/ 96611:
|
|
@@ -178585,10 +180418,12 @@ const file_exclusion_1 = __nccwpck_require__(69342);
|
|
|
178585
180418
|
class PRAnalyzer {
|
|
178586
180419
|
octokit;
|
|
178587
180420
|
maxRetries;
|
|
180421
|
+
baseDelay;
|
|
178588
180422
|
fileExclusionHelper;
|
|
178589
|
-
constructor(octokit, maxRetries = 3, workingDirectory = path.resolve(process.cwd())) {
|
|
180423
|
+
constructor(octokit, maxRetries = 3, workingDirectory = path.resolve(process.cwd()), baseDelay = 1000) {
|
|
178590
180424
|
this.octokit = octokit;
|
|
178591
180425
|
this.maxRetries = maxRetries;
|
|
180426
|
+
this.baseDelay = baseDelay;
|
|
178592
180427
|
this.fileExclusionHelper = new file_exclusion_1.FileExclusionHelper(workingDirectory);
|
|
178593
180428
|
}
|
|
178594
180429
|
/**
|
|
@@ -178780,7 +180615,7 @@ class PRAnalyzer {
|
|
|
178780
180615
|
}
|
|
178781
180616
|
// Check if this is a retryable error
|
|
178782
180617
|
if (this.isRetryableError(error)) {
|
|
178783
|
-
const delay = Math.min(
|
|
180618
|
+
const delay = Math.min(this.baseDelay * Math.pow(2, attempt), this.baseDelay * 5); // Exponential backoff
|
|
178784
180619
|
await new Promise(resolve => setTimeout(resolve, delay));
|
|
178785
180620
|
}
|
|
178786
180621
|
else {
|
|
@@ -185511,6 +187346,7 @@ const liquid_extensions_1 = __nccwpck_require__(33042);
|
|
|
185511
187346
|
const env_resolver_1 = __nccwpck_require__(58749);
|
|
185512
187347
|
const sandbox_1 = __nccwpck_require__(12630);
|
|
185513
187348
|
const template_context_1 = __nccwpck_require__(1581);
|
|
187349
|
+
const oauth2_token_cache_1 = __nccwpck_require__(34713);
|
|
185514
187350
|
const logger_1 = __nccwpck_require__(86999);
|
|
185515
187351
|
const fs = __importStar(__nccwpck_require__(79896));
|
|
185516
187352
|
const path = __importStar(__nccwpck_require__(16928));
|
|
@@ -185542,13 +187378,15 @@ class HttpClientProvider extends check_provider_interface_1.CheckProvider {
|
|
|
185542
187378
|
if (cfg.type !== 'http_client') {
|
|
185543
187379
|
return false;
|
|
185544
187380
|
}
|
|
185545
|
-
// Must have
|
|
185546
|
-
|
|
187381
|
+
// Must have either `url` or `base_url` specified
|
|
187382
|
+
const hasUrl = typeof cfg.url === 'string' && cfg.url;
|
|
187383
|
+
const hasBaseUrl = typeof cfg.base_url === 'string' && cfg.base_url;
|
|
187384
|
+
if (!hasUrl && !hasBaseUrl) {
|
|
185547
187385
|
return false;
|
|
185548
187386
|
}
|
|
185549
|
-
// Validate URL format
|
|
187387
|
+
// Validate URL format (check whichever is provided)
|
|
185550
187388
|
try {
|
|
185551
|
-
new URL(cfg.url);
|
|
187389
|
+
new URL((hasUrl ? cfg.url : cfg.base_url));
|
|
185552
187390
|
return true;
|
|
185553
187391
|
}
|
|
185554
187392
|
catch {
|
|
@@ -185556,13 +187394,34 @@ class HttpClientProvider extends check_provider_interface_1.CheckProvider {
|
|
|
185556
187394
|
}
|
|
185557
187395
|
}
|
|
185558
187396
|
async execute(prInfo, config, dependencyResults, context) {
|
|
185559
|
-
const
|
|
187397
|
+
const baseUrl = config.base_url;
|
|
187398
|
+
const rawPath = config.path;
|
|
187399
|
+
const pathParams = config.params || {};
|
|
187400
|
+
const queryParams = config.query || {};
|
|
187401
|
+
const authConfig = config.auth;
|
|
187402
|
+
// Build URL: either direct `url` or `base_url` + `path` with param substitution
|
|
187403
|
+
let url;
|
|
187404
|
+
if (baseUrl && rawPath) {
|
|
187405
|
+
// Substitute {param} placeholders in path
|
|
187406
|
+
let resolvedPath = rawPath;
|
|
187407
|
+
for (const [key, value] of Object.entries(pathParams)) {
|
|
187408
|
+
resolvedPath = resolvedPath.replace(`{${key}}`, encodeURIComponent(value));
|
|
187409
|
+
}
|
|
187410
|
+
url = `${baseUrl.replace(/\/+$/, '')}/${resolvedPath.replace(/^\/+/, '')}`;
|
|
187411
|
+
// Append query parameters
|
|
187412
|
+
if (Object.keys(queryParams).length > 0) {
|
|
187413
|
+
const qs = new URLSearchParams(queryParams).toString();
|
|
187414
|
+
url += `${url.includes('?') ? '&' : '?'}${qs}`;
|
|
187415
|
+
}
|
|
187416
|
+
}
|
|
187417
|
+
else {
|
|
187418
|
+
url = config.url;
|
|
187419
|
+
}
|
|
185560
187420
|
const method = config.method || 'GET';
|
|
185561
187421
|
const headers = config.headers || {};
|
|
185562
187422
|
const timeout = config.timeout || 30000;
|
|
185563
187423
|
const transform = config.transform;
|
|
185564
187424
|
const transformJs = config.transform_js;
|
|
185565
|
-
const bodyTemplate = config.body;
|
|
185566
187425
|
const outputFileTemplate = config.output_file;
|
|
185567
187426
|
const skipIfExists = config.skip_if_exists !== false; // Default true for caching
|
|
185568
187427
|
// Track resolved URL for error messages
|
|
@@ -185583,9 +187442,14 @@ class HttpClientProvider extends check_provider_interface_1.CheckProvider {
|
|
|
185583
187442
|
renderedUrl = await this.liquid.parseAndRender(renderedUrl, templateContext);
|
|
185584
187443
|
resolvedUrlForErrors = renderedUrl; // Update after Liquid rendering
|
|
185585
187444
|
}
|
|
185586
|
-
// Prepare request body
|
|
187445
|
+
// Prepare request body — supports both Liquid template strings and JSON objects
|
|
185587
187446
|
let requestBody;
|
|
185588
|
-
|
|
187447
|
+
const rawBody = config.body;
|
|
187448
|
+
const bodyTemplate = typeof rawBody === 'string' ? rawBody : undefined;
|
|
187449
|
+
if (rawBody && typeof rawBody === 'object') {
|
|
187450
|
+
requestBody = JSON.stringify(rawBody);
|
|
187451
|
+
}
|
|
187452
|
+
else if (bodyTemplate) {
|
|
185589
187453
|
// First resolve shell-style environment variables
|
|
185590
187454
|
let resolvedBody = String(env_resolver_1.EnvironmentResolver.resolveValue(bodyTemplate));
|
|
185591
187455
|
// Then render Liquid templates if present
|
|
@@ -185611,6 +187475,12 @@ class HttpClientProvider extends check_provider_interface_1.CheckProvider {
|
|
|
185611
187475
|
logger_1.logger.verbose(`[http_client] ${key}: ${maskedValue}`);
|
|
185612
187476
|
}
|
|
185613
187477
|
}
|
|
187478
|
+
// Inject OAuth2 Bearer token if auth config is provided
|
|
187479
|
+
if (authConfig?.type === 'oauth2_client_credentials') {
|
|
187480
|
+
const tokenCache = oauth2_token_cache_1.OAuth2TokenCache.getInstance();
|
|
187481
|
+
const token = await tokenCache.getToken(authConfig);
|
|
187482
|
+
resolvedHeaders['Authorization'] = `Bearer ${token}`;
|
|
187483
|
+
}
|
|
185614
187484
|
// Resolve output_file path if specified
|
|
185615
187485
|
let resolvedOutputFile;
|
|
185616
187486
|
if (outputFileTemplate) {
|
|
@@ -185908,6 +187778,11 @@ class HttpClientProvider extends check_provider_interface_1.CheckProvider {
|
|
|
185908
187778
|
return [
|
|
185909
187779
|
'type',
|
|
185910
187780
|
'url',
|
|
187781
|
+
'base_url',
|
|
187782
|
+
'path',
|
|
187783
|
+
'params',
|
|
187784
|
+
'query',
|
|
187785
|
+
'auth',
|
|
185911
187786
|
'method',
|
|
185912
187787
|
'headers',
|
|
185913
187788
|
'body',
|
|
@@ -198589,6 +200464,9 @@ class SlackSocketRunner {
|
|
|
198589
200464
|
genericScheduler;
|
|
198590
200465
|
messageTriggerEvaluator;
|
|
198591
200466
|
activeThreads = new Set();
|
|
200467
|
+
heartbeatTimer;
|
|
200468
|
+
lastPong = 0;
|
|
200469
|
+
closing = false; // prevent duplicate reconnects
|
|
198592
200470
|
constructor(engine, cfg, opts) {
|
|
198593
200471
|
const app = opts.appToken || process.env.SLACK_APP_TOKEN || '';
|
|
198594
200472
|
if (!app)
|
|
@@ -198779,21 +200657,93 @@ class SlackSocketRunner {
|
|
|
198779
200657
|
return json.url;
|
|
198780
200658
|
}
|
|
198781
200659
|
async connect(url) {
|
|
198782
|
-
|
|
198783
|
-
this.
|
|
200660
|
+
// Close previous WebSocket to prevent ghost event handlers
|
|
200661
|
+
this.closeWebSocket();
|
|
200662
|
+
const ws = new ws_1.default(url);
|
|
200663
|
+
this.ws = ws;
|
|
200664
|
+
this.closing = false;
|
|
200665
|
+
ws.on('open', () => {
|
|
198784
200666
|
this.retryCount = 0; // Reset on successful connection
|
|
200667
|
+
this.lastPong = Date.now();
|
|
198785
200668
|
logger_1.logger.info('[SlackSocket] WebSocket connected');
|
|
200669
|
+
this.startHeartbeat();
|
|
198786
200670
|
});
|
|
198787
|
-
|
|
200671
|
+
ws.on('close', (code, reason) => {
|
|
198788
200672
|
logger_1.logger.warn(`[SlackSocket] WebSocket closed: ${code} ${reason}`);
|
|
198789
|
-
|
|
200673
|
+
this.stopHeartbeat();
|
|
200674
|
+
// Only reconnect if this is still the active WebSocket
|
|
200675
|
+
if (this.ws === ws && !this.closing) {
|
|
200676
|
+
this.closing = true;
|
|
200677
|
+
setTimeout(() => this.restart(), 1000);
|
|
200678
|
+
}
|
|
198790
200679
|
});
|
|
198791
|
-
|
|
200680
|
+
ws.on('error', err => {
|
|
198792
200681
|
logger_1.logger.error(`[SlackSocket] WebSocket error: ${err}`);
|
|
198793
200682
|
});
|
|
198794
|
-
|
|
200683
|
+
ws.on('pong', () => {
|
|
200684
|
+
this.lastPong = Date.now();
|
|
200685
|
+
});
|
|
200686
|
+
ws.on('message', data => this.handleMessage(data.toString()).catch(() => { }));
|
|
200687
|
+
}
|
|
200688
|
+
/**
|
|
200689
|
+
* Close the current WebSocket connection and stop heartbeat.
|
|
200690
|
+
* Safe to call multiple times.
|
|
200691
|
+
*/
|
|
200692
|
+
closeWebSocket() {
|
|
200693
|
+
this.stopHeartbeat();
|
|
200694
|
+
if (this.ws) {
|
|
200695
|
+
const old = this.ws;
|
|
200696
|
+
this.ws = undefined;
|
|
200697
|
+
try {
|
|
200698
|
+
// Remove listeners to prevent ghost close/message handlers
|
|
200699
|
+
old.removeAllListeners();
|
|
200700
|
+
old.close();
|
|
200701
|
+
}
|
|
200702
|
+
catch {
|
|
200703
|
+
// best effort
|
|
200704
|
+
}
|
|
200705
|
+
}
|
|
200706
|
+
}
|
|
200707
|
+
/**
|
|
200708
|
+
* Start periodic WebSocket ping to detect dead connections.
|
|
200709
|
+
* If no pong is received within 60s, force a reconnect.
|
|
200710
|
+
*/
|
|
200711
|
+
startHeartbeat() {
|
|
200712
|
+
this.stopHeartbeat();
|
|
200713
|
+
const PING_INTERVAL_MS = 30_000; // ping every 30s
|
|
200714
|
+
const PONG_TIMEOUT_MS = 60_000; // dead if no pong for 60s
|
|
200715
|
+
this.heartbeatTimer = setInterval(() => {
|
|
200716
|
+
if (!this.ws || this.ws.readyState !== ws_1.default.OPEN)
|
|
200717
|
+
return;
|
|
200718
|
+
// Check if last pong is stale
|
|
200719
|
+
const sincePong = Date.now() - this.lastPong;
|
|
200720
|
+
if (this.lastPong > 0 && sincePong > PONG_TIMEOUT_MS) {
|
|
200721
|
+
logger_1.logger.warn(`[SlackSocket] No pong received for ${Math.round(sincePong / 1000)}s, forcing reconnect`);
|
|
200722
|
+
this.closeWebSocket();
|
|
200723
|
+
this.closing = true;
|
|
200724
|
+
this.restart();
|
|
200725
|
+
return;
|
|
200726
|
+
}
|
|
200727
|
+
try {
|
|
200728
|
+
this.ws.ping();
|
|
200729
|
+
}
|
|
200730
|
+
catch {
|
|
200731
|
+
// ping failed — connection is likely dead
|
|
200732
|
+
logger_1.logger.warn('[SlackSocket] Ping failed, forcing reconnect');
|
|
200733
|
+
this.closeWebSocket();
|
|
200734
|
+
this.closing = true;
|
|
200735
|
+
this.restart();
|
|
200736
|
+
}
|
|
200737
|
+
}, PING_INTERVAL_MS);
|
|
200738
|
+
}
|
|
200739
|
+
stopHeartbeat() {
|
|
200740
|
+
if (this.heartbeatTimer) {
|
|
200741
|
+
clearInterval(this.heartbeatTimer);
|
|
200742
|
+
this.heartbeatTimer = undefined;
|
|
200743
|
+
}
|
|
198795
200744
|
}
|
|
198796
200745
|
async restart() {
|
|
200746
|
+
this.closing = false;
|
|
198797
200747
|
try {
|
|
198798
200748
|
const url = await this.openConnection();
|
|
198799
200749
|
await this.connect(url);
|
|
@@ -198803,7 +200753,7 @@ class SlackSocketRunner {
|
|
|
198803
200753
|
// Exponential backoff: 2s, 4s, 8s, 16s, 32s, capped at 60s
|
|
198804
200754
|
const delay = Math.min(2000 * Math.pow(2, this.retryCount - 1), 60000);
|
|
198805
200755
|
logger_1.logger.error(`[SlackSocket] Restart failed (attempt ${this.retryCount}), retrying in ${Math.round(delay / 1000)}s: ${e instanceof Error ? e.message : e}`);
|
|
198806
|
-
setTimeout(() => this.restart()
|
|
200756
|
+
setTimeout(() => this.restart(), delay);
|
|
198807
200757
|
}
|
|
198808
200758
|
}
|
|
198809
200759
|
send(obj) {
|
|
@@ -198881,6 +200831,16 @@ class SlackSocketRunner {
|
|
|
198881
200831
|
return;
|
|
198882
200832
|
if (env.envelope_id)
|
|
198883
200833
|
this.send({ envelope_id: env.envelope_id }); // ack ASAP
|
|
200834
|
+
// Handle Slack disconnect events — proactively reconnect before the connection dies
|
|
200835
|
+
if (env.type === 'disconnect') {
|
|
200836
|
+
const reason = env.reason || 'unknown';
|
|
200837
|
+
logger_1.logger.info(`[SlackSocket] Received disconnect event (reason: ${reason}), reconnecting`);
|
|
200838
|
+
// Slack will close the connection shortly; proactively reconnect now
|
|
200839
|
+
this.closeWebSocket();
|
|
200840
|
+
this.closing = true;
|
|
200841
|
+
this.restart();
|
|
200842
|
+
return;
|
|
200843
|
+
}
|
|
198884
200844
|
if (env.type !== 'events_api' || !env.payload) {
|
|
198885
200845
|
if (process.env.VISOR_DEBUG === 'true') {
|
|
198886
200846
|
logger_1.logger.debug(`[SlackSocket] Dropping non-events payload: type=${String(env.type || '-')}`);
|
|
@@ -199584,17 +201544,10 @@ class SlackSocketRunner {
|
|
|
199584
201544
|
logger_1.logger.warn(`[SlackSocket] Error stopping generic scheduler: ${e instanceof Error ? e.message : e}`);
|
|
199585
201545
|
}
|
|
199586
201546
|
}
|
|
199587
|
-
// Close WebSocket connection
|
|
199588
|
-
|
|
199589
|
-
|
|
199590
|
-
|
|
199591
|
-
this.ws = undefined;
|
|
199592
|
-
logger_1.logger.info('[SlackSocket] WebSocket closed');
|
|
199593
|
-
}
|
|
199594
|
-
catch {
|
|
199595
|
-
// Best effort
|
|
199596
|
-
}
|
|
199597
|
-
}
|
|
201547
|
+
// Close WebSocket connection and stop heartbeat
|
|
201548
|
+
this.closing = true; // prevent reconnect on close
|
|
201549
|
+
this.closeWebSocket();
|
|
201550
|
+
logger_1.logger.info('[SlackSocket] WebSocket closed');
|
|
199598
201551
|
}
|
|
199599
201552
|
/**
|
|
199600
201553
|
* Get the scheduler instance
|
|
@@ -200209,7 +202162,7 @@ class StateMachineExecutionEngine {
|
|
|
200209
202162
|
try {
|
|
200210
202163
|
logger_1.logger.debug(`[PolicyEngine] Loading enterprise policy engine (engine=${configWithTagFilter.policy.engine})`);
|
|
200211
202164
|
// @ts-ignore — enterprise/ may not exist in OSS builds (caught at runtime)
|
|
200212
|
-
const { loadEnterprisePolicyEngine } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(
|
|
202165
|
+
const { loadEnterprisePolicyEngine } = await Promise.resolve().then(() => __importStar(__nccwpck_require__(87068)));
|
|
200213
202166
|
context.policyEngine = await loadEnterprisePolicyEngine(configWithTagFilter.policy);
|
|
200214
202167
|
logger_1.logger.debug(`[PolicyEngine] Initialized: ${context.policyEngine?.constructor?.name || 'unknown'}`);
|
|
200215
202168
|
}
|
|
@@ -210483,7 +212436,7 @@ async function initTelemetry(opts = {}) {
|
|
|
210483
212436
|
const path = __nccwpck_require__(16928);
|
|
210484
212437
|
const outDir = opts.file?.dir ||
|
|
210485
212438
|
process.env.VISOR_TRACE_DIR ||
|
|
210486
|
-
|
|
212439
|
+
path.join(process.cwd(), 'output', 'traces');
|
|
210487
212440
|
fs.mkdirSync(outDir, { recursive: true });
|
|
210488
212441
|
const ts = new Date().toISOString().replace(/[:.]/g, '-');
|
|
210489
212442
|
process.env.VISOR_FALLBACK_TRACE_FILE = path.join(outDir, `run-${ts}.ndjson`);
|
|
@@ -210688,7 +212641,7 @@ async function shutdownTelemetry() {
|
|
|
210688
212641
|
if (process.env.VISOR_TRACE_REPORT === 'true') {
|
|
210689
212642
|
const fs = __nccwpck_require__(79896);
|
|
210690
212643
|
const path = __nccwpck_require__(16928);
|
|
210691
|
-
const outDir = process.env.VISOR_TRACE_DIR ||
|
|
212644
|
+
const outDir = process.env.VISOR_TRACE_DIR || path.join(process.cwd(), 'output', 'traces');
|
|
210692
212645
|
if (!fs.existsSync(outDir))
|
|
210693
212646
|
fs.mkdirSync(outDir, { recursive: true });
|
|
210694
212647
|
const ts = new Date().toISOString().replace(/[:.]/g, '-');
|
|
@@ -211187,7 +213140,7 @@ function __getOrCreateNdjsonPath() {
|
|
|
211187
213140
|
fs.mkdirSync(dir, { recursive: true });
|
|
211188
213141
|
return __ndjsonPath;
|
|
211189
213142
|
}
|
|
211190
|
-
const outDir = process.env.VISOR_TRACE_DIR ||
|
|
213143
|
+
const outDir = process.env.VISOR_TRACE_DIR || path.join(process.cwd(), 'output', 'traces');
|
|
211191
213144
|
if (!fs.existsSync(outDir))
|
|
211192
213145
|
fs.mkdirSync(outDir, { recursive: true });
|
|
211193
213146
|
if (!__ndjsonPath) {
|
|
@@ -221188,6 +223141,130 @@ function emitMermaidFromMarkdown(checkName, markdown, origin) {
|
|
|
221188
223141
|
}
|
|
221189
223142
|
|
|
221190
223143
|
|
|
223144
|
+
/***/ }),
|
|
223145
|
+
|
|
223146
|
+
/***/ 34713:
|
|
223147
|
+
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
|
223148
|
+
|
|
223149
|
+
"use strict";
|
|
223150
|
+
|
|
223151
|
+
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
223152
|
+
exports.OAuth2TokenCache = void 0;
|
|
223153
|
+
const logger_1 = __nccwpck_require__(86999);
|
|
223154
|
+
const env_resolver_1 = __nccwpck_require__(58749);
|
|
223155
|
+
/**
|
|
223156
|
+
* Singleton cache for OAuth2 tokens.
|
|
223157
|
+
*
|
|
223158
|
+
* - Keyed by hash(token_url + client_id) to support multiple providers
|
|
223159
|
+
* - Lazy refresh: only fetches when expired or near-expiry
|
|
223160
|
+
* - Deduplicates concurrent requests: if two calls hit with an expired token,
|
|
223161
|
+
* both await the same fetch promise
|
|
223162
|
+
*/
|
|
223163
|
+
class OAuth2TokenCache {
|
|
223164
|
+
static instance;
|
|
223165
|
+
cache = new Map();
|
|
223166
|
+
static getInstance() {
|
|
223167
|
+
if (!OAuth2TokenCache.instance) {
|
|
223168
|
+
OAuth2TokenCache.instance = new OAuth2TokenCache();
|
|
223169
|
+
}
|
|
223170
|
+
return OAuth2TokenCache.instance;
|
|
223171
|
+
}
|
|
223172
|
+
/** Visible for testing */
|
|
223173
|
+
static resetInstance() {
|
|
223174
|
+
OAuth2TokenCache.instance = undefined;
|
|
223175
|
+
}
|
|
223176
|
+
/**
|
|
223177
|
+
* Get a valid Bearer token for the given config.
|
|
223178
|
+
* Returns a cached token if still valid, otherwise fetches a new one.
|
|
223179
|
+
*/
|
|
223180
|
+
async getToken(config) {
|
|
223181
|
+
const clientId = String(env_resolver_1.EnvironmentResolver.resolveValue(config.client_id));
|
|
223182
|
+
const clientSecret = String(env_resolver_1.EnvironmentResolver.resolveValue(config.client_secret));
|
|
223183
|
+
const tokenUrl = String(env_resolver_1.EnvironmentResolver.resolveValue(config.token_url));
|
|
223184
|
+
const bufferMs = (config.token_ttl_buffer ?? 300) * 1000;
|
|
223185
|
+
const cacheKey = `${tokenUrl}|${clientId}`;
|
|
223186
|
+
const cached = this.cache.get(cacheKey);
|
|
223187
|
+
// Return cached token if still valid (with buffer)
|
|
223188
|
+
if (cached && cached.expires_at - bufferMs > Date.now()) {
|
|
223189
|
+
logger_1.logger.verbose('[oauth2] Using cached token');
|
|
223190
|
+
return cached.access_token;
|
|
223191
|
+
}
|
|
223192
|
+
// If another request is already refreshing, await it
|
|
223193
|
+
if (cached?.refreshPromise) {
|
|
223194
|
+
logger_1.logger.verbose('[oauth2] Awaiting in-flight token refresh');
|
|
223195
|
+
return cached.refreshPromise;
|
|
223196
|
+
}
|
|
223197
|
+
// Fetch a new token
|
|
223198
|
+
const refreshPromise = this.fetchToken(tokenUrl, clientId, clientSecret, config.scopes);
|
|
223199
|
+
// Store the promise so concurrent callers share it
|
|
223200
|
+
if (cached) {
|
|
223201
|
+
cached.refreshPromise = refreshPromise;
|
|
223202
|
+
}
|
|
223203
|
+
else {
|
|
223204
|
+
this.cache.set(cacheKey, {
|
|
223205
|
+
access_token: '',
|
|
223206
|
+
expires_at: 0,
|
|
223207
|
+
refreshPromise,
|
|
223208
|
+
});
|
|
223209
|
+
}
|
|
223210
|
+
try {
|
|
223211
|
+
const token = await refreshPromise;
|
|
223212
|
+
return token;
|
|
223213
|
+
}
|
|
223214
|
+
finally {
|
|
223215
|
+
// Clear the in-flight promise regardless of outcome
|
|
223216
|
+
const entry = this.cache.get(cacheKey);
|
|
223217
|
+
if (entry) {
|
|
223218
|
+
entry.refreshPromise = undefined;
|
|
223219
|
+
}
|
|
223220
|
+
}
|
|
223221
|
+
}
|
|
223222
|
+
async fetchToken(tokenUrl, clientId, clientSecret, scopes) {
|
|
223223
|
+
logger_1.logger.verbose(`[oauth2] Fetching token from ${tokenUrl}`);
|
|
223224
|
+
const credentials = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
|
|
223225
|
+
const bodyParams = new URLSearchParams({ grant_type: 'client_credentials' });
|
|
223226
|
+
if (scopes?.length) {
|
|
223227
|
+
bodyParams.set('scope', scopes.join(' '));
|
|
223228
|
+
}
|
|
223229
|
+
const response = await fetch(tokenUrl, {
|
|
223230
|
+
method: 'POST',
|
|
223231
|
+
headers: {
|
|
223232
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
223233
|
+
Authorization: `Basic ${credentials}`,
|
|
223234
|
+
},
|
|
223235
|
+
body: bodyParams.toString(),
|
|
223236
|
+
});
|
|
223237
|
+
if (!response.ok) {
|
|
223238
|
+
let errorDetail = '';
|
|
223239
|
+
try {
|
|
223240
|
+
errorDetail = await response.text();
|
|
223241
|
+
}
|
|
223242
|
+
catch { }
|
|
223243
|
+
throw new Error(`OAuth2 token request failed: HTTP ${response.status} ${response.statusText}${errorDetail ? ` - ${errorDetail.substring(0, 200)}` : ''}`);
|
|
223244
|
+
}
|
|
223245
|
+
const data = (await response.json());
|
|
223246
|
+
if (!data.access_token) {
|
|
223247
|
+
throw new Error('OAuth2 token response missing access_token');
|
|
223248
|
+
}
|
|
223249
|
+
// Default to 1 hour if expires_in not provided
|
|
223250
|
+
const expiresIn = data.expires_in ?? 3600;
|
|
223251
|
+
const expiresAt = Date.now() + expiresIn * 1000;
|
|
223252
|
+
const cacheKey = `${tokenUrl}|${clientId}`;
|
|
223253
|
+
this.cache.set(cacheKey, {
|
|
223254
|
+
access_token: data.access_token,
|
|
223255
|
+
expires_at: expiresAt,
|
|
223256
|
+
});
|
|
223257
|
+
logger_1.logger.verbose(`[oauth2] Token acquired, expires in ${expiresIn}s`);
|
|
223258
|
+
return data.access_token;
|
|
223259
|
+
}
|
|
223260
|
+
/** Clear all cached tokens (for testing or credential rotation) */
|
|
223261
|
+
clear() {
|
|
223262
|
+
this.cache.clear();
|
|
223263
|
+
}
|
|
223264
|
+
}
|
|
223265
|
+
exports.OAuth2TokenCache = OAuth2TokenCache;
|
|
223266
|
+
|
|
223267
|
+
|
|
221191
223268
|
/***/ }),
|
|
221192
223269
|
|
|
221193
223270
|
/***/ 12630:
|
|
@@ -225148,22 +227225,6 @@ class WorkflowRegistry {
|
|
|
225148
227225
|
exports.WorkflowRegistry = WorkflowRegistry;
|
|
225149
227226
|
|
|
225150
227227
|
|
|
225151
|
-
/***/ }),
|
|
225152
|
-
|
|
225153
|
-
/***/ 7065:
|
|
225154
|
-
/***/ ((module) => {
|
|
225155
|
-
|
|
225156
|
-
module.exports = eval("require")("./enterprise/loader");
|
|
225157
|
-
|
|
225158
|
-
|
|
225159
|
-
/***/ }),
|
|
225160
|
-
|
|
225161
|
-
/***/ 71370:
|
|
225162
|
-
/***/ ((module) => {
|
|
225163
|
-
|
|
225164
|
-
module.exports = eval("require")("./enterprise/policy/policy-input-builder");
|
|
225165
|
-
|
|
225166
|
-
|
|
225167
227228
|
/***/ }),
|
|
225168
227229
|
|
|
225169
227230
|
/***/ 18327:
|
|
@@ -248108,9 +250169,7 @@ async function acquireFileLock(lockPath, version2) {
|
|
|
248108
250169
|
};
|
|
248109
250170
|
try {
|
|
248110
250171
|
await import_fs_extra2.default.writeFile(lockPath, JSON.stringify(lockData), { flag: "wx" });
|
|
248111
|
-
|
|
248112
|
-
console.log(`Acquired file lock: ${lockPath}`);
|
|
248113
|
-
}
|
|
250172
|
+
console.log(`Acquired file lock: ${lockPath}`);
|
|
248114
250173
|
return true;
|
|
248115
250174
|
} catch (error2) {
|
|
248116
250175
|
if (error2.code === "EEXIST") {
|
|
@@ -248118,15 +250177,11 @@ async function acquireFileLock(lockPath, version2) {
|
|
|
248118
250177
|
const existingLock = JSON.parse(await import_fs_extra2.default.readFile(lockPath, "utf-8"));
|
|
248119
250178
|
const lockAge = Date.now() - existingLock.timestamp;
|
|
248120
250179
|
if (lockAge > LOCK_TIMEOUT_MS) {
|
|
248121
|
-
|
|
248122
|
-
console.log(`Removing stale lock file (age: ${Math.round(lockAge / 1e3)}s, pid: ${existingLock.pid})`);
|
|
248123
|
-
}
|
|
250180
|
+
console.log(`Removing stale lock file (age: ${Math.round(lockAge / 1e3)}s, pid: ${existingLock.pid})`);
|
|
248124
250181
|
await import_fs_extra2.default.remove(lockPath);
|
|
248125
250182
|
return false;
|
|
248126
250183
|
}
|
|
248127
|
-
|
|
248128
|
-
console.log(`Download in progress by process ${existingLock.pid}, waiting...`);
|
|
248129
|
-
}
|
|
250184
|
+
console.log(`Download in progress by process ${existingLock.pid}, waiting...`);
|
|
248130
250185
|
return false;
|
|
248131
250186
|
} catch (readError) {
|
|
248132
250187
|
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
@@ -248167,36 +250222,36 @@ async function releaseFileLock(lockPath) {
|
|
|
248167
250222
|
}
|
|
248168
250223
|
async function waitForFileLock(lockPath, binaryPath) {
|
|
248169
250224
|
const startTime = Date.now();
|
|
250225
|
+
let lastStatusTime = startTime;
|
|
250226
|
+
console.log(`Waiting for file lock to clear: ${lockPath}`);
|
|
248170
250227
|
while (Date.now() - startTime < MAX_LOCK_WAIT_MS) {
|
|
248171
250228
|
if (await import_fs_extra2.default.pathExists(binaryPath)) {
|
|
248172
|
-
|
|
248173
|
-
|
|
248174
|
-
}
|
|
250229
|
+
const waitedSeconds = Math.round((Date.now() - startTime) / 1e3);
|
|
250230
|
+
console.log(`Binary now available at ${binaryPath}, download completed by another process (waited ${waitedSeconds}s)`);
|
|
248175
250231
|
return true;
|
|
248176
250232
|
}
|
|
248177
250233
|
const lockExists = await import_fs_extra2.default.pathExists(lockPath);
|
|
248178
250234
|
if (!lockExists) {
|
|
248179
|
-
|
|
248180
|
-
console.log(`Lock file removed but binary not found - download may have failed`);
|
|
248181
|
-
}
|
|
250235
|
+
console.log(`Lock file removed but binary not found - download may have failed`);
|
|
248182
250236
|
return false;
|
|
248183
250237
|
}
|
|
248184
250238
|
try {
|
|
248185
250239
|
const lockData = JSON.parse(await import_fs_extra2.default.readFile(lockPath, "utf-8"));
|
|
248186
250240
|
const lockAge = Date.now() - lockData.timestamp;
|
|
248187
250241
|
if (lockAge > LOCK_TIMEOUT_MS) {
|
|
248188
|
-
|
|
248189
|
-
console.log(`Lock expired (age: ${Math.round(lockAge / 1e3)}s), will retry download`);
|
|
248190
|
-
}
|
|
250242
|
+
console.log(`Lock expired (age: ${Math.round(lockAge / 1e3)}s), will retry download`);
|
|
248191
250243
|
return false;
|
|
248192
250244
|
}
|
|
248193
250245
|
} catch {
|
|
248194
250246
|
}
|
|
250247
|
+
if (Date.now() - lastStatusTime >= 15e3) {
|
|
250248
|
+
const elapsedSeconds = Math.round((Date.now() - startTime) / 1e3);
|
|
250249
|
+
console.log(`Still waiting for file lock (${elapsedSeconds}s/${MAX_LOCK_WAIT_MS / 1e3}s max)`);
|
|
250250
|
+
lastStatusTime = Date.now();
|
|
250251
|
+
}
|
|
248195
250252
|
await new Promise((resolve8) => setTimeout(resolve8, LOCK_POLL_INTERVAL_MS));
|
|
248196
250253
|
}
|
|
248197
|
-
|
|
248198
|
-
console.log(`Timeout waiting for file lock`);
|
|
248199
|
-
}
|
|
250254
|
+
console.log(`Timeout waiting for file lock after ${MAX_LOCK_WAIT_MS / 1e3}s`);
|
|
248200
250255
|
return false;
|
|
248201
250256
|
}
|
|
248202
250257
|
async function withDownloadLock(version2, downloadFn) {
|
|
@@ -248210,9 +250265,7 @@ async function withDownloadLock(version2, downloadFn) {
|
|
|
248210
250265
|
}
|
|
248211
250266
|
downloadLocks.delete(lockKey);
|
|
248212
250267
|
} else {
|
|
248213
|
-
|
|
248214
|
-
console.log(`Download already in progress in this process for version ${lockKey}, waiting...`);
|
|
248215
|
-
}
|
|
250268
|
+
console.log(`Download already in progress in this process for version ${lockKey}, waiting...`);
|
|
248216
250269
|
try {
|
|
248217
250270
|
return await lock.promise;
|
|
248218
250271
|
} catch (error2) {
|
|
@@ -248222,10 +250275,16 @@ async function withDownloadLock(version2, downloadFn) {
|
|
|
248222
250275
|
}
|
|
248223
250276
|
}
|
|
248224
250277
|
}
|
|
250278
|
+
let timeoutId = null;
|
|
248225
250279
|
const downloadPromise = Promise.race([
|
|
248226
250280
|
downloadFn(),
|
|
248227
250281
|
new Promise(
|
|
248228
|
-
(_, reject2) =>
|
|
250282
|
+
(_, reject2) => {
|
|
250283
|
+
timeoutId = setTimeout(() => reject2(new Error(`Download timeout after ${LOCK_TIMEOUT_MS / 1e3}s`)), LOCK_TIMEOUT_MS);
|
|
250284
|
+
if (timeoutId.unref) {
|
|
250285
|
+
timeoutId.unref();
|
|
250286
|
+
}
|
|
250287
|
+
}
|
|
248229
250288
|
)
|
|
248230
250289
|
]);
|
|
248231
250290
|
downloadLocks.set(lockKey, {
|
|
@@ -248236,6 +250295,9 @@ async function withDownloadLock(version2, downloadFn) {
|
|
|
248236
250295
|
const result = await downloadPromise;
|
|
248237
250296
|
return result;
|
|
248238
250297
|
} finally {
|
|
250298
|
+
if (timeoutId) {
|
|
250299
|
+
clearTimeout(timeoutId);
|
|
250300
|
+
}
|
|
248239
250301
|
downloadLocks.delete(lockKey);
|
|
248240
250302
|
}
|
|
248241
250303
|
}
|
|
@@ -283766,14 +285828,14 @@ function resolveTargetPath(target, cwd) {
|
|
|
283766
285828
|
}
|
|
283767
285829
|
return filePart + suffix;
|
|
283768
285830
|
}
|
|
283769
|
-
var import_path5, searchSchema, searchAllSchema, querySchema, extractSchema, delegateSchema, listSkillsSchema, useSkillSchema, listFilesSchema, searchFilesSchema, readImageSchema, bashSchema, analyzeAllSchema, executePlanSchema, cleanupExecutePlanSchema, attemptCompletionSchema, searchDescription, queryDescription, extractDescription, delegateDescription, bashDescription, analyzeAllDescription;
|
|
285831
|
+
var import_path5, searchSchema, searchAllSchema, querySchema, extractSchema, delegateSchema, listSkillsSchema, useSkillSchema, listFilesSchema, searchFilesSchema, readImageSchema, bashSchema, analyzeAllSchema, executePlanSchema, cleanupExecutePlanSchema, attemptCompletionSchema, searchDescription, searchDelegateDescription, queryDescription, extractDescription, delegateDescription, bashDescription, analyzeAllDescription;
|
|
283770
285832
|
var init_common2 = __esm({
|
|
283771
285833
|
"src/tools/common.js"() {
|
|
283772
285834
|
"use strict";
|
|
283773
285835
|
init_zod();
|
|
283774
285836
|
import_path5 = __nccwpck_require__(16928);
|
|
283775
285837
|
searchSchema = external_exports.object({
|
|
283776
|
-
query: external_exports.string().describe("Search query
|
|
285838
|
+
query: external_exports.string().describe("Search query \u2014 natural language questions or Elasticsearch-style keywords both work. For keywords: use quotes for exact phrases, AND/OR for boolean logic, - for negation. Probe handles stemming and camelCase/snake_case splitting automatically, so do NOT try case or style variations of the same keyword."),
|
|
283777
285839
|
path: external_exports.string().optional().default(".").describe('Path to search in. For dependencies use "go:github.com/owner/repo", "js:package_name", or "rust:cargo_name" etc.'),
|
|
283778
285840
|
exact: external_exports.boolean().optional().default(false).describe('Default (false) enables stemming and keyword splitting for exploratory search - "getUserData" matches "get", "user", "data", etc. Set true for precise symbol lookup where "getUserData" matches only "getUserData". Use true when you know the exact symbol name.'),
|
|
283779
285841
|
maxTokens: external_exports.number().nullable().optional().describe("Maximum tokens to return. Default is 20000. Set to null for unlimited results."),
|
|
@@ -283781,7 +285843,7 @@ var init_common2 = __esm({
|
|
|
283781
285843
|
nextPage: external_exports.boolean().optional().default(false).describe("Set to true when requesting the next page of results. Requires passing the same session ID from the previous search output.")
|
|
283782
285844
|
});
|
|
283783
285845
|
searchAllSchema = external_exports.object({
|
|
283784
|
-
query: external_exports.string().describe("Search query
|
|
285846
|
+
query: external_exports.string().describe("Search query \u2014 natural language questions or Elasticsearch-style keywords both work. For keywords: use quotes for exact phrases, AND/OR for boolean logic, - for negation. Probe handles stemming and camelCase/snake_case splitting automatically, so do NOT try case or style variations of the same keyword."),
|
|
283785
285847
|
path: external_exports.string().optional().default(".").describe("Path to search in."),
|
|
283786
285848
|
exact: external_exports.boolean().optional().default(false).describe("Use exact matching instead of stemming."),
|
|
283787
285849
|
maxTokensPerPage: external_exports.number().optional().default(2e4).describe("Tokens per page when paginating. Default 20000."),
|
|
@@ -283888,7 +285950,8 @@ var init_common2 = __esm({
|
|
|
283888
285950
|
};
|
|
283889
285951
|
}
|
|
283890
285952
|
};
|
|
283891
|
-
searchDescription =
|
|
285953
|
+
searchDescription = 'Search code in the repository. Free-form questions are accepted, but Elasticsearch-style keyword queries work best. Use this tool first for any code-related questions. NOTE: By default, search handles stemming, case-insensitive matching, and camelCase/snake_case splitting automatically \u2014 do NOT manually try keyword variations like "getAllUsers" then "get_all_users" then "GetAllUsers". One search covers all variations.';
|
|
285954
|
+
searchDelegateDescription = 'Search code in the repository by asking a question. Accepts natural language questions (e.g., "How does authentication work?", "Where is the user validation logic?"). A specialized subagent breaks down your question into targeted keyword searches and returns extracted code blocks. Do NOT formulate keyword queries yourself \u2014 just ask the question naturally.';
|
|
283892
285955
|
queryDescription = "Search code using ast-grep structural pattern matching. Use this tool to find specific code structures like functions, classes, or methods.";
|
|
283893
285956
|
extractDescription = "Extract code blocks from files based on file paths and optional line numbers. Use this tool to see complete context after finding relevant files. Line numbers from output can be used with edit start_line/end_line for precise editing.";
|
|
283894
285957
|
delegateDescription = "Automatically delegate big distinct tasks to specialized probe subagents within the agentic loop. Used by AI agents to break down complex requests into focused, parallel tasks.";
|
|
@@ -349208,6 +351271,7 @@ function generateSandboxGlobals(options) {
|
|
|
349208
351271
|
executing.add(p5);
|
|
349209
351272
|
results.push(p5);
|
|
349210
351273
|
if (executing.size >= mapConcurrency) {
|
|
351274
|
+
console.error(`[map] Concurrency limit reached (${executing.size}/${mapConcurrency}), waiting for a slot...`);
|
|
349211
351275
|
await Promise.race(executing);
|
|
349212
351276
|
}
|
|
349213
351277
|
}
|
|
@@ -353437,8 +355501,23 @@ __export(ProbeAgent_exports, {
|
|
|
353437
355501
|
ENGINE_ACTIVITY_TIMEOUT_DEFAULT: () => ENGINE_ACTIVITY_TIMEOUT_DEFAULT,
|
|
353438
355502
|
ENGINE_ACTIVITY_TIMEOUT_MAX: () => ENGINE_ACTIVITY_TIMEOUT_MAX,
|
|
353439
355503
|
ENGINE_ACTIVITY_TIMEOUT_MIN: () => ENGINE_ACTIVITY_TIMEOUT_MIN,
|
|
353440
|
-
ProbeAgent: () => ProbeAgent
|
|
355504
|
+
ProbeAgent: () => ProbeAgent,
|
|
355505
|
+
debugLogToolResults: () => debugLogToolResults,
|
|
355506
|
+
debugTruncate: () => debugTruncate
|
|
353441
355507
|
});
|
|
355508
|
+
function debugTruncate(s5, limit = 200) {
|
|
355509
|
+
if (s5.length <= limit) return s5;
|
|
355510
|
+
const half = Math.floor(limit / 2);
|
|
355511
|
+
return s5.substring(0, half) + ` ... [${s5.length} chars] ... ` + s5.substring(s5.length - half);
|
|
355512
|
+
}
|
|
355513
|
+
function debugLogToolResults(toolResults) {
|
|
355514
|
+
if (!toolResults || toolResults.length === 0) return;
|
|
355515
|
+
for (const tr of toolResults) {
|
|
355516
|
+
const argsStr = JSON.stringify(tr.args || {});
|
|
355517
|
+
const resultStr = typeof tr.result === "string" ? tr.result : JSON.stringify(tr.result || "");
|
|
355518
|
+
console.log(`[DEBUG] tool: ${tr.toolName} | args: ${debugTruncate(argsStr)} | result: ${debugTruncate(resultStr)}`);
|
|
355519
|
+
}
|
|
355520
|
+
}
|
|
353442
355521
|
var import_dotenv, import_anthropic2, import_openai2, import_google2, import_ai4, import_crypto8, import_events4, import_fs10, import_promises6, import_path15, ENGINE_ACTIVITY_TIMEOUT_DEFAULT, ENGINE_ACTIVITY_TIMEOUT_MIN, ENGINE_ACTIVITY_TIMEOUT_MAX, MAX_TOOL_ITERATIONS, MAX_HISTORY_MESSAGES, MAX_IMAGE_FILE_SIZE, ProbeAgent;
|
|
353443
355522
|
var init_ProbeAgent = __esm({
|
|
353444
355523
|
"src/agent/ProbeAgent.js"() {
|
|
@@ -353558,6 +355637,7 @@ var init_ProbeAgent = __esm({
|
|
|
353558
355637
|
this.enableExecutePlan = !!options.enableExecutePlan;
|
|
353559
355638
|
this.debug = options.debug || process.env.DEBUG === "1";
|
|
353560
355639
|
this.cancelled = false;
|
|
355640
|
+
this._abortController = new AbortController();
|
|
353561
355641
|
this.tracer = options.tracer || null;
|
|
353562
355642
|
this.outline = !!options.outline;
|
|
353563
355643
|
this.searchDelegate = options.searchDelegate !== void 0 ? !!options.searchDelegate : true;
|
|
@@ -353586,6 +355666,7 @@ var init_ProbeAgent = __esm({
|
|
|
353586
355666
|
this.completionPrompt = options.completionPrompt || null;
|
|
353587
355667
|
this.thinkingEffort = options.thinkingEffort || null;
|
|
353588
355668
|
const effectiveAllowedTools = options.disableTools ? [] : options.allowedTools;
|
|
355669
|
+
this._rawAllowedTools = options.allowedTools;
|
|
353589
355670
|
this.allowedTools = this._parseAllowedTools(effectiveAllowedTools);
|
|
353590
355671
|
this.storageAdapter = options.storageAdapter || new InMemoryStorageAdapter();
|
|
353591
355672
|
this.hooks = new HookManager();
|
|
@@ -353728,6 +355809,16 @@ var init_ProbeAgent = __esm({
|
|
|
353728
355809
|
_filterMcpTools(mcpToolNames) {
|
|
353729
355810
|
return mcpToolNames.filter((toolName) => this._isMcpToolAllowed(toolName));
|
|
353730
355811
|
}
|
|
355812
|
+
/**
|
|
355813
|
+
* Check if query tool was explicitly listed in allowedTools (not via wildcard).
|
|
355814
|
+
* Query (ast-grep) is excluded by default because models struggle with AST pattern syntax.
|
|
355815
|
+
* @returns {boolean}
|
|
355816
|
+
* @private
|
|
355817
|
+
*/
|
|
355818
|
+
_isQueryExplicitlyAllowed() {
|
|
355819
|
+
if (!this._rawAllowedTools) return false;
|
|
355820
|
+
return Array.isArray(this._rawAllowedTools) && this._rawAllowedTools.includes("query");
|
|
355821
|
+
}
|
|
353731
355822
|
/**
|
|
353732
355823
|
* Check if tracer is AppTracer (expects sessionId as first param) vs SimpleAppTracer
|
|
353733
355824
|
* @returns {boolean} - True if tracer is AppTracer style (requires sessionId)
|
|
@@ -354003,6 +356094,8 @@ var init_ProbeAgent = __esm({
|
|
|
354003
356094
|
searchDelegateModel: this.searchDelegateModel,
|
|
354004
356095
|
delegationManager: this.delegationManager,
|
|
354005
356096
|
// Per-instance delegation limits
|
|
356097
|
+
parentAbortSignal: this._abortController.signal,
|
|
356098
|
+
// Propagate cancellation to delegations
|
|
354006
356099
|
outputBuffer: this._outputBuffer,
|
|
354007
356100
|
concurrencyLimiter: this.concurrencyLimiter,
|
|
354008
356101
|
// Global AI concurrency limiter
|
|
@@ -354019,7 +356112,7 @@ var init_ProbeAgent = __esm({
|
|
|
354019
356112
|
if (wrappedTools.searchToolInstance && isToolAllowed("search")) {
|
|
354020
356113
|
this.toolImplementations.search = wrappedTools.searchToolInstance;
|
|
354021
356114
|
}
|
|
354022
|
-
if (wrappedTools.queryToolInstance && isToolAllowed("query")) {
|
|
356115
|
+
if (wrappedTools.queryToolInstance && isToolAllowed("query") && this._isQueryExplicitlyAllowed()) {
|
|
354023
356116
|
this.toolImplementations.query = wrappedTools.queryToolInstance;
|
|
354024
356117
|
}
|
|
354025
356118
|
if (wrappedTools.extractToolInstance && isToolAllowed("extract")) {
|
|
@@ -354450,6 +356543,15 @@ var init_ProbeAgent = __esm({
|
|
|
354450
356543
|
}
|
|
354451
356544
|
const controller = new AbortController();
|
|
354452
356545
|
const timeoutState = { timeoutId: null };
|
|
356546
|
+
if (this._abortController.signal.aborted) {
|
|
356547
|
+
controller.abort();
|
|
356548
|
+
} else {
|
|
356549
|
+
const onAgentAbort = () => controller.abort();
|
|
356550
|
+
this._abortController.signal.addEventListener("abort", onAgentAbort, { once: true });
|
|
356551
|
+
controller.signal.addEventListener("abort", () => {
|
|
356552
|
+
this._abortController.signal.removeEventListener("abort", onAgentAbort);
|
|
356553
|
+
}, { once: true });
|
|
356554
|
+
}
|
|
354453
356555
|
if (this.maxOperationTimeout && this.maxOperationTimeout > 0) {
|
|
354454
356556
|
timeoutState.timeoutId = setTimeout(() => {
|
|
354455
356557
|
controller.abort();
|
|
@@ -354753,7 +356855,8 @@ var init_ProbeAgent = __esm({
|
|
|
354753
356855
|
allowEdit: this.allowEdit,
|
|
354754
356856
|
allowedTools: allowedToolsForDelegate,
|
|
354755
356857
|
debug: this.debug,
|
|
354756
|
-
tracer: this.tracer
|
|
356858
|
+
tracer: this.tracer,
|
|
356859
|
+
parentAbortSignal: this._abortController.signal
|
|
354757
356860
|
};
|
|
354758
356861
|
if (this.debug) {
|
|
354759
356862
|
console.log(`[DEBUG] Executing delegate tool`);
|
|
@@ -354948,12 +357051,13 @@ var init_ProbeAgent = __esm({
|
|
|
354948
357051
|
const toolMap = {
|
|
354949
357052
|
search: {
|
|
354950
357053
|
schema: searchSchema,
|
|
354951
|
-
description: "Search code in the repository using keyword queries with Elasticsearch syntax."
|
|
354952
|
-
},
|
|
354953
|
-
query: {
|
|
354954
|
-
schema: querySchema,
|
|
354955
|
-
description: "Search code using ast-grep structural pattern matching."
|
|
357054
|
+
description: this.searchDelegate ? "Search code in the repository by asking a question. Accepts natural language questions \u2014 a subagent breaks them into targeted keyword searches and returns extracted code blocks. Do NOT formulate keyword queries yourself." : "Search code in the repository using keyword queries with Elasticsearch syntax. Handles stemming, case-insensitive matching, and camelCase/snake_case splitting automatically \u2014 do NOT try keyword variations manually."
|
|
354956
357055
|
},
|
|
357056
|
+
// query tool (ast-grep) removed from AI-facing tools — models struggle with pattern syntax
|
|
357057
|
+
// query: {
|
|
357058
|
+
// schema: querySchema,
|
|
357059
|
+
// description: 'Search code using ast-grep structural pattern matching.'
|
|
357060
|
+
// },
|
|
354957
357061
|
extract: {
|
|
354958
357062
|
schema: extractSchema,
|
|
354959
357063
|
description: "Extract code blocks from files based on file paths and optional line numbers."
|
|
@@ -355621,25 +357725,27 @@ ${this.architectureContext.content}
|
|
|
355621
357725
|
} else {
|
|
355622
357726
|
systemPrompt += predefinedPrompts["code-explorer"] + "\n\n";
|
|
355623
357727
|
}
|
|
357728
|
+
const searchToolDesc1 = this.searchDelegate ? '- search: Ask natural language questions to find code (e.g., "How does authentication work?"). A subagent handles keyword searches and returns extracted code blocks. Do NOT formulate keyword queries \u2014 just ask questions.' : "- search: Find code patterns using keyword queries with Elasticsearch syntax. Handles stemming and case variations automatically \u2014 do NOT try manual keyword variations.";
|
|
355624
357729
|
systemPrompt += `You have access to powerful code search and analysis tools through MCP:
|
|
355625
|
-
|
|
357730
|
+
${searchToolDesc1}
|
|
355626
357731
|
- extract: Extract specific code sections with context
|
|
355627
|
-
- query: Use AST patterns for structural code matching
|
|
355628
357732
|
- listFiles: Browse directory contents
|
|
355629
357733
|
- searchFiles: Find files by name patterns`;
|
|
355630
357734
|
if (this.enableBash) {
|
|
355631
357735
|
systemPrompt += `
|
|
355632
357736
|
- bash: Execute bash commands for system operations`;
|
|
355633
357737
|
}
|
|
355634
|
-
const
|
|
355635
|
-
const
|
|
357738
|
+
const searchGuidance1 = this.searchDelegate ? "1. Start with search \u2014 ask a question about what you want to understand. It returns extracted code blocks directly." : "1. Start with search to find relevant code patterns. One search per concept is usually enough \u2014 probe handles stemming and case variations.";
|
|
357739
|
+
const extractGuidance1 = this.searchDelegate ? "2. Use extract only if you need more context or a full file" : "2. Use extract to get detailed context when needed";
|
|
355636
357740
|
systemPrompt += `
|
|
355637
357741
|
|
|
355638
357742
|
When exploring code:
|
|
355639
|
-
${
|
|
355640
|
-
${
|
|
357743
|
+
${searchGuidance1}
|
|
357744
|
+
${extractGuidance1}
|
|
355641
357745
|
3. Prefer focused, specific searches over broad queries
|
|
355642
|
-
4.
|
|
357746
|
+
4. Do NOT repeat the same search or try trivial keyword variations \u2014 probe handles stemming and case variations automatically
|
|
357747
|
+
5. If 2-3 consecutive searches return no results for a concept, stop searching for it \u2014 the term likely does not exist in that codebase
|
|
357748
|
+
6. Combine multiple tools to build complete understanding`;
|
|
355643
357749
|
if (this.allowedFolders && this.allowedFolders.length > 0) {
|
|
355644
357750
|
systemPrompt += `
|
|
355645
357751
|
|
|
@@ -355674,25 +357780,27 @@ Workspace: ${this.allowedFolders.join(", ")}`;
|
|
|
355674
357780
|
} else {
|
|
355675
357781
|
systemPrompt += predefinedPrompts["code-explorer"] + "\n\n";
|
|
355676
357782
|
}
|
|
357783
|
+
const searchToolDesc2 = this.searchDelegate ? '- search: Ask natural language questions to find code (e.g., "How does authentication work?"). A subagent handles keyword searches and returns extracted code blocks. Do NOT formulate keyword queries \u2014 just ask questions.' : "- search: Find code patterns using keyword queries with Elasticsearch syntax. Handles stemming and case variations automatically \u2014 do NOT try manual keyword variations.";
|
|
355677
357784
|
systemPrompt += `You have access to powerful code search and analysis tools through MCP:
|
|
355678
|
-
|
|
357785
|
+
${searchToolDesc2}
|
|
355679
357786
|
- extract: Extract specific code sections with context
|
|
355680
|
-
- query: Use AST patterns for structural code matching
|
|
355681
357787
|
- listFiles: Browse directory contents
|
|
355682
357788
|
- searchFiles: Find files by name patterns`;
|
|
355683
357789
|
if (this.enableBash) {
|
|
355684
357790
|
systemPrompt += `
|
|
355685
357791
|
- bash: Execute bash commands for system operations`;
|
|
355686
357792
|
}
|
|
355687
|
-
const
|
|
355688
|
-
const
|
|
357793
|
+
const searchGuidance2 = this.searchDelegate ? "1. Start with search \u2014 ask a question about what you want to understand. It returns extracted code blocks directly." : "1. Start with search to find relevant code patterns. One search per concept is usually enough \u2014 probe handles stemming and case variations.";
|
|
357794
|
+
const extractGuidance2 = this.searchDelegate ? "2. Use extract only if you need more context or a full file" : "2. Use extract to get detailed context when needed";
|
|
355689
357795
|
systemPrompt += `
|
|
355690
357796
|
|
|
355691
357797
|
When exploring code:
|
|
355692
|
-
${
|
|
355693
|
-
${
|
|
357798
|
+
${searchGuidance2}
|
|
357799
|
+
${extractGuidance2}
|
|
355694
357800
|
3. Prefer focused, specific searches over broad queries
|
|
355695
|
-
4.
|
|
357801
|
+
4. Do NOT repeat the same search or try trivial keyword variations \u2014 probe handles stemming and case variations automatically
|
|
357802
|
+
5. If 2-3 consecutive searches return no results for a concept, stop searching for it \u2014 the term likely does not exist in that codebase
|
|
357803
|
+
6. Combine multiple tools to build complete understanding`;
|
|
355696
357804
|
if (this.allowedFolders && this.allowedFolders.length > 0) {
|
|
355697
357805
|
systemPrompt += `
|
|
355698
357806
|
|
|
@@ -355743,10 +357851,10 @@ Workspace: ${this.allowedFolders.join(", ")}`;
|
|
|
355743
357851
|
Follow these instructions carefully:
|
|
355744
357852
|
1. Analyze the user's request.
|
|
355745
357853
|
2. Use the available tools step-by-step to fulfill the request.
|
|
355746
|
-
3. You should always prefer the search tool for code-related questions.${this.searchDelegate ? "
|
|
357854
|
+
3. You should always prefer the search tool for code-related questions.${this.searchDelegate ? " Ask natural language questions \u2014 the search subagent handles keyword formulation and returns extracted code blocks. Use extract only to expand context or read full files." : " Search handles stemming and case variations automatically \u2014 do NOT try keyword variations manually. Read full files only if really necessary."}
|
|
355747
357855
|
4. Ensure to get really deep and understand the full picture before answering.
|
|
355748
357856
|
5. Once the task is fully completed, use the attempt_completion tool to provide the final result.
|
|
355749
|
-
6. Prefer concise and focused search queries. Use specific keywords and phrases to narrow down results
|
|
357857
|
+
6. ${this.searchDelegate ? "Ask clear, specific questions when searching. Each search should target a distinct concept or question." : "Prefer concise and focused search queries. Use specific keywords and phrases to narrow down results."}${this.allowEdit ? `
|
|
355750
357858
|
7. When modifying files, choose the appropriate tool:
|
|
355751
357859
|
- Use 'edit' for all code modifications:
|
|
355752
357860
|
* PREFERRED: Use start_line (and optionally end_line) for line-targeted editing \u2014 this is the safest and most precise approach.${this.hashLines ? ' Use the line:hash references from extract/search output (e.g. "42:ab") for integrity verification.' : ""} Always use extract first to see line numbers${this.hashLines ? " and hashes" : ""}, then edit by line reference.
|
|
@@ -356070,6 +358178,10 @@ You are working with a workspace. Available paths: ${workspaceDesc}
|
|
|
356070
358178
|
completionResult = result;
|
|
356071
358179
|
completionAttempted = true;
|
|
356072
358180
|
}, toolContext);
|
|
358181
|
+
if (this.debug) {
|
|
358182
|
+
const toolNames = Object.keys(tools2);
|
|
358183
|
+
console.log(`[DEBUG] Agent tools registered (${toolNames.length}): ${toolNames.join(", ")}`);
|
|
358184
|
+
}
|
|
356073
358185
|
let maxResponseTokens = this.maxResponseTokens;
|
|
356074
358186
|
if (!maxResponseTokens) {
|
|
356075
358187
|
maxResponseTokens = 4e3;
|
|
@@ -356109,6 +358221,7 @@ You are working with a workspace. Available paths: ${workspaceDesc}
|
|
|
356109
358221
|
}
|
|
356110
358222
|
if (this.debug) {
|
|
356111
358223
|
console.log(`[DEBUG] Step ${currentIteration}/${maxIterations} finished (reason: ${finishReason}, tools: ${toolResults?.length || 0})`);
|
|
358224
|
+
debugLogToolResults(toolResults);
|
|
356112
358225
|
}
|
|
356113
358226
|
}
|
|
356114
358227
|
};
|
|
@@ -356265,6 +358378,7 @@ Double-check your response based on the criteria above. If everything looks good
|
|
|
356265
358378
|
}
|
|
356266
358379
|
if (this.debug) {
|
|
356267
358380
|
console.log(`[DEBUG] Completion prompt step finished (reason: ${finishReason}, tools: ${toolResults?.length || 0})`);
|
|
358381
|
+
debugLogToolResults(toolResults);
|
|
356268
358382
|
}
|
|
356269
358383
|
}
|
|
356270
358384
|
};
|
|
@@ -356975,6 +359089,9 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
356975
359089
|
* Clean up resources (including MCP connections)
|
|
356976
359090
|
*/
|
|
356977
359091
|
async cleanup() {
|
|
359092
|
+
if (!this._abortController.signal.aborted) {
|
|
359093
|
+
this._abortController.abort();
|
|
359094
|
+
}
|
|
356978
359095
|
if (this.mcpBridge) {
|
|
356979
359096
|
try {
|
|
356980
359097
|
await this.mcpBridge.cleanup();
|
|
@@ -356998,14 +359115,25 @@ Convert your previous response content into actual JSON data that follows this s
|
|
|
356998
359115
|
this.clearHistory();
|
|
356999
359116
|
}
|
|
357000
359117
|
/**
|
|
357001
|
-
* Cancel the current request
|
|
359118
|
+
* Cancel the current request and all in-flight delegations.
|
|
359119
|
+
* Aborts the internal AbortController so streamText, subagents,
|
|
359120
|
+
* and any code checking the signal will stop.
|
|
357002
359121
|
*/
|
|
357003
359122
|
cancel() {
|
|
357004
359123
|
this.cancelled = true;
|
|
359124
|
+
this._abortController.abort();
|
|
357005
359125
|
if (this.debug) {
|
|
357006
359126
|
console.log(`[DEBUG] Agent cancelled for session ${this.sessionId}`);
|
|
357007
359127
|
}
|
|
357008
359128
|
}
|
|
359129
|
+
/**
|
|
359130
|
+
* Get the abort signal for this agent.
|
|
359131
|
+
* Delegations and subagents should check this signal.
|
|
359132
|
+
* @returns {AbortSignal}
|
|
359133
|
+
*/
|
|
359134
|
+
get abortSignal() {
|
|
359135
|
+
return this._abortController.signal;
|
|
359136
|
+
}
|
|
357009
359137
|
};
|
|
357010
359138
|
}
|
|
357011
359139
|
});
|
|
@@ -357038,12 +359166,17 @@ async function delegate({
|
|
|
357038
359166
|
mcpConfigPath = null,
|
|
357039
359167
|
delegationManager = null,
|
|
357040
359168
|
// Optional per-instance manager, falls back to default singleton
|
|
357041
|
-
concurrencyLimiter = null
|
|
359169
|
+
concurrencyLimiter = null,
|
|
357042
359170
|
// Optional global AI concurrency limiter
|
|
359171
|
+
parentAbortSignal = null
|
|
359172
|
+
// Optional AbortSignal from parent to cancel this delegation
|
|
357043
359173
|
}) {
|
|
357044
359174
|
if (!task || typeof task !== "string") {
|
|
357045
359175
|
throw new Error("Task parameter is required and must be a string");
|
|
357046
359176
|
}
|
|
359177
|
+
if (parentAbortSignal?.aborted) {
|
|
359178
|
+
throw new Error("Delegation cancelled: parent operation was aborted");
|
|
359179
|
+
}
|
|
357047
359180
|
const hasExplicitTimeout = Object.prototype.hasOwnProperty.call(arguments?.[0] ?? {}, "timeout");
|
|
357048
359181
|
if (!hasExplicitTimeout) {
|
|
357049
359182
|
const envTimeoutMs = parseInt(process.env.DELEGATION_TIMEOUT_MS || "", 10);
|
|
@@ -357128,12 +359261,37 @@ async function delegate({
|
|
|
357128
359261
|
}
|
|
357129
359262
|
const timeoutPromise = new Promise((_, reject2) => {
|
|
357130
359263
|
timeoutId = setTimeout(() => {
|
|
359264
|
+
subagent.cancel();
|
|
357131
359265
|
reject2(new Error(`Delegation timed out after ${timeout} seconds`));
|
|
357132
359266
|
}, timeout * 1e3);
|
|
357133
359267
|
});
|
|
359268
|
+
let parentAbortHandler;
|
|
359269
|
+
const parentAbortPromise = new Promise((_, reject2) => {
|
|
359270
|
+
if (parentAbortSignal) {
|
|
359271
|
+
if (parentAbortSignal.aborted) {
|
|
359272
|
+
subagent.cancel();
|
|
359273
|
+
reject2(new Error("Delegation cancelled: parent operation was aborted"));
|
|
359274
|
+
return;
|
|
359275
|
+
}
|
|
359276
|
+
parentAbortHandler = () => {
|
|
359277
|
+
subagent.cancel();
|
|
359278
|
+
reject2(new Error("Delegation cancelled: parent operation was aborted"));
|
|
359279
|
+
};
|
|
359280
|
+
parentAbortSignal.addEventListener("abort", parentAbortHandler, { once: true });
|
|
359281
|
+
}
|
|
359282
|
+
});
|
|
357134
359283
|
const answerOptions = schema ? { schema } : void 0;
|
|
357135
359284
|
const answerPromise = answerOptions ? subagent.answer(task, [], answerOptions) : subagent.answer(task);
|
|
357136
|
-
const
|
|
359285
|
+
const racers = [answerPromise, timeoutPromise];
|
|
359286
|
+
if (parentAbortSignal) racers.push(parentAbortPromise);
|
|
359287
|
+
let response;
|
|
359288
|
+
try {
|
|
359289
|
+
response = await Promise.race(racers);
|
|
359290
|
+
} finally {
|
|
359291
|
+
if (parentAbortHandler && parentAbortSignal) {
|
|
359292
|
+
parentAbortSignal.removeEventListener("abort", parentAbortHandler);
|
|
359293
|
+
}
|
|
359294
|
+
}
|
|
357137
359295
|
if (timeoutId !== null) {
|
|
357138
359296
|
clearTimeout(timeoutId);
|
|
357139
359297
|
timeoutId = null;
|
|
@@ -357289,10 +359447,9 @@ var init_delegate = __esm({
|
|
|
357289
359447
|
if (this.tryAcquire(parentSessionId)) {
|
|
357290
359448
|
return true;
|
|
357291
359449
|
}
|
|
357292
|
-
|
|
357293
|
-
console.error(`[DelegationManager] Slot unavailable (${this.globalActive}/${this.maxConcurrent}), queuing... (queue size: ${this.waitQueue.length}, timeout: ${effectiveTimeout}ms)`);
|
|
357294
|
-
}
|
|
359450
|
+
console.error(`[DelegationManager] Slot unavailable (${this.globalActive}/${this.maxConcurrent}), queuing... (queue size: ${this.waitQueue.length + 1}, timeout: ${effectiveTimeout}ms)`);
|
|
357295
359451
|
return new Promise((resolve8, reject2) => {
|
|
359452
|
+
const queuedAt = Date.now();
|
|
357296
359453
|
const entry = {
|
|
357297
359454
|
resolve: null,
|
|
357298
359455
|
// Will be wrapped below
|
|
@@ -357300,20 +359457,23 @@ var init_delegate = __esm({
|
|
|
357300
359457
|
// Will be wrapped below
|
|
357301
359458
|
parentSessionId,
|
|
357302
359459
|
debug,
|
|
357303
|
-
queuedAt
|
|
357304
|
-
timeoutId: null
|
|
359460
|
+
queuedAt,
|
|
359461
|
+
timeoutId: null,
|
|
359462
|
+
reminderId: null
|
|
357305
359463
|
};
|
|
357306
359464
|
let settled = false;
|
|
357307
359465
|
entry.resolve = (value) => {
|
|
357308
359466
|
if (settled) return;
|
|
357309
359467
|
settled = true;
|
|
357310
359468
|
if (entry.timeoutId) clearTimeout(entry.timeoutId);
|
|
359469
|
+
if (entry.reminderId) clearInterval(entry.reminderId);
|
|
357311
359470
|
resolve8(value);
|
|
357312
359471
|
};
|
|
357313
359472
|
entry.reject = (error2) => {
|
|
357314
359473
|
if (settled) return;
|
|
357315
359474
|
settled = true;
|
|
357316
359475
|
if (entry.timeoutId) clearTimeout(entry.timeoutId);
|
|
359476
|
+
if (entry.reminderId) clearInterval(entry.reminderId);
|
|
357317
359477
|
reject2(error2);
|
|
357318
359478
|
};
|
|
357319
359479
|
if (effectiveTimeout > 0) {
|
|
@@ -357325,6 +359485,13 @@ var init_delegate = __esm({
|
|
|
357325
359485
|
entry.reject(new Error(`Delegation queue timeout: waited ${effectiveTimeout}ms for an available slot`));
|
|
357326
359486
|
}, effectiveTimeout);
|
|
357327
359487
|
}
|
|
359488
|
+
entry.reminderId = setInterval(() => {
|
|
359489
|
+
const waitedSeconds = Math.round((Date.now() - queuedAt) / 1e3);
|
|
359490
|
+
console.error(`[DelegationManager] Still waiting for slot (${waitedSeconds}s). ${this.globalActive}/${this.maxConcurrent} active, ${this.waitQueue.length} queued.`);
|
|
359491
|
+
}, 15e3);
|
|
359492
|
+
if (entry.reminderId.unref) {
|
|
359493
|
+
entry.reminderId.unref();
|
|
359494
|
+
}
|
|
357328
359495
|
this.waitQueue.push(entry);
|
|
357329
359496
|
});
|
|
357330
359497
|
}
|
|
@@ -357363,18 +359530,14 @@ var init_delegate = __esm({
|
|
|
357363
359530
|
const sessionData = this.sessionDelegations.get(parentSessionId);
|
|
357364
359531
|
const sessionCount = sessionData?.count || 0;
|
|
357365
359532
|
if (sessionCount >= this.maxPerSession) {
|
|
357366
|
-
|
|
357367
|
-
console.error(`[DelegationManager] Session limit (${this.maxPerSession}) reached for queued item, rejecting`);
|
|
357368
|
-
}
|
|
359533
|
+
console.error(`[DelegationManager] Session limit (${this.maxPerSession}) reached for queued item, rejecting`);
|
|
357369
359534
|
toReject.push({ reject: reject2, error: new Error(`Maximum delegations per session (${this.maxPerSession}) reached for session ${parentSessionId}`) });
|
|
357370
359535
|
continue;
|
|
357371
359536
|
}
|
|
357372
359537
|
}
|
|
357373
359538
|
this._incrementCounters(parentSessionId);
|
|
357374
|
-
|
|
357375
|
-
|
|
357376
|
-
console.error(`[DelegationManager] Granted slot from queue (waited ${waitTime}ms). Active: ${this.globalActive}/${this.maxConcurrent}`);
|
|
357377
|
-
}
|
|
359539
|
+
const waitTime = Date.now() - queuedAt;
|
|
359540
|
+
console.error(`[DelegationManager] Granted slot from queue (waited ${waitTime}ms). Active: ${this.globalActive}/${this.maxConcurrent}`);
|
|
357378
359541
|
toResolve.push(resolve8);
|
|
357379
359542
|
}
|
|
357380
359543
|
if (toResolve.length > 0 || toReject.length > 0) {
|
|
@@ -357424,6 +359587,9 @@ var init_delegate = __esm({
|
|
|
357424
359587
|
if (entry.timeoutId) {
|
|
357425
359588
|
clearTimeout(entry.timeoutId);
|
|
357426
359589
|
}
|
|
359590
|
+
if (entry.reminderId) {
|
|
359591
|
+
clearInterval(entry.reminderId);
|
|
359592
|
+
}
|
|
357427
359593
|
if (entry.reject) {
|
|
357428
359594
|
entry.reject(new Error("DelegationManager was cleaned up"));
|
|
357429
359595
|
}
|
|
@@ -357546,8 +359712,9 @@ Instructions:
|
|
|
357546
359712
|
promptType: "code-researcher",
|
|
357547
359713
|
allowedTools: ["extract"],
|
|
357548
359714
|
maxIterations: 5,
|
|
357549
|
-
delegationManager: options.delegationManager
|
|
359715
|
+
delegationManager: options.delegationManager,
|
|
357550
359716
|
// Per-instance delegation limits
|
|
359717
|
+
parentAbortSignal: options.parentAbortSignal || null
|
|
357551
359718
|
// timeout removed - inherit default from delegate (300s)
|
|
357552
359719
|
});
|
|
357553
359720
|
return { chunk, result };
|
|
@@ -357567,16 +359734,12 @@ async function processChunksParallel(chunks, extractionPrompt, maxWorkers, optio
|
|
|
357567
359734
|
return result;
|
|
357568
359735
|
});
|
|
357569
359736
|
active.add(promise);
|
|
357570
|
-
|
|
357571
|
-
console.error(`[analyze_all] Started processing chunk ${chunk.id}/${chunk.total}`);
|
|
357572
|
-
}
|
|
359737
|
+
console.error(`[analyze_all] Started processing chunk ${chunk.id}/${chunk.total}`);
|
|
357573
359738
|
}
|
|
357574
359739
|
if (active.size > 0) {
|
|
357575
359740
|
const result = await Promise.race(active);
|
|
357576
359741
|
results.push(result);
|
|
357577
|
-
|
|
357578
|
-
console.error(`[analyze_all] Completed chunk ${result.chunk.id}/${result.chunk.total}`);
|
|
357579
|
-
}
|
|
359742
|
+
console.error(`[analyze_all] Completed chunk ${result.chunk.id}/${result.chunk.total}`);
|
|
357580
359743
|
}
|
|
357581
359744
|
}
|
|
357582
359745
|
results.sort((a5, b5) => a5.chunk.id - b5.chunk.id);
|
|
@@ -357646,8 +359809,9 @@ Organize all findings into clear categories with items listed under each.${compl
|
|
|
357646
359809
|
promptType: "code-researcher",
|
|
357647
359810
|
allowedTools: [],
|
|
357648
359811
|
maxIterations: 5,
|
|
357649
|
-
delegationManager: options.delegationManager
|
|
359812
|
+
delegationManager: options.delegationManager,
|
|
357650
359813
|
// Per-instance delegation limits
|
|
359814
|
+
parentAbortSignal: options.parentAbortSignal || null
|
|
357651
359815
|
// timeout removed - inherit default from delegate (300s)
|
|
357652
359816
|
});
|
|
357653
359817
|
return result;
|
|
@@ -357711,8 +359875,9 @@ CRITICAL: Do NOT guess keywords. Actually run searches and see what returns resu
|
|
|
357711
359875
|
promptType: "code-researcher",
|
|
357712
359876
|
// Full tool access for exploration and experimentation
|
|
357713
359877
|
maxIterations: 15,
|
|
357714
|
-
delegationManager: options.delegationManager
|
|
359878
|
+
delegationManager: options.delegationManager,
|
|
357715
359879
|
// Per-instance delegation limits
|
|
359880
|
+
parentAbortSignal: options.parentAbortSignal || null
|
|
357716
359881
|
// timeout removed - inherit default from delegate (300s)
|
|
357717
359882
|
});
|
|
357718
359883
|
const plan = parsePlanningResult(stripResultTags(result));
|
|
@@ -357769,8 +359934,9 @@ When done, use the attempt_completion tool with your answer as the result.`;
|
|
|
357769
359934
|
promptType: "code-researcher",
|
|
357770
359935
|
allowedTools: [],
|
|
357771
359936
|
maxIterations: 5,
|
|
357772
|
-
delegationManager: options.delegationManager
|
|
359937
|
+
delegationManager: options.delegationManager,
|
|
357773
359938
|
// Per-instance delegation limits
|
|
359939
|
+
parentAbortSignal: options.parentAbortSignal || null
|
|
357774
359940
|
// timeout removed - inherit default from delegate (300s)
|
|
357775
359941
|
});
|
|
357776
359942
|
return stripResultTags(result);
|
|
@@ -358083,11 +360249,41 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
|
|
|
358083
360249
|
"- extract: Verify code snippets to ensure targets are actually relevant before including them.",
|
|
358084
360250
|
"- listFiles: Understand directory structure to find where relevant code might live.",
|
|
358085
360251
|
"",
|
|
358086
|
-
"
|
|
360252
|
+
"CRITICAL - How probe search works (do NOT ignore):",
|
|
360253
|
+
"- By default (exact=false), probe ALREADY handles stemming, case-insensitive matching, and camelCase/snake_case splitting.",
|
|
360254
|
+
'- Searching "allowed_ips" ALREADY matches "AllowedIPs", "allowedIps", "allowed_ips", etc. Do NOT manually try case/style variations.',
|
|
360255
|
+
'- Searching "getUserData" ALREADY matches "get", "user", "data" and their variations.',
|
|
360256
|
+
"- NEVER repeat the same search query \u2014 you will get the same results.",
|
|
360257
|
+
"- NEVER search trivial variations of the same keyword (e.g., AllowedIPs then allowedIps then allowed_ips). This is wasteful \u2014 probe handles it.",
|
|
360258
|
+
"- If a search returns no results, the term likely does not exist in that path. Try a genuinely DIFFERENT keyword or concept, not a variation.",
|
|
360259
|
+
"- If 2-3 consecutive searches return no results for a concept, STOP searching for it and move on.",
|
|
360260
|
+
"",
|
|
360261
|
+
"GOOD search strategy (do this):",
|
|
360262
|
+
' Query: "How does authentication work and how are sessions managed?"',
|
|
360263
|
+
' \u2192 search "authentication" \u2192 search "session management" (two different concepts)',
|
|
360264
|
+
' Query: "Find the IP allowlist middleware"',
|
|
360265
|
+
' \u2192 search "allowlist middleware" (one search, probe handles IP/ip/Ip variations)',
|
|
360266
|
+
' Query: "How does BM25 scoring work with SIMD optimization?"',
|
|
360267
|
+
' \u2192 search "BM25 scoring" \u2192 search "SIMD optimization" (two different concepts)',
|
|
360268
|
+
"",
|
|
360269
|
+
"BAD search strategy (never do this):",
|
|
360270
|
+
' \u2192 search "AllowedIPs" \u2192 search "allowedIps" \u2192 search "allowed_ips" (WRONG: these are trivial case variations, probe handles them)',
|
|
360271
|
+
' \u2192 search "CIDR" \u2192 search "cidr" \u2192 search "Cidr" \u2192 search "*cidr*" (WRONG: same keyword repeated with variations)',
|
|
360272
|
+
' \u2192 search "error handling" \u2192 search "error handling" \u2192 search "error handling" (WRONG: repeating exact same query)',
|
|
360273
|
+
"",
|
|
360274
|
+
"Keyword tips:",
|
|
360275
|
+
"- Common programming keywords are filtered as stopwords when unquoted: function, class, return, new, struct, impl, var, let, const, etc.",
|
|
360276
|
+
'- Avoid searching for these alone \u2014 combine with a specific term (e.g., "middleware function" is fine, "function" alone is too generic).',
|
|
360277
|
+
'- To bypass stopword filtering: wrap terms in quotes ("return", "struct") or set exact=true. Both disable stemming and splitting too.',
|
|
360278
|
+
"- Multiple words without operators use OR logic: foo bar = foo OR bar. Use AND explicitly if you need both: foo AND bar.",
|
|
360279
|
+
'- camelCase terms are split: getUserData becomes "get", "user", "data" \u2014 so one search covers all naming styles.',
|
|
360280
|
+
"",
|
|
360281
|
+
"Strategy:",
|
|
358087
360282
|
"1. Analyze the query - identify key concepts, entities, and relationships",
|
|
358088
|
-
|
|
358089
|
-
"3.
|
|
358090
|
-
"4.
|
|
360283
|
+
"2. Run ONE focused search per concept with the most natural keyword. Trust probe to handle variations.",
|
|
360284
|
+
"3. If a search returns results, use extract to verify relevance",
|
|
360285
|
+
"4. Only try a different keyword if the first one returned irrelevant results (not if it returned no results \u2014 that means the concept is absent)",
|
|
360286
|
+
"5. Combine all relevant targets in your final response",
|
|
358091
360287
|
"",
|
|
358092
360288
|
`Query: ${searchQuery}`,
|
|
358093
360289
|
`Search path(s): ${searchPath}`,
|
|
@@ -358140,9 +360336,12 @@ var init_vercel = __esm({
|
|
|
358140
360336
|
}
|
|
358141
360337
|
return result;
|
|
358142
360338
|
};
|
|
360339
|
+
const previousSearches = /* @__PURE__ */ new Set();
|
|
360340
|
+
const paginationCounts = /* @__PURE__ */ new Map();
|
|
360341
|
+
const MAX_PAGES_PER_QUERY = 3;
|
|
358143
360342
|
return (0, import_ai5.tool)({
|
|
358144
360343
|
name: "search",
|
|
358145
|
-
description: searchDelegate ?
|
|
360344
|
+
description: searchDelegate ? searchDelegateDescription : searchDescription,
|
|
358146
360345
|
inputSchema: searchSchema,
|
|
358147
360346
|
execute: async ({ query: searchQuery, path: path9, allow_tests, exact, maxTokens: paramMaxTokens, language, session, nextPage }) => {
|
|
358148
360347
|
const effectiveMaxTokens = paramMaxTokens || maxTokens;
|
|
@@ -358180,6 +360379,26 @@ var init_vercel = __esm({
|
|
|
358180
360379
|
return await search(searchOptions);
|
|
358181
360380
|
};
|
|
358182
360381
|
if (!searchDelegate) {
|
|
360382
|
+
const searchKey = `${searchQuery}::${searchPath}::${exact || false}`;
|
|
360383
|
+
if (!nextPage) {
|
|
360384
|
+
if (previousSearches.has(searchKey)) {
|
|
360385
|
+
if (debug) {
|
|
360386
|
+
console.error(`[DEDUP] Blocked duplicate search: "${searchQuery}" in "${searchPath}"`);
|
|
360387
|
+
}
|
|
360388
|
+
return "DUPLICATE SEARCH BLOCKED: You already searched for this exact query in this path. Do NOT repeat the same search. If you need more results, set nextPage=true with the session ID from the previous search. Otherwise, try a genuinely different keyword, use extract to examine results you already found, or use attempt_completion if you have enough information.";
|
|
360389
|
+
}
|
|
360390
|
+
previousSearches.add(searchKey);
|
|
360391
|
+
paginationCounts.set(searchKey, 0);
|
|
360392
|
+
} else {
|
|
360393
|
+
const pageCount = (paginationCounts.get(searchKey) || 0) + 1;
|
|
360394
|
+
paginationCounts.set(searchKey, pageCount);
|
|
360395
|
+
if (pageCount > MAX_PAGES_PER_QUERY) {
|
|
360396
|
+
if (debug) {
|
|
360397
|
+
console.error(`[DEDUP] Blocked excessive pagination (page ${pageCount}/${MAX_PAGES_PER_QUERY}): "${searchQuery}" in "${searchPath}"`);
|
|
360398
|
+
}
|
|
360399
|
+
return `PAGINATION LIMIT REACHED: You have already retrieved ${MAX_PAGES_PER_QUERY} pages of results for this query. You have enough results \u2014 use extract to examine specific files, or use attempt_completion to return your findings.`;
|
|
360400
|
+
}
|
|
360401
|
+
}
|
|
358183
360402
|
try {
|
|
358184
360403
|
const result = maybeAnnotate(await runRawSearch());
|
|
358185
360404
|
if (options.fileTracker && typeof result === "string") {
|
|
@@ -358218,7 +360437,8 @@ var init_vercel = __esm({
|
|
|
358218
360437
|
promptType: "code-searcher",
|
|
358219
360438
|
allowedTools: ["search", "extract", "listFiles", "attempt_completion"],
|
|
358220
360439
|
searchDelegate: false,
|
|
358221
|
-
schema: CODE_SEARCH_SCHEMA
|
|
360440
|
+
schema: CODE_SEARCH_SCHEMA,
|
|
360441
|
+
parentAbortSignal: options.parentAbortSignal || null
|
|
358222
360442
|
});
|
|
358223
360443
|
const delegateResult = options.tracer?.withSpan ? await options.tracer.withSpan("search.delegate", runDelegation, {
|
|
358224
360444
|
"search.query": searchQuery,
|
|
@@ -358432,7 +360652,7 @@ var init_vercel = __esm({
|
|
|
358432
360652
|
name: "delegate",
|
|
358433
360653
|
description: delegateDescription,
|
|
358434
360654
|
inputSchema: delegateSchema,
|
|
358435
|
-
execute: async ({ task, currentIteration, maxIterations, parentSessionId, path: path9, provider, model, tracer, searchDelegate }) => {
|
|
360655
|
+
execute: async ({ task, currentIteration, maxIterations, parentSessionId, path: path9, provider, model, tracer, searchDelegate, parentAbortSignal }) => {
|
|
358436
360656
|
if (!task || typeof task !== "string") {
|
|
358437
360657
|
throw new Error("Task parameter is required and must be a non-empty string");
|
|
358438
360658
|
}
|
|
@@ -358490,8 +360710,9 @@ var init_vercel = __esm({
|
|
|
358490
360710
|
enableMcp,
|
|
358491
360711
|
mcpConfig,
|
|
358492
360712
|
mcpConfigPath,
|
|
358493
|
-
delegationManager
|
|
360713
|
+
delegationManager,
|
|
358494
360714
|
// Per-instance delegation limits
|
|
360715
|
+
parentAbortSignal
|
|
358495
360716
|
});
|
|
358496
360717
|
return result;
|
|
358497
360718
|
}
|
|
@@ -358529,8 +360750,9 @@ var init_vercel = __esm({
|
|
|
358529
360750
|
provider: options.provider,
|
|
358530
360751
|
model: options.model,
|
|
358531
360752
|
tracer: options.tracer,
|
|
358532
|
-
delegationManager
|
|
360753
|
+
delegationManager,
|
|
358533
360754
|
// Per-instance delegation limits
|
|
360755
|
+
parentAbortSignal: options.parentAbortSignal || null
|
|
358534
360756
|
});
|
|
358535
360757
|
return result;
|
|
358536
360758
|
} catch (error2) {
|
|
@@ -395746,7 +397968,7 @@ module.exports = /*#__PURE__*/JSON.parse('{"100":"Continue","101":"Switching Pro
|
|
|
395746
397968
|
/***/ ((module) => {
|
|
395747
397969
|
|
|
395748
397970
|
"use strict";
|
|
395749
|
-
module.exports = /*#__PURE__*/JSON.parse('{"name":"@probelabs/visor","version":"0.1.
|
|
397971
|
+
module.exports = /*#__PURE__*/JSON.parse('{"name":"@probelabs/visor","version":"0.1.42","main":"dist/index.js","bin":{"visor":"./dist/index.js"},"exports":{".":{"require":"./dist/index.js","import":"./dist/index.js"},"./sdk":{"types":"./dist/sdk/sdk.d.ts","import":"./dist/sdk/sdk.mjs","require":"./dist/sdk/sdk.js"},"./cli":{"require":"./dist/index.js"}},"files":["dist/","defaults/","action.yml","README.md","LICENSE"],"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"scripts":{"build:cli":"ncc build src/index.ts -o dist && cp -r defaults dist/ && cp -r output dist/ && cp -r docs dist/ && cp -r examples dist/ && cp -r src/debug-visualizer/ui dist/debug-visualizer/ && node scripts/inject-version.js && echo \'#!/usr/bin/env node\' | cat - dist/index.js > temp && mv temp dist/index.js && chmod +x dist/index.js","build:sdk":"tsup src/sdk.ts --dts --sourcemap --format esm,cjs --out-dir dist/sdk","build":"./scripts/build-oss.sh","build:ee":"npm run build:cli && npm run build:sdk","test":"jest && npm run test:yaml","test:unit":"jest","prepublishOnly":"npm run build","test:watch":"jest --watch","test:coverage":"jest --coverage","test:ee":"jest --testPathPatterns=\'tests/ee\' --testPathIgnorePatterns=\'/node_modules/\' --no-coverage","test:manual:bash":"RUN_MANUAL_TESTS=true jest tests/manual/bash-config-manual.test.ts","lint":"eslint src tests --ext .ts","lint:fix":"eslint src tests --ext .ts --fix","format":"prettier --write src tests","format:check":"prettier --check src tests","clean":"","clean:traces":"node scripts/clean-traces.js","prebuild":"npm run clean && node scripts/generate-config-schema.js","pretest":"npm run clean:traces && node scripts/generate-config-schema.js && npm run build:cli","pretest:unit":"npm run clean:traces && node scripts/generate-config-schema.js && npm run build:cli","test:with-build":"npm run build:cli && jest","test:yaml":"node dist/index.js test --progress compact","test:yaml:parallel":"node dist/index.js test --progress compact --max-parallel 4","prepare":"husky","pre-commit":"lint-staged","deploy:site":"cd site && npx wrangler pages deploy . --project-name=visor-site --commit-dirty=true","deploy:worker":"npx wrangler deploy","deploy":"npm run deploy:site && npm run deploy:worker","publish:ee":"./scripts/publish-ee.sh","release":"./scripts/release.sh","release:patch":"./scripts/release.sh patch","release:minor":"./scripts/release.sh minor","release:major":"./scripts/release.sh major","release:prerelease":"./scripts/release.sh prerelease","docs:validate":"node scripts/validate-readme-links.js","workshop:setup":"npm install -D reveal-md@6.1.2","workshop:serve":"cd workshop && reveal-md slides.md -w","workshop:export":"reveal-md workshop/slides.md --static workshop/build","workshop:pdf":"reveal-md workshop/slides.md --print workshop/Visor-Workshop.pdf --print-size letter","workshop:pdf:ci":"reveal-md workshop/slides.md --print workshop/Visor-Workshop.pdf --print-size letter --puppeteer-launch-args=\\"--no-sandbox --disable-dev-shm-usage\\"","workshop:pdf:a4":"reveal-md workshop/slides.md --print workshop/Visor-Workshop-A4.pdf --print-size A4","workshop:build":"npm run workshop:export && npm run workshop:pdf","simulate:issue":"TS_NODE_TRANSPILE_ONLY=1 ts-node scripts/simulate-gh-run.ts --event issues --action opened --debug","simulate:comment":"TS_NODE_TRANSPILE_ONLY=1 ts-node scripts/simulate-gh-run.ts --event issue_comment --action created --debug"},"keywords":["code-review","ai","github-action","cli","pr-review","visor"],"author":"Probe Labs","license":"MIT","description":"AI workflow engine for code review, assistants, and automation — orchestrate checks, MCP tools, and AI providers with YAML-driven pipelines","repository":{"type":"git","url":"git+https://github.com/probelabs/visor.git"},"bugs":{"url":"https://github.com/probelabs/visor/issues"},"homepage":"https://github.com/probelabs/visor#readme","dependencies":{"@actions/core":"^1.11.1","@apidevtools/swagger-parser":"^12.1.0","@modelcontextprotocol/sdk":"^1.25.3","@nyariv/sandboxjs":"github:probelabs/SandboxJS#f1c13b8eee98734a8ea024061eada4aa9a9ff2e9","@octokit/action":"^8.0.2","@octokit/auth-app":"^8.1.0","@octokit/core":"^7.0.3","@octokit/rest":"^22.0.0","@opentelemetry/api":"^1.9.0","@opentelemetry/core":"^1.30.1","@opentelemetry/exporter-trace-otlp-grpc":"^0.203.0","@opentelemetry/exporter-trace-otlp-http":"^0.203.0","@opentelemetry/instrumentation":"^0.203.0","@opentelemetry/resources":"^1.30.1","@opentelemetry/sdk-metrics":"^1.30.1","@opentelemetry/sdk-node":"^0.203.0","@opentelemetry/sdk-trace-base":"^1.30.1","@opentelemetry/semantic-conventions":"^1.30.1","@probelabs/probe":"^0.6.0-rc280","@types/commander":"^2.12.0","@types/uuid":"^10.0.0","acorn":"^8.16.0","acorn-walk":"^8.3.5","ajv":"^8.17.1","ajv-formats":"^3.0.1","better-sqlite3":"^11.0.0","blessed":"^0.1.81","cli-table3":"^0.6.5","commander":"^14.0.0","deepmerge":"^4.3.1","dotenv":"^17.2.3","ignore":"^7.0.5","js-yaml":"^4.1.0","jsonpath-plus":"^10.4.0","liquidjs":"^10.21.1","minimatch":"^10.2.2","node-cron":"^3.0.3","open":"^9.1.0","simple-git":"^3.28.0","uuid":"^11.1.0","ws":"^8.18.3"},"optionalDependencies":{"@anthropic/claude-code-sdk":"npm:null@*","@open-policy-agent/opa-wasm":"^1.10.0","knex":"^3.1.0","mysql2":"^3.11.0","pg":"^8.13.0","tedious":"^19.0.0"},"devDependencies":{"@eslint/js":"^9.34.0","@kie/act-js":"^2.6.2","@kie/mock-github":"^2.0.1","@swc/core":"^1.13.2","@swc/jest":"^0.2.37","@types/better-sqlite3":"^7.6.0","@types/blessed":"^0.1.27","@types/jest":"^30.0.0","@types/js-yaml":"^4.0.9","@types/node":"^24.3.0","@types/node-cron":"^3.0.11","@types/ws":"^8.18.1","@typescript-eslint/eslint-plugin":"^8.42.0","@typescript-eslint/parser":"^8.42.0","@vercel/ncc":"^0.38.4","eslint":"^9.34.0","eslint-config-prettier":"^10.1.8","eslint-plugin-prettier":"^5.5.4","husky":"^9.1.7","jest":"^30.1.3","lint-staged":"^16.1.6","prettier":"^3.6.2","reveal-md":"^6.1.2","ts-json-schema-generator":"^1.5.1","ts-node":"^10.9.2","tsup":"^8.5.0","typescript":"^5.9.2","wrangler":"^3.0.0"},"peerDependenciesMeta":{"@anthropic/claude-code-sdk":{"optional":true}},"directories":{"test":"tests"},"lint-staged":{"src/**/*.{ts,js}":["eslint --fix","prettier --write"],"tests/**/*.{ts,js}":["eslint --fix","prettier --write"],"*.{json,md,yml,yaml}":["prettier --write"]}}');
|
|
395750
397972
|
|
|
395751
397973
|
/***/ })
|
|
395752
397974
|
|