mobbdev 1.2.0 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1371,7 +1371,7 @@ import Debug19 from "debug";
1371
1371
  import { hideBin } from "yargs/helpers";
1372
1372
 
1373
1373
  // src/args/yargs.ts
1374
- import chalk12 from "chalk";
1374
+ import chalk13 from "chalk";
1375
1375
  import yargs from "yargs/yargs";
1376
1376
 
1377
1377
  // src/args/commands/convert_to_sarif.ts
@@ -6572,7 +6572,7 @@ async function getAdoSdk(params) {
6572
6572
  const url = new URL(repoUrl);
6573
6573
  const origin2 = url.origin.toLowerCase().endsWith(".visualstudio.com") ? DEFUALT_ADO_ORIGIN : url.origin.toLowerCase();
6574
6574
  const params2 = `path=/&versionDescriptor[versionOptions]=0&versionDescriptor[versionType]=commit&versionDescriptor[version]=${branch}&resolveLfs=true&$format=zip&api-version=5.0&download=true`;
6575
- const path24 = [
6575
+ const path25 = [
6576
6576
  prefixPath,
6577
6577
  owner,
6578
6578
  projectName,
@@ -6583,7 +6583,7 @@ async function getAdoSdk(params) {
6583
6583
  "items",
6584
6584
  "items"
6585
6585
  ].filter(Boolean).join("/");
6586
- return new URL(`${path24}?${params2}`, origin2).toString();
6586
+ return new URL(`${path25}?${params2}`, origin2).toString();
6587
6587
  },
6588
6588
  async getAdoBranchList({ repoUrl }) {
6589
6589
  try {
@@ -8180,8 +8180,8 @@ function extractBlameRanges(data) {
8180
8180
  }
8181
8181
  function buildBlameFragment(ref) {
8182
8182
  const escapedRef = safeGraphQLString(ref, "ref");
8183
- return (path24, index) => {
8184
- const escapedPath = safeGraphQLString(path24, "path");
8183
+ return (path25, index) => {
8184
+ const escapedPath = safeGraphQLString(path25, "path");
8185
8185
  return `
8186
8186
  file${index}: object(expression: "${escapedRef}") {
8187
8187
  ... on Commit {
@@ -8229,8 +8229,8 @@ async function processBlameAttempt(params) {
8229
8229
  )
8230
8230
  );
8231
8231
  for (const batchResult of batchResults) {
8232
- for (const [path24, blameData] of batchResult) {
8233
- result.set(path24, blameData);
8232
+ for (const [path25, blameData] of batchResult) {
8233
+ result.set(path25, blameData);
8234
8234
  }
8235
8235
  }
8236
8236
  return result;
@@ -8550,14 +8550,14 @@ function getGithubSdk(params = {}) {
8550
8550
  };
8551
8551
  },
8552
8552
  async getGithubBlameRanges(params2) {
8553
- const { ref, gitHubUrl, path: path24 } = params2;
8553
+ const { ref, gitHubUrl, path: path25 } = params2;
8554
8554
  const { owner, repo } = parseGithubOwnerAndRepo(gitHubUrl);
8555
8555
  const res = await octokit.graphql(
8556
8556
  GET_BLAME_DOCUMENT,
8557
8557
  {
8558
8558
  owner,
8559
8559
  repo,
8560
- path: path24,
8560
+ path: path25,
8561
8561
  ref
8562
8562
  }
8563
8563
  );
@@ -9030,8 +9030,8 @@ function getGithubSdk(params = {}) {
9030
9030
  return executeBatchGraphQL(octokit, params2.owner, params2.repo, {
9031
9031
  items: params2.filePaths,
9032
9032
  aliasPrefix: "file",
9033
- buildFragment: (path24, index) => {
9034
- const escapedPath = safeGraphQLString(path24, "path");
9033
+ buildFragment: (path25, index) => {
9034
+ const escapedPath = safeGraphQLString(path25, "path");
9035
9035
  return `
9036
9036
  file${index}: object(expression: "${escapedRef}") {
9037
9037
  ... on Commit {
@@ -9346,11 +9346,11 @@ var GithubSCMLib = class _GithubSCMLib extends SCMLib {
9346
9346
  markdownComment: comment
9347
9347
  });
9348
9348
  }
9349
- async getRepoBlameRanges(ref, path24) {
9349
+ async getRepoBlameRanges(ref, path25) {
9350
9350
  this._validateUrl();
9351
9351
  return await this.githubSdk.getGithubBlameRanges({
9352
9352
  ref,
9353
- path: path24,
9353
+ path: path25,
9354
9354
  gitHubUrl: this.url
9355
9355
  });
9356
9356
  }
@@ -10282,13 +10282,13 @@ function parseGitlabOwnerAndRepo(gitlabUrl) {
10282
10282
  const { organization, repoName, projectPath } = parsingResult;
10283
10283
  return { owner: organization, repo: repoName, projectPath };
10284
10284
  }
10285
- async function getGitlabBlameRanges({ ref, gitlabUrl, path: path24 }, options) {
10285
+ async function getGitlabBlameRanges({ ref, gitlabUrl, path: path25 }, options) {
10286
10286
  const { projectPath } = parseGitlabOwnerAndRepo(gitlabUrl);
10287
10287
  const api2 = getGitBeaker({
10288
10288
  url: gitlabUrl,
10289
10289
  gitlabAuthToken: options?.gitlabAuthToken
10290
10290
  });
10291
- const resp = await api2.RepositoryFiles.allFileBlames(projectPath, path24, ref);
10291
+ const resp = await api2.RepositoryFiles.allFileBlames(projectPath, path25, ref);
10292
10292
  let lineNumber = 1;
10293
10293
  return resp.filter((range) => range.lines).map((range) => {
10294
10294
  const oldLineNumber = lineNumber;
@@ -10462,10 +10462,10 @@ var GitlabSCMLib = class extends SCMLib {
10462
10462
  markdownComment: comment
10463
10463
  });
10464
10464
  }
10465
- async getRepoBlameRanges(ref, path24) {
10465
+ async getRepoBlameRanges(ref, path25) {
10466
10466
  this._validateUrl();
10467
10467
  return await getGitlabBlameRanges(
10468
- { ref, path: path24, gitlabUrl: this.url },
10468
+ { ref, path: path25, gitlabUrl: this.url },
10469
10469
  {
10470
10470
  url: this.url,
10471
10471
  gitlabAuthToken: this.accessToken
@@ -11524,6 +11524,13 @@ var convertToSarifCodePathPatternsOption = {
11524
11524
  type: "string",
11525
11525
  array: true
11526
11526
  };
11527
+ var pollingOption = {
11528
+ describe: chalk2.bold(
11529
+ "Use HTTP polling instead of WebSocket for status updates. Useful for proxy environments or firewalls that block WebSocket connections. Polling interval: 5 seconds, timeout: 30 minutes."
11530
+ ),
11531
+ type: "boolean",
11532
+ default: false
11533
+ };
11527
11534
 
11528
11535
  // src/args/commands/convert_to_sarif.ts
11529
11536
  function convertToSarifBuilder(args) {
@@ -11555,7 +11562,8 @@ var mobbCliCommand = {
11555
11562
  uploadAiBlame: "upload-ai-blame",
11556
11563
  claudeCodeInstallHook: "claude-code-install-hook",
11557
11564
  claudeCodeProcessHook: "claude-code-process-hook",
11558
- windsurfIntellijMonitor: "windsurf-intellij-monitor"
11565
+ windsurfIntellijInstallHook: "windsurf-intellij-install-hook",
11566
+ windsurfIntellijProcessHook: "windsurf-intellij-process-hook"
11559
11567
  };
11560
11568
  var ScanContext = {
11561
11569
  FULL_SCAN: "FULL_SCAN",
@@ -12235,6 +12243,61 @@ var GQLClient = class {
12235
12243
  }
12236
12244
  );
12237
12245
  }
12246
+ async pollForAnalysisState(params) {
12247
+ const { analysisId, callbackStates, callback, timeoutInMs } = params;
12248
+ const startTime = Date.now();
12249
+ const maxDuration = timeoutInMs ?? 30 * 60 * 1e3;
12250
+ const pollingIntervalSec = REPORT_STATE_CHECK_DELAY / 1e3;
12251
+ debug6(
12252
+ `[pollForAnalysisState] Starting polling for analysis ${analysisId}, target states: ${callbackStates.join(", ")}, interval: ${pollingIntervalSec}s`
12253
+ );
12254
+ let isPolling = true;
12255
+ let pollCount = 0;
12256
+ while (isPolling) {
12257
+ pollCount++;
12258
+ const elapsedSec = Math.round((Date.now() - startTime) / 1e3);
12259
+ if (Date.now() - startTime > maxDuration) {
12260
+ debug6(
12261
+ `[pollForAnalysisState] Timeout expired after ${pollCount} polls (${elapsedSec}s)`
12262
+ );
12263
+ throw new ReportDigestError(
12264
+ `Polling timeout expired for analysis: ${analysisId} with timeout: ${maxDuration}ms`,
12265
+ `Analysis timed out after ${Math.round(maxDuration / 6e4)} minutes. Please try again or check the Mobb platform for status.`
12266
+ );
12267
+ }
12268
+ debug6(
12269
+ `[pollForAnalysisState] Poll #${pollCount} (elapsed: ${elapsedSec}s) - fetching analysis state...`
12270
+ );
12271
+ const analysis = await this.getAnalysis(analysisId);
12272
+ debug6(
12273
+ `[pollForAnalysisState] Poll #${pollCount} - current state: ${analysis.state}`
12274
+ );
12275
+ if (!analysis.state || analysis.state === "Failed" /* Failed */) {
12276
+ const errorMessage = analysis.failReason || `Analysis failed with id: ${analysis.id}`;
12277
+ debug6(`[pollForAnalysisState] Analysis failed: ${errorMessage}`);
12278
+ throw new ReportDigestError(errorMessage, analysis.failReason ?? "");
12279
+ }
12280
+ if (callbackStates.includes(analysis.state)) {
12281
+ debug6(
12282
+ `[pollForAnalysisState] Target state reached: ${analysis.state} after ${pollCount} polls (${elapsedSec}s)`
12283
+ );
12284
+ await callback(analysis.id);
12285
+ isPolling = false;
12286
+ return {
12287
+ analysis: {
12288
+ id: analysis.id,
12289
+ state: analysis.state,
12290
+ failReason: analysis.failReason
12291
+ }
12292
+ };
12293
+ }
12294
+ debug6(
12295
+ `[pollForAnalysisState] State ${analysis.state} not in target states, waiting ${pollingIntervalSec}s before next poll...`
12296
+ );
12297
+ await sleep(REPORT_STATE_CHECK_DELAY);
12298
+ }
12299
+ throw new Error(`Unexpected end of polling for analysis: ${analysisId}`);
12300
+ }
12238
12301
  async getFixReportsByRepoUrl({ repoUrl }) {
12239
12302
  const res = await this._clientSdk.GetFixReportsByRepoUrl({
12240
12303
  repoUrl
@@ -12635,7 +12698,8 @@ var debug8 = Debug7("mobbdev:index");
12635
12698
  async function sendReport({
12636
12699
  spinner,
12637
12700
  submitVulnerabilityReportVariables,
12638
- gqlClient
12701
+ gqlClient,
12702
+ polling
12639
12703
  }) {
12640
12704
  try {
12641
12705
  const submitRes = await gqlClient.submitVulnerabilityReport(
@@ -12646,19 +12710,32 @@ async function sendReport({
12646
12710
  throw new Error("\u{1F575}\uFE0F\u200D\u2642\uFE0F Mobb analysis failed");
12647
12711
  }
12648
12712
  spinner.update({ text: progressMassages.processingVulnerabilityReport });
12649
- await gqlClient.subscribeToAnalysis({
12650
- subscribeToAnalysisParams: {
12651
- analysisId: submitRes.submitVulnerabilityReport.fixReportId
12652
- },
12653
- callback: () => spinner.update({
12654
- text: "\u2699\uFE0F Vulnerability report processed successfully"
12655
- }),
12656
- callbackStates: [
12657
- "Digested" /* Digested */,
12658
- "Finished" /* Finished */
12659
- ],
12660
- timeoutInMs: VUL_REPORT_DIGEST_TIMEOUT_MS
12713
+ const callback = (_analysisId) => spinner.update({
12714
+ text: "\u2699\uFE0F Vulnerability report processed successfully"
12661
12715
  });
12716
+ const callbackStates = [
12717
+ "Digested" /* Digested */,
12718
+ "Finished" /* Finished */
12719
+ ];
12720
+ if (polling) {
12721
+ debug8("[sendReport] Using POLLING mode for analysis state updates");
12722
+ await gqlClient.pollForAnalysisState({
12723
+ analysisId: submitRes.submitVulnerabilityReport.fixReportId,
12724
+ callback,
12725
+ callbackStates,
12726
+ timeoutInMs: VUL_REPORT_DIGEST_TIMEOUT_MS
12727
+ });
12728
+ } else {
12729
+ debug8("[sendReport] Using WEBSOCKET mode for analysis state updates");
12730
+ await gqlClient.subscribeToAnalysis({
12731
+ subscribeToAnalysisParams: {
12732
+ analysisId: submitRes.submitVulnerabilityReport.fixReportId
12733
+ },
12734
+ callback,
12735
+ callbackStates,
12736
+ timeoutInMs: VUL_REPORT_DIGEST_TIMEOUT_MS
12737
+ });
12738
+ }
12662
12739
  return submitRes;
12663
12740
  } catch (e) {
12664
12741
  spinner.error({ text: "\u{1F575}\uFE0F\u200D\u2642\uFE0F Mobb analysis failed" });
@@ -12885,7 +12962,7 @@ async function postIssueComment(params) {
12885
12962
  fpDescription
12886
12963
  } = params;
12887
12964
  const {
12888
- path: path24,
12965
+ path: path25,
12889
12966
  startLine,
12890
12967
  vulnerabilityReportIssue: {
12891
12968
  vulnerabilityReportIssueTags,
@@ -12900,7 +12977,7 @@ async function postIssueComment(params) {
12900
12977
  Refresh the page in order to see the changes.`,
12901
12978
  pull_number: pullRequest,
12902
12979
  commit_id: commitSha,
12903
- path: path24,
12980
+ path: path25,
12904
12981
  line: startLine
12905
12982
  });
12906
12983
  const commentId = commentRes.data.id;
@@ -12934,7 +13011,7 @@ async function postFixComment(params) {
12934
13011
  scanner
12935
13012
  } = params;
12936
13013
  const {
12937
- path: path24,
13014
+ path: path25,
12938
13015
  startLine,
12939
13016
  vulnerabilityReportIssue: { fixId, vulnerabilityReportIssueTags, category },
12940
13017
  vulnerabilityReportIssueId
@@ -12952,7 +13029,7 @@ async function postFixComment(params) {
12952
13029
  Refresh the page in order to see the changes.`,
12953
13030
  pull_number: pullRequest,
12954
13031
  commit_id: commitSha,
12955
- path: path24,
13032
+ path: path25,
12956
13033
  line: startLine
12957
13034
  });
12958
13035
  const commentId = commentRes.data.id;
@@ -13196,44 +13273,57 @@ async function handleAutoPr(params) {
13196
13273
  commitDirectly,
13197
13274
  prId,
13198
13275
  createSpinner: createSpinner5,
13199
- createOnePr
13276
+ createOnePr,
13277
+ polling
13200
13278
  } = params;
13201
13279
  const createAutoPrSpinner = createSpinner5(
13202
13280
  "\u{1F504} Waiting for the analysis to finish before initiating automatic pull request creation"
13203
13281
  ).start();
13204
- return await gqlClient.subscribeToAnalysis({
13205
- subscribeToAnalysisParams: {
13206
- analysisId
13207
- },
13208
- callback: async (analysisId2) => {
13209
- const autoPrAnalysisRes = await gqlClient.autoPrAnalysis({
13210
- analysisId: analysisId2,
13211
- commitDirectly,
13212
- prId,
13213
- prStrategy: createOnePr ? "CONDENSE" /* Condense */ : "SPREAD" /* Spread */
13282
+ const callback = async (analysisId2) => {
13283
+ const autoPrAnalysisRes = await gqlClient.autoPrAnalysis({
13284
+ analysisId: analysisId2,
13285
+ commitDirectly,
13286
+ prId,
13287
+ prStrategy: createOnePr ? "CONDENSE" /* Condense */ : "SPREAD" /* Spread */
13288
+ });
13289
+ debug12("auto pr analysis res %o", autoPrAnalysisRes);
13290
+ if (autoPrAnalysisRes.autoPrAnalysis?.__typename === "AutoPrError") {
13291
+ createAutoPrSpinner.error({
13292
+ text: `\u{1F504} Automatic pull request failed - ${autoPrAnalysisRes.autoPrAnalysis.error}`
13214
13293
  });
13215
- debug12("auto pr analysis res %o", autoPrAnalysisRes);
13216
- if (autoPrAnalysisRes.autoPrAnalysis?.__typename === "AutoPrError") {
13217
- createAutoPrSpinner.error({
13218
- text: `\u{1F504} Automatic pull request failed - ${autoPrAnalysisRes.autoPrAnalysis.error}`
13219
- });
13220
- return;
13221
- }
13222
- if (autoPrAnalysisRes.autoPrAnalysis?.__typename === "AutoPrSuccess") {
13223
- const { appliedAutoPrIssueTypes } = autoPrAnalysisRes.autoPrAnalysis;
13224
- if (appliedAutoPrIssueTypes.length === 0) {
13225
- createAutoPrSpinner.success({
13226
- text: "\u{1F504} Automatic pull request did not find any new fixes to open a pull request for"
13227
- });
13228
- return;
13229
- }
13294
+ return;
13295
+ }
13296
+ if (autoPrAnalysisRes.autoPrAnalysis?.__typename === "AutoPrSuccess") {
13297
+ const { appliedAutoPrIssueTypes } = autoPrAnalysisRes.autoPrAnalysis;
13298
+ if (appliedAutoPrIssueTypes.length === 0) {
13230
13299
  createAutoPrSpinner.success({
13231
- text: `\u{1F504} Automatic pull request creation initiated successfully for the following issue types: ${appliedAutoPrIssueTypes}`
13300
+ text: "\u{1F504} Automatic pull request did not find any new fixes to open a pull request for"
13232
13301
  });
13302
+ return;
13233
13303
  }
13234
- },
13235
- callbackStates: ["Finished" /* Finished */]
13236
- });
13304
+ createAutoPrSpinner.success({
13305
+ text: `\u{1F504} Automatic pull request creation initiated successfully for the following issue types: ${appliedAutoPrIssueTypes}`
13306
+ });
13307
+ }
13308
+ };
13309
+ const callbackStates = ["Finished" /* Finished */];
13310
+ if (polling) {
13311
+ debug12("[handleAutoPr] Using POLLING mode for analysis state updates");
13312
+ return await gqlClient.pollForAnalysisState({
13313
+ analysisId,
13314
+ callback,
13315
+ callbackStates
13316
+ });
13317
+ } else {
13318
+ debug12("[handleAutoPr] Using WEBSOCKET mode for analysis state updates");
13319
+ return await gqlClient.subscribeToAnalysis({
13320
+ subscribeToAnalysisParams: {
13321
+ analysisId
13322
+ },
13323
+ callback,
13324
+ callbackStates
13325
+ });
13326
+ }
13237
13327
  }
13238
13328
 
13239
13329
  // src/features/analysis/git.ts
@@ -13542,8 +13632,8 @@ if (typeof __filename !== "undefined") {
13542
13632
  }
13543
13633
  var costumeRequire = createRequire(moduleUrl);
13544
13634
  var getCheckmarxPath = () => {
13545
- const os13 = type();
13546
- const cxFileName = os13 === "Windows_NT" ? "cx.exe" : "cx";
13635
+ const os14 = type();
13636
+ const cxFileName = os14 === "Windows_NT" ? "cx.exe" : "cx";
13547
13637
  try {
13548
13638
  return costumeRequire.resolve(`.bin/${cxFileName}`);
13549
13639
  } catch (e) {
@@ -13986,7 +14076,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
13986
14076
  autoPr,
13987
14077
  createOnePr,
13988
14078
  commitDirectly,
13989
- pullRequest
14079
+ pullRequest,
14080
+ polling
13990
14081
  } = params;
13991
14082
  debug18("start %s %s", dirname, repo);
13992
14083
  const { createSpinner: createSpinner5 } = Spinner2({ ci });
@@ -14098,7 +14189,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
14098
14189
  repoUrl: repo,
14099
14190
  sha,
14100
14191
  reference,
14101
- shouldScan
14192
+ shouldScan,
14193
+ polling
14102
14194
  });
14103
14195
  uploadReportSpinner.success({ text: "\u{1F4C1} Report uploaded successfully" });
14104
14196
  const mobbSpinner = createSpinner5("\u{1F575}\uFE0F\u200D\u2642\uFE0F Initiating Mobb analysis").start();
@@ -14116,7 +14208,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
14116
14208
  pullRequest: params.pullRequest,
14117
14209
  scanSource: _getScanSource(command, ci),
14118
14210
  scanContext: ScanContext.BUGSY
14119
- }
14211
+ },
14212
+ polling
14120
14213
  });
14121
14214
  if (sendReportRes.submitVulnerabilityReport.__typename !== "VulnerabilityReport") {
14122
14215
  mobbSpinner.error({ text: "\u{1F575}\uFE0F\u200D\u2642\uFE0F Mobb analysis failed" });
@@ -14132,7 +14225,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
14132
14225
  commitDirectly,
14133
14226
  prId: pullRequest,
14134
14227
  createSpinner: createSpinner5,
14135
- createOnePr
14228
+ createOnePr,
14229
+ polling
14136
14230
  });
14137
14231
  }
14138
14232
  await askToOpenAnalysis();
@@ -14142,7 +14236,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
14142
14236
  githubActionToken,
14143
14237
  analysisId: reportUploadInfo.fixReportId,
14144
14238
  scanner,
14145
- gqlClient
14239
+ gqlClient,
14240
+ polling
14146
14241
  });
14147
14242
  }
14148
14243
  return reportUploadInfo.fixReportId;
@@ -14237,7 +14332,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
14237
14332
  projectId,
14238
14333
  command,
14239
14334
  ci,
14240
- shouldScan: shouldScan2
14335
+ shouldScan: shouldScan2,
14336
+ polling
14241
14337
  });
14242
14338
  const res = await _zipAndUploadRepo({
14243
14339
  srcPath,
@@ -14260,7 +14356,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
14260
14356
  projectId,
14261
14357
  command,
14262
14358
  ci,
14263
- shouldScan: shouldScan2
14359
+ shouldScan: shouldScan2,
14360
+ polling
14264
14361
  });
14265
14362
  }
14266
14363
  const mobbSpinner2 = createSpinner5("\u{1F575}\uFE0F\u200D\u2642\uFE0F Initiating Mobb analysis").start();
@@ -14278,7 +14375,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
14278
14375
  pullRequest: params.pullRequest,
14279
14376
  experimentalEnabled: !!experimentalEnabled,
14280
14377
  scanContext: ScanContext.BUGSY
14281
- }
14378
+ },
14379
+ polling
14282
14380
  });
14283
14381
  if (command === "review") {
14284
14382
  await waitForAnaysisAndReviewPr({
@@ -14286,7 +14384,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
14286
14384
  githubActionToken,
14287
14385
  analysisId: reportUploadInfo.fixReportId,
14288
14386
  scanner,
14289
- gqlClient
14387
+ gqlClient,
14388
+ polling
14290
14389
  });
14291
14390
  }
14292
14391
  } catch (e) {
@@ -14303,7 +14402,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
14303
14402
  commitDirectly,
14304
14403
  prId: pullRequest,
14305
14404
  createSpinner: createSpinner5,
14306
- createOnePr
14405
+ createOnePr,
14406
+ polling
14307
14407
  });
14308
14408
  }
14309
14409
  await askToOpenAnalysis();
@@ -14351,7 +14451,8 @@ async function _digestReport({
14351
14451
  repoUrl,
14352
14452
  sha,
14353
14453
  reference,
14354
- shouldScan
14454
+ shouldScan,
14455
+ polling
14355
14456
  }) {
14356
14457
  const digestSpinner = createSpinner4(
14357
14458
  progressMassages.processingVulnerabilityReport
@@ -14368,19 +14469,46 @@ async function _digestReport({
14368
14469
  shouldScan
14369
14470
  }
14370
14471
  );
14371
- await gqlClient.subscribeToAnalysis({
14372
- subscribeToAnalysisParams: {
14373
- analysisId: fixReportId
14374
- },
14375
- callback: () => digestSpinner.update({
14376
- text: progressMassages.processingVulnerabilityReportSuccess
14377
- }),
14378
- callbackStates: [
14379
- "Digested" /* Digested */,
14380
- "Finished" /* Finished */
14381
- ],
14382
- timeoutInMs: VUL_REPORT_DIGEST_TIMEOUT_MS
14472
+ const callbackStates = [
14473
+ "Digested" /* Digested */,
14474
+ "Finished" /* Finished */
14475
+ ];
14476
+ const callback = (_analysisId) => digestSpinner.update({
14477
+ text: progressMassages.processingVulnerabilityReportSuccess
14383
14478
  });
14479
+ if (polling) {
14480
+ debug18(
14481
+ "[_digestReport] Using POLLING mode for analysis state updates (--polling flag enabled)"
14482
+ );
14483
+ console.log(
14484
+ chalk6.cyan(
14485
+ "\u{1F504} [Polling Mode] Using HTTP polling instead of WebSocket for status updates"
14486
+ )
14487
+ );
14488
+ await gqlClient.pollForAnalysisState({
14489
+ analysisId: fixReportId,
14490
+ callback,
14491
+ callbackStates,
14492
+ timeoutInMs: VUL_REPORT_DIGEST_TIMEOUT_MS
14493
+ });
14494
+ } else {
14495
+ debug18(
14496
+ "[_digestReport] Using WEBSOCKET mode for analysis state updates (default)"
14497
+ );
14498
+ console.log(
14499
+ chalk6.cyan(
14500
+ "\u{1F50C} [WebSocket Mode] Using WebSocket subscription for status updates"
14501
+ )
14502
+ );
14503
+ await gqlClient.subscribeToAnalysis({
14504
+ subscribeToAnalysisParams: {
14505
+ analysisId: fixReportId
14506
+ },
14507
+ callback,
14508
+ callbackStates,
14509
+ timeoutInMs: VUL_REPORT_DIGEST_TIMEOUT_MS
14510
+ });
14511
+ }
14384
14512
  const vulnFiles = await gqlClient.getVulnerabilityReportPaths(
14385
14513
  vulnerabilityReportId
14386
14514
  );
@@ -14401,7 +14529,8 @@ async function waitForAnaysisAndReviewPr({
14401
14529
  githubActionToken,
14402
14530
  analysisId,
14403
14531
  scanner,
14404
- gqlClient
14532
+ gqlClient,
14533
+ polling
14405
14534
  }) {
14406
14535
  const params = z29.object({
14407
14536
  repo: z29.string().url(),
@@ -14418,20 +14547,45 @@ async function waitForAnaysisAndReviewPr({
14418
14547
  propagateExceptions: true
14419
14548
  }
14420
14549
  );
14421
- await gqlClient.subscribeToAnalysis({
14422
- subscribeToAnalysisParams: {
14423
- analysisId
14424
- },
14425
- callback: (analysisId2) => {
14426
- return addFixCommentsForPr({
14427
- analysisId: analysisId2,
14428
- gqlClient,
14429
- scm,
14430
- scanner: z29.nativeEnum(SCANNERS).parse(scanner)
14431
- });
14432
- },
14433
- callbackStates: ["Finished" /* Finished */]
14434
- });
14550
+ const callback = (analysisId2) => {
14551
+ return addFixCommentsForPr({
14552
+ analysisId: analysisId2,
14553
+ gqlClient,
14554
+ scm,
14555
+ scanner: z29.nativeEnum(SCANNERS).parse(scanner)
14556
+ });
14557
+ };
14558
+ if (polling) {
14559
+ debug18(
14560
+ "[waitForAnaysisAndReviewPr] Using POLLING mode for analysis state updates"
14561
+ );
14562
+ console.log(
14563
+ chalk6.cyan(
14564
+ "\u{1F504} [Polling Mode] Waiting for analysis completion using HTTP polling"
14565
+ )
14566
+ );
14567
+ await gqlClient.pollForAnalysisState({
14568
+ analysisId,
14569
+ callback,
14570
+ callbackStates: ["Finished" /* Finished */]
14571
+ });
14572
+ } else {
14573
+ debug18(
14574
+ "[waitForAnaysisAndReviewPr] Using WEBSOCKET mode for analysis state updates"
14575
+ );
14576
+ console.log(
14577
+ chalk6.cyan(
14578
+ "\u{1F50C} [WebSocket Mode] Waiting for analysis completion using WebSocket"
14579
+ )
14580
+ );
14581
+ await gqlClient.subscribeToAnalysis({
14582
+ subscribeToAnalysisParams: {
14583
+ analysisId
14584
+ },
14585
+ callback,
14586
+ callbackStates: ["Finished" /* Finished */]
14587
+ });
14588
+ }
14435
14589
  }
14436
14590
 
14437
14591
  // src/commands/index.ts
@@ -14446,7 +14600,8 @@ async function review(params, { skipPrompts = true } = {}) {
14446
14600
  pullRequest,
14447
14601
  githubToken,
14448
14602
  scanner,
14449
- srcPath
14603
+ srcPath,
14604
+ polling
14450
14605
  } = params;
14451
14606
  await runAnalysis(
14452
14607
  {
@@ -14462,7 +14617,8 @@ async function review(params, { skipPrompts = true } = {}) {
14462
14617
  githubToken,
14463
14618
  scanner,
14464
14619
  command: "review",
14465
- srcPath
14620
+ srcPath,
14621
+ polling
14466
14622
  },
14467
14623
  { skipPrompts }
14468
14624
  );
@@ -14480,7 +14636,8 @@ async function analyze({
14480
14636
  autoPr,
14481
14637
  createOnePr,
14482
14638
  commitDirectly,
14483
- pullRequest
14639
+ pullRequest,
14640
+ polling
14484
14641
  }, { skipPrompts = false } = {}) {
14485
14642
  !ci && await showWelcomeMessage(skipPrompts);
14486
14643
  await runAnalysis(
@@ -14498,7 +14655,8 @@ async function analyze({
14498
14655
  autoPr,
14499
14656
  commitDirectly,
14500
14657
  pullRequest,
14501
- createOnePr
14658
+ createOnePr,
14659
+ polling
14502
14660
  },
14503
14661
  { skipPrompts }
14504
14662
  );
@@ -14638,7 +14796,7 @@ function analyzeBuilder(yargs2) {
14638
14796
  describe: chalk8.bold("Number of the pull request"),
14639
14797
  type: "number",
14640
14798
  demandOption: false
14641
- }).example(
14799
+ }).option("polling", pollingOption).example(
14642
14800
  "npx mobbdev@latest analyze -r https://github.com/WebGoat/WebGoat -f <your_vulnerability_report_path>",
14643
14801
  "analyze an existing repository"
14644
14802
  ).help();
@@ -14947,7 +15105,14 @@ var defaultLogger2 = {
14947
15105
  }
14948
15106
  };
14949
15107
  var PromptItemZ = z31.object({
14950
- type: z31.enum(["USER_PROMPT", "AI_RESPONSE", "TOOL_EXECUTION", "AI_THINKING"]),
15108
+ type: z31.enum([
15109
+ "USER_PROMPT",
15110
+ "AI_RESPONSE",
15111
+ "TOOL_EXECUTION",
15112
+ "AI_THINKING",
15113
+ "MCP_TOOL_CALL"
15114
+ // MCP (Model Context Protocol) tool invocation
15115
+ ]),
14951
15116
  attachedFiles: z31.array(
14952
15117
  z31.object({
14953
15118
  relativePath: z31.string(),
@@ -14965,7 +15130,12 @@ var PromptItemZ = z31.object({
14965
15130
  parameters: z31.string(),
14966
15131
  result: z31.string(),
14967
15132
  rawArguments: z31.string().optional(),
14968
- accepted: z31.boolean().optional()
15133
+ accepted: z31.boolean().optional(),
15134
+ // MCP-specific fields (only populated for MCP_TOOL_CALL type)
15135
+ mcpServer: z31.string().optional(),
15136
+ // MCP server name (e.g., "datadog", "mobb-mcp")
15137
+ mcpToolName: z31.string().optional()
15138
+ // MCP tool name without prefix (e.g., "scan_and_fix_vulnerabilities")
14969
15139
  }).optional()
14970
15140
  });
14971
15141
  var PromptItemArrayZ = z31.array(PromptItemZ);
@@ -15698,8 +15868,8 @@ var WorkspaceService = class {
15698
15868
  * Sets a known workspace path that was discovered through successful validation
15699
15869
  * @param path The validated workspace path to store
15700
15870
  */
15701
- static setKnownWorkspacePath(path24) {
15702
- this.knownWorkspacePath = path24;
15871
+ static setKnownWorkspacePath(path25) {
15872
+ this.knownWorkspacePath = path25;
15703
15873
  }
15704
15874
  /**
15705
15875
  * Gets the known workspace path that was previously validated
@@ -16972,10 +17142,10 @@ var getHostInfo = (additionalMcpList) => {
16972
17142
  const ideConfigPaths = /* @__PURE__ */ new Set();
16973
17143
  for (const ide of IDEs) {
16974
17144
  const configPaths = getMCPConfigPaths(ide);
16975
- configPaths.forEach((path24) => ideConfigPaths.add(path24));
17145
+ configPaths.forEach((path25) => ideConfigPaths.add(path25));
16976
17146
  }
16977
17147
  const uniqueAdditionalPaths = additionalMcpList.filter(
16978
- (path24) => !ideConfigPaths.has(path24)
17148
+ (path25) => !ideConfigPaths.has(path25)
16979
17149
  );
16980
17150
  for (const ide of IDEs) {
16981
17151
  const cfg = readMCPConfig(ide);
@@ -20268,7 +20438,7 @@ init_configs();
20268
20438
  import fs19 from "fs/promises";
20269
20439
  import nodePath from "path";
20270
20440
  var getLocalFiles = async ({
20271
- path: path24,
20441
+ path: path25,
20272
20442
  maxFileSize = MCP_MAX_FILE_SIZE,
20273
20443
  maxFiles,
20274
20444
  isAllFilesScan,
@@ -20276,17 +20446,17 @@ var getLocalFiles = async ({
20276
20446
  scanRecentlyChangedFiles
20277
20447
  }) => {
20278
20448
  logDebug(`[${scanContext}] Starting getLocalFiles`, {
20279
- path: path24,
20449
+ path: path25,
20280
20450
  maxFileSize,
20281
20451
  maxFiles,
20282
20452
  isAllFilesScan,
20283
20453
  scanRecentlyChangedFiles
20284
20454
  });
20285
20455
  try {
20286
- const resolvedRepoPath = await fs19.realpath(path24);
20456
+ const resolvedRepoPath = await fs19.realpath(path25);
20287
20457
  logDebug(`[${scanContext}] Resolved repository path`, {
20288
20458
  resolvedRepoPath,
20289
- originalPath: path24
20459
+ originalPath: path25
20290
20460
  });
20291
20461
  const gitService = new GitService(resolvedRepoPath, log);
20292
20462
  const gitValidation = await gitService.validateRepository();
@@ -20299,7 +20469,7 @@ var getLocalFiles = async ({
20299
20469
  if (!gitValidation.isValid || isAllFilesScan) {
20300
20470
  try {
20301
20471
  files = await FileUtils.getLastChangedFiles({
20302
- dir: path24,
20472
+ dir: path25,
20303
20473
  maxFileSize,
20304
20474
  maxFiles,
20305
20475
  isAllFilesScan
@@ -20391,7 +20561,7 @@ var getLocalFiles = async ({
20391
20561
  logError(`${scanContext}Unexpected error in getLocalFiles`, {
20392
20562
  error: error instanceof Error ? error.message : String(error),
20393
20563
  stack: error instanceof Error ? error.stack : void 0,
20394
- path: path24
20564
+ path: path25
20395
20565
  });
20396
20566
  throw error;
20397
20567
  }
@@ -22539,14 +22709,14 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
22539
22709
  * since the last scan.
22540
22710
  */
22541
22711
  async scanForSecurityVulnerabilities({
22542
- path: path24,
22712
+ path: path25,
22543
22713
  isAllDetectionRulesScan,
22544
22714
  isAllFilesScan,
22545
22715
  scanContext
22546
22716
  }) {
22547
22717
  this.hasAuthenticationFailed = false;
22548
22718
  logDebug(`[${scanContext}] Scanning for new security vulnerabilities`, {
22549
- path: path24
22719
+ path: path25
22550
22720
  });
22551
22721
  if (!this.gqlClient) {
22552
22722
  logInfo(`[${scanContext}] No GQL client found, skipping scan`);
@@ -22562,11 +22732,11 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
22562
22732
  }
22563
22733
  logDebug(
22564
22734
  `[${scanContext}] Connected to the API, assembling list of files to scan`,
22565
- { path: path24 }
22735
+ { path: path25 }
22566
22736
  );
22567
22737
  const isBackgroundScan = scanContext === ScanContext.BACKGROUND_INITIAL || scanContext === ScanContext.BACKGROUND_PERIODIC;
22568
22738
  const files = await getLocalFiles({
22569
- path: path24,
22739
+ path: path25,
22570
22740
  isAllFilesScan,
22571
22741
  scanContext,
22572
22742
  scanRecentlyChangedFiles: !isBackgroundScan
@@ -22592,13 +22762,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
22592
22762
  });
22593
22763
  const { fixReportId, projectId } = await scanFiles({
22594
22764
  fileList: filesToScan.map((file) => file.relativePath),
22595
- repositoryPath: path24,
22765
+ repositoryPath: path25,
22596
22766
  gqlClient: this.gqlClient,
22597
22767
  isAllDetectionRulesScan,
22598
22768
  scanContext
22599
22769
  });
22600
22770
  logInfo(
22601
- `[${scanContext}] Security scan completed for ${path24} reportId: ${fixReportId} projectId: ${projectId}`
22771
+ `[${scanContext}] Security scan completed for ${path25} reportId: ${fixReportId} projectId: ${projectId}`
22602
22772
  );
22603
22773
  if (isAllFilesScan) {
22604
22774
  return;
@@ -22892,13 +23062,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
22892
23062
  });
22893
23063
  return scannedFiles.some((file) => file.relativePath === fixFile);
22894
23064
  }
22895
- async getFreshFixes({ path: path24 }) {
23065
+ async getFreshFixes({ path: path25 }) {
22896
23066
  const scanContext = ScanContext.USER_REQUEST;
22897
- logDebug(`[${scanContext}] Getting fresh fixes`, { path: path24 });
22898
- if (this.path !== path24) {
22899
- this.path = path24;
23067
+ logDebug(`[${scanContext}] Getting fresh fixes`, { path: path25 });
23068
+ if (this.path !== path25) {
23069
+ this.path = path25;
22900
23070
  this.reset();
22901
- logInfo(`[${scanContext}] Reset service state for new path`, { path: path24 });
23071
+ logInfo(`[${scanContext}] Reset service state for new path`, { path: path25 });
22902
23072
  }
22903
23073
  try {
22904
23074
  const loginContext = createMcpLoginContext("check_new_fixes");
@@ -22917,7 +23087,7 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
22917
23087
  }
22918
23088
  throw error;
22919
23089
  }
22920
- this.triggerScan({ path: path24, gqlClient: this.gqlClient });
23090
+ this.triggerScan({ path: path25, gqlClient: this.gqlClient });
22921
23091
  let isMvsAutoFixEnabled = null;
22922
23092
  try {
22923
23093
  isMvsAutoFixEnabled = await this.gqlClient.getMvsAutoFixSettings();
@@ -22951,33 +23121,33 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
22951
23121
  return noFreshFixesPrompt;
22952
23122
  }
22953
23123
  triggerScan({
22954
- path: path24,
23124
+ path: path25,
22955
23125
  gqlClient
22956
23126
  }) {
22957
- if (this.path !== path24) {
22958
- this.path = path24;
23127
+ if (this.path !== path25) {
23128
+ this.path = path25;
22959
23129
  this.reset();
22960
- logInfo(`Reset service state for new path in triggerScan`, { path: path24 });
23130
+ logInfo(`Reset service state for new path in triggerScan`, { path: path25 });
22961
23131
  }
22962
23132
  this.gqlClient = gqlClient;
22963
23133
  if (!this.intervalId) {
22964
- this.startPeriodicScanning(path24);
22965
- this.executeInitialScan(path24);
22966
- void this.executeInitialFullScan(path24);
23134
+ this.startPeriodicScanning(path25);
23135
+ this.executeInitialScan(path25);
23136
+ void this.executeInitialFullScan(path25);
22967
23137
  }
22968
23138
  }
22969
- startPeriodicScanning(path24) {
23139
+ startPeriodicScanning(path25) {
22970
23140
  const scanContext = ScanContext.BACKGROUND_PERIODIC;
22971
23141
  logDebug(
22972
23142
  `[${scanContext}] Starting periodic scan for new security vulnerabilities`,
22973
23143
  {
22974
- path: path24
23144
+ path: path25
22975
23145
  }
22976
23146
  );
22977
23147
  this.intervalId = setInterval(() => {
22978
- logDebug(`[${scanContext}] Triggering periodic security scan`, { path: path24 });
23148
+ logDebug(`[${scanContext}] Triggering periodic security scan`, { path: path25 });
22979
23149
  this.scanForSecurityVulnerabilities({
22980
- path: path24,
23150
+ path: path25,
22981
23151
  scanContext
22982
23152
  }).catch((error) => {
22983
23153
  logError(`[${scanContext}] Error during periodic security scan`, {
@@ -22986,45 +23156,45 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
22986
23156
  });
22987
23157
  }, MCP_PERIODIC_CHECK_INTERVAL);
22988
23158
  }
22989
- async executeInitialFullScan(path24) {
23159
+ async executeInitialFullScan(path25) {
22990
23160
  const scanContext = ScanContext.FULL_SCAN;
22991
- logDebug(`[${scanContext}] Triggering initial full security scan`, { path: path24 });
23161
+ logDebug(`[${scanContext}] Triggering initial full security scan`, { path: path25 });
22992
23162
  logDebug(`[${scanContext}] Full scan paths scanned`, {
22993
23163
  fullScanPathsScanned: this.fullScanPathsScanned
22994
23164
  });
22995
- if (this.fullScanPathsScanned.includes(path24)) {
23165
+ if (this.fullScanPathsScanned.includes(path25)) {
22996
23166
  logDebug(`[${scanContext}] Full scan already executed for this path`, {
22997
- path: path24
23167
+ path: path25
22998
23168
  });
22999
23169
  return;
23000
23170
  }
23001
23171
  configStore.set("fullScanPathsScanned", [
23002
23172
  ...this.fullScanPathsScanned,
23003
- path24
23173
+ path25
23004
23174
  ]);
23005
23175
  try {
23006
23176
  await this.scanForSecurityVulnerabilities({
23007
- path: path24,
23177
+ path: path25,
23008
23178
  isAllFilesScan: true,
23009
23179
  isAllDetectionRulesScan: true,
23010
23180
  scanContext: ScanContext.FULL_SCAN
23011
23181
  });
23012
- if (!this.fullScanPathsScanned.includes(path24)) {
23013
- this.fullScanPathsScanned.push(path24);
23182
+ if (!this.fullScanPathsScanned.includes(path25)) {
23183
+ this.fullScanPathsScanned.push(path25);
23014
23184
  configStore.set("fullScanPathsScanned", this.fullScanPathsScanned);
23015
23185
  }
23016
- logInfo(`[${scanContext}] Full scan completed`, { path: path24 });
23186
+ logInfo(`[${scanContext}] Full scan completed`, { path: path25 });
23017
23187
  } catch (error) {
23018
23188
  logError(`[${scanContext}] Error during initial full security scan`, {
23019
23189
  error
23020
23190
  });
23021
23191
  }
23022
23192
  }
23023
- executeInitialScan(path24) {
23193
+ executeInitialScan(path25) {
23024
23194
  const scanContext = ScanContext.BACKGROUND_INITIAL;
23025
- logDebug(`[${scanContext}] Triggering initial security scan`, { path: path24 });
23195
+ logDebug(`[${scanContext}] Triggering initial security scan`, { path: path25 });
23026
23196
  this.scanForSecurityVulnerabilities({
23027
- path: path24,
23197
+ path: path25,
23028
23198
  scanContext: ScanContext.BACKGROUND_INITIAL
23029
23199
  }).catch((error) => {
23030
23200
  logError(`[${scanContext}] Error during initial security scan`, { error });
@@ -23121,9 +23291,9 @@ Example payload:
23121
23291
  `Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
23122
23292
  );
23123
23293
  }
23124
- const path24 = pathValidationResult.path;
23294
+ const path25 = pathValidationResult.path;
23125
23295
  const resultText = await this.newFixesService.getFreshFixes({
23126
- path: path24
23296
+ path: path25
23127
23297
  });
23128
23298
  logInfo("CheckForNewAvailableFixesTool execution completed", {
23129
23299
  resultText
@@ -23301,8 +23471,8 @@ Call this tool instead of ${MCP_TOOL_SCAN_AND_FIX_VULNERABILITIES} when you only
23301
23471
  `Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
23302
23472
  );
23303
23473
  }
23304
- const path24 = pathValidationResult.path;
23305
- const gitService = new GitService(path24, log);
23474
+ const path25 = pathValidationResult.path;
23475
+ const gitService = new GitService(path25, log);
23306
23476
  const gitValidation = await gitService.validateRepository();
23307
23477
  if (!gitValidation.isValid) {
23308
23478
  throw new Error(`Invalid git repository: ${gitValidation.error}`);
@@ -23687,9 +23857,9 @@ Example payload:
23687
23857
  `Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
23688
23858
  );
23689
23859
  }
23690
- const path24 = pathValidationResult.path;
23860
+ const path25 = pathValidationResult.path;
23691
23861
  const files = await getLocalFiles({
23692
- path: path24,
23862
+ path: path25,
23693
23863
  maxFileSize: MCP_MAX_FILE_SIZE,
23694
23864
  maxFiles: args.maxFiles,
23695
23865
  scanContext: ScanContext.USER_REQUEST,
@@ -23709,7 +23879,7 @@ Example payload:
23709
23879
  try {
23710
23880
  const fixResult = await this.vulnerabilityFixService.processVulnerabilities({
23711
23881
  fileList: files.map((file) => file.relativePath),
23712
- repositoryPath: path24,
23882
+ repositoryPath: path25,
23713
23883
  offset: args.offset,
23714
23884
  limit: args.limit,
23715
23885
  isRescan: args.rescan || !!args.maxFiles
@@ -23844,7 +24014,7 @@ function reviewBuilder(yargs2) {
23844
24014
  ),
23845
24015
  type: "string",
23846
24016
  demandOption: false
23847
- }).example(
24017
+ }).option("polling", pollingOption).example(
23848
24018
  "npx mobbdev@latest review -r https://github.com/WebGoat/WebGoat -f <your_vulnerability_report_path> --ch <pr_last_commit> --pr <pr_number> --ref <pr_branch_name> --api-key <api_key> --src-path <your_repo_path>",
23849
24019
  "add fixes to your pr"
23850
24020
  ).help();
@@ -23864,7 +24034,7 @@ async function reviewHandler(args) {
23864
24034
 
23865
24035
  // src/args/commands/scan.ts
23866
24036
  function scanBuilder(args) {
23867
- return args.coerce("scanner", (arg) => arg.toLowerCase()).option("repo", repoOption).option("ref", refOption).option("scanner", scannerOptions).option("org", organizationIdOptions).option("mobb-project-name", mobbProjectNameOption).option("y", yesOption).option("ci", ciOption).option("api-key", apiKeyOption).option("cx-project-name", projectNameOption).option("auto-pr", autoPrOption).example(
24037
+ return args.coerce("scanner", (arg) => arg.toLowerCase()).option("repo", repoOption).option("ref", refOption).option("scanner", scannerOptions).option("org", organizationIdOptions).option("mobb-project-name", mobbProjectNameOption).option("y", yesOption).option("ci", ciOption).option("api-key", apiKeyOption).option("cx-project-name", projectNameOption).option("auto-pr", autoPrOption).option("polling", pollingOption).example(
23868
24038
  "npx mobbdev@latest scan -r https://github.com/WebGoat/WebGoat",
23869
24039
  "Scan an existing repository"
23870
24040
  ).help();
@@ -23922,10 +24092,8 @@ async function addScmTokenHandler(args) {
23922
24092
  await addScmToken(args);
23923
24093
  }
23924
24094
 
23925
- // src/features/codeium_intellij/data_collector_monitor.ts
23926
- import { setTimeout as setTimeout4 } from "timers/promises";
23927
-
23928
24095
  // src/features/codeium_intellij/data_collector.ts
24096
+ import { z as z46 } from "zod";
23929
24097
  init_GitService();
23930
24098
 
23931
24099
  // src/features/codeium_intellij/codeium_language_server_grpc_client.ts
@@ -24065,9 +24233,45 @@ function findRunningCodeiumLanguageServers() {
24065
24233
  }
24066
24234
 
24067
24235
  // src/features/codeium_intellij/data_collector.ts
24068
- async function getLatestInferences(startFrom) {
24236
+ var HookDataSchema2 = z46.object({
24237
+ trajectory_id: z46.string()
24238
+ });
24239
+ async function processAndUploadHookData2() {
24240
+ const tracePayload = await getTraceDataForHook();
24241
+ if (!tracePayload) {
24242
+ console.warn("Warning: Failed to retrieve chat data.");
24243
+ return;
24244
+ }
24245
+ try {
24246
+ const uploadSuccess = await uploadAiBlameHandlerFromExtension({
24247
+ prompts: tracePayload.prompts,
24248
+ inference: tracePayload.inference,
24249
+ model: tracePayload.model,
24250
+ tool: tracePayload.tool,
24251
+ responseTime: tracePayload.responseTime,
24252
+ blameType: "CHAT" /* Chat */,
24253
+ sessionId: tracePayload.sessionId,
24254
+ repositoryUrl: tracePayload.repositoryUrl
24255
+ });
24256
+ if (uploadSuccess) {
24257
+ console.log("Uploaded trace data.");
24258
+ } else {
24259
+ console.warn("Failed to upload trace data.");
24260
+ }
24261
+ } catch (e) {
24262
+ console.warn("Failed to upload trace data:", e);
24263
+ }
24264
+ }
24265
+ function validateHookData2(data) {
24266
+ return HookDataSchema2.parse(data);
24267
+ }
24268
+ async function getTraceDataForHook() {
24269
+ const rawData = await readStdinData();
24270
+ const hookData = validateHookData2(rawData);
24271
+ return await getTraceDataForTrajectory(hookData.trajectory_id);
24272
+ }
24273
+ async function getTraceDataForTrajectory(trajectoryId) {
24069
24274
  const instances = findRunningCodeiumLanguageServers();
24070
- const results = [];
24071
24275
  for (const instance of instances) {
24072
24276
  const client = await getGrpcClient(instance.port, instance.csrf);
24073
24277
  if (!client) {
@@ -24077,20 +24281,13 @@ async function getLatestInferences(startFrom) {
24077
24281
  for (const [cascadeId, chatSummary] of Object.entries(
24078
24282
  chats.trajectorySummaries
24079
24283
  )) {
24080
- const chatLastModifiedSecStr = chatSummary.lastModifiedTime?.seconds;
24081
- if (!chatLastModifiedSecStr) {
24284
+ if (chatSummary.trajectoryId !== trajectoryId) {
24082
24285
  continue;
24083
24286
  }
24084
- const chatLastModifiedSec = Number(chatLastModifiedSecStr);
24085
- if (chatLastModifiedSec >= Math.floor(startFrom.getTime() / 1e3)) {
24086
- const traceData = await processChat(client, cascadeId);
24087
- if (traceData) {
24088
- results.push(traceData);
24089
- }
24090
- }
24287
+ return await processChat(client, cascadeId);
24091
24288
  }
24092
24289
  }
24093
- return results;
24290
+ return null;
24094
24291
  }
24095
24292
  async function processChat(client, cascadeId) {
24096
24293
  const chatDetails = await client.GetCascadeTrajectory({
@@ -24186,7 +24383,11 @@ function processChatStepPlannerResponse(step) {
24186
24383
  }
24187
24384
  function processChatStepCodeAction(step) {
24188
24385
  const inferences = [];
24386
+ const toolCallName = step.metadata?.toolCall?.name;
24189
24387
  const unifiedDiff = step.codeAction?.actionResult?.edit?.diff?.unifiedDiff;
24388
+ if (!toolCallName) {
24389
+ return { prompts: [], inferences };
24390
+ }
24190
24391
  if (!unifiedDiff) {
24191
24392
  return { prompts: [], inferences };
24192
24393
  }
@@ -24198,63 +24399,119 @@ function processChatStepCodeAction(step) {
24198
24399
  return { prompts: [], inferences };
24199
24400
  }
24200
24401
 
24201
- // src/features/codeium_intellij/data_collector_monitor.ts
24202
- var POLL_INTERVAL = 5 * 1e3;
24203
- async function startMonitoring() {
24204
- await getAuthenticatedGQLClient({ isSkipPrompts: true });
24205
- let lastUpdateTs = /* @__PURE__ */ new Date();
24206
- for (; ; ) {
24207
- let tracePayloads = [];
24208
- try {
24209
- const startFrom = lastUpdateTs;
24210
- lastUpdateTs = /* @__PURE__ */ new Date();
24211
- tracePayloads = await getLatestInferences(startFrom);
24212
- } catch (e) {
24213
- console.error("Failed to retrieve Windsurf IntelliJ inferences.", e);
24214
- }
24215
- for (const tracePayload of tracePayloads) {
24216
- await tryUpload(tracePayload);
24217
- }
24218
- await setTimeout4(POLL_INTERVAL);
24219
- }
24402
+ // src/features/codeium_intellij/install_hook.ts
24403
+ import fsPromises6 from "fs/promises";
24404
+ import os13 from "os";
24405
+ import path24 from "path";
24406
+ import chalk12 from "chalk";
24407
+ function getCodeiumHooksPath() {
24408
+ return path24.join(os13.homedir(), ".codeium", "hooks.json");
24220
24409
  }
24221
- async function tryUpload(tracePayload) {
24410
+ async function readCodeiumHooks() {
24411
+ const hooksPath = getCodeiumHooksPath();
24222
24412
  try {
24223
- const uploadSuccess = await uploadAiBlameHandlerFromExtension({
24224
- prompts: tracePayload.prompts,
24225
- inference: tracePayload.inference,
24226
- model: tracePayload.model,
24227
- tool: tracePayload.tool,
24228
- responseTime: tracePayload.responseTime,
24229
- blameType: "CHAT" /* Chat */,
24230
- sessionId: tracePayload.sessionId,
24231
- repositoryUrl: tracePayload.repositoryUrl
24232
- });
24233
- if (uploadSuccess) {
24234
- console.log("Uploaded trace data.");
24235
- } else {
24236
- console.warn("Failed to upload trace data.");
24413
+ const content = await fsPromises6.readFile(hooksPath, "utf-8");
24414
+ return JSON.parse(content);
24415
+ } catch {
24416
+ return {};
24417
+ }
24418
+ }
24419
+ async function writeCodeiumHooks(config2) {
24420
+ const hooksPath = getCodeiumHooksPath();
24421
+ const dir = path24.dirname(hooksPath);
24422
+ await fsPromises6.mkdir(dir, { recursive: true });
24423
+ await fsPromises6.writeFile(
24424
+ hooksPath,
24425
+ JSON.stringify(config2, null, 2),
24426
+ "utf-8"
24427
+ );
24428
+ }
24429
+ async function installWindsurfHooks(options = {}) {
24430
+ const hooksPath = getCodeiumHooksPath();
24431
+ console.log(chalk12.blue("Installing Mobb hooks in Windsurf IntelliJ..."));
24432
+ const config2 = await readCodeiumHooks();
24433
+ if (!config2.hooks) {
24434
+ config2.hooks = {};
24435
+ }
24436
+ if (!config2.hooks.post_write_code) {
24437
+ config2.hooks.post_write_code = [];
24438
+ }
24439
+ let command = "npx --yes mobbdev@latest windsurf-intellij-process-hook";
24440
+ if (options.saveEnv) {
24441
+ const envVars = [];
24442
+ if (process.env["WEB_APP_URL"]) {
24443
+ envVars.push(`WEB_APP_URL="${process.env["WEB_APP_URL"]}"`);
24237
24444
  }
24238
- } catch (e) {
24239
- console.warn("Failed to upload trace data:", e);
24445
+ if (process.env["API_URL"]) {
24446
+ envVars.push(`API_URL="${process.env["API_URL"]}"`);
24447
+ }
24448
+ if (envVars.length > 0) {
24449
+ command = `${envVars.join(" ")} ${command}`;
24450
+ console.log(
24451
+ chalk12.blue(
24452
+ `Adding environment variables to hook command: ${envVars.join(", ")}`
24453
+ )
24454
+ );
24455
+ }
24456
+ }
24457
+ const mobbHook = {
24458
+ command,
24459
+ show_output: true
24460
+ };
24461
+ const existingHookIndex = config2.hooks.post_write_code.findIndex(
24462
+ (hook) => hook.command?.includes("mobbdev@latest windsurf-intellij-process-hook")
24463
+ );
24464
+ if (existingHookIndex >= 0) {
24465
+ console.log(chalk12.yellow("Mobb hook already exists, updating..."));
24466
+ config2.hooks.post_write_code[existingHookIndex] = mobbHook;
24467
+ } else {
24468
+ console.log(chalk12.green("Adding new Mobb hook..."));
24469
+ config2.hooks.post_write_code.push(mobbHook);
24240
24470
  }
24471
+ await writeCodeiumHooks(config2);
24472
+ console.log(
24473
+ chalk12.green(
24474
+ `\u2705 Mobb hooks ${options.saveEnv ? "and environment variables " : ""}installed successfully in ${hooksPath}`
24475
+ )
24476
+ );
24241
24477
  }
24242
24478
 
24243
24479
  // src/args/commands/windsurf_intellij.ts
24244
- var windsurfIntellijMonitorBuilder = (yargs2) => {
24480
+ var windsurfIntellijInstallHookBuilder = (yargs2) => {
24481
+ return yargs2.option("save-env", {
24482
+ type: "boolean",
24483
+ description: "Save WEB_APP_URL, and API_URL environment variables to hooks config",
24484
+ default: false
24485
+ }).example(
24486
+ "$0 windsurf-intellij-install-hook",
24487
+ "Install Windsurf IntelliJ hooks for data collection"
24488
+ ).example(
24489
+ "$0 windsurf-intellij-install-hook --save-env",
24490
+ "Install hooks and save environment variables to config"
24491
+ ).strict();
24492
+ };
24493
+ var windsurfIntellijProcessHookBuilder = (yargs2) => {
24245
24494
  return yargs2.example(
24246
- "$0 windsurf-intellij-monitor",
24247
- "Start monitoring Windsurf IntelliJ for AI inference data"
24495
+ "$0 windsurf-intellij-process-hook",
24496
+ "Process Windsurf IntelliJ hook data and upload to backend"
24248
24497
  ).strict();
24249
24498
  };
24250
- var windsurfIntellijMonitorHandler = async () => {
24499
+ var windsurfIntellijInstallHookHandler = async (argv) => {
24500
+ try {
24501
+ await getAuthenticatedGQLClient({ isSkipPrompts: false });
24502
+ await installWindsurfHooks({ saveEnv: argv["save-env"] });
24503
+ process.exit(0);
24504
+ } catch (error) {
24505
+ console.error("Failed to install Windsurf IntelliJ hooks:", error);
24506
+ process.exit(1);
24507
+ }
24508
+ };
24509
+ var windsurfIntellijProcessHookHandler = async () => {
24251
24510
  try {
24252
- console.log("Starting Windsurf IntelliJ monitor...");
24253
- console.log("Polling for AI inference data from running IDE instances.");
24254
- console.log("Press Ctrl+C to stop.\n");
24255
- await startMonitoring();
24511
+ await processAndUploadHookData2();
24512
+ process.exit(0);
24256
24513
  } catch (error) {
24257
- console.error("Windsurf IntelliJ monitor failed:", error);
24514
+ console.error("Failed to process Windsurf IntelliJ hook data:", error);
24258
24515
  process.exit(1);
24259
24516
  }
24260
24517
  };
@@ -24263,78 +24520,83 @@ var windsurfIntellijMonitorHandler = async () => {
24263
24520
  var parseArgs = async (args) => {
24264
24521
  const yargsInstance = yargs(args);
24265
24522
  return yargsInstance.updateStrings({
24266
- "Commands:": chalk12.yellow.underline.bold("Commands:"),
24267
- "Options:": chalk12.yellow.underline.bold("Options:"),
24268
- "Examples:": chalk12.yellow.underline.bold("Examples:"),
24269
- "Show help": chalk12.bold("Show help")
24523
+ "Commands:": chalk13.yellow.underline.bold("Commands:"),
24524
+ "Options:": chalk13.yellow.underline.bold("Options:"),
24525
+ "Examples:": chalk13.yellow.underline.bold("Examples:"),
24526
+ "Show help": chalk13.bold("Show help")
24270
24527
  }).usage(
24271
- `${chalk12.bold(
24528
+ `${chalk13.bold(
24272
24529
  "\n Bugsy - Trusted, Automatic Vulnerability Fixer \u{1F575}\uFE0F\u200D\u2642\uFE0F\n\n"
24273
- )} ${chalk12.yellow.underline.bold("Usage:")}
24274
- $0 ${chalk12.green(
24530
+ )} ${chalk13.yellow.underline.bold("Usage:")}
24531
+ $0 ${chalk13.green(
24275
24532
  "<command>"
24276
- )} ${chalk12.dim("[options]")}
24533
+ )} ${chalk13.dim("[options]")}
24277
24534
  `
24278
24535
  ).version(false).command(
24279
24536
  mobbCliCommand.scan,
24280
- chalk12.bold(
24537
+ chalk13.bold(
24281
24538
  "Scan your code for vulnerabilities, get automated fixes right away."
24282
24539
  ),
24283
24540
  scanBuilder,
24284
24541
  scanHandler
24285
24542
  ).command(
24286
24543
  mobbCliCommand.analyze,
24287
- chalk12.bold(
24544
+ chalk13.bold(
24288
24545
  "Provide a code repository, get automated fixes right away. You can also provide a vulnerability report to analyze or have Mobb scan the code for you."
24289
24546
  ),
24290
24547
  analyzeBuilder,
24291
24548
  analyzeHandler
24292
24549
  ).command(
24293
24550
  mobbCliCommand.review,
24294
- chalk12.bold(
24551
+ chalk13.bold(
24295
24552
  "Mobb will review your github pull requests and provide comments with fixes "
24296
24553
  ),
24297
24554
  reviewBuilder,
24298
24555
  reviewHandler
24299
24556
  ).command(
24300
24557
  mobbCliCommand.addScmToken,
24301
- chalk12.bold(
24558
+ chalk13.bold(
24302
24559
  "Add your SCM (Github, Gitlab, Azure DevOps) token to Mobb to enable automated fixes."
24303
24560
  ),
24304
24561
  addScmTokenBuilder,
24305
24562
  addScmTokenHandler
24306
24563
  ).command(
24307
24564
  mobbCliCommand.convertToSarif,
24308
- chalk12.bold("Convert an existing SAST report to SARIF format."),
24565
+ chalk13.bold("Convert an existing SAST report to SARIF format."),
24309
24566
  convertToSarifBuilder,
24310
24567
  convertToSarifHandler
24311
24568
  ).command(
24312
24569
  mobbCliCommand.mcp,
24313
- chalk12.bold("Launch the MCP (Model Context Protocol) server."),
24570
+ chalk13.bold("Launch the MCP (Model Context Protocol) server."),
24314
24571
  mcpBuilder,
24315
24572
  mcpHandler
24316
24573
  ).command(
24317
24574
  mobbCliCommand.uploadAiBlame,
24318
- chalk12.bold(
24575
+ chalk13.bold(
24319
24576
  "Upload AI Blame inference artifacts (prompt + inference) and finalize them."
24320
24577
  ),
24321
24578
  uploadAiBlameBuilder,
24322
24579
  uploadAiBlameCommandHandler
24323
24580
  ).command(
24324
24581
  mobbCliCommand.claudeCodeInstallHook,
24325
- chalk12.bold("Install Claude Code hooks for data collection."),
24582
+ chalk13.bold("Install Claude Code hooks for data collection."),
24326
24583
  claudeCodeInstallHookBuilder,
24327
24584
  claudeCodeInstallHookHandler
24328
24585
  ).command(
24329
24586
  mobbCliCommand.claudeCodeProcessHook,
24330
- chalk12.bold("Process Claude Code hook data and upload to backend."),
24587
+ chalk13.bold("Process Claude Code hook data and upload to backend."),
24331
24588
  claudeCodeProcessHookBuilder,
24332
24589
  claudeCodeProcessHookHandler
24333
24590
  ).command(
24334
- mobbCliCommand.windsurfIntellijMonitor,
24335
- chalk12.bold("Monitor Windsurf IntelliJ for AI inference data."),
24336
- windsurfIntellijMonitorBuilder,
24337
- windsurfIntellijMonitorHandler
24591
+ mobbCliCommand.windsurfIntellijInstallHook,
24592
+ chalk13.bold("Install Windsurf IntelliJ hooks for data collection."),
24593
+ windsurfIntellijInstallHookBuilder,
24594
+ windsurfIntellijInstallHookHandler
24595
+ ).command(
24596
+ mobbCliCommand.windsurfIntellijProcessHook,
24597
+ chalk13.bold("Process Windsurf IntelliJ hook data and upload to backend."),
24598
+ windsurfIntellijProcessHookBuilder,
24599
+ windsurfIntellijProcessHookHandler
24338
24600
  ).example(
24339
24601
  "npx mobbdev@latest scan -r https://github.com/WebGoat/WebGoat",
24340
24602
  "Scan an existing repository"
@@ -24343,7 +24605,7 @@ var parseArgs = async (args) => {
24343
24605
  handler() {
24344
24606
  yargsInstance.showHelp();
24345
24607
  }
24346
- }).strictOptions().help("h").alias("h", "help").epilog(chalk12.bgBlue("Made with \u2764\uFE0F by Mobb")).showHelpOnFail(true).wrap(Math.min(120, yargsInstance.terminalWidth())).parse();
24608
+ }).strictOptions().help("h").alias("h", "help").epilog(chalk13.bgBlue("Made with \u2764\uFE0F by Mobb")).showHelpOnFail(true).wrap(Math.min(120, yargsInstance.terminalWidth())).parse();
24347
24609
  };
24348
24610
 
24349
24611
  // src/index.ts