mobbdev 1.1.7 → 1.1.8
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.
|
@@ -51,26 +51,18 @@ declare const PromptItemZ: z.ZodObject<{
|
|
|
51
51
|
name: string;
|
|
52
52
|
parameters: string;
|
|
53
53
|
result: string;
|
|
54
|
-
accepted?: boolean | undefined;
|
|
55
54
|
rawArguments?: string | undefined;
|
|
55
|
+
accepted?: boolean | undefined;
|
|
56
56
|
}, {
|
|
57
57
|
name: string;
|
|
58
58
|
parameters: string;
|
|
59
59
|
result: string;
|
|
60
|
-
accepted?: boolean | undefined;
|
|
61
60
|
rawArguments?: string | undefined;
|
|
61
|
+
accepted?: boolean | undefined;
|
|
62
62
|
}>>;
|
|
63
63
|
}, "strip", z.ZodTypeAny, {
|
|
64
64
|
type: "USER_PROMPT" | "AI_RESPONSE" | "TOOL_EXECUTION" | "AI_THINKING";
|
|
65
|
-
tool?: {
|
|
66
|
-
name: string;
|
|
67
|
-
parameters: string;
|
|
68
|
-
result: string;
|
|
69
|
-
accepted?: boolean | undefined;
|
|
70
|
-
rawArguments?: string | undefined;
|
|
71
|
-
} | undefined;
|
|
72
65
|
date?: Date | undefined;
|
|
73
|
-
text?: string | undefined;
|
|
74
66
|
attachedFiles?: {
|
|
75
67
|
relativePath: string;
|
|
76
68
|
startLine?: number | undefined;
|
|
@@ -79,17 +71,17 @@ declare const PromptItemZ: z.ZodObject<{
|
|
|
79
71
|
inputCount: number;
|
|
80
72
|
outputCount: number;
|
|
81
73
|
} | undefined;
|
|
82
|
-
|
|
83
|
-
type: "USER_PROMPT" | "AI_RESPONSE" | "TOOL_EXECUTION" | "AI_THINKING";
|
|
74
|
+
text?: string | undefined;
|
|
84
75
|
tool?: {
|
|
85
76
|
name: string;
|
|
86
77
|
parameters: string;
|
|
87
78
|
result: string;
|
|
88
|
-
accepted?: boolean | undefined;
|
|
89
79
|
rawArguments?: string | undefined;
|
|
80
|
+
accepted?: boolean | undefined;
|
|
90
81
|
} | undefined;
|
|
82
|
+
}, {
|
|
83
|
+
type: "USER_PROMPT" | "AI_RESPONSE" | "TOOL_EXECUTION" | "AI_THINKING";
|
|
91
84
|
date?: Date | undefined;
|
|
92
|
-
text?: string | undefined;
|
|
93
85
|
attachedFiles?: {
|
|
94
86
|
relativePath: string;
|
|
95
87
|
startLine?: number | undefined;
|
|
@@ -98,6 +90,14 @@ declare const PromptItemZ: z.ZodObject<{
|
|
|
98
90
|
inputCount: number;
|
|
99
91
|
outputCount: number;
|
|
100
92
|
} | undefined;
|
|
93
|
+
text?: string | undefined;
|
|
94
|
+
tool?: {
|
|
95
|
+
name: string;
|
|
96
|
+
parameters: string;
|
|
97
|
+
result: string;
|
|
98
|
+
rawArguments?: string | undefined;
|
|
99
|
+
accepted?: boolean | undefined;
|
|
100
|
+
} | undefined;
|
|
101
101
|
}>;
|
|
102
102
|
type PromptItem = z.infer<typeof PromptItemZ>;
|
|
103
103
|
declare const PromptItemArrayZ: z.ZodArray<z.ZodObject<{
|
|
@@ -134,26 +134,18 @@ declare const PromptItemArrayZ: z.ZodArray<z.ZodObject<{
|
|
|
134
134
|
name: string;
|
|
135
135
|
parameters: string;
|
|
136
136
|
result: string;
|
|
137
|
-
accepted?: boolean | undefined;
|
|
138
137
|
rawArguments?: string | undefined;
|
|
138
|
+
accepted?: boolean | undefined;
|
|
139
139
|
}, {
|
|
140
140
|
name: string;
|
|
141
141
|
parameters: string;
|
|
142
142
|
result: string;
|
|
143
|
-
accepted?: boolean | undefined;
|
|
144
143
|
rawArguments?: string | undefined;
|
|
144
|
+
accepted?: boolean | undefined;
|
|
145
145
|
}>>;
|
|
146
146
|
}, "strip", z.ZodTypeAny, {
|
|
147
147
|
type: "USER_PROMPT" | "AI_RESPONSE" | "TOOL_EXECUTION" | "AI_THINKING";
|
|
148
|
-
tool?: {
|
|
149
|
-
name: string;
|
|
150
|
-
parameters: string;
|
|
151
|
-
result: string;
|
|
152
|
-
accepted?: boolean | undefined;
|
|
153
|
-
rawArguments?: string | undefined;
|
|
154
|
-
} | undefined;
|
|
155
148
|
date?: Date | undefined;
|
|
156
|
-
text?: string | undefined;
|
|
157
149
|
attachedFiles?: {
|
|
158
150
|
relativePath: string;
|
|
159
151
|
startLine?: number | undefined;
|
|
@@ -162,17 +154,17 @@ declare const PromptItemArrayZ: z.ZodArray<z.ZodObject<{
|
|
|
162
154
|
inputCount: number;
|
|
163
155
|
outputCount: number;
|
|
164
156
|
} | undefined;
|
|
165
|
-
|
|
166
|
-
type: "USER_PROMPT" | "AI_RESPONSE" | "TOOL_EXECUTION" | "AI_THINKING";
|
|
157
|
+
text?: string | undefined;
|
|
167
158
|
tool?: {
|
|
168
159
|
name: string;
|
|
169
160
|
parameters: string;
|
|
170
161
|
result: string;
|
|
171
|
-
accepted?: boolean | undefined;
|
|
172
162
|
rawArguments?: string | undefined;
|
|
163
|
+
accepted?: boolean | undefined;
|
|
173
164
|
} | undefined;
|
|
165
|
+
}, {
|
|
166
|
+
type: "USER_PROMPT" | "AI_RESPONSE" | "TOOL_EXECUTION" | "AI_THINKING";
|
|
174
167
|
date?: Date | undefined;
|
|
175
|
-
text?: string | undefined;
|
|
176
168
|
attachedFiles?: {
|
|
177
169
|
relativePath: string;
|
|
178
170
|
startLine?: number | undefined;
|
|
@@ -181,6 +173,14 @@ declare const PromptItemArrayZ: z.ZodArray<z.ZodObject<{
|
|
|
181
173
|
inputCount: number;
|
|
182
174
|
outputCount: number;
|
|
183
175
|
} | undefined;
|
|
176
|
+
text?: string | undefined;
|
|
177
|
+
tool?: {
|
|
178
|
+
name: string;
|
|
179
|
+
parameters: string;
|
|
180
|
+
result: string;
|
|
181
|
+
rawArguments?: string | undefined;
|
|
182
|
+
accepted?: boolean | undefined;
|
|
183
|
+
} | undefined;
|
|
184
184
|
}>, "many">;
|
|
185
185
|
type PromptItemArray = z.infer<typeof PromptItemArrayZ>;
|
|
186
186
|
type UploadAiBlameOptions = {
|
|
@@ -736,7 +736,7 @@ var GetVulByNodesMetadataDocument = `
|
|
|
736
736
|
where: {id: {_eq: $vulnerabilityReportId}}
|
|
737
737
|
) {
|
|
738
738
|
vulnerabilityReportIssues(
|
|
739
|
-
where: {fixId: {_is_null: true}, category: {_in: [Irrelevant, FalsePositive, Filtered]}}
|
|
739
|
+
where: {fixId: {_is_null: true}, category: {_in: [Irrelevant, FalsePositive, Filtered]}, _not: {vulnerabilityReportIssueTags: {vulnerability_report_issue_tag_value: {_eq: SUPPRESSED}}}}
|
|
740
740
|
) {
|
|
741
741
|
id
|
|
742
742
|
safeIssueType
|
package/dist/index.mjs
CHANGED
|
@@ -1996,7 +1996,7 @@ var GetVulByNodesMetadataDocument = `
|
|
|
1996
1996
|
where: {id: {_eq: $vulnerabilityReportId}}
|
|
1997
1997
|
) {
|
|
1998
1998
|
vulnerabilityReportIssues(
|
|
1999
|
-
where: {fixId: {_is_null: true}, category: {_in: [Irrelevant, FalsePositive, Filtered]}}
|
|
1999
|
+
where: {fixId: {_is_null: true}, category: {_in: [Irrelevant, FalsePositive, Filtered]}, _not: {vulnerabilityReportIssueTags: {vulnerability_report_issue_tag_value: {_eq: SUPPRESSED}}}}
|
|
2000
2000
|
) {
|
|
2001
2001
|
id
|
|
2002
2002
|
safeIssueType
|
|
@@ -7512,6 +7512,61 @@ var GET_BLAME_DOCUMENT = `
|
|
|
7512
7512
|
}
|
|
7513
7513
|
}
|
|
7514
7514
|
`;
|
|
7515
|
+
var GITHUB_GRAPHQL_FRAGMENTS = {
|
|
7516
|
+
/**
|
|
7517
|
+
* Fragment for fetching PR additions/deletions.
|
|
7518
|
+
* Use with pullRequest(number: $n) alias.
|
|
7519
|
+
*/
|
|
7520
|
+
PR_CHANGES: `
|
|
7521
|
+
additions
|
|
7522
|
+
deletions
|
|
7523
|
+
`,
|
|
7524
|
+
/**
|
|
7525
|
+
* Fragment for fetching PR comments.
|
|
7526
|
+
* Returns first 100 comments with author info.
|
|
7527
|
+
*/
|
|
7528
|
+
PR_COMMENTS: `
|
|
7529
|
+
comments(first: 100) {
|
|
7530
|
+
nodes {
|
|
7531
|
+
author {
|
|
7532
|
+
login
|
|
7533
|
+
__typename
|
|
7534
|
+
}
|
|
7535
|
+
body
|
|
7536
|
+
}
|
|
7537
|
+
}
|
|
7538
|
+
`,
|
|
7539
|
+
/**
|
|
7540
|
+
* Fragment for fetching blame data.
|
|
7541
|
+
* Use with object(expression: $ref) on Commit type.
|
|
7542
|
+
*/
|
|
7543
|
+
BLAME_RANGES: `
|
|
7544
|
+
blame(path: "$path") {
|
|
7545
|
+
ranges {
|
|
7546
|
+
startingLine
|
|
7547
|
+
endingLine
|
|
7548
|
+
commit {
|
|
7549
|
+
oid
|
|
7550
|
+
author {
|
|
7551
|
+
user {
|
|
7552
|
+
name
|
|
7553
|
+
login
|
|
7554
|
+
email
|
|
7555
|
+
}
|
|
7556
|
+
}
|
|
7557
|
+
}
|
|
7558
|
+
}
|
|
7559
|
+
}
|
|
7560
|
+
`,
|
|
7561
|
+
/**
|
|
7562
|
+
* Fragment for fetching commit timestamp.
|
|
7563
|
+
* Use with object(oid: $sha) on Commit type.
|
|
7564
|
+
*/
|
|
7565
|
+
COMMIT_TIMESTAMP: `
|
|
7566
|
+
oid
|
|
7567
|
+
committedDate
|
|
7568
|
+
`
|
|
7569
|
+
};
|
|
7515
7570
|
|
|
7516
7571
|
// src/features/analysis/scm/github/utils/encrypt_secret.ts
|
|
7517
7572
|
import sodium from "libsodium-wrappers";
|
|
@@ -7649,6 +7704,36 @@ async function githubValidateParams(url, accessToken) {
|
|
|
7649
7704
|
|
|
7650
7705
|
// src/features/analysis/scm/github/github.ts
|
|
7651
7706
|
var MAX_GH_PR_BODY_LENGTH = 65536;
|
|
7707
|
+
async function executeBatchGraphQL(octokit, owner, repo, config2) {
|
|
7708
|
+
const { items, aliasPrefix, buildFragment, extractResult } = config2;
|
|
7709
|
+
if (items.length === 0) {
|
|
7710
|
+
return /* @__PURE__ */ new Map();
|
|
7711
|
+
}
|
|
7712
|
+
const fragments = items.map((item, index) => buildFragment(item, index)).join("\n");
|
|
7713
|
+
const query = `
|
|
7714
|
+
query Batch${aliasPrefix}($owner: String!, $repo: String!) {
|
|
7715
|
+
repository(owner: $owner, name: $repo) {
|
|
7716
|
+
${fragments}
|
|
7717
|
+
}
|
|
7718
|
+
}
|
|
7719
|
+
`;
|
|
7720
|
+
const response = await octokit.graphql(query, { owner, repo });
|
|
7721
|
+
const result = /* @__PURE__ */ new Map();
|
|
7722
|
+
items.forEach((item, index) => {
|
|
7723
|
+
const data = response.repository[`${aliasPrefix}${index}`];
|
|
7724
|
+
if (data) {
|
|
7725
|
+
const extracted = extractResult(
|
|
7726
|
+
data,
|
|
7727
|
+
item,
|
|
7728
|
+
index
|
|
7729
|
+
);
|
|
7730
|
+
if (extracted !== void 0) {
|
|
7731
|
+
result.set(item, extracted);
|
|
7732
|
+
}
|
|
7733
|
+
}
|
|
7734
|
+
});
|
|
7735
|
+
return result;
|
|
7736
|
+
}
|
|
7652
7737
|
function getGithubSdk(params = {}) {
|
|
7653
7738
|
const octokit = getOctoKit(params);
|
|
7654
7739
|
return {
|
|
@@ -8114,6 +8199,125 @@ function getGithubSdk(params = {}) {
|
|
|
8114
8199
|
pull_number: params2.pull_number
|
|
8115
8200
|
});
|
|
8116
8201
|
return { data };
|
|
8202
|
+
},
|
|
8203
|
+
/**
|
|
8204
|
+
* Batch fetch additions/deletions for multiple PRs via GraphQL.
|
|
8205
|
+
* Uses GITHUB_GRAPHQL_FRAGMENTS.PR_CHANGES for the field selection.
|
|
8206
|
+
*/
|
|
8207
|
+
async getPrAdditionsDeletionsBatch(params2) {
|
|
8208
|
+
return executeBatchGraphQL(octokit, params2.owner, params2.repo, {
|
|
8209
|
+
items: params2.prNumbers,
|
|
8210
|
+
aliasPrefix: "pr",
|
|
8211
|
+
buildFragment: (prNumber, index) => `
|
|
8212
|
+
pr${index}: pullRequest(number: ${prNumber}) {
|
|
8213
|
+
${GITHUB_GRAPHQL_FRAGMENTS.PR_CHANGES}
|
|
8214
|
+
}`,
|
|
8215
|
+
extractResult: (data) => {
|
|
8216
|
+
const prData = data;
|
|
8217
|
+
if (prData.additions !== void 0 && prData.deletions !== void 0) {
|
|
8218
|
+
return {
|
|
8219
|
+
additions: prData.additions,
|
|
8220
|
+
deletions: prData.deletions
|
|
8221
|
+
};
|
|
8222
|
+
}
|
|
8223
|
+
return void 0;
|
|
8224
|
+
}
|
|
8225
|
+
});
|
|
8226
|
+
},
|
|
8227
|
+
/**
|
|
8228
|
+
* Batch fetch comments for multiple PRs via GraphQL.
|
|
8229
|
+
* Uses GITHUB_GRAPHQL_FRAGMENTS.PR_COMMENTS for the field selection.
|
|
8230
|
+
*/
|
|
8231
|
+
async getPrCommentsBatch(params2) {
|
|
8232
|
+
return executeBatchGraphQL(octokit, params2.owner, params2.repo, {
|
|
8233
|
+
items: params2.prNumbers,
|
|
8234
|
+
aliasPrefix: "pr",
|
|
8235
|
+
buildFragment: (prNumber, index) => `
|
|
8236
|
+
pr${index}: pullRequest(number: ${prNumber}) {
|
|
8237
|
+
${GITHUB_GRAPHQL_FRAGMENTS.PR_COMMENTS}
|
|
8238
|
+
}`,
|
|
8239
|
+
extractResult: (data) => {
|
|
8240
|
+
const prData = data;
|
|
8241
|
+
if (prData.comments?.nodes) {
|
|
8242
|
+
return prData.comments.nodes.map((node) => ({
|
|
8243
|
+
author: node.author ? { login: node.author.login, type: node.author.__typename } : null,
|
|
8244
|
+
body: node.body
|
|
8245
|
+
}));
|
|
8246
|
+
}
|
|
8247
|
+
return void 0;
|
|
8248
|
+
}
|
|
8249
|
+
});
|
|
8250
|
+
},
|
|
8251
|
+
/**
|
|
8252
|
+
* Batch fetch blame data for multiple files via GraphQL.
|
|
8253
|
+
* Field selection matches GITHUB_GRAPHQL_FRAGMENTS.BLAME_RANGES pattern.
|
|
8254
|
+
*/
|
|
8255
|
+
async getBlameBatch(params2) {
|
|
8256
|
+
return executeBatchGraphQL(octokit, params2.owner, params2.repo, {
|
|
8257
|
+
items: params2.filePaths,
|
|
8258
|
+
aliasPrefix: "file",
|
|
8259
|
+
buildFragment: (path22, index) => `
|
|
8260
|
+
file${index}: object(expression: "${params2.ref}") {
|
|
8261
|
+
... on Commit {
|
|
8262
|
+
blame(path: "${path22}") {
|
|
8263
|
+
ranges {
|
|
8264
|
+
startingLine
|
|
8265
|
+
endingLine
|
|
8266
|
+
commit {
|
|
8267
|
+
oid
|
|
8268
|
+
author {
|
|
8269
|
+
user {
|
|
8270
|
+
name
|
|
8271
|
+
login
|
|
8272
|
+
email
|
|
8273
|
+
}
|
|
8274
|
+
}
|
|
8275
|
+
}
|
|
8276
|
+
}
|
|
8277
|
+
}
|
|
8278
|
+
}
|
|
8279
|
+
}`,
|
|
8280
|
+
extractResult: (data) => {
|
|
8281
|
+
const fileData = data;
|
|
8282
|
+
if (fileData.blame?.ranges) {
|
|
8283
|
+
return fileData.blame.ranges.map((range) => ({
|
|
8284
|
+
startingLine: range.startingLine,
|
|
8285
|
+
endingLine: range.endingLine,
|
|
8286
|
+
commitSha: range.commit.oid,
|
|
8287
|
+
email: range.commit.author.user?.email || "",
|
|
8288
|
+
name: range.commit.author.user?.name || "",
|
|
8289
|
+
login: range.commit.author.user?.login || ""
|
|
8290
|
+
}));
|
|
8291
|
+
}
|
|
8292
|
+
return void 0;
|
|
8293
|
+
}
|
|
8294
|
+
});
|
|
8295
|
+
},
|
|
8296
|
+
/**
|
|
8297
|
+
* Batch fetch commit timestamps for multiple commits via GraphQL.
|
|
8298
|
+
* Uses GITHUB_GRAPHQL_FRAGMENTS.COMMIT_TIMESTAMP for the field selection.
|
|
8299
|
+
*/
|
|
8300
|
+
async getCommitsBatch(params2) {
|
|
8301
|
+
return executeBatchGraphQL(octokit, params2.owner, params2.repo, {
|
|
8302
|
+
items: params2.commitShas,
|
|
8303
|
+
aliasPrefix: "commit",
|
|
8304
|
+
buildFragment: (sha, index) => `
|
|
8305
|
+
commit${index}: object(oid: "${sha}") {
|
|
8306
|
+
... on Commit {
|
|
8307
|
+
${GITHUB_GRAPHQL_FRAGMENTS.COMMIT_TIMESTAMP}
|
|
8308
|
+
}
|
|
8309
|
+
}`,
|
|
8310
|
+
extractResult: (data) => {
|
|
8311
|
+
const commitData = data;
|
|
8312
|
+
if (commitData.oid && commitData.committedDate) {
|
|
8313
|
+
return {
|
|
8314
|
+
sha: commitData.oid,
|
|
8315
|
+
timestamp: new Date(commitData.committedDate)
|
|
8316
|
+
};
|
|
8317
|
+
}
|
|
8318
|
+
return void 0;
|
|
8319
|
+
}
|
|
8320
|
+
});
|
|
8117
8321
|
}
|
|
8118
8322
|
};
|
|
8119
8323
|
}
|
|
@@ -8382,7 +8586,7 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
8382
8586
|
comment_id: commentId
|
|
8383
8587
|
});
|
|
8384
8588
|
}
|
|
8385
|
-
async getCommitDiff(commitSha) {
|
|
8589
|
+
async getCommitDiff(commitSha, options) {
|
|
8386
8590
|
this._validateAccessTokenAndUrl();
|
|
8387
8591
|
const { owner, repo } = parseGithubOwnerAndRepo(this.url);
|
|
8388
8592
|
const { commit, diff } = await this.githubSdk.getCommitWithDiff({
|
|
@@ -8393,43 +8597,49 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
8393
8597
|
const commitTimestamp = commit.commit.committer?.date ? new Date(commit.commit.committer.date) : new Date(commit.commit.author?.date || Date.now());
|
|
8394
8598
|
let parentCommits;
|
|
8395
8599
|
if (commit.parents && commit.parents.length > 0) {
|
|
8600
|
+
if (options?.parentCommitTimestamps) {
|
|
8601
|
+
parentCommits = commit.parents.map((p) => options.parentCommitTimestamps.get(p.sha)).filter((p) => p !== void 0);
|
|
8602
|
+
} else {
|
|
8603
|
+
try {
|
|
8604
|
+
parentCommits = await Promise.all(
|
|
8605
|
+
commit.parents.map(async (parent) => {
|
|
8606
|
+
const parentCommit = await this.githubSdk.getCommit({
|
|
8607
|
+
owner,
|
|
8608
|
+
repo,
|
|
8609
|
+
commitSha: parent.sha
|
|
8610
|
+
});
|
|
8611
|
+
const parentTimestamp = parentCommit.data.committer?.date ? new Date(parentCommit.data.committer.date) : new Date(Date.now());
|
|
8612
|
+
return {
|
|
8613
|
+
sha: parent.sha,
|
|
8614
|
+
timestamp: parentTimestamp
|
|
8615
|
+
};
|
|
8616
|
+
})
|
|
8617
|
+
);
|
|
8618
|
+
} catch (error) {
|
|
8619
|
+
console.error("Failed to fetch parent commit timestamps", {
|
|
8620
|
+
error,
|
|
8621
|
+
commitSha,
|
|
8622
|
+
owner,
|
|
8623
|
+
repo
|
|
8624
|
+
});
|
|
8625
|
+
parentCommits = void 0;
|
|
8626
|
+
}
|
|
8627
|
+
}
|
|
8628
|
+
}
|
|
8629
|
+
let repositoryCreatedAt = options?.repositoryCreatedAt;
|
|
8630
|
+
if (repositoryCreatedAt === void 0) {
|
|
8396
8631
|
try {
|
|
8397
|
-
|
|
8398
|
-
|
|
8399
|
-
const parentCommit = await this.githubSdk.getCommit({
|
|
8400
|
-
owner,
|
|
8401
|
-
repo,
|
|
8402
|
-
commitSha: parent.sha
|
|
8403
|
-
});
|
|
8404
|
-
const parentTimestamp = parentCommit.data.committer?.date ? new Date(parentCommit.data.committer.date) : new Date(Date.now());
|
|
8405
|
-
return {
|
|
8406
|
-
sha: parent.sha,
|
|
8407
|
-
timestamp: parentTimestamp
|
|
8408
|
-
};
|
|
8409
|
-
})
|
|
8410
|
-
);
|
|
8632
|
+
const repoData = await this.githubSdk.getRepository({ owner, repo });
|
|
8633
|
+
repositoryCreatedAt = repoData.data.created_at ? new Date(repoData.data.created_at) : void 0;
|
|
8411
8634
|
} catch (error) {
|
|
8412
|
-
console.error("Failed to fetch
|
|
8635
|
+
console.error("Failed to fetch repository creation date", {
|
|
8413
8636
|
error,
|
|
8414
|
-
commitSha,
|
|
8415
8637
|
owner,
|
|
8416
8638
|
repo
|
|
8417
8639
|
});
|
|
8418
|
-
|
|
8640
|
+
repositoryCreatedAt = void 0;
|
|
8419
8641
|
}
|
|
8420
8642
|
}
|
|
8421
|
-
let repositoryCreatedAt;
|
|
8422
|
-
try {
|
|
8423
|
-
const repoData = await this.githubSdk.getRepository({ owner, repo });
|
|
8424
|
-
repositoryCreatedAt = repoData.data.created_at ? new Date(repoData.data.created_at) : void 0;
|
|
8425
|
-
} catch (error) {
|
|
8426
|
-
console.error("Failed to fetch repository creation date", {
|
|
8427
|
-
error,
|
|
8428
|
-
owner,
|
|
8429
|
-
repo
|
|
8430
|
-
});
|
|
8431
|
-
repositoryCreatedAt = void 0;
|
|
8432
|
-
}
|
|
8433
8643
|
return {
|
|
8434
8644
|
diff,
|
|
8435
8645
|
commitTimestamp,
|
|
@@ -8445,15 +8655,37 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
8445
8655
|
this._validateAccessTokenAndUrl();
|
|
8446
8656
|
const { owner, repo } = parseGithubOwnerAndRepo(this.url);
|
|
8447
8657
|
const prNumber = Number(submitRequestId);
|
|
8448
|
-
const [prRes, commitsRes, filesRes] = await Promise.all([
|
|
8658
|
+
const [prRes, commitsRes, filesRes, repoData] = await Promise.all([
|
|
8449
8659
|
this.githubSdk.getPr({ owner, repo, pull_number: prNumber }),
|
|
8450
8660
|
this.githubSdk.getPrCommits({ owner, repo, pull_number: prNumber }),
|
|
8451
|
-
this.githubSdk.listPRFiles({ owner, repo, pull_number: prNumber })
|
|
8661
|
+
this.githubSdk.listPRFiles({ owner, repo, pull_number: prNumber }),
|
|
8662
|
+
this.githubSdk.getRepository({ owner, repo })
|
|
8452
8663
|
]);
|
|
8453
8664
|
const pr = prRes.data;
|
|
8454
|
-
const
|
|
8665
|
+
const repositoryCreatedAt = repoData.data.created_at ? new Date(repoData.data.created_at) : void 0;
|
|
8666
|
+
const allParentShas = /* @__PURE__ */ new Set();
|
|
8667
|
+
for (const commit of commitsRes.data) {
|
|
8668
|
+
if (commit.parents) {
|
|
8669
|
+
for (const parent of commit.parents) {
|
|
8670
|
+
allParentShas.add(parent.sha);
|
|
8671
|
+
}
|
|
8672
|
+
}
|
|
8673
|
+
}
|
|
8674
|
+
const [parentCommitTimestamps, prDiff] = await Promise.all([
|
|
8675
|
+
this.githubSdk.getCommitsBatch({
|
|
8676
|
+
owner,
|
|
8677
|
+
repo,
|
|
8678
|
+
commitShas: Array.from(allParentShas)
|
|
8679
|
+
}),
|
|
8680
|
+
this.getPrDiff({ pull_number: prNumber })
|
|
8681
|
+
]);
|
|
8455
8682
|
const commits = await Promise.all(
|
|
8456
|
-
commitsRes.data.map(
|
|
8683
|
+
commitsRes.data.map(
|
|
8684
|
+
(commit) => this.getCommitDiff(commit.sha, {
|
|
8685
|
+
repositoryCreatedAt,
|
|
8686
|
+
parentCommitTimestamps
|
|
8687
|
+
})
|
|
8688
|
+
)
|
|
8457
8689
|
);
|
|
8458
8690
|
const diffLines = await this._attributeLinesViaBlame(
|
|
8459
8691
|
pr.head.ref,
|
|
@@ -8480,104 +8712,83 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
8480
8712
|
this._validateAccessToken();
|
|
8481
8713
|
const { owner, repo } = parseGithubOwnerAndRepo(repoUrl);
|
|
8482
8714
|
const pullsRes = await this.githubSdk.getRepoPullRequests({ owner, repo });
|
|
8483
|
-
const
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
|
|
8488
|
-
|
|
8489
|
-
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
|
|
8497
|
-
|
|
8498
|
-
|
|
8499
|
-
|
|
8500
|
-
|
|
8501
|
-
|
|
8502
|
-
|
|
8503
|
-
|
|
8504
|
-
|
|
8505
|
-
|
|
8506
|
-
|
|
8507
|
-
|
|
8508
|
-
|
|
8509
|
-
|
|
8510
|
-
|
|
8511
|
-
|
|
8715
|
+
const prNumbers = pullsRes.data.map((pr) => pr.number);
|
|
8716
|
+
const [additionsDeletionsMap, commentsMap] = await Promise.all([
|
|
8717
|
+
this.githubSdk.getPrAdditionsDeletionsBatch({ owner, repo, prNumbers }),
|
|
8718
|
+
this.githubSdk.getPrCommentsBatch({ owner, repo, prNumbers })
|
|
8719
|
+
]);
|
|
8720
|
+
const submitRequests = pullsRes.data.map((pr) => {
|
|
8721
|
+
let status = "open";
|
|
8722
|
+
if (pr.state === "closed") {
|
|
8723
|
+
status = pr.merged_at ? "merged" : "closed";
|
|
8724
|
+
} else if (pr.draft) {
|
|
8725
|
+
status = "draft";
|
|
8726
|
+
}
|
|
8727
|
+
const changedLinesData = additionsDeletionsMap.get(pr.number);
|
|
8728
|
+
const changedLines = changedLinesData ? {
|
|
8729
|
+
added: changedLinesData.additions,
|
|
8730
|
+
removed: changedLinesData.deletions
|
|
8731
|
+
} : { added: 0, removed: 0 };
|
|
8732
|
+
const comments = commentsMap.get(pr.number) || [];
|
|
8733
|
+
const tickets = this._extractLinearTicketsFromComments(comments);
|
|
8734
|
+
return {
|
|
8735
|
+
submitRequestId: String(pr.number),
|
|
8736
|
+
submitRequestNumber: pr.number,
|
|
8737
|
+
title: pr.title,
|
|
8738
|
+
status,
|
|
8739
|
+
sourceBranch: pr.head.ref,
|
|
8740
|
+
targetBranch: pr.base.ref,
|
|
8741
|
+
authorName: pr.user?.name || pr.user?.login,
|
|
8742
|
+
authorEmail: pr.user?.email || void 0,
|
|
8743
|
+
createdAt: new Date(pr.created_at),
|
|
8744
|
+
updatedAt: new Date(pr.updated_at),
|
|
8745
|
+
description: pr.body || void 0,
|
|
8746
|
+
tickets,
|
|
8747
|
+
changedLines
|
|
8748
|
+
};
|
|
8749
|
+
});
|
|
8512
8750
|
return submitRequests;
|
|
8513
8751
|
}
|
|
8514
|
-
|
|
8515
|
-
|
|
8516
|
-
|
|
8517
|
-
|
|
8518
|
-
|
|
8519
|
-
|
|
8520
|
-
|
|
8521
|
-
|
|
8522
|
-
|
|
8523
|
-
|
|
8524
|
-
|
|
8525
|
-
|
|
8526
|
-
|
|
8527
|
-
|
|
8528
|
-
|
|
8529
|
-
|
|
8530
|
-
|
|
8531
|
-
|
|
8532
|
-
|
|
8533
|
-
|
|
8534
|
-
|
|
8535
|
-
|
|
8536
|
-
|
|
8537
|
-
const
|
|
8538
|
-
|
|
8539
|
-
|
|
8540
|
-
|
|
8541
|
-
if (tickets.some((t) => t.name === name && t.url === url)) {
|
|
8542
|
-
continue;
|
|
8543
|
-
}
|
|
8544
|
-
if (!name || !url) {
|
|
8545
|
-
continue;
|
|
8546
|
-
}
|
|
8547
|
-
const urlParts = url.split("/");
|
|
8548
|
-
const titleSlug = urlParts[urlParts.length - 1] || "";
|
|
8549
|
-
const title = titleSlug.replace(/-/g, " ");
|
|
8550
|
-
tickets.push({ name, title, url });
|
|
8752
|
+
/**
|
|
8753
|
+
* Parse a Linear ticket from URL and name
|
|
8754
|
+
* Returns null if invalid or missing data
|
|
8755
|
+
*/
|
|
8756
|
+
_parseLinearTicket(url, name) {
|
|
8757
|
+
if (!name || !url) return null;
|
|
8758
|
+
const urlParts = url.split("/");
|
|
8759
|
+
const titleSlug = urlParts[urlParts.length - 1] || "";
|
|
8760
|
+
const title = titleSlug.replace(/-/g, " ");
|
|
8761
|
+
return { name, title, url };
|
|
8762
|
+
}
|
|
8763
|
+
/**
|
|
8764
|
+
* Extract Linear ticket links from pre-fetched comments (pure function, no API calls)
|
|
8765
|
+
*/
|
|
8766
|
+
_extractLinearTicketsFromComments(comments) {
|
|
8767
|
+
const tickets = [];
|
|
8768
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8769
|
+
for (const comment of comments) {
|
|
8770
|
+
if (comment.author?.login === "linear[bot]" || comment.author?.type === "Bot") {
|
|
8771
|
+
const body = comment.body || "";
|
|
8772
|
+
const htmlPattern = /<a href="(https:\/\/linear\.app\/[^"]+)">([A-Z]+-\d+)<\/a>/g;
|
|
8773
|
+
let match;
|
|
8774
|
+
while ((match = htmlPattern.exec(body)) !== null) {
|
|
8775
|
+
const ticket = this._parseLinearTicket(match[1], match[2]);
|
|
8776
|
+
if (ticket && !seen.has(`${ticket.name}|${ticket.url}`)) {
|
|
8777
|
+
seen.add(`${ticket.name}|${ticket.url}`);
|
|
8778
|
+
tickets.push(ticket);
|
|
8551
8779
|
}
|
|
8552
8780
|
}
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
|
|
8556
|
-
|
|
8557
|
-
|
|
8558
|
-
|
|
8559
|
-
|
|
8560
|
-
try {
|
|
8561
|
-
const diffRes = await this.githubSdk.getPrDiff({
|
|
8562
|
-
owner,
|
|
8563
|
-
repo,
|
|
8564
|
-
pull_number: prNumber
|
|
8565
|
-
});
|
|
8566
|
-
const diff = z21.string().parse(diffRes.data);
|
|
8567
|
-
let added = 0;
|
|
8568
|
-
let removed = 0;
|
|
8569
|
-
const lines = diff.split("\n");
|
|
8570
|
-
for (const line of lines) {
|
|
8571
|
-
if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
8572
|
-
added++;
|
|
8573
|
-
} else if (line.startsWith("-") && !line.startsWith("---")) {
|
|
8574
|
-
removed++;
|
|
8781
|
+
const markdownPattern = /\[([A-Z]+-\d+)\]\((https:\/\/linear\.app\/[^)]+)\)/g;
|
|
8782
|
+
while ((match = markdownPattern.exec(body)) !== null) {
|
|
8783
|
+
const ticket = this._parseLinearTicket(match[2], match[1]);
|
|
8784
|
+
if (ticket && !seen.has(`${ticket.name}|${ticket.url}`)) {
|
|
8785
|
+
seen.add(`${ticket.name}|${ticket.url}`);
|
|
8786
|
+
tickets.push(ticket);
|
|
8787
|
+
}
|
|
8575
8788
|
}
|
|
8576
8789
|
}
|
|
8577
|
-
return { added, removed };
|
|
8578
|
-
} catch (error) {
|
|
8579
|
-
return { added: 0, removed: 0 };
|
|
8580
8790
|
}
|
|
8791
|
+
return tickets;
|
|
8581
8792
|
}
|
|
8582
8793
|
/**
|
|
8583
8794
|
* Optimized helper to parse added line numbers from a unified diff patch
|
|
@@ -8605,48 +8816,59 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
8605
8816
|
return addedLines;
|
|
8606
8817
|
}
|
|
8607
8818
|
/**
|
|
8608
|
-
*
|
|
8819
|
+
* Process blame data for a single file to attribute lines to commits
|
|
8820
|
+
* Uses pre-fetched blame data instead of making API calls
|
|
8609
8821
|
*/
|
|
8610
|
-
|
|
8611
|
-
|
|
8612
|
-
|
|
8613
|
-
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
|
|
8619
|
-
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
|
|
8623
|
-
|
|
8624
|
-
|
|
8625
|
-
commitSha: blameRange.commitSha
|
|
8626
|
-
});
|
|
8627
|
-
}
|
|
8822
|
+
_processFileBlameSafe(file, blameData, prCommitShas) {
|
|
8823
|
+
const addedLines = this._parseAddedLinesFromPatch(file.patch);
|
|
8824
|
+
const addedLinesSet = new Set(addedLines);
|
|
8825
|
+
const fileAttributions = [];
|
|
8826
|
+
for (const blameRange of blameData) {
|
|
8827
|
+
if (!prCommitShas.has(blameRange.commitSha)) {
|
|
8828
|
+
continue;
|
|
8829
|
+
}
|
|
8830
|
+
for (let lineNum = blameRange.startingLine; lineNum <= blameRange.endingLine; lineNum++) {
|
|
8831
|
+
if (addedLinesSet.has(lineNum)) {
|
|
8832
|
+
fileAttributions.push({
|
|
8833
|
+
file: file.filename,
|
|
8834
|
+
line: lineNum,
|
|
8835
|
+
commitSha: blameRange.commitSha
|
|
8836
|
+
});
|
|
8628
8837
|
}
|
|
8629
8838
|
}
|
|
8630
|
-
return fileAttributions;
|
|
8631
|
-
} catch (error) {
|
|
8632
|
-
return [];
|
|
8633
8839
|
}
|
|
8840
|
+
return fileAttributions;
|
|
8634
8841
|
}
|
|
8635
8842
|
/**
|
|
8636
8843
|
* Optimized helper to attribute PR lines to commits using blame API
|
|
8637
|
-
*
|
|
8844
|
+
* Batch blame queries for minimal API call time (1 call instead of M calls)
|
|
8638
8845
|
*/
|
|
8639
8846
|
async _attributeLinesViaBlame(headRef, changedFiles, prCommits) {
|
|
8640
8847
|
const prCommitShas = new Set(prCommits.map((c) => c.commitSha));
|
|
8641
8848
|
const filesWithAdditions = changedFiles.filter(
|
|
8642
8849
|
(file) => file.patch?.includes("\n+")
|
|
8643
8850
|
);
|
|
8644
|
-
|
|
8645
|
-
|
|
8646
|
-
|
|
8647
|
-
|
|
8648
|
-
|
|
8649
|
-
|
|
8851
|
+
if (filesWithAdditions.length === 0) {
|
|
8852
|
+
return [];
|
|
8853
|
+
}
|
|
8854
|
+
const { owner, repo } = parseGithubOwnerAndRepo(this.url);
|
|
8855
|
+
const blameMap = await this.githubSdk.getBlameBatch({
|
|
8856
|
+
owner,
|
|
8857
|
+
repo,
|
|
8858
|
+
ref: headRef,
|
|
8859
|
+
filePaths: filesWithAdditions.map((f) => f.filename)
|
|
8860
|
+
});
|
|
8861
|
+
const allAttributions = [];
|
|
8862
|
+
for (const file of filesWithAdditions) {
|
|
8863
|
+
const blameData = blameMap.get(file.filename) || [];
|
|
8864
|
+
const fileAttributions = this._processFileBlameSafe(
|
|
8865
|
+
file,
|
|
8866
|
+
blameData,
|
|
8867
|
+
prCommitShas
|
|
8868
|
+
);
|
|
8869
|
+
allAttributions.push(...fileAttributions);
|
|
8870
|
+
}
|
|
8871
|
+
return allAttributions;
|
|
8650
8872
|
}
|
|
8651
8873
|
};
|
|
8652
8874
|
|