mobbdev 1.2.28 → 1.2.32

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.
@@ -851,12 +851,15 @@ var init_client_generates = __esm({
851
851
  }
852
852
  `;
853
853
  AnalyzeCommitForExtensionAiBlameDocument = `
854
- mutation AnalyzeCommitForExtensionAIBlame($repositoryURL: String!, $commitSha: String!, $organizationId: String!, $commitTimestamp: Timestamp) {
854
+ mutation AnalyzeCommitForExtensionAIBlame($repositoryURL: String!, $commitSha: String!, $organizationId: String!, $commitTimestamp: Timestamp, $commitAuthor: GitIdentityInput, $commitCommitter: GitIdentityInput, $commitCoAuthors: [GitIdentityInput!]) {
855
855
  analyzeCommitForAIBlame(
856
856
  repositoryURL: $repositoryURL
857
857
  commitSha: $commitSha
858
858
  organizationId: $organizationId
859
859
  commitTimestamp: $commitTimestamp
860
+ commitAuthor: $commitAuthor
861
+ commitCommitter: $commitCommitter
862
+ commitCoAuthors: $commitCoAuthors
860
863
  ) {
861
864
  __typename
862
865
  ... on ProcessAIBlameFinalResult {
@@ -2636,6 +2639,217 @@ var init_configs = __esm({
2636
2639
  }
2637
2640
  });
2638
2641
 
2642
+ // src/utils/blame/gitBlameTypes.ts
2643
+ import { z as z19 } from "zod";
2644
+ function parseCoAuthorValue(raw) {
2645
+ const trimmed = raw.trim();
2646
+ if (!trimmed) {
2647
+ return null;
2648
+ }
2649
+ const openBracket = trimmed.lastIndexOf("<");
2650
+ const closeBracket = trimmed.lastIndexOf(">");
2651
+ if (openBracket === -1 || closeBracket === -1 || closeBracket < openBracket) {
2652
+ return null;
2653
+ }
2654
+ const name = trimmed.slice(0, openBracket).trim();
2655
+ const email = trimmed.slice(openBracket + 1, closeBracket).trim();
2656
+ if (!name || !email) {
2657
+ return null;
2658
+ }
2659
+ return { name, email };
2660
+ }
2661
+ function parseCommitLine(line) {
2662
+ const parts = line.split("\0");
2663
+ if (parts.length < 7) {
2664
+ return null;
2665
+ }
2666
+ return {
2667
+ sha: parts[0],
2668
+ author: { name: parts[2], email: parts[1] },
2669
+ committer: { name: parts[4], email: parts[3] },
2670
+ coAuthors: parts.slice(7).map(parseCoAuthorValue).filter((v) => v !== null),
2671
+ timestamp: parseInt(parts[5], 10),
2672
+ message: parts[6]
2673
+ };
2674
+ }
2675
+ var PrepareGitBlameMessageZ, PrepareGitBlameResponseMessageZ, CommitMetadataZ, LineToCommitMapZ, CommitMetadataMapZ, BlameInfoZ, LineRangeZ, PrContextZ, PrepareCommitBlameMessageZ, BlameLineInfoZ, FileBlameDataZ, ChunkFetchResultZ, FileBlameResponseEntryZ, CommitBlameDataZ, CommitInfoZ, GitIdentityZ, COMMIT_LOG_FORMAT, CommitDataZ, PrDiffDataZ, PrStatsZ, CommitsManifestZ, BlameLineEntryZ, BlameLinesDataZ, PrepareCommitBlameResponseMessageZ;
2676
+ var init_gitBlameTypes = __esm({
2677
+ "src/utils/blame/gitBlameTypes.ts"() {
2678
+ "use strict";
2679
+ PrepareGitBlameMessageZ = z19.object({
2680
+ reportId: z19.string(),
2681
+ repoArchivePath: z19.string()
2682
+ });
2683
+ PrepareGitBlameResponseMessageZ = z19.object({
2684
+ reportId: z19.string()
2685
+ });
2686
+ CommitMetadataZ = z19.object({
2687
+ author: z19.string().optional(),
2688
+ "author-mail": z19.string().optional(),
2689
+ "author-time": z19.string().optional(),
2690
+ "author-tz": z19.string().optional(),
2691
+ committer: z19.string().optional(),
2692
+ "committer-mail": z19.string().optional(),
2693
+ "committer-time": z19.string().optional(),
2694
+ "committer-tz": z19.string().optional(),
2695
+ summary: z19.string().optional(),
2696
+ filename: z19.string().optional()
2697
+ });
2698
+ LineToCommitMapZ = z19.record(z19.string(), z19.string());
2699
+ CommitMetadataMapZ = z19.record(z19.string(), CommitMetadataZ);
2700
+ BlameInfoZ = z19.object({
2701
+ lineToCommit: LineToCommitMapZ,
2702
+ commitMetadata: CommitMetadataMapZ
2703
+ });
2704
+ LineRangeZ = z19.object({
2705
+ /** First line in chunk (1-indexed) */
2706
+ start: z19.number(),
2707
+ /** Last line in chunk (inclusive) */
2708
+ end: z19.number()
2709
+ });
2710
+ PrContextZ = z19.object({
2711
+ prNumber: z19.number(),
2712
+ repositoryUrl: z19.string(),
2713
+ organizationId: z19.string(),
2714
+ userEmail: z19.string(),
2715
+ source: z19.enum(["pr", "github"]),
2716
+ githubContext: z19.object({
2717
+ prNumber: z19.number(),
2718
+ installationId: z19.number(),
2719
+ repositoryURL: z19.string()
2720
+ }).optional()
2721
+ });
2722
+ PrepareCommitBlameMessageZ = z19.object({
2723
+ /** Commit blame request ID from database (for tracking and updating status) */
2724
+ commitBlameRequestId: z19.string(),
2725
+ /** Organization ID (for org-scoped caching) */
2726
+ organizationId: z19.string(),
2727
+ /** Full repository URL (e.g., https://github.com/org/repo) */
2728
+ repositoryUrl: z19.string(),
2729
+ /** Commit SHA to analyze (typically PR head commit) */
2730
+ commitSha: z19.string(),
2731
+ /** Authentication headers for repository access (e.g., GitHub token) */
2732
+ extraHeaders: z19.record(z19.string(), z19.string()).default({}),
2733
+ // --- PR analysis fields ---
2734
+ /** Target branch name (from getPr() base.ref). When set, enables PR analysis mode. */
2735
+ targetBranch: z19.string().optional(),
2736
+ /** Context for triggering blame attribution analysis after SCM agent completes. */
2737
+ prContext: PrContextZ.optional(),
2738
+ /** User email for blame attribution analysis trigger context (used for both PR and single commit flows). */
2739
+ userEmail: z19.string().optional()
2740
+ });
2741
+ BlameLineInfoZ = z19.object({
2742
+ /** Line number as it appeared in the introducing commit */
2743
+ originalLineNumber: z19.number(),
2744
+ /** Commit SHA that introduced this line */
2745
+ commitSha: z19.string(),
2746
+ /** Author name for this line */
2747
+ authorName: z19.string().optional(),
2748
+ /** Author email for this line */
2749
+ authorEmail: z19.string().optional()
2750
+ }).nullable();
2751
+ FileBlameDataZ = z19.array(BlameLineInfoZ);
2752
+ ChunkFetchResultZ = z19.object({
2753
+ filePath: z19.string(),
2754
+ lines: z19.array(z19.number()),
2755
+ data: FileBlameDataZ.nullable()
2756
+ });
2757
+ FileBlameResponseEntryZ = z19.object({
2758
+ /** Chunk index (0 for small files, 0-N for large file chunks) */
2759
+ chunkIndex: z19.number(),
2760
+ /** Blame data array (1-indexed, index 0 is null) */
2761
+ blameData: FileBlameDataZ
2762
+ });
2763
+ CommitBlameDataZ = z19.record(
2764
+ z19.string(),
2765
+ // fileName
2766
+ z19.array(FileBlameResponseEntryZ)
2767
+ );
2768
+ CommitInfoZ = z19.object({
2769
+ /** Number of parent commits (1 = normal commit, 2+ = merge commit, null = failed to determine) */
2770
+ parentCount: z19.number().nullable()
2771
+ });
2772
+ GitIdentityZ = z19.object({
2773
+ name: z19.string(),
2774
+ email: z19.string()
2775
+ });
2776
+ COMMIT_LOG_FORMAT = "%H%x00%ae%x00%an%x00%ce%x00%cn%x00%at%x00%s%x00%(trailers:key=Co-authored-by,valueonly,separator=%x00)";
2777
+ CommitDataZ = z19.object({
2778
+ diff: z19.string(),
2779
+ author: GitIdentityZ,
2780
+ committer: GitIdentityZ,
2781
+ coAuthors: z19.array(GitIdentityZ),
2782
+ timestamp: z19.number(),
2783
+ // Unix timestamp in seconds
2784
+ message: z19.string().optional(),
2785
+ parentCount: z19.number().nullable()
2786
+ });
2787
+ PrDiffDataZ = z19.object({
2788
+ diff: z19.string()
2789
+ });
2790
+ PrStatsZ = z19.object({
2791
+ additions: z19.number(),
2792
+ deletions: z19.number()
2793
+ });
2794
+ CommitsManifestZ = z19.object({
2795
+ commits: z19.array(z19.string())
2796
+ // Array of commit SHAs in order
2797
+ });
2798
+ BlameLineEntryZ = z19.object({
2799
+ file: z19.string(),
2800
+ line: z19.number(),
2801
+ originalCommitSha: z19.string(),
2802
+ originalLineNumber: z19.number()
2803
+ });
2804
+ BlameLinesDataZ = z19.object({
2805
+ lines: z19.array(BlameLineEntryZ)
2806
+ });
2807
+ PrepareCommitBlameResponseMessageZ = z19.object({
2808
+ /** Commit blame request ID (matches request, used to update specific DB record) */
2809
+ commitBlameRequestId: z19.string(),
2810
+ /** Organization ID (matches request) */
2811
+ organizationId: z19.string(),
2812
+ /** Repository URL (matches request) */
2813
+ repositoryUrl: z19.string(),
2814
+ /** Commit SHA analyzed (matches request) */
2815
+ commitSha: z19.string(),
2816
+ /** Processing status */
2817
+ status: z19.enum(["success", "failure"]),
2818
+ /** Error message (only present if status is 'failure') */
2819
+ error: z19.string().optional(),
2820
+ /**
2821
+ * Blame data for all processed files/chunks.
2822
+ * Empty dictionary if status is 'failure'.
2823
+ * Contains line mappings as arrays if status is 'success'.
2824
+ */
2825
+ blameData: CommitBlameDataZ,
2826
+ /**
2827
+ * Info about each commit referenced in the blame data plus the head commit.
2828
+ * Keyed by commit SHA, deduplicated.
2829
+ * Empty dictionary if status is 'failure'.
2830
+ */
2831
+ commits: z19.record(z19.string(), CommitInfoZ).default({}),
2832
+ // --- New PR diff computation response fields ---
2833
+ /** S3 paths for commit-level data (commitSha → S3 key). Present in PR analysis mode and single commit mode. */
2834
+ commitDataS3Paths: z19.record(z19.string(), z19.string()).optional(),
2835
+ /** S3 key for PR diff JSON. Present in PR analysis mode. */
2836
+ prDiffS3Path: z19.string().optional(),
2837
+ /** S3 key for commits manifest. Present in PR analysis mode. */
2838
+ commitsManifestS3Path: z19.string().optional(),
2839
+ /** S3 key for per-line targeted blame data. Present in PR analysis mode. */
2840
+ blameLinesS3Path: z19.string().optional(),
2841
+ /** S3 key for PR stats (additions/deletions). Present in PR analysis mode. */
2842
+ prStatsS3Path: z19.string().optional(),
2843
+ /** PR context passed through from request for response handler. */
2844
+ prContext: PrContextZ.optional(),
2845
+ /** PR title from the request metadata (passed through). */
2846
+ prTitle: z19.string().optional(),
2847
+ /** User email passed through from request for single commit blame attribution analysis trigger. */
2848
+ userEmail: z19.string().optional()
2849
+ });
2850
+ }
2851
+ });
2852
+
2639
2853
  // src/features/analysis/scm/services/ExcludedDirs.ts
2640
2854
  var EXCLUDED_DIRS;
2641
2855
  var init_ExcludedDirs = __esm({
@@ -3308,6 +3522,7 @@ var init_GitService = __esm({
3308
3522
  "src/features/analysis/scm/services/GitService.ts"() {
3309
3523
  "use strict";
3310
3524
  init_configs();
3525
+ init_gitBlameTypes();
3311
3526
  init_urlParser2();
3312
3527
  init_FileUtils();
3313
3528
  MAX_COMMIT_DIFF_SIZE_BYTES = 3 * 1024 * 1024;
@@ -3804,7 +4019,7 @@ ${rootContent}`;
3804
4019
  const DIFF_DELIMITER = "---MOBB_DIFF_START---";
3805
4020
  const output = await this.git.show([
3806
4021
  commitSha,
3807
- `--format=%cI%n${DIFF_DELIMITER}`,
4022
+ `--format=${COMMIT_LOG_FORMAT}%n${DIFF_DELIMITER}`,
3808
4023
  "--patch"
3809
4024
  ]);
3810
4025
  const delimiterIndex = output.indexOf(DIFF_DELIMITER);
@@ -3825,16 +4040,16 @@ ${rootContent}`;
3825
4040
  });
3826
4041
  return null;
3827
4042
  }
3828
- const metadataLines = metadataOutput.trim().split("\n");
3829
- if (metadataLines.length < 1 || !metadataLines[0]) {
4043
+ const metadataLine = metadataOutput.trim();
4044
+ const parsed = parseCommitLine(metadataLine);
4045
+ if (!parsed) {
3830
4046
  this.log("[GitService] Unexpected metadata format", "warning", {
3831
4047
  commitSha,
3832
- metadataLines
4048
+ metadataLine
3833
4049
  });
3834
4050
  return null;
3835
4051
  }
3836
- const timestampStr = metadataLines[0];
3837
- const timestamp = new Date(timestampStr);
4052
+ const timestamp = new Date(parsed.timestamp * 1e3);
3838
4053
  this.log("[GitService] Local commit data retrieved", "debug", {
3839
4054
  commitSha,
3840
4055
  diffSizeBytes,
@@ -3842,7 +4057,10 @@ ${rootContent}`;
3842
4057
  });
3843
4058
  return {
3844
4059
  diff,
3845
- timestamp
4060
+ timestamp,
4061
+ author: parsed.author,
4062
+ committer: parsed.committer,
4063
+ coAuthors: parsed.coAuthors
3846
4064
  };
3847
4065
  } catch (error) {
3848
4066
  const errorMessage = `Failed to get local commit data: ${error.message}`;
@@ -3860,11 +4078,11 @@ import * as os3 from "os";
3860
4078
  import path6 from "path";
3861
4079
  import chalk3 from "chalk";
3862
4080
  import { withFile } from "tmp-promise";
3863
- import z26 from "zod";
4081
+ import z27 from "zod";
3864
4082
 
3865
4083
  // src/commands/handleMobbLogin.ts
3866
4084
  import chalk2 from "chalk";
3867
- import Debug6 from "debug";
4085
+ import Debug7 from "debug";
3868
4086
 
3869
4087
  // src/utils/dirname.ts
3870
4088
  import fs from "fs";
@@ -4085,10 +4303,8 @@ var errorMessages = {
4085
4303
  var VUL_REPORT_DIGEST_TIMEOUT_MS = 1e3 * 60 * 30;
4086
4304
 
4087
4305
  // src/features/analysis/graphql/gql.ts
4088
- import fetchOrig from "cross-fetch";
4089
- import Debug5 from "debug";
4306
+ import Debug6 from "debug";
4090
4307
  import { GraphQLClient } from "graphql-request";
4091
- import { HttpsProxyAgent } from "https-proxy-agent";
4092
4308
  import { v4 as uuidv4 } from "uuid";
4093
4309
 
4094
4310
  // src/mcp/core/Errors.ts
@@ -4118,6 +4334,71 @@ var ScanContext = {
4118
4334
  BUGSY: "BUGSY"
4119
4335
  };
4120
4336
 
4337
+ // src/utils/proxy.ts
4338
+ import fetchOrig from "cross-fetch";
4339
+ import Debug2 from "debug";
4340
+ import { HttpsProxyAgent } from "https-proxy-agent";
4341
+
4342
+ // src/utils/url.ts
4343
+ function httpToWsUrl(url) {
4344
+ const parsed = new URL(url);
4345
+ parsed.protocol = parsed.protocol === "https:" ? "wss:" : "ws:";
4346
+ return parsed.toString();
4347
+ }
4348
+
4349
+ // src/utils/proxy.ts
4350
+ var debug2 = Debug2("mobbdev:proxy");
4351
+ function getHttpProxy() {
4352
+ return process.env["HTTPS_PROXY"] || process.env["HTTP_PROXY"] || "";
4353
+ }
4354
+ function getHttpProxyOnly() {
4355
+ return process.env["HTTP_PROXY"] || "";
4356
+ }
4357
+ function getProxyAgent(url) {
4358
+ try {
4359
+ const parsedUrl = new URL(url);
4360
+ const hostname = parsedUrl.hostname.toLowerCase();
4361
+ if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]") {
4362
+ debug2("Skipping proxy for localhost URL: %s", url);
4363
+ return void 0;
4364
+ }
4365
+ const noProxy = process.env["NO_PROXY"] || process.env["no_proxy"];
4366
+ if (noProxy) {
4367
+ const noProxyList = noProxy.split(",").map((h) => h.trim().toLowerCase());
4368
+ if (noProxyList.includes(hostname) || noProxyList.includes("*")) {
4369
+ debug2("Skipping proxy due to NO_PROXY for: %s", url);
4370
+ return void 0;
4371
+ }
4372
+ }
4373
+ const isHttp = parsedUrl.protocol === "http:";
4374
+ const isHttps = parsedUrl.protocol === "https:";
4375
+ const proxy = isHttps ? getHttpProxy() : isHttp ? getHttpProxyOnly() : null;
4376
+ if (proxy) {
4377
+ debug2("Using proxy %s", proxy);
4378
+ debug2("Proxy agent %o", proxy);
4379
+ return new HttpsProxyAgent(proxy);
4380
+ }
4381
+ } catch (err) {
4382
+ debug2(`Skipping proxy for ${url}. Reason: ${err.message}`);
4383
+ }
4384
+ return void 0;
4385
+ }
4386
+ var fetchWithProxy = (url, options = {}) => {
4387
+ try {
4388
+ const agent = getProxyAgent(url.toString());
4389
+ if (agent) {
4390
+ return fetchOrig(url, {
4391
+ ...options,
4392
+ // @ts-expect-error Node-fetch doesn't type 'agent', but it's valid
4393
+ agent
4394
+ });
4395
+ }
4396
+ } catch (err) {
4397
+ debug2(`Skipping proxy for ${url}. Reason: ${err.message}`);
4398
+ }
4399
+ return fetchOrig(url, options);
4400
+ };
4401
+
4121
4402
  // src/utils/subscribe/subscribe.ts
4122
4403
  import { createClient } from "graphql-ws";
4123
4404
  import WebsocketNode from "isomorphic-ws";
@@ -4146,11 +4427,11 @@ function getGraphQlHeaders(options) {
4146
4427
  }
4147
4428
 
4148
4429
  // src/utils/subscribe/subscribe.ts
4149
- var DEFAULT_API_URL2 = "https://api.mobb.ai/v1/graphql";
4430
+ var DEFAULT_API_URL2 = "wss://api.mobb.ai/v1/graphql";
4150
4431
  var SUBSCRIPTION_TIMEOUT_MS = 30 * 60 * 1e3;
4151
4432
  function createWSClient(options) {
4152
- const url = options.url || (process.env["API_URL"] || DEFAULT_API_URL2).replace("http", "ws");
4153
- const websocketImpl = options.websocket || (typeof WebSocket !== "undefined" ? WebSocket : WebsocketNode);
4433
+ const url = options.url || (process.env["API_URL"] ? httpToWsUrl(process.env["API_URL"]) : DEFAULT_API_URL2);
4434
+ const websocketImpl = options.websocket || WebsocketNode;
4154
4435
  const CustomWebSocket = options.proxyAgent ? (
4155
4436
  // biome-ignore lint/suspicious/noExplicitAny: Dynamic WebSocket extension requires any cast for cross-platform compatibility
4156
4437
  class extends websocketImpl {
@@ -6169,7 +6450,7 @@ var DEFUALT_ADO_ORIGIN = scmCloudUrl.Ado;
6169
6450
  init_env();
6170
6451
  import querystring from "querystring";
6171
6452
  import * as api from "azure-devops-node-api";
6172
- import Debug2 from "debug";
6453
+ import Debug3 from "debug";
6173
6454
  import { z as z18 } from "zod";
6174
6455
 
6175
6456
  // src/features/analysis/scm/ado/validation.ts
@@ -6208,7 +6489,7 @@ var accountsZ = z17.object({
6208
6489
  });
6209
6490
 
6210
6491
  // src/features/analysis/scm/ado/utils.ts
6211
- var debug2 = Debug2("mobbdev:scm:ado");
6492
+ var debug3 = Debug3("mobbdev:scm:ado");
6212
6493
 
6213
6494
  // src/features/analysis/scm/ado/AdoSCMLib.ts
6214
6495
  import { setTimeout as setTimeout2 } from "timers/promises";
@@ -6220,39 +6501,39 @@ init_GitService();
6220
6501
  import querystring2 from "querystring";
6221
6502
  import * as bitbucketPkgNode from "bitbucket";
6222
6503
  import bitbucketPkg from "bitbucket";
6223
- import Debug3 from "debug";
6224
- import { z as z20 } from "zod";
6504
+ import Debug4 from "debug";
6505
+ import { z as z21 } from "zod";
6225
6506
 
6226
6507
  // src/features/analysis/scm/bitbucket/validation.ts
6227
- import { z as z19 } from "zod";
6228
- var BitbucketAuthResultZ = z19.object({
6229
- access_token: z19.string(),
6230
- token_type: z19.string(),
6231
- refresh_token: z19.string()
6508
+ import { z as z20 } from "zod";
6509
+ var BitbucketAuthResultZ = z20.object({
6510
+ access_token: z20.string(),
6511
+ token_type: z20.string(),
6512
+ refresh_token: z20.string()
6232
6513
  });
6233
6514
 
6234
6515
  // src/features/analysis/scm/bitbucket/bitbucket.ts
6235
- var debug3 = Debug3("scm:bitbucket");
6516
+ var debug4 = Debug4("scm:bitbucket");
6236
6517
  var BITBUCKET_HOSTNAME = "bitbucket.org";
6237
- var TokenExpiredErrorZ = z20.object({
6238
- status: z20.number(),
6239
- error: z20.object({
6240
- type: z20.string(),
6241
- error: z20.object({
6242
- message: z20.string()
6518
+ var TokenExpiredErrorZ = z21.object({
6519
+ status: z21.number(),
6520
+ error: z21.object({
6521
+ type: z21.string(),
6522
+ error: z21.object({
6523
+ message: z21.string()
6243
6524
  })
6244
6525
  })
6245
6526
  });
6246
6527
  var BITBUCKET_ACCESS_TOKEN_URL = `https://${BITBUCKET_HOSTNAME}/site/oauth2/access_token`;
6247
- var BitbucketParseResultZ = z20.object({
6248
- organization: z20.string(),
6249
- repoName: z20.string(),
6250
- hostname: z20.literal(BITBUCKET_HOSTNAME)
6528
+ var BitbucketParseResultZ = z21.object({
6529
+ organization: z21.string(),
6530
+ repoName: z21.string(),
6531
+ hostname: z21.literal(BITBUCKET_HOSTNAME)
6251
6532
  });
6252
6533
 
6253
6534
  // src/features/analysis/scm/bitbucket/BitbucketSCMLib.ts
6254
6535
  import { setTimeout as setTimeout3 } from "timers/promises";
6255
- import { z as z21 } from "zod";
6536
+ import { z as z22 } from "zod";
6256
6537
 
6257
6538
  // src/features/analysis/scm/constants.ts
6258
6539
  var REPORT_DEFAULT_FILE_NAME = "report.json";
@@ -6261,7 +6542,7 @@ var REPORT_DEFAULT_FILE_NAME = "report.json";
6261
6542
  init_env();
6262
6543
 
6263
6544
  // src/features/analysis/scm/github/GithubSCMLib.ts
6264
- import { z as z22 } from "zod";
6545
+ import { z as z23 } from "zod";
6265
6546
  init_client_generates();
6266
6547
 
6267
6548
  // src/features/analysis/scm/github/github.ts
@@ -6283,7 +6564,7 @@ import {
6283
6564
  AccessLevel,
6284
6565
  Gitlab
6285
6566
  } from "@gitbeaker/rest";
6286
- import Debug4 from "debug";
6567
+ import Debug5 from "debug";
6287
6568
  import pLimit from "p-limit";
6288
6569
  import {
6289
6570
  Agent,
@@ -6293,151 +6574,107 @@ import {
6293
6574
 
6294
6575
  // src/utils/contextLogger.ts
6295
6576
  import debugModule from "debug";
6296
- var debug4 = debugModule("mobb:shared");
6577
+ var debug5 = debugModule("mobb:shared");
6297
6578
 
6298
6579
  // src/features/analysis/scm/gitlab/gitlab.ts
6299
6580
  init_env();
6300
6581
 
6301
6582
  // src/features/analysis/scm/gitlab/types.ts
6302
- import { z as z23 } from "zod";
6303
- var GitlabAuthResultZ = z23.object({
6304
- access_token: z23.string(),
6305
- token_type: z23.string(),
6306
- refresh_token: z23.string()
6583
+ import { z as z24 } from "zod";
6584
+ var GitlabAuthResultZ = z24.object({
6585
+ access_token: z24.string(),
6586
+ token_type: z24.string(),
6587
+ refresh_token: z24.string()
6307
6588
  });
6308
6589
 
6309
6590
  // src/features/analysis/scm/gitlab/gitlab.ts
6310
- var debug5 = Debug4("scm:gitlab");
6591
+ var debug6 = Debug5("scm:gitlab");
6311
6592
 
6312
6593
  // src/features/analysis/scm/gitlab/GitlabSCMLib.ts
6313
6594
  init_client_generates();
6314
6595
 
6315
6596
  // src/features/analysis/scm/scmFactory.ts
6316
- import { z as z24 } from "zod";
6597
+ import { z as z25 } from "zod";
6317
6598
 
6318
6599
  // src/features/analysis/graphql/gql.ts
6319
6600
  init_client_generates();
6320
6601
 
6321
6602
  // src/features/analysis/graphql/types.ts
6322
6603
  init_client_generates();
6323
- import { z as z25 } from "zod";
6324
- var VulnerabilityReportIssueCodeNodeZ = z25.object({
6325
- vulnerabilityReportIssueId: z25.string(),
6326
- path: z25.string(),
6327
- startLine: z25.number(),
6328
- vulnerabilityReportIssue: z25.object({
6329
- fixId: z25.string(),
6330
- category: z25.nativeEnum(Vulnerability_Report_Issue_Category_Enum),
6331
- safeIssueType: z25.string(),
6332
- vulnerabilityReportIssueTags: z25.array(
6333
- z25.object({
6334
- tag: z25.nativeEnum(Vulnerability_Report_Issue_Tag_Enum)
6604
+ import { z as z26 } from "zod";
6605
+ var VulnerabilityReportIssueCodeNodeZ = z26.object({
6606
+ vulnerabilityReportIssueId: z26.string(),
6607
+ path: z26.string(),
6608
+ startLine: z26.number(),
6609
+ vulnerabilityReportIssue: z26.object({
6610
+ fixId: z26.string(),
6611
+ category: z26.nativeEnum(Vulnerability_Report_Issue_Category_Enum),
6612
+ safeIssueType: z26.string(),
6613
+ vulnerabilityReportIssueTags: z26.array(
6614
+ z26.object({
6615
+ tag: z26.nativeEnum(Vulnerability_Report_Issue_Tag_Enum)
6335
6616
  })
6336
6617
  )
6337
6618
  })
6338
6619
  });
6339
- var VulnerabilityReportIssueNoFixCodeNodeZ = z25.object({
6340
- vulnerabilityReportIssues: z25.array(
6341
- z25.object({
6342
- id: z25.string(),
6343
- fixId: z25.string().nullable(),
6344
- category: z25.nativeEnum(Vulnerability_Report_Issue_Category_Enum),
6345
- safeIssueType: z25.string(),
6346
- fpId: z25.string().uuid().nullable(),
6347
- codeNodes: z25.array(
6348
- z25.object({
6349
- path: z25.string(),
6350
- startLine: z25.number()
6620
+ var VulnerabilityReportIssueNoFixCodeNodeZ = z26.object({
6621
+ vulnerabilityReportIssues: z26.array(
6622
+ z26.object({
6623
+ id: z26.string(),
6624
+ fixId: z26.string().nullable(),
6625
+ category: z26.nativeEnum(Vulnerability_Report_Issue_Category_Enum),
6626
+ safeIssueType: z26.string(),
6627
+ fpId: z26.string().uuid().nullable(),
6628
+ codeNodes: z26.array(
6629
+ z26.object({
6630
+ path: z26.string(),
6631
+ startLine: z26.number()
6351
6632
  })
6352
6633
  ),
6353
- vulnerabilityReportIssueTags: z25.array(
6354
- z25.object({
6355
- tag: z25.nativeEnum(Vulnerability_Report_Issue_Tag_Enum)
6634
+ vulnerabilityReportIssueTags: z26.array(
6635
+ z26.object({
6636
+ tag: z26.nativeEnum(Vulnerability_Report_Issue_Tag_Enum)
6356
6637
  })
6357
6638
  )
6358
6639
  })
6359
6640
  )
6360
6641
  });
6361
- var GetVulByNodesMetadataZ = z25.object({
6362
- vulnerabilityReportIssueCodeNodes: z25.array(VulnerabilityReportIssueCodeNodeZ),
6363
- nonFixablePrVuls: z25.object({
6364
- aggregate: z25.object({
6365
- count: z25.number()
6642
+ var GetVulByNodesMetadataZ = z26.object({
6643
+ vulnerabilityReportIssueCodeNodes: z26.array(VulnerabilityReportIssueCodeNodeZ),
6644
+ nonFixablePrVuls: z26.object({
6645
+ aggregate: z26.object({
6646
+ count: z26.number()
6366
6647
  })
6367
6648
  }),
6368
- fixablePrVuls: z25.object({
6369
- aggregate: z25.object({
6370
- count: z25.number()
6649
+ fixablePrVuls: z26.object({
6650
+ aggregate: z26.object({
6651
+ count: z26.number()
6371
6652
  })
6372
6653
  }),
6373
- totalScanVulnerabilities: z25.object({
6374
- aggregate: z25.object({
6375
- count: z25.number()
6654
+ totalScanVulnerabilities: z26.object({
6655
+ aggregate: z26.object({
6656
+ count: z26.number()
6376
6657
  })
6377
6658
  }),
6378
- irrelevantVulnerabilityReportIssue: z25.array(
6659
+ irrelevantVulnerabilityReportIssue: z26.array(
6379
6660
  VulnerabilityReportIssueNoFixCodeNodeZ
6380
6661
  )
6381
6662
  });
6382
6663
 
6383
6664
  // src/features/analysis/graphql/gql.ts
6384
- var debug6 = Debug5("mobbdev:gql");
6665
+ var debug7 = Debug6("mobbdev:gql");
6385
6666
  var API_KEY_HEADER_NAME = "x-mobb-key";
6386
6667
  var REPORT_STATE_CHECK_DELAY = 5 * 1e3;
6387
- function getProxyAgent(url) {
6388
- try {
6389
- const parsedUrl = new URL(url);
6390
- const hostname = parsedUrl.hostname.toLowerCase();
6391
- if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]") {
6392
- debug6("Skipping proxy for localhost URL: %s", url);
6393
- return void 0;
6394
- }
6395
- const noProxy = process.env["NO_PROXY"] || process.env["no_proxy"];
6396
- if (noProxy) {
6397
- const noProxyList = noProxy.split(",").map((h) => h.trim().toLowerCase());
6398
- if (noProxyList.includes(hostname) || noProxyList.includes("*")) {
6399
- debug6("Skipping proxy due to NO_PROXY for: %s", url);
6400
- return void 0;
6401
- }
6402
- }
6403
- const isHttp = parsedUrl.protocol === "http:";
6404
- const isHttps = parsedUrl.protocol === "https:";
6405
- const proxy = isHttps ? HTTPS_PROXY || HTTP_PROXY : isHttp ? HTTP_PROXY : null;
6406
- if (proxy) {
6407
- debug6("Using proxy %s", proxy);
6408
- debug6("Proxy agent %o", proxy);
6409
- return new HttpsProxyAgent(proxy);
6410
- }
6411
- } catch (err) {
6412
- debug6(`Skipping proxy for ${url}. Reason: ${err.message}`);
6413
- }
6414
- return void 0;
6415
- }
6416
- var fetchWithProxy = (url, options = {}) => {
6417
- try {
6418
- const agent = getProxyAgent(url.toString());
6419
- if (agent) {
6420
- return fetchOrig(url, {
6421
- ...options,
6422
- // @ts-expect-error Node-fetch doesn't type 'agent', but it's valid
6423
- agent
6424
- });
6425
- }
6426
- } catch (err) {
6427
- debug6(`Skipping proxy for ${url}. Reason: ${err.message}`);
6428
- }
6429
- return fetchOrig(url, options);
6430
- };
6431
6668
  var GQLClient = class {
6432
6669
  constructor(args) {
6433
6670
  __publicField(this, "_client");
6434
6671
  __publicField(this, "_clientSdk");
6435
6672
  __publicField(this, "_apiUrl");
6436
6673
  __publicField(this, "_auth");
6437
- debug6(`init with ${args}`);
6674
+ debug7(`init with ${args}`);
6438
6675
  this._auth = args;
6439
6676
  this._apiUrl = args.apiUrl || API_URL;
6440
- debug6(
6677
+ debug7(
6441
6678
  "GQLClient constructor: resolved apiUrl=%s (from param: %s)",
6442
6679
  this._apiUrl,
6443
6680
  args.apiUrl || "fallback to API_URL constant"
@@ -6449,7 +6686,7 @@ var GQLClient = class {
6449
6686
  fetch: fetchWithProxy,
6450
6687
  requestMiddleware: (request) => {
6451
6688
  const requestId = uuidv4();
6452
- debug6(
6689
+ debug7(
6453
6690
  `sending API request with id: ${requestId} and with request: ${request.body}`
6454
6691
  );
6455
6692
  return {
@@ -6486,7 +6723,7 @@ var GQLClient = class {
6486
6723
  await this.getUserInfo();
6487
6724
  } catch (e) {
6488
6725
  if (e?.toString().startsWith("FetchError")) {
6489
- debug6("verify connection failed %o", e);
6726
+ debug7("verify connection failed %o", e);
6490
6727
  return false;
6491
6728
  }
6492
6729
  }
@@ -6498,7 +6735,7 @@ var GQLClient = class {
6498
6735
  try {
6499
6736
  info = await this.getUserInfo();
6500
6737
  } catch (e) {
6501
- debug6("verify token failed %o", e);
6738
+ debug7("verify token failed %o", e);
6502
6739
  return false;
6503
6740
  }
6504
6741
  return info?.email || true;
@@ -6559,7 +6796,7 @@ var GQLClient = class {
6559
6796
  try {
6560
6797
  await this._clientSdk.CreateCommunityUser();
6561
6798
  } catch (e) {
6562
- debug6("create community user failed %o", e);
6799
+ debug7("create community user failed %o", e);
6563
6800
  }
6564
6801
  }
6565
6802
  async updateScmToken(args) {
@@ -6739,11 +6976,13 @@ var GQLClient = class {
6739
6976
  this._auth.type === "apiKey" ? {
6740
6977
  apiKey: this._auth.apiKey,
6741
6978
  type: "apiKey",
6979
+ url: httpToWsUrl(this._apiUrl),
6742
6980
  timeoutInMs: params.timeoutInMs,
6743
6981
  proxyAgent: getProxyAgent(this._apiUrl)
6744
6982
  } : {
6745
6983
  token: this._auth.token,
6746
6984
  type: "token",
6985
+ url: httpToWsUrl(this._apiUrl),
6747
6986
  timeoutInMs: params.timeoutInMs,
6748
6987
  proxyAgent: getProxyAgent(this._apiUrl)
6749
6988
  }
@@ -6756,7 +6995,7 @@ var GQLClient = class {
6756
6995
  const startTime = Date.now();
6757
6996
  const maxDuration = timeoutInMs ?? 30 * 60 * 1e3;
6758
6997
  const pollingIntervalSec = REPORT_STATE_CHECK_DELAY / 1e3;
6759
- debug6(
6998
+ debug7(
6760
6999
  `[pollForAnalysisState] Starting polling for analysis ${analysisId}, target states: ${callbackStates.join(", ")}, interval: ${pollingIntervalSec}s`
6761
7000
  );
6762
7001
  let isPolling = true;
@@ -6765,7 +7004,7 @@ var GQLClient = class {
6765
7004
  pollCount++;
6766
7005
  const elapsedSec = Math.round((Date.now() - startTime) / 1e3);
6767
7006
  if (Date.now() - startTime > maxDuration) {
6768
- debug6(
7007
+ debug7(
6769
7008
  `[pollForAnalysisState] Timeout expired after ${pollCount} polls (${elapsedSec}s)`
6770
7009
  );
6771
7010
  throw new ReportDigestError(
@@ -6773,20 +7012,20 @@ var GQLClient = class {
6773
7012
  `Analysis timed out after ${Math.round(maxDuration / 6e4)} minutes. Please try again or check the Mobb platform for status.`
6774
7013
  );
6775
7014
  }
6776
- debug6(
7015
+ debug7(
6777
7016
  `[pollForAnalysisState] Poll #${pollCount} (elapsed: ${elapsedSec}s) - fetching analysis state...`
6778
7017
  );
6779
7018
  const analysis = await this.getAnalysis(analysisId);
6780
- debug6(
7019
+ debug7(
6781
7020
  `[pollForAnalysisState] Poll #${pollCount} - current state: ${analysis.state}`
6782
7021
  );
6783
7022
  if (!analysis.state || analysis.state === "Failed" /* Failed */) {
6784
7023
  const errorMessage = analysis.failReason || `Analysis failed with id: ${analysis.id}`;
6785
- debug6(`[pollForAnalysisState] Analysis failed: ${errorMessage}`);
7024
+ debug7(`[pollForAnalysisState] Analysis failed: ${errorMessage}`);
6786
7025
  throw new ReportDigestError(errorMessage, analysis.failReason ?? "");
6787
7026
  }
6788
7027
  if (callbackStates.includes(analysis.state)) {
6789
- debug6(
7028
+ debug7(
6790
7029
  `[pollForAnalysisState] Target state reached: ${analysis.state} after ${pollCount} polls (${elapsedSec}s)`
6791
7030
  );
6792
7031
  await callback(analysis.id);
@@ -6799,7 +7038,7 @@ var GQLClient = class {
6799
7038
  }
6800
7039
  };
6801
7040
  }
6802
- debug6(
7041
+ debug7(
6803
7042
  `[pollForAnalysisState] State ${analysis.state} not in target states, waiting ${pollingIntervalSec}s before next poll...`
6804
7043
  );
6805
7044
  await sleep(REPORT_STATE_CHECK_DELAY);
@@ -7068,7 +7307,7 @@ var AuthManager = class {
7068
7307
  };
7069
7308
 
7070
7309
  // src/commands/handleMobbLogin.ts
7071
- var debug7 = Debug6("mobbdev:commands");
7310
+ var debug8 = Debug7("mobbdev:commands");
7072
7311
  var LOGIN_MAX_WAIT2 = 10 * 60 * 1e3;
7073
7312
  var LOGIN_CHECK_DELAY2 = 5 * 1e3;
7074
7313
  var MOBB_LOGIN_REQUIRED_MSG = `\u{1F513} Login to Mobb is Required, you will be redirected to our login page, once the authorization is complete return to this prompt, ${chalk2.bgBlue(
@@ -7080,7 +7319,7 @@ async function getAuthenticatedGQLClient({
7080
7319
  apiUrl,
7081
7320
  webAppUrl
7082
7321
  }) {
7083
- debug7(
7322
+ debug8(
7084
7323
  "getAuthenticatedGQLClient called with: apiUrl=%s, webAppUrl=%s",
7085
7324
  apiUrl || "undefined",
7086
7325
  webAppUrl || "undefined"
@@ -7103,7 +7342,7 @@ async function handleMobbLogin({
7103
7342
  webAppUrl,
7104
7343
  loginContext
7105
7344
  }) {
7106
- debug7(
7345
+ debug8(
7107
7346
  "handleMobbLogin: resolved URLs - apiUrl=%s (from param: %s), webAppUrl=%s (from param: %s)",
7108
7347
  apiUrl || "fallback",
7109
7348
  apiUrl || "fallback",
@@ -7122,7 +7361,7 @@ async function handleMobbLogin({
7122
7361
  return authManager.getGQLClient();
7123
7362
  }
7124
7363
  } catch (error) {
7125
- debug7("Authentication check failed:", error);
7364
+ debug8("Authentication check failed:", error);
7126
7365
  }
7127
7366
  if (apiKey) {
7128
7367
  createSpinner().start().error({
@@ -7174,9 +7413,9 @@ init_GitService();
7174
7413
  init_urlParser2();
7175
7414
 
7176
7415
  // src/features/analysis/upload-file.ts
7177
- import Debug7 from "debug";
7416
+ import Debug8 from "debug";
7178
7417
  import fetch3, { File, fileFrom, FormData } from "node-fetch";
7179
- var debug8 = Debug7("mobbdev:upload-file");
7418
+ var debug9 = Debug8("mobbdev:upload-file");
7180
7419
  async function uploadFile({
7181
7420
  file,
7182
7421
  url,
@@ -7189,9 +7428,9 @@ async function uploadFile({
7189
7428
  logInfo(`FileUpload: upload file start ${url}`);
7190
7429
  logInfo(`FileUpload: upload fields`, uploadFields);
7191
7430
  logInfo(`FileUpload: upload key ${uploadKey}`);
7192
- debug8("upload file start %s", url);
7193
- debug8("upload fields %o", uploadFields);
7194
- debug8("upload key %s", uploadKey);
7431
+ debug9("upload file start %s", url);
7432
+ debug9("upload fields %o", uploadFields);
7433
+ debug9("upload key %s", uploadKey);
7195
7434
  const form = new FormData();
7196
7435
  Object.entries(uploadFields).forEach(([key, value]) => {
7197
7436
  form.append(key, value);
@@ -7200,11 +7439,11 @@ async function uploadFile({
7200
7439
  form.append("key", uploadKey);
7201
7440
  }
7202
7441
  if (typeof file === "string") {
7203
- debug8("upload file from path %s", file);
7442
+ debug9("upload file from path %s", file);
7204
7443
  logInfo(`FileUpload: upload file from path ${file}`);
7205
7444
  form.append("file", await fileFrom(file));
7206
7445
  } else {
7207
- debug8("upload file from buffer");
7446
+ debug9("upload file from buffer");
7208
7447
  logInfo(`FileUpload: upload file from buffer`);
7209
7448
  form.append("file", new File([new Uint8Array(file)], "file"));
7210
7449
  }
@@ -7215,11 +7454,11 @@ async function uploadFile({
7215
7454
  agent
7216
7455
  });
7217
7456
  if (!response.ok) {
7218
- debug8("error from S3 %s %s", response.body, response.status);
7457
+ debug9("error from S3 %s %s", response.body, response.status);
7219
7458
  logInfo(`FileUpload: error from S3 ${response.body} ${response.status}`);
7220
7459
  throw new Error(`Failed to upload the file: ${response.status}`);
7221
7460
  }
7222
- debug8("upload file done");
7461
+ debug9("upload file done");
7223
7462
  logInfo(`FileUpload: upload file done`);
7224
7463
  }
7225
7464
 
@@ -7317,7 +7556,8 @@ import { OpenRedaction } from "@openredaction/openredaction";
7317
7556
  var openRedaction = new OpenRedaction({
7318
7557
  patterns: [
7319
7558
  // Core Personal Data
7320
- "EMAIL",
7559
+ // Removed EMAIL - causes false positives in code/test snippets (e.g. --author="Eve Author <eve@example.com>")
7560
+ // Prefer false negatives over false positives for this use case.
7321
7561
  "SSN",
7322
7562
  "NATIONAL_INSURANCE_UK",
7323
7563
  "DATE_OF_BIRTH",
@@ -7332,7 +7572,8 @@ var openRedaction = new OpenRedaction({
7332
7572
  "VISA_MRZ",
7333
7573
  "TAX_ID",
7334
7574
  // Financial Data (removed SWIFT_BIC, CARD_AUTH_CODE - too broad, causing false positives with authentication words)
7335
- "CREDIT_CARD",
7575
+ // Removed CREDIT_CARD - causes false positives on zero-filled UUIDs (e.g. '00000000-0000-0000-0000-000000000000')
7576
+ // Prefer false negatives over false positives for this use case.
7336
7577
  "IBAN",
7337
7578
  "BANK_ACCOUNT_UK",
7338
7579
  "ROUTING_NUMBER_US",
@@ -7418,15 +7659,6 @@ async function sanitizeDataWithCounts(obj) {
7418
7659
  ...piiDetections.low
7419
7660
  ];
7420
7661
  for (const detection of allDetections) {
7421
- if (detection.type === "CREDIT_CARD") {
7422
- const start = detection.position[0];
7423
- const end = detection.position[1];
7424
- const charBefore = (start > 0 ? str[start - 1] : "") ?? "";
7425
- const charAfter = str[end] ?? "";
7426
- if (charBefore === "." || charBefore >= "0" && charBefore <= "9" || charAfter >= "0" && charAfter <= "9") {
7427
- continue;
7428
- }
7429
- }
7430
7662
  counts.detections.total++;
7431
7663
  if (detection.severity === "high") counts.detections.high++;
7432
7664
  else if (detection.severity === "medium") counts.detections.medium++;
@@ -7479,8 +7711,8 @@ var defaultLogger = {
7479
7711
  }
7480
7712
  }
7481
7713
  };
7482
- var PromptItemZ = z26.object({
7483
- type: z26.enum([
7714
+ var PromptItemZ = z27.object({
7715
+ type: z27.enum([
7484
7716
  "USER_PROMPT",
7485
7717
  "AI_RESPONSE",
7486
7718
  "TOOL_EXECUTION",
@@ -7488,32 +7720,32 @@ var PromptItemZ = z26.object({
7488
7720
  "MCP_TOOL_CALL"
7489
7721
  // MCP (Model Context Protocol) tool invocation
7490
7722
  ]),
7491
- attachedFiles: z26.array(
7492
- z26.object({
7493
- relativePath: z26.string(),
7494
- startLine: z26.number().optional()
7723
+ attachedFiles: z27.array(
7724
+ z27.object({
7725
+ relativePath: z27.string(),
7726
+ startLine: z27.number().optional()
7495
7727
  })
7496
7728
  ).optional(),
7497
- tokens: z26.object({
7498
- inputCount: z26.number(),
7499
- outputCount: z26.number()
7729
+ tokens: z27.object({
7730
+ inputCount: z27.number(),
7731
+ outputCount: z27.number()
7500
7732
  }).optional(),
7501
- text: z26.string().optional(),
7502
- date: z26.date().optional(),
7503
- tool: z26.object({
7504
- name: z26.string(),
7505
- parameters: z26.string(),
7506
- result: z26.string(),
7507
- rawArguments: z26.string().optional(),
7508
- accepted: z26.boolean().optional(),
7733
+ text: z27.string().optional(),
7734
+ date: z27.date().optional(),
7735
+ tool: z27.object({
7736
+ name: z27.string(),
7737
+ parameters: z27.string(),
7738
+ result: z27.string(),
7739
+ rawArguments: z27.string().optional(),
7740
+ accepted: z27.boolean().optional(),
7509
7741
  // MCP-specific fields (only populated for MCP_TOOL_CALL type)
7510
- mcpServer: z26.string().optional(),
7742
+ mcpServer: z27.string().optional(),
7511
7743
  // MCP server name (e.g., "datadog", "mobb-mcp")
7512
- mcpToolName: z26.string().optional()
7744
+ mcpToolName: z27.string().optional()
7513
7745
  // MCP tool name without prefix (e.g., "scan_and_fix_vulnerabilities")
7514
7746
  }).optional()
7515
7747
  });
7516
- var PromptItemArrayZ = z26.array(PromptItemZ);
7748
+ var PromptItemArrayZ = z27.array(PromptItemZ);
7517
7749
  async function getRepositoryUrl() {
7518
7750
  try {
7519
7751
  const gitService = new GitService(process.cwd());