mobbdev 1.2.1 → 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/README.md CHANGED
@@ -175,6 +175,24 @@ Here is a simple example of a command line that will run Bugsy in your pipeline:
175
175
  npx mobbdev analyze --ci --scan-file $SAST_RESULTS_FILENAME --repo $CI_PROJECT_URL --ref $CI_COMMIT_REF_NAME --api-key $MOBB_API_KEY
176
176
  ```
177
177
 
178
+ ### Polling Mode
179
+
180
+ By default, Bugsy uses WebSocket connections for real-time status updates during analysis. However, some proxy environments or firewalls may block WebSocket connections. In these cases, you can use the `--polling` flag to use HTTP polling instead:
181
+
182
+ ```shell
183
+ npx mobbdev analyze --scan-file report.json --repo https://github.com/org/repo --polling
184
+ ```
185
+
186
+ **When to use `--polling`:**
187
+ - Your network blocks WebSocket connections (ws:// or wss://)
188
+ - You're behind a corporate proxy that doesn't support WebSocket
189
+ - You experience connection timeouts with the default WebSocket mode
190
+
191
+ **Polling characteristics:**
192
+ - Polling interval: 5 seconds
193
+ - Timeout: 30 minutes
194
+ - Slightly higher latency compared to WebSocket mode, but works in restricted environments
195
+
178
196
  ## Contribution
179
197
 
180
198
  Install the dependencies and run the tests:
@@ -21,7 +21,7 @@ type Logger = {
21
21
  error: (msg: string, data?: unknown) => void;
22
22
  };
23
23
  declare const PromptItemZ: z.ZodObject<{
24
- type: z.ZodEnum<["USER_PROMPT", "AI_RESPONSE", "TOOL_EXECUTION", "AI_THINKING"]>;
24
+ type: z.ZodEnum<["USER_PROMPT", "AI_RESPONSE", "TOOL_EXECUTION", "AI_THINKING", "MCP_TOOL_CALL"]>;
25
25
  attachedFiles: z.ZodOptional<z.ZodArray<z.ZodObject<{
26
26
  relativePath: z.ZodString;
27
27
  startLine: z.ZodOptional<z.ZodNumber>;
@@ -50,30 +50,28 @@ declare const PromptItemZ: z.ZodObject<{
50
50
  result: z.ZodString;
51
51
  rawArguments: z.ZodOptional<z.ZodString>;
52
52
  accepted: z.ZodOptional<z.ZodBoolean>;
53
+ mcpServer: z.ZodOptional<z.ZodString>;
54
+ mcpToolName: z.ZodOptional<z.ZodString>;
53
55
  }, "strip", z.ZodTypeAny, {
54
56
  name: string;
55
57
  parameters: string;
56
58
  result: string;
57
- accepted?: boolean | undefined;
58
59
  rawArguments?: string | undefined;
60
+ accepted?: boolean | undefined;
61
+ mcpServer?: string | undefined;
62
+ mcpToolName?: string | undefined;
59
63
  }, {
60
64
  name: string;
61
65
  parameters: string;
62
66
  result: string;
63
- accepted?: boolean | undefined;
64
67
  rawArguments?: string | undefined;
68
+ accepted?: boolean | undefined;
69
+ mcpServer?: string | undefined;
70
+ mcpToolName?: string | undefined;
65
71
  }>>;
66
72
  }, "strip", z.ZodTypeAny, {
67
- type: "USER_PROMPT" | "AI_RESPONSE" | "TOOL_EXECUTION" | "AI_THINKING";
68
- tool?: {
69
- name: string;
70
- parameters: string;
71
- result: string;
72
- accepted?: boolean | undefined;
73
- rawArguments?: string | undefined;
74
- } | undefined;
73
+ type: "USER_PROMPT" | "AI_RESPONSE" | "TOOL_EXECUTION" | "AI_THINKING" | "MCP_TOOL_CALL";
75
74
  date?: Date | undefined;
76
- text?: string | undefined;
77
75
  attachedFiles?: {
78
76
  relativePath: string;
79
77
  startLine?: number | undefined;
@@ -82,17 +80,19 @@ declare const PromptItemZ: z.ZodObject<{
82
80
  inputCount: number;
83
81
  outputCount: number;
84
82
  } | undefined;
85
- }, {
86
- type: "USER_PROMPT" | "AI_RESPONSE" | "TOOL_EXECUTION" | "AI_THINKING";
83
+ text?: string | undefined;
87
84
  tool?: {
88
85
  name: string;
89
86
  parameters: string;
90
87
  result: string;
91
- accepted?: boolean | undefined;
92
88
  rawArguments?: string | undefined;
89
+ accepted?: boolean | undefined;
90
+ mcpServer?: string | undefined;
91
+ mcpToolName?: string | undefined;
93
92
  } | undefined;
93
+ }, {
94
+ type: "USER_PROMPT" | "AI_RESPONSE" | "TOOL_EXECUTION" | "AI_THINKING" | "MCP_TOOL_CALL";
94
95
  date?: Date | undefined;
95
- text?: string | undefined;
96
96
  attachedFiles?: {
97
97
  relativePath: string;
98
98
  startLine?: number | undefined;
@@ -101,10 +101,20 @@ declare const PromptItemZ: z.ZodObject<{
101
101
  inputCount: number;
102
102
  outputCount: number;
103
103
  } | undefined;
104
+ text?: string | undefined;
105
+ tool?: {
106
+ name: string;
107
+ parameters: string;
108
+ result: string;
109
+ rawArguments?: string | undefined;
110
+ accepted?: boolean | undefined;
111
+ mcpServer?: string | undefined;
112
+ mcpToolName?: string | undefined;
113
+ } | undefined;
104
114
  }>;
105
115
  type PromptItem = z.infer<typeof PromptItemZ>;
106
116
  declare const PromptItemArrayZ: z.ZodArray<z.ZodObject<{
107
- type: z.ZodEnum<["USER_PROMPT", "AI_RESPONSE", "TOOL_EXECUTION", "AI_THINKING"]>;
117
+ type: z.ZodEnum<["USER_PROMPT", "AI_RESPONSE", "TOOL_EXECUTION", "AI_THINKING", "MCP_TOOL_CALL"]>;
108
118
  attachedFiles: z.ZodOptional<z.ZodArray<z.ZodObject<{
109
119
  relativePath: z.ZodString;
110
120
  startLine: z.ZodOptional<z.ZodNumber>;
@@ -133,30 +143,28 @@ declare const PromptItemArrayZ: z.ZodArray<z.ZodObject<{
133
143
  result: z.ZodString;
134
144
  rawArguments: z.ZodOptional<z.ZodString>;
135
145
  accepted: z.ZodOptional<z.ZodBoolean>;
146
+ mcpServer: z.ZodOptional<z.ZodString>;
147
+ mcpToolName: z.ZodOptional<z.ZodString>;
136
148
  }, "strip", z.ZodTypeAny, {
137
149
  name: string;
138
150
  parameters: string;
139
151
  result: string;
140
- accepted?: boolean | undefined;
141
152
  rawArguments?: string | undefined;
153
+ accepted?: boolean | undefined;
154
+ mcpServer?: string | undefined;
155
+ mcpToolName?: string | undefined;
142
156
  }, {
143
157
  name: string;
144
158
  parameters: string;
145
159
  result: string;
146
- accepted?: boolean | undefined;
147
160
  rawArguments?: string | undefined;
161
+ accepted?: boolean | undefined;
162
+ mcpServer?: string | undefined;
163
+ mcpToolName?: string | undefined;
148
164
  }>>;
149
165
  }, "strip", z.ZodTypeAny, {
150
- type: "USER_PROMPT" | "AI_RESPONSE" | "TOOL_EXECUTION" | "AI_THINKING";
151
- tool?: {
152
- name: string;
153
- parameters: string;
154
- result: string;
155
- accepted?: boolean | undefined;
156
- rawArguments?: string | undefined;
157
- } | undefined;
166
+ type: "USER_PROMPT" | "AI_RESPONSE" | "TOOL_EXECUTION" | "AI_THINKING" | "MCP_TOOL_CALL";
158
167
  date?: Date | undefined;
159
- text?: string | undefined;
160
168
  attachedFiles?: {
161
169
  relativePath: string;
162
170
  startLine?: number | undefined;
@@ -165,17 +173,19 @@ declare const PromptItemArrayZ: z.ZodArray<z.ZodObject<{
165
173
  inputCount: number;
166
174
  outputCount: number;
167
175
  } | undefined;
168
- }, {
169
- type: "USER_PROMPT" | "AI_RESPONSE" | "TOOL_EXECUTION" | "AI_THINKING";
176
+ text?: string | undefined;
170
177
  tool?: {
171
178
  name: string;
172
179
  parameters: string;
173
180
  result: string;
174
- accepted?: boolean | undefined;
175
181
  rawArguments?: string | undefined;
182
+ accepted?: boolean | undefined;
183
+ mcpServer?: string | undefined;
184
+ mcpToolName?: string | undefined;
176
185
  } | undefined;
186
+ }, {
187
+ type: "USER_PROMPT" | "AI_RESPONSE" | "TOOL_EXECUTION" | "AI_THINKING" | "MCP_TOOL_CALL";
177
188
  date?: Date | undefined;
178
- text?: string | undefined;
179
189
  attachedFiles?: {
180
190
  relativePath: string;
181
191
  startLine?: number | undefined;
@@ -184,6 +194,16 @@ declare const PromptItemArrayZ: z.ZodArray<z.ZodObject<{
184
194
  inputCount: number;
185
195
  outputCount: number;
186
196
  } | undefined;
197
+ text?: string | undefined;
198
+ tool?: {
199
+ name: string;
200
+ parameters: string;
201
+ result: string;
202
+ rawArguments?: string | undefined;
203
+ accepted?: boolean | undefined;
204
+ mcpServer?: string | undefined;
205
+ mcpToolName?: string | undefined;
206
+ } | undefined;
187
207
  }>, "many">;
188
208
  type PromptItemArray = z.infer<typeof PromptItemArrayZ>;
189
209
  type UploadAiBlameOptions = {
@@ -6401,6 +6401,61 @@ var GQLClient = class {
6401
6401
  }
6402
6402
  );
6403
6403
  }
6404
+ async pollForAnalysisState(params) {
6405
+ const { analysisId, callbackStates, callback, timeoutInMs } = params;
6406
+ const startTime = Date.now();
6407
+ const maxDuration = timeoutInMs ?? 30 * 60 * 1e3;
6408
+ const pollingIntervalSec = REPORT_STATE_CHECK_DELAY / 1e3;
6409
+ debug6(
6410
+ `[pollForAnalysisState] Starting polling for analysis ${analysisId}, target states: ${callbackStates.join(", ")}, interval: ${pollingIntervalSec}s`
6411
+ );
6412
+ let isPolling = true;
6413
+ let pollCount = 0;
6414
+ while (isPolling) {
6415
+ pollCount++;
6416
+ const elapsedSec = Math.round((Date.now() - startTime) / 1e3);
6417
+ if (Date.now() - startTime > maxDuration) {
6418
+ debug6(
6419
+ `[pollForAnalysisState] Timeout expired after ${pollCount} polls (${elapsedSec}s)`
6420
+ );
6421
+ throw new ReportDigestError(
6422
+ `Polling timeout expired for analysis: ${analysisId} with timeout: ${maxDuration}ms`,
6423
+ `Analysis timed out after ${Math.round(maxDuration / 6e4)} minutes. Please try again or check the Mobb platform for status.`
6424
+ );
6425
+ }
6426
+ debug6(
6427
+ `[pollForAnalysisState] Poll #${pollCount} (elapsed: ${elapsedSec}s) - fetching analysis state...`
6428
+ );
6429
+ const analysis = await this.getAnalysis(analysisId);
6430
+ debug6(
6431
+ `[pollForAnalysisState] Poll #${pollCount} - current state: ${analysis.state}`
6432
+ );
6433
+ if (!analysis.state || analysis.state === "Failed" /* Failed */) {
6434
+ const errorMessage = analysis.failReason || `Analysis failed with id: ${analysis.id}`;
6435
+ debug6(`[pollForAnalysisState] Analysis failed: ${errorMessage}`);
6436
+ throw new ReportDigestError(errorMessage, analysis.failReason ?? "");
6437
+ }
6438
+ if (callbackStates.includes(analysis.state)) {
6439
+ debug6(
6440
+ `[pollForAnalysisState] Target state reached: ${analysis.state} after ${pollCount} polls (${elapsedSec}s)`
6441
+ );
6442
+ await callback(analysis.id);
6443
+ isPolling = false;
6444
+ return {
6445
+ analysis: {
6446
+ id: analysis.id,
6447
+ state: analysis.state,
6448
+ failReason: analysis.failReason
6449
+ }
6450
+ };
6451
+ }
6452
+ debug6(
6453
+ `[pollForAnalysisState] State ${analysis.state} not in target states, waiting ${pollingIntervalSec}s before next poll...`
6454
+ );
6455
+ await sleep(REPORT_STATE_CHECK_DELAY);
6456
+ }
6457
+ throw new Error(`Unexpected end of polling for analysis: ${analysisId}`);
6458
+ }
6404
6459
  async getFixReportsByRepoUrl({ repoUrl }) {
6405
6460
  const res = await this._clientSdk.GetFixReportsByRepoUrl({
6406
6461
  repoUrl
@@ -7061,7 +7116,14 @@ var defaultLogger = {
7061
7116
  }
7062
7117
  };
7063
7118
  var PromptItemZ = z26.object({
7064
- type: z26.enum(["USER_PROMPT", "AI_RESPONSE", "TOOL_EXECUTION", "AI_THINKING"]),
7119
+ type: z26.enum([
7120
+ "USER_PROMPT",
7121
+ "AI_RESPONSE",
7122
+ "TOOL_EXECUTION",
7123
+ "AI_THINKING",
7124
+ "MCP_TOOL_CALL"
7125
+ // MCP (Model Context Protocol) tool invocation
7126
+ ]),
7065
7127
  attachedFiles: z26.array(
7066
7128
  z26.object({
7067
7129
  relativePath: z26.string(),
@@ -7079,7 +7141,12 @@ var PromptItemZ = z26.object({
7079
7141
  parameters: z26.string(),
7080
7142
  result: z26.string(),
7081
7143
  rawArguments: z26.string().optional(),
7082
- accepted: z26.boolean().optional()
7144
+ accepted: z26.boolean().optional(),
7145
+ // MCP-specific fields (only populated for MCP_TOOL_CALL type)
7146
+ mcpServer: z26.string().optional(),
7147
+ // MCP server name (e.g., "datadog", "mobb-mcp")
7148
+ mcpToolName: z26.string().optional()
7149
+ // MCP tool name without prefix (e.g., "scan_and_fix_vulnerabilities")
7083
7150
  }).optional()
7084
7151
  });
7085
7152
  var PromptItemArrayZ = z26.array(PromptItemZ);
package/dist/index.mjs CHANGED
@@ -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) {
@@ -12236,6 +12243,61 @@ var GQLClient = class {
12236
12243
  }
12237
12244
  );
12238
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
+ }
12239
12301
  async getFixReportsByRepoUrl({ repoUrl }) {
12240
12302
  const res = await this._clientSdk.GetFixReportsByRepoUrl({
12241
12303
  repoUrl
@@ -12636,7 +12698,8 @@ var debug8 = Debug7("mobbdev:index");
12636
12698
  async function sendReport({
12637
12699
  spinner,
12638
12700
  submitVulnerabilityReportVariables,
12639
- gqlClient
12701
+ gqlClient,
12702
+ polling
12640
12703
  }) {
12641
12704
  try {
12642
12705
  const submitRes = await gqlClient.submitVulnerabilityReport(
@@ -12647,19 +12710,32 @@ async function sendReport({
12647
12710
  throw new Error("\u{1F575}\uFE0F\u200D\u2642\uFE0F Mobb analysis failed");
12648
12711
  }
12649
12712
  spinner.update({ text: progressMassages.processingVulnerabilityReport });
12650
- await gqlClient.subscribeToAnalysis({
12651
- subscribeToAnalysisParams: {
12652
- analysisId: submitRes.submitVulnerabilityReport.fixReportId
12653
- },
12654
- callback: () => spinner.update({
12655
- text: "\u2699\uFE0F Vulnerability report processed successfully"
12656
- }),
12657
- callbackStates: [
12658
- "Digested" /* Digested */,
12659
- "Finished" /* Finished */
12660
- ],
12661
- timeoutInMs: VUL_REPORT_DIGEST_TIMEOUT_MS
12713
+ const callback = (_analysisId) => spinner.update({
12714
+ text: "\u2699\uFE0F Vulnerability report processed successfully"
12662
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
+ }
12663
12739
  return submitRes;
12664
12740
  } catch (e) {
12665
12741
  spinner.error({ text: "\u{1F575}\uFE0F\u200D\u2642\uFE0F Mobb analysis failed" });
@@ -13197,44 +13273,57 @@ async function handleAutoPr(params) {
13197
13273
  commitDirectly,
13198
13274
  prId,
13199
13275
  createSpinner: createSpinner5,
13200
- createOnePr
13276
+ createOnePr,
13277
+ polling
13201
13278
  } = params;
13202
13279
  const createAutoPrSpinner = createSpinner5(
13203
13280
  "\u{1F504} Waiting for the analysis to finish before initiating automatic pull request creation"
13204
13281
  ).start();
13205
- return await gqlClient.subscribeToAnalysis({
13206
- subscribeToAnalysisParams: {
13207
- analysisId
13208
- },
13209
- callback: async (analysisId2) => {
13210
- const autoPrAnalysisRes = await gqlClient.autoPrAnalysis({
13211
- analysisId: analysisId2,
13212
- commitDirectly,
13213
- prId,
13214
- 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}`
13215
13293
  });
13216
- debug12("auto pr analysis res %o", autoPrAnalysisRes);
13217
- if (autoPrAnalysisRes.autoPrAnalysis?.__typename === "AutoPrError") {
13218
- createAutoPrSpinner.error({
13219
- text: `\u{1F504} Automatic pull request failed - ${autoPrAnalysisRes.autoPrAnalysis.error}`
13220
- });
13221
- return;
13222
- }
13223
- if (autoPrAnalysisRes.autoPrAnalysis?.__typename === "AutoPrSuccess") {
13224
- const { appliedAutoPrIssueTypes } = autoPrAnalysisRes.autoPrAnalysis;
13225
- if (appliedAutoPrIssueTypes.length === 0) {
13226
- createAutoPrSpinner.success({
13227
- text: "\u{1F504} Automatic pull request did not find any new fixes to open a pull request for"
13228
- });
13229
- return;
13230
- }
13294
+ return;
13295
+ }
13296
+ if (autoPrAnalysisRes.autoPrAnalysis?.__typename === "AutoPrSuccess") {
13297
+ const { appliedAutoPrIssueTypes } = autoPrAnalysisRes.autoPrAnalysis;
13298
+ if (appliedAutoPrIssueTypes.length === 0) {
13231
13299
  createAutoPrSpinner.success({
13232
- 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"
13233
13301
  });
13302
+ return;
13234
13303
  }
13235
- },
13236
- callbackStates: ["Finished" /* Finished */]
13237
- });
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
+ }
13238
13327
  }
13239
13328
 
13240
13329
  // src/features/analysis/git.ts
@@ -13987,7 +14076,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
13987
14076
  autoPr,
13988
14077
  createOnePr,
13989
14078
  commitDirectly,
13990
- pullRequest
14079
+ pullRequest,
14080
+ polling
13991
14081
  } = params;
13992
14082
  debug18("start %s %s", dirname, repo);
13993
14083
  const { createSpinner: createSpinner5 } = Spinner2({ ci });
@@ -14099,7 +14189,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
14099
14189
  repoUrl: repo,
14100
14190
  sha,
14101
14191
  reference,
14102
- shouldScan
14192
+ shouldScan,
14193
+ polling
14103
14194
  });
14104
14195
  uploadReportSpinner.success({ text: "\u{1F4C1} Report uploaded successfully" });
14105
14196
  const mobbSpinner = createSpinner5("\u{1F575}\uFE0F\u200D\u2642\uFE0F Initiating Mobb analysis").start();
@@ -14117,7 +14208,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
14117
14208
  pullRequest: params.pullRequest,
14118
14209
  scanSource: _getScanSource(command, ci),
14119
14210
  scanContext: ScanContext.BUGSY
14120
- }
14211
+ },
14212
+ polling
14121
14213
  });
14122
14214
  if (sendReportRes.submitVulnerabilityReport.__typename !== "VulnerabilityReport") {
14123
14215
  mobbSpinner.error({ text: "\u{1F575}\uFE0F\u200D\u2642\uFE0F Mobb analysis failed" });
@@ -14133,7 +14225,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
14133
14225
  commitDirectly,
14134
14226
  prId: pullRequest,
14135
14227
  createSpinner: createSpinner5,
14136
- createOnePr
14228
+ createOnePr,
14229
+ polling
14137
14230
  });
14138
14231
  }
14139
14232
  await askToOpenAnalysis();
@@ -14143,7 +14236,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
14143
14236
  githubActionToken,
14144
14237
  analysisId: reportUploadInfo.fixReportId,
14145
14238
  scanner,
14146
- gqlClient
14239
+ gqlClient,
14240
+ polling
14147
14241
  });
14148
14242
  }
14149
14243
  return reportUploadInfo.fixReportId;
@@ -14238,7 +14332,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
14238
14332
  projectId,
14239
14333
  command,
14240
14334
  ci,
14241
- shouldScan: shouldScan2
14335
+ shouldScan: shouldScan2,
14336
+ polling
14242
14337
  });
14243
14338
  const res = await _zipAndUploadRepo({
14244
14339
  srcPath,
@@ -14261,7 +14356,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
14261
14356
  projectId,
14262
14357
  command,
14263
14358
  ci,
14264
- shouldScan: shouldScan2
14359
+ shouldScan: shouldScan2,
14360
+ polling
14265
14361
  });
14266
14362
  }
14267
14363
  const mobbSpinner2 = createSpinner5("\u{1F575}\uFE0F\u200D\u2642\uFE0F Initiating Mobb analysis").start();
@@ -14279,7 +14375,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
14279
14375
  pullRequest: params.pullRequest,
14280
14376
  experimentalEnabled: !!experimentalEnabled,
14281
14377
  scanContext: ScanContext.BUGSY
14282
- }
14378
+ },
14379
+ polling
14283
14380
  });
14284
14381
  if (command === "review") {
14285
14382
  await waitForAnaysisAndReviewPr({
@@ -14287,7 +14384,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
14287
14384
  githubActionToken,
14288
14385
  analysisId: reportUploadInfo.fixReportId,
14289
14386
  scanner,
14290
- gqlClient
14387
+ gqlClient,
14388
+ polling
14291
14389
  });
14292
14390
  }
14293
14391
  } catch (e) {
@@ -14304,7 +14402,8 @@ async function _scan(params, { skipPrompts = false } = {}) {
14304
14402
  commitDirectly,
14305
14403
  prId: pullRequest,
14306
14404
  createSpinner: createSpinner5,
14307
- createOnePr
14405
+ createOnePr,
14406
+ polling
14308
14407
  });
14309
14408
  }
14310
14409
  await askToOpenAnalysis();
@@ -14352,7 +14451,8 @@ async function _digestReport({
14352
14451
  repoUrl,
14353
14452
  sha,
14354
14453
  reference,
14355
- shouldScan
14454
+ shouldScan,
14455
+ polling
14356
14456
  }) {
14357
14457
  const digestSpinner = createSpinner4(
14358
14458
  progressMassages.processingVulnerabilityReport
@@ -14369,19 +14469,46 @@ async function _digestReport({
14369
14469
  shouldScan
14370
14470
  }
14371
14471
  );
14372
- await gqlClient.subscribeToAnalysis({
14373
- subscribeToAnalysisParams: {
14374
- analysisId: fixReportId
14375
- },
14376
- callback: () => digestSpinner.update({
14377
- text: progressMassages.processingVulnerabilityReportSuccess
14378
- }),
14379
- callbackStates: [
14380
- "Digested" /* Digested */,
14381
- "Finished" /* Finished */
14382
- ],
14383
- 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
14384
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
+ }
14385
14512
  const vulnFiles = await gqlClient.getVulnerabilityReportPaths(
14386
14513
  vulnerabilityReportId
14387
14514
  );
@@ -14402,7 +14529,8 @@ async function waitForAnaysisAndReviewPr({
14402
14529
  githubActionToken,
14403
14530
  analysisId,
14404
14531
  scanner,
14405
- gqlClient
14532
+ gqlClient,
14533
+ polling
14406
14534
  }) {
14407
14535
  const params = z29.object({
14408
14536
  repo: z29.string().url(),
@@ -14419,20 +14547,45 @@ async function waitForAnaysisAndReviewPr({
14419
14547
  propagateExceptions: true
14420
14548
  }
14421
14549
  );
14422
- await gqlClient.subscribeToAnalysis({
14423
- subscribeToAnalysisParams: {
14424
- analysisId
14425
- },
14426
- callback: (analysisId2) => {
14427
- return addFixCommentsForPr({
14428
- analysisId: analysisId2,
14429
- gqlClient,
14430
- scm,
14431
- scanner: z29.nativeEnum(SCANNERS).parse(scanner)
14432
- });
14433
- },
14434
- callbackStates: ["Finished" /* Finished */]
14435
- });
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
+ }
14436
14589
  }
14437
14590
 
14438
14591
  // src/commands/index.ts
@@ -14447,7 +14600,8 @@ async function review(params, { skipPrompts = true } = {}) {
14447
14600
  pullRequest,
14448
14601
  githubToken,
14449
14602
  scanner,
14450
- srcPath
14603
+ srcPath,
14604
+ polling
14451
14605
  } = params;
14452
14606
  await runAnalysis(
14453
14607
  {
@@ -14463,7 +14617,8 @@ async function review(params, { skipPrompts = true } = {}) {
14463
14617
  githubToken,
14464
14618
  scanner,
14465
14619
  command: "review",
14466
- srcPath
14620
+ srcPath,
14621
+ polling
14467
14622
  },
14468
14623
  { skipPrompts }
14469
14624
  );
@@ -14481,7 +14636,8 @@ async function analyze({
14481
14636
  autoPr,
14482
14637
  createOnePr,
14483
14638
  commitDirectly,
14484
- pullRequest
14639
+ pullRequest,
14640
+ polling
14485
14641
  }, { skipPrompts = false } = {}) {
14486
14642
  !ci && await showWelcomeMessage(skipPrompts);
14487
14643
  await runAnalysis(
@@ -14499,7 +14655,8 @@ async function analyze({
14499
14655
  autoPr,
14500
14656
  commitDirectly,
14501
14657
  pullRequest,
14502
- createOnePr
14658
+ createOnePr,
14659
+ polling
14503
14660
  },
14504
14661
  { skipPrompts }
14505
14662
  );
@@ -14639,7 +14796,7 @@ function analyzeBuilder(yargs2) {
14639
14796
  describe: chalk8.bold("Number of the pull request"),
14640
14797
  type: "number",
14641
14798
  demandOption: false
14642
- }).example(
14799
+ }).option("polling", pollingOption).example(
14643
14800
  "npx mobbdev@latest analyze -r https://github.com/WebGoat/WebGoat -f <your_vulnerability_report_path>",
14644
14801
  "analyze an existing repository"
14645
14802
  ).help();
@@ -14948,7 +15105,14 @@ var defaultLogger2 = {
14948
15105
  }
14949
15106
  };
14950
15107
  var PromptItemZ = z31.object({
14951
- 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
+ ]),
14952
15116
  attachedFiles: z31.array(
14953
15117
  z31.object({
14954
15118
  relativePath: z31.string(),
@@ -14966,7 +15130,12 @@ var PromptItemZ = z31.object({
14966
15130
  parameters: z31.string(),
14967
15131
  result: z31.string(),
14968
15132
  rawArguments: z31.string().optional(),
14969
- 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")
14970
15139
  }).optional()
14971
15140
  });
14972
15141
  var PromptItemArrayZ = z31.array(PromptItemZ);
@@ -23845,7 +24014,7 @@ function reviewBuilder(yargs2) {
23845
24014
  ),
23846
24015
  type: "string",
23847
24016
  demandOption: false
23848
- }).example(
24017
+ }).option("polling", pollingOption).example(
23849
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>",
23850
24019
  "add fixes to your pr"
23851
24020
  ).help();
@@ -23865,7 +24034,7 @@ async function reviewHandler(args) {
23865
24034
 
23866
24035
  // src/args/commands/scan.ts
23867
24036
  function scanBuilder(args) {
23868
- 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(
23869
24038
  "npx mobbdev@latest scan -r https://github.com/WebGoat/WebGoat",
23870
24039
  "Scan an existing repository"
23871
24040
  ).help();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mobbdev",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "Automated secure code remediation tool",
5
5
  "repository": "git+https://github.com/mobb-dev/bugsy.git",
6
6
  "main": "dist/index.mjs",