@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.
Files changed (88) hide show
  1. package/dist/index.js +2363 -141
  2. package/dist/pr-analyzer.d.ts +2 -1
  3. package/dist/pr-analyzer.d.ts.map +1 -1
  4. package/dist/providers/http-client-provider.d.ts.map +1 -1
  5. package/dist/sdk/{check-provider-registry-6P2KJ423.mjs → check-provider-registry-PU67PWTU.mjs} +5 -5
  6. package/dist/sdk/{check-provider-registry-TTVN3V2O.mjs → check-provider-registry-TGPICTHD.mjs} +5 -5
  7. package/dist/sdk/{chunk-EO4IJNM7.mjs → chunk-E7NRUDWL.mjs} +2 -2
  8. package/dist/sdk/{chunk-G5JBWW3O.mjs → chunk-P5P6BOO7.mjs} +161 -21
  9. package/dist/sdk/chunk-P5P6BOO7.mjs.map +1 -0
  10. package/dist/sdk/{chunk-GMHSXC5K.mjs → chunk-RV5SK4FZ.mjs} +3 -3
  11. package/dist/sdk/{chunk-S47KBQQK.mjs → chunk-T5USZCCM.mjs} +2 -2
  12. package/dist/sdk/{chunk-S47KBQQK.mjs.map → chunk-T5USZCCM.mjs.map} +1 -1
  13. package/dist/sdk/{chunk-GOJRNYTV.mjs → chunk-WSYVK6ML.mjs} +188 -22
  14. package/dist/sdk/chunk-WSYVK6ML.mjs.map +1 -0
  15. package/dist/sdk/{failure-condition-evaluator-N3VNLWZD.mjs → failure-condition-evaluator-GPANOHP2.mjs} +3 -3
  16. package/dist/sdk/{github-frontend-ATORHHF6.mjs → github-frontend-P274ISBJ.mjs} +3 -3
  17. package/dist/sdk/{host-JROON6IT.mjs → host-AIMRV5YL.mjs} +2 -2
  18. package/dist/sdk/{host-OBXKDFT7.mjs → host-QYPOS4R6.mjs} +2 -2
  19. package/dist/sdk/knex-store-CRORFJE6.mjs +527 -0
  20. package/dist/sdk/knex-store-CRORFJE6.mjs.map +1 -0
  21. package/dist/sdk/loader-NJCF7DUS.mjs +89 -0
  22. package/dist/sdk/loader-NJCF7DUS.mjs.map +1 -0
  23. package/dist/sdk/opa-policy-engine-S2S2ULEI.mjs +655 -0
  24. package/dist/sdk/opa-policy-engine-S2S2ULEI.mjs.map +1 -0
  25. package/dist/sdk/{routing-QCDX43XD.mjs → routing-BXHP2E62.mjs} +4 -4
  26. package/dist/sdk/{schedule-tool-D5TSTGP2.mjs → schedule-tool-5FVFYH2A.mjs} +5 -5
  27. package/dist/sdk/{schedule-tool-XCGJI2VB.mjs → schedule-tool-MQHISNJ6.mjs} +5 -5
  28. package/dist/sdk/{schedule-tool-handler-DKHHPZAG.mjs → schedule-tool-handler-4TCT2P7A.mjs} +5 -5
  29. package/dist/sdk/{schedule-tool-handler-OKZ53WMC.mjs → schedule-tool-handler-TZYXM664.mjs} +5 -5
  30. package/dist/sdk/sdk.js +1779 -265
  31. package/dist/sdk/sdk.js.map +1 -1
  32. package/dist/sdk/sdk.mjs +4 -4
  33. package/dist/sdk/{trace-helpers-J5CJ4PUN.mjs → trace-helpers-UG6FOWVV.mjs} +2 -2
  34. package/dist/sdk/validator-XTZJZZJH.mjs +134 -0
  35. package/dist/sdk/validator-XTZJZZJH.mjs.map +1 -0
  36. package/dist/sdk/{workflow-check-provider-T6WFK4RB.mjs → workflow-check-provider-BE2SVYWW.mjs} +5 -5
  37. package/dist/sdk/{workflow-check-provider-WLUAJPAS.mjs → workflow-check-provider-QKHL6AFT.mjs} +5 -5
  38. package/dist/slack/socket-runner.d.ts +14 -0
  39. package/dist/slack/socket-runner.d.ts.map +1 -1
  40. package/dist/utils/oauth2-token-cache.d.ts +44 -0
  41. package/dist/utils/oauth2-token-cache.d.ts.map +1 -0
  42. package/package.json +2 -2
  43. package/dist/output/traces/run-2026-03-06T06-08-10-897Z.ndjson +0 -138
  44. package/dist/output/traces/run-2026-03-06T06-08-55-016Z.ndjson +0 -2235
  45. package/dist/sdk/check-provider-registry-4SHN3GSH.mjs +0 -29
  46. package/dist/sdk/chunk-G5JBWW3O.mjs.map +0 -1
  47. package/dist/sdk/chunk-GOJRNYTV.mjs.map +0 -1
  48. package/dist/sdk/chunk-J236ZVYX.mjs +0 -1502
  49. package/dist/sdk/chunk-J236ZVYX.mjs.map +0 -1
  50. package/dist/sdk/chunk-LDE33FGE.mjs +0 -443
  51. package/dist/sdk/chunk-LDE33FGE.mjs.map +0 -1
  52. package/dist/sdk/chunk-MYROK4LB.mjs +0 -43917
  53. package/dist/sdk/chunk-MYROK4LB.mjs.map +0 -1
  54. package/dist/sdk/chunk-XDIBL7QB.mjs +0 -739
  55. package/dist/sdk/chunk-XDIBL7QB.mjs.map +0 -1
  56. package/dist/sdk/failure-condition-evaluator-M6SIUQF4.mjs +0 -17
  57. package/dist/sdk/github-frontend-MHXL2Q2V.mjs +0 -1368
  58. package/dist/sdk/github-frontend-MHXL2Q2V.mjs.map +0 -1
  59. package/dist/sdk/routing-TGJD66Q5.mjs +0 -25
  60. package/dist/sdk/schedule-tool-C5QN5OQU.mjs +0 -35
  61. package/dist/sdk/schedule-tool-handler-OKZ53WMC.mjs.map +0 -1
  62. package/dist/sdk/schedule-tool-handler-ZUMPNAVY.mjs +0 -39
  63. package/dist/sdk/schedule-tool-handler-ZUMPNAVY.mjs.map +0 -1
  64. package/dist/sdk/trace-helpers-J5CJ4PUN.mjs.map +0 -1
  65. package/dist/sdk/trace-helpers-KFQJ7IAG.mjs +0 -25
  66. package/dist/sdk/trace-helpers-KFQJ7IAG.mjs.map +0 -1
  67. package/dist/sdk/workflow-check-provider-RBYA6ZGU.mjs +0 -29
  68. package/dist/sdk/workflow-check-provider-RBYA6ZGU.mjs.map +0 -1
  69. package/dist/sdk/workflow-check-provider-T6WFK4RB.mjs.map +0 -1
  70. package/dist/sdk/workflow-check-provider-WLUAJPAS.mjs.map +0 -1
  71. package/dist/traces/run-2026-03-06T06-08-10-897Z.ndjson +0 -138
  72. package/dist/traces/run-2026-03-06T06-08-55-016Z.ndjson +0 -2235
  73. /package/dist/sdk/{check-provider-registry-4SHN3GSH.mjs.map → check-provider-registry-PU67PWTU.mjs.map} +0 -0
  74. /package/dist/sdk/{check-provider-registry-6P2KJ423.mjs.map → check-provider-registry-TGPICTHD.mjs.map} +0 -0
  75. /package/dist/sdk/{chunk-EO4IJNM7.mjs.map → chunk-E7NRUDWL.mjs.map} +0 -0
  76. /package/dist/sdk/{chunk-GMHSXC5K.mjs.map → chunk-RV5SK4FZ.mjs.map} +0 -0
  77. /package/dist/sdk/{check-provider-registry-TTVN3V2O.mjs.map → failure-condition-evaluator-GPANOHP2.mjs.map} +0 -0
  78. /package/dist/sdk/{github-frontend-ATORHHF6.mjs.map → github-frontend-P274ISBJ.mjs.map} +0 -0
  79. /package/dist/sdk/{host-JROON6IT.mjs.map → host-AIMRV5YL.mjs.map} +0 -0
  80. /package/dist/sdk/{host-OBXKDFT7.mjs.map → host-QYPOS4R6.mjs.map} +0 -0
  81. /package/dist/sdk/{failure-condition-evaluator-M6SIUQF4.mjs.map → routing-BXHP2E62.mjs.map} +0 -0
  82. /package/dist/sdk/{failure-condition-evaluator-N3VNLWZD.mjs.map → schedule-tool-5FVFYH2A.mjs.map} +0 -0
  83. /package/dist/sdk/{routing-QCDX43XD.mjs.map → schedule-tool-MQHISNJ6.mjs.map} +0 -0
  84. /package/dist/sdk/{routing-TGJD66Q5.mjs.map → schedule-tool-handler-4TCT2P7A.mjs.map} +0 -0
  85. /package/dist/sdk/{schedule-tool-C5QN5OQU.mjs.map → schedule-tool-handler-TZYXM664.mjs.map} +0 -0
  86. /package/dist/sdk/{schedule-tool-D5TSTGP2.mjs.map → trace-helpers-UG6FOWVV.mjs.map} +0 -0
  87. /package/dist/sdk/{schedule-tool-XCGJI2VB.mjs.map → workflow-check-provider-BE2SVYWW.mjs.map} +0 -0
  88. /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.165';
3
- process.env.PROBE_VERSION = '0.6.0-rc278';
4
- process.env.VISOR_COMMIT_SHA = '0431072b1fb3764b178496aa042aaa2a90adfd84';
5
- process.env.VISOR_COMMIT_SHORT = '0431072b';
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__(71370)));
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(1000 * Math.pow(2, attempt), 5000); // Exponential backoff, max 5s
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 URL specified
185546
- if (typeof cfg.url !== 'string' || !cfg.url) {
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 url = config.url;
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 if provided
187445
+ // Prepare request body supports both Liquid template strings and JSON objects
185587
187446
  let requestBody;
185588
- if (bodyTemplate) {
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
- this.ws = new ws_1.default(url);
198783
- this.ws.on('open', () => {
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
- this.ws.on('close', (code, reason) => {
200671
+ ws.on('close', (code, reason) => {
198788
200672
  logger_1.logger.warn(`[SlackSocket] WebSocket closed: ${code} ${reason}`);
198789
- setTimeout(() => this.restart().catch(() => { }), 1000);
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
- this.ws.on('error', err => {
200680
+ ws.on('error', err => {
198792
200681
  logger_1.logger.error(`[SlackSocket] WebSocket error: ${err}`);
198793
200682
  });
198794
- this.ws.on('message', data => this.handleMessage(data.toString()).catch(() => { }));
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().catch(() => { }), delay);
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
- if (this.ws) {
199589
- try {
199590
- this.ws.close();
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__(7065)));
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
- __nccwpck_require__.ab + "traces";
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 || __nccwpck_require__.ab + "traces";
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 || __nccwpck_require__.ab + "traces";
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
- if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
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
- if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
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
- if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
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
- if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
248173
- console.log(`Binary now available at ${binaryPath}, download completed by another process`);
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
- if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
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
- if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
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
- if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
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
- if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
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) => setTimeout(() => reject2(new Error(`Download timeout after ${LOCK_TIMEOUT_MS / 1e3}s`)), LOCK_TIMEOUT_MS)
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 with Elasticsearch syntax. Use quotes for exact matches, AND/OR for boolean logic, - for negation."),
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 with Elasticsearch syntax. Use quotes for exact matches, AND/OR for boolean logic, - for negation."),
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 = "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.";
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
- - search: Find code patterns using semantic search
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 searchGuidance = this.searchDelegate ? "1. Start with search to retrieve extracted code blocks" : "1. Start with search to find relevant code patterns";
355635
- const extractGuidance = this.searchDelegate ? "2. Use extract only if you need more context or a full file" : "2. Use extract to get detailed context when needed";
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
- ${searchGuidance}
355640
- ${extractGuidance}
357743
+ ${searchGuidance1}
357744
+ ${extractGuidance1}
355641
357745
  3. Prefer focused, specific searches over broad queries
355642
- 4. Combine multiple tools to build complete understanding`;
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
- - search: Find code patterns using semantic search
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 searchGuidance = this.searchDelegate ? "1. Start with search to retrieve extracted code blocks" : "1. Start with search to find relevant code patterns";
355688
- const extractGuidance = this.searchDelegate ? "2. Use extract only if you need more context or a full file" : "2. Use extract to get detailed context when needed";
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
- ${searchGuidance}
355693
- ${extractGuidance}
357798
+ ${searchGuidance2}
357799
+ ${extractGuidance2}
355694
357800
  3. Prefer focused, specific searches over broad queries
355695
- 4. Combine multiple tools to build complete understanding`;
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 ? " It already returns extracted code blocks; use extract only to expand context or read full files." : " Read full files only if really necessary."}
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.${this.allowEdit ? `
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 response = await Promise.race([answerPromise, timeoutPromise]);
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
- if (debug) {
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: Date.now(),
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
- if (debug) {
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
- if (debug) {
357375
- const waitTime = Date.now() - queuedAt;
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
- if (options.debug) {
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
- if (options.debug) {
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
- "Strategy for complex queries:",
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
- '2. Run focused searches for each independent concept (e.g., for "how do payments work and how are emails sent", search "payments" and "emails" separately since they are unrelated)',
358089
- "3. Use extract to verify relevance of promising results",
358090
- "4. Combine all relevant targets in your final response",
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 ? `${searchDescription} (delegates code search to a subagent and returns extracted code blocks)` : searchDescription,
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.165","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-rc278","@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"]}}');
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