mobbdev 1.3.5 → 1.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/args/commands/upload_ai_blame.d.mts +32 -32
- package/dist/args/commands/upload_ai_blame.mjs +29 -5
- package/dist/index.mjs +874 -254
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -326,6 +326,7 @@ var init_client_generates = __esm({
|
|
|
326
326
|
IssueType_Enum2["SystemExitShouldReraise"] = "SYSTEM_EXIT_SHOULD_RERAISE";
|
|
327
327
|
IssueType_Enum2["SystemInformationLeak"] = "SYSTEM_INFORMATION_LEAK";
|
|
328
328
|
IssueType_Enum2["SystemInformationLeakExternal"] = "SYSTEM_INFORMATION_LEAK_EXTERNAL";
|
|
329
|
+
IssueType_Enum2["TaintedNumericCast"] = "TAINTED_NUMERIC_CAST";
|
|
329
330
|
IssueType_Enum2["TarSlip"] = "TAR_SLIP";
|
|
330
331
|
IssueType_Enum2["TrustBoundaryViolation"] = "TRUST_BOUNDARY_VIOLATION";
|
|
331
332
|
IssueType_Enum2["TypeConfusion"] = "TYPE_CONFUSION";
|
|
@@ -1450,7 +1451,8 @@ var init_getIssueType = __esm({
|
|
|
1450
1451
|
["DJANGO_BLANK_FIELD_NEEDS_NULL_OR_DEFAULT" /* DjangoBlankFieldNeedsNullOrDefault */]: "Django Blank Field Needs Null or Default",
|
|
1451
1452
|
["REDUNDANT_NIL_ERROR_CHECK" /* RedundantNilErrorCheck */]: "Redundant Nil Error Check",
|
|
1452
1453
|
["MISSING_WORKFLOW_PERMISSIONS" /* MissingWorkflowPermissions */]: "Missing Workflow Permissions",
|
|
1453
|
-
["EXCESSIVE_SECRETS_EXPOSURE" /* ExcessiveSecretsExposure */]: "Excessive Secrets Exposure"
|
|
1454
|
+
["EXCESSIVE_SECRETS_EXPOSURE" /* ExcessiveSecretsExposure */]: "Excessive Secrets Exposure",
|
|
1455
|
+
["TAINTED_NUMERIC_CAST" /* TaintedNumericCast */]: "Tainted Numeric Cast"
|
|
1454
1456
|
};
|
|
1455
1457
|
issueTypeZ = z.nativeEnum(IssueType_Enum);
|
|
1456
1458
|
getIssueTypeFriendlyString = (issueType) => {
|
|
@@ -3577,8 +3579,8 @@ var init_FileUtils = __esm({
|
|
|
3577
3579
|
const fullPath = path.join(dir, item);
|
|
3578
3580
|
try {
|
|
3579
3581
|
await fsPromises.access(fullPath, fs.constants.R_OK);
|
|
3580
|
-
const
|
|
3581
|
-
if (
|
|
3582
|
+
const stat3 = await fsPromises.stat(fullPath);
|
|
3583
|
+
if (stat3.isDirectory()) {
|
|
3582
3584
|
if (isRootLevel && excludedRootDirectories.includes(item)) {
|
|
3583
3585
|
continue;
|
|
3584
3586
|
}
|
|
@@ -3590,7 +3592,7 @@ var init_FileUtils = __esm({
|
|
|
3590
3592
|
name: item,
|
|
3591
3593
|
fullPath,
|
|
3592
3594
|
relativePath: path.relative(rootDir, fullPath),
|
|
3593
|
-
time:
|
|
3595
|
+
time: stat3.mtime.getTime(),
|
|
3594
3596
|
isFile: true
|
|
3595
3597
|
});
|
|
3596
3598
|
}
|
|
@@ -4642,7 +4644,8 @@ var fixDetailsData = {
|
|
|
4642
4644
|
["DJANGO_BLANK_FIELD_NEEDS_NULL_OR_DEFAULT" /* DjangoBlankFieldNeedsNullOrDefault */]: void 0,
|
|
4643
4645
|
["REDUNDANT_NIL_ERROR_CHECK" /* RedundantNilErrorCheck */]: void 0,
|
|
4644
4646
|
["MISSING_WORKFLOW_PERMISSIONS" /* MissingWorkflowPermissions */]: void 0,
|
|
4645
|
-
["EXCESSIVE_SECRETS_EXPOSURE" /* ExcessiveSecretsExposure */]: void 0
|
|
4647
|
+
["EXCESSIVE_SECRETS_EXPOSURE" /* ExcessiveSecretsExposure */]: void 0,
|
|
4648
|
+
["TAINTED_NUMERIC_CAST" /* TaintedNumericCast */]: void 0
|
|
4646
4649
|
};
|
|
4647
4650
|
|
|
4648
4651
|
// src/features/analysis/scm/shared/src/commitDescriptionMarkup.ts
|
|
@@ -6174,6 +6177,17 @@ var openRedirect3 = {
|
|
|
6174
6177
|
}
|
|
6175
6178
|
};
|
|
6176
6179
|
|
|
6180
|
+
// src/features/analysis/scm/shared/src/storedQuestionData/python/ssrf.ts
|
|
6181
|
+
var ssrf5 = {
|
|
6182
|
+
domainsAllowlist: {
|
|
6183
|
+
content: () => "Allowed URL prefixes",
|
|
6184
|
+
description: () => `The security risk of this issue is the ability of an attacker to provide input that shoots HTTP requests from your server to arbitrary URLs, including internal ones, like \`https://admin.mycompany.com\`
|
|
6185
|
+
|
|
6186
|
+
To eliminate the risk and fix the issue, check out your app logic and make a whitelist of URLs this API should be allowed to call.`,
|
|
6187
|
+
guidance: () => ""
|
|
6188
|
+
}
|
|
6189
|
+
};
|
|
6190
|
+
|
|
6177
6191
|
// src/features/analysis/scm/shared/src/storedQuestionData/python/uncheckedLoopCondition.ts
|
|
6178
6192
|
var uncheckedLoopCondition3 = {
|
|
6179
6193
|
loopLimit: {
|
|
@@ -6195,7 +6209,8 @@ var vulnerabilities14 = {
|
|
|
6195
6209
|
["OPEN_REDIRECT" /* OpenRedirect */]: openRedirect3,
|
|
6196
6210
|
["UNCHECKED_LOOP_CONDITION" /* UncheckedLoopCondition */]: uncheckedLoopCondition3,
|
|
6197
6211
|
["DUPLICATED_STRINGS" /* DuplicatedStrings */]: duplicatedStrings2,
|
|
6198
|
-
["MISSING_ENCODING_FILE_OPEN" /* MissingEncodingFileOpen */]: missingEncoding
|
|
6212
|
+
["MISSING_ENCODING_FILE_OPEN" /* MissingEncodingFileOpen */]: missingEncoding,
|
|
6213
|
+
["SSRF" /* Ssrf */]: ssrf5
|
|
6199
6214
|
};
|
|
6200
6215
|
var python_default2 = vulnerabilities14;
|
|
6201
6216
|
|
|
@@ -7117,7 +7132,7 @@ async function getAdoSdk(params) {
|
|
|
7117
7132
|
const url = new URL(repoUrl);
|
|
7118
7133
|
const origin = url.origin.toLowerCase().endsWith(".visualstudio.com") ? DEFUALT_ADO_ORIGIN : url.origin.toLowerCase();
|
|
7119
7134
|
const params2 = `path=/&versionDescriptor[versionOptions]=0&versionDescriptor[versionType]=commit&versionDescriptor[version]=${branch}&resolveLfs=true&$format=zip&api-version=5.0&download=true`;
|
|
7120
|
-
const
|
|
7135
|
+
const path32 = [
|
|
7121
7136
|
prefixPath,
|
|
7122
7137
|
owner,
|
|
7123
7138
|
projectName,
|
|
@@ -7128,7 +7143,7 @@ async function getAdoSdk(params) {
|
|
|
7128
7143
|
"items",
|
|
7129
7144
|
"items"
|
|
7130
7145
|
].filter(Boolean).join("/");
|
|
7131
|
-
return new URL(`${
|
|
7146
|
+
return new URL(`${path32}?${params2}`, origin).toString();
|
|
7132
7147
|
},
|
|
7133
7148
|
async getAdoBranchList({ repoUrl }) {
|
|
7134
7149
|
try {
|
|
@@ -7217,8 +7232,8 @@ async function getAdoSdk(params) {
|
|
|
7217
7232
|
const changeType = entry.changeType;
|
|
7218
7233
|
return changeType !== 16 && entry.item?.path;
|
|
7219
7234
|
}).map((entry) => {
|
|
7220
|
-
const
|
|
7221
|
-
return
|
|
7235
|
+
const path32 = entry.item.path;
|
|
7236
|
+
return path32.startsWith("/") ? path32.slice(1) : path32;
|
|
7222
7237
|
});
|
|
7223
7238
|
},
|
|
7224
7239
|
async searchAdoPullRequests({
|
|
@@ -8091,6 +8106,15 @@ var BitbucketParseResultZ = z20.object({
|
|
|
8091
8106
|
repoName: z20.string(),
|
|
8092
8107
|
hostname: z20.literal(BITBUCKET_HOSTNAME)
|
|
8093
8108
|
});
|
|
8109
|
+
var UserWorkspacePermissionsRepositoriesResponseZ = z20.object({
|
|
8110
|
+
values: z20.array(
|
|
8111
|
+
z20.object({
|
|
8112
|
+
repository: z20.object({
|
|
8113
|
+
full_name: z20.string().optional()
|
|
8114
|
+
}).optional()
|
|
8115
|
+
})
|
|
8116
|
+
).optional()
|
|
8117
|
+
});
|
|
8094
8118
|
function parseBitbucketOrganizationAndRepo(bitbucketUrl) {
|
|
8095
8119
|
const parsedGitHubUrl = normalizeUrl(bitbucketUrl);
|
|
8096
8120
|
const parsingResult = parseScmURL(parsedGitHubUrl, "Bitbucket" /* Bitbucket */);
|
|
@@ -8155,12 +8179,17 @@ function getBitbucketSdk(params) {
|
|
|
8155
8179
|
const { repoUrl } = params2;
|
|
8156
8180
|
const { repo_slug, workspace } = parseBitbucketOrganizationAndRepo(repoUrl);
|
|
8157
8181
|
const fullRepoName = `${workspace}/${repo_slug}`;
|
|
8158
|
-
const res = await bitbucketClient.
|
|
8182
|
+
const res = await bitbucketClient.request({
|
|
8183
|
+
method: "GET",
|
|
8184
|
+
url: "/user/workspaces/{workspace}/permissions/repositories",
|
|
8185
|
+
workspace,
|
|
8159
8186
|
q: `repository.full_name~"${fullRepoName}"`
|
|
8160
8187
|
});
|
|
8161
|
-
|
|
8162
|
-
|
|
8163
|
-
)
|
|
8188
|
+
const parsed = UserWorkspacePermissionsRepositoriesResponseZ.safeParse(
|
|
8189
|
+
res.data
|
|
8190
|
+
);
|
|
8191
|
+
const values = parsed.success ? parsed.data.values : void 0;
|
|
8192
|
+
return values?.some((v) => v.repository?.full_name === fullRepoName) ?? false;
|
|
8164
8193
|
},
|
|
8165
8194
|
async createPullRequest(params2) {
|
|
8166
8195
|
const { repo_slug, workspace } = parseBitbucketOrganizationAndRepo(
|
|
@@ -8926,12 +8955,18 @@ function getOctoKit(options) {
|
|
|
8926
8955
|
timeout: 1e4
|
|
8927
8956
|
// 10 second timeout
|
|
8928
8957
|
},
|
|
8929
|
-
retry
|
|
8930
|
-
|
|
8931
|
-
|
|
8958
|
+
// Always retry on transient failures. 401 is intentionally retryable:
|
|
8959
|
+
// GitHub briefly returns 401 for an OAuth token in the first few seconds
|
|
8960
|
+
// after it is minted, and without this, validateRepoUrl and every
|
|
8961
|
+
// downstream SCM call can fail permanently on that propagation glitch.
|
|
8962
|
+
// Trade-off: a genuinely revoked/invalid token surfaces after ~14s of
|
|
8963
|
+
// backoff (@octokit/plugin-retry uses retryCount^2 * 1000 ms: 1s, 4s, 9s)
|
|
8964
|
+
// instead of immediately. Acceptable given the alternative is permanent
|
|
8965
|
+
// failure / 10-minute test timeouts.
|
|
8966
|
+
retry: {
|
|
8967
|
+
doNotRetry: [400, 403, 404, 422],
|
|
8932
8968
|
retries: 3
|
|
8933
|
-
|
|
8934
|
-
} : { enabled: false },
|
|
8969
|
+
},
|
|
8935
8970
|
throttle: options?.isEnableRetries ? {
|
|
8936
8971
|
onRateLimit: (retryAfter, options2, octokit, retryCount) => {
|
|
8937
8972
|
octokit.log.warn(
|
|
@@ -9593,10 +9628,13 @@ function getGithubSdk(params = {}) {
|
|
|
9593
9628
|
return res;
|
|
9594
9629
|
},
|
|
9595
9630
|
/**
|
|
9596
|
-
*
|
|
9597
|
-
* https://docs.github.com/en/rest/
|
|
9631
|
+
* List PRs using GitHub's REST `/repos/{owner}/{repo}/pulls` endpoint.
|
|
9632
|
+
* https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#list-pull-requests
|
|
9633
|
+
*
|
|
9634
|
+
* Uses the 5000/hr core rate-limit bucket and reads freshly-created PRs
|
|
9635
|
+
* without the indexing lag of the Search API.
|
|
9598
9636
|
*/
|
|
9599
|
-
async
|
|
9637
|
+
async listPullRequests(params2) {
|
|
9600
9638
|
const {
|
|
9601
9639
|
owner,
|
|
9602
9640
|
repo,
|
|
@@ -9606,26 +9644,22 @@ function getGithubSdk(params = {}) {
|
|
|
9606
9644
|
perPage = 10,
|
|
9607
9645
|
page = 1
|
|
9608
9646
|
} = params2;
|
|
9609
|
-
|
|
9610
|
-
|
|
9611
|
-
|
|
9612
|
-
|
|
9613
|
-
|
|
9614
|
-
|
|
9615
|
-
|
|
9616
|
-
}
|
|
9617
|
-
const githubSortField = sort.field === "updated" || sort.field === "created" ? sort.field : "comments";
|
|
9618
|
-
const response = await octokit.rest.search.issuesAndPullRequests({
|
|
9619
|
-
q: query,
|
|
9620
|
-
sort: githubSortField,
|
|
9621
|
-
order: sort.order,
|
|
9647
|
+
const restSortField = sort.field === "updated" || sort.field === "created" ? sort.field : "popularity";
|
|
9648
|
+
const response = await octokit.rest.pulls.list({
|
|
9649
|
+
owner,
|
|
9650
|
+
repo,
|
|
9651
|
+
state,
|
|
9652
|
+
sort: restSortField,
|
|
9653
|
+
direction: sort.order,
|
|
9622
9654
|
per_page: perPage,
|
|
9623
9655
|
page
|
|
9624
9656
|
});
|
|
9657
|
+
const filtered = updatedAfter ? response.data.filter((pr) => new Date(pr.updated_at) >= updatedAfter) : response.data;
|
|
9658
|
+
const hitDescCutoff = sort.order === "desc" && updatedAfter !== void 0 && filtered.length < response.data.length;
|
|
9659
|
+
const hasMore = response.data.length === perPage && !hitDescCutoff;
|
|
9625
9660
|
return {
|
|
9626
|
-
items:
|
|
9627
|
-
|
|
9628
|
-
hasMore: page * perPage < response.data.total_count
|
|
9661
|
+
items: filtered,
|
|
9662
|
+
hasMore
|
|
9629
9663
|
};
|
|
9630
9664
|
},
|
|
9631
9665
|
/**
|
|
@@ -10225,8 +10259,12 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
10225
10259
|
}).map((file) => file.filename);
|
|
10226
10260
|
}
|
|
10227
10261
|
/**
|
|
10228
|
-
* Override searchSubmitRequests to use GitHub's
|
|
10229
|
-
*
|
|
10262
|
+
* Override searchSubmitRequests to use GitHub's REST `/pulls` endpoint.
|
|
10263
|
+
*
|
|
10264
|
+
* The Search API we used previously has a separate 30/min secondary rate
|
|
10265
|
+
* limit and an async index that lags on just-created PRs. `/pulls` hits
|
|
10266
|
+
* GitHub's primary datastore, uses the 5000/hr core bucket, and returns
|
|
10267
|
+
* `head.ref` / `base.ref` so we can populate sourceBranch / targetBranch.
|
|
10230
10268
|
*/
|
|
10231
10269
|
async searchSubmitRequests(params) {
|
|
10232
10270
|
this._validateAccessToken();
|
|
@@ -10234,7 +10272,7 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
10234
10272
|
const page = parseCursorSafe(params.cursor, 1);
|
|
10235
10273
|
const perPage = params.limit || 10;
|
|
10236
10274
|
const sort = params.sort || { field: "updated", order: "desc" };
|
|
10237
|
-
const
|
|
10275
|
+
const listResult = await this.githubSdk.listPullRequests({
|
|
10238
10276
|
owner,
|
|
10239
10277
|
repo,
|
|
10240
10278
|
updatedAfter: params.filters?.updatedAfter,
|
|
@@ -10243,38 +10281,33 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
10243
10281
|
perPage,
|
|
10244
10282
|
page
|
|
10245
10283
|
});
|
|
10246
|
-
const results =
|
|
10284
|
+
const results = listResult.items.map((pr) => {
|
|
10247
10285
|
let status = "open";
|
|
10248
|
-
if (
|
|
10249
|
-
status =
|
|
10250
|
-
} else if (
|
|
10286
|
+
if (pr.state === "closed") {
|
|
10287
|
+
status = pr.merged_at ? "merged" : "closed";
|
|
10288
|
+
} else if (pr.draft) {
|
|
10251
10289
|
status = "draft";
|
|
10252
10290
|
}
|
|
10253
10291
|
return {
|
|
10254
|
-
submitRequestId: String(
|
|
10255
|
-
submitRequestNumber:
|
|
10256
|
-
title:
|
|
10292
|
+
submitRequestId: String(pr.number),
|
|
10293
|
+
submitRequestNumber: pr.number,
|
|
10294
|
+
title: pr.title,
|
|
10257
10295
|
status,
|
|
10258
|
-
sourceBranch: "",
|
|
10259
|
-
|
|
10260
|
-
|
|
10261
|
-
// Not available in search API
|
|
10262
|
-
authorName: issue.user?.login,
|
|
10296
|
+
sourceBranch: pr.head?.ref ?? "",
|
|
10297
|
+
targetBranch: pr.base?.ref ?? "",
|
|
10298
|
+
authorName: pr.user?.login,
|
|
10263
10299
|
authorEmail: void 0,
|
|
10264
|
-
|
|
10265
|
-
|
|
10266
|
-
|
|
10267
|
-
description: issue.body || void 0,
|
|
10300
|
+
createdAt: new Date(pr.created_at),
|
|
10301
|
+
updatedAt: new Date(pr.updated_at),
|
|
10302
|
+
description: pr.body || void 0,
|
|
10268
10303
|
tickets: [],
|
|
10269
|
-
// Would need separate parsing
|
|
10270
10304
|
changedLines: { added: 0, removed: 0 }
|
|
10271
|
-
// Not available in search API
|
|
10272
10305
|
};
|
|
10273
10306
|
});
|
|
10274
10307
|
return {
|
|
10275
10308
|
results,
|
|
10276
|
-
nextCursor:
|
|
10277
|
-
hasMore:
|
|
10309
|
+
nextCursor: listResult.hasMore ? String(page + 1) : void 0,
|
|
10310
|
+
hasMore: listResult.hasMore
|
|
10278
10311
|
};
|
|
10279
10312
|
}
|
|
10280
10313
|
/**
|
|
@@ -13893,13 +13926,13 @@ function maskString(str, showStart = 2, showEnd = 2) {
|
|
|
13893
13926
|
}
|
|
13894
13927
|
return str.slice(0, showStart) + "*".repeat(str.length - showStart - showEnd) + str.slice(-showEnd);
|
|
13895
13928
|
}
|
|
13896
|
-
async function sanitizeDataWithCounts(obj) {
|
|
13929
|
+
async function sanitizeDataWithCounts(obj, options) {
|
|
13897
13930
|
const counts = {
|
|
13898
13931
|
detections: { total: 0, high: 0, medium: 0, low: 0 }
|
|
13899
13932
|
};
|
|
13900
13933
|
const MAX_SCAN_LENGTH = 1e5;
|
|
13901
13934
|
const sanitizeString = async (str) => {
|
|
13902
|
-
if (str.length > MAX_SCAN_LENGTH) {
|
|
13935
|
+
if (!options?.noSizeLimit && str.length > MAX_SCAN_LENGTH) {
|
|
13903
13936
|
return str;
|
|
13904
13937
|
}
|
|
13905
13938
|
let result = str;
|
|
@@ -14358,7 +14391,7 @@ async function prepareAndSendTracyRecords(client, rawRecords, workingDir, option
|
|
|
14358
14391
|
`${shouldSanitize ? "sanitize" : "serialize"} ${rawRecords.length} records`,
|
|
14359
14392
|
() => Promise.all(
|
|
14360
14393
|
rawRecords.map(async (record, index) => {
|
|
14361
|
-
if (record.rawData != null) {
|
|
14394
|
+
if (record.rawData != null && record.rawDataS3Key == null) {
|
|
14362
14395
|
const serialized = shouldSanitize ? await sanitizeRawData(record.rawData) : JSON.stringify(record.rawData);
|
|
14363
14396
|
serializedRawDataByIndex.set(index, serialized);
|
|
14364
14397
|
}
|
|
@@ -15028,7 +15061,7 @@ async function postIssueComment(params) {
|
|
|
15028
15061
|
fpDescription
|
|
15029
15062
|
} = params;
|
|
15030
15063
|
const {
|
|
15031
|
-
path:
|
|
15064
|
+
path: path32,
|
|
15032
15065
|
startLine,
|
|
15033
15066
|
vulnerabilityReportIssue: {
|
|
15034
15067
|
vulnerabilityReportIssueTags,
|
|
@@ -15043,7 +15076,7 @@ async function postIssueComment(params) {
|
|
|
15043
15076
|
Refresh the page in order to see the changes.`,
|
|
15044
15077
|
pull_number: pullRequest,
|
|
15045
15078
|
commit_id: commitSha,
|
|
15046
|
-
path:
|
|
15079
|
+
path: path32,
|
|
15047
15080
|
line: startLine
|
|
15048
15081
|
});
|
|
15049
15082
|
const commentId = commentRes.data.id;
|
|
@@ -15077,7 +15110,7 @@ async function postFixComment(params) {
|
|
|
15077
15110
|
scanner
|
|
15078
15111
|
} = params;
|
|
15079
15112
|
const {
|
|
15080
|
-
path:
|
|
15113
|
+
path: path32,
|
|
15081
15114
|
startLine,
|
|
15082
15115
|
vulnerabilityReportIssue: { fixId, vulnerabilityReportIssueTags, category },
|
|
15083
15116
|
vulnerabilityReportIssueId
|
|
@@ -15095,7 +15128,7 @@ async function postFixComment(params) {
|
|
|
15095
15128
|
Refresh the page in order to see the changes.`,
|
|
15096
15129
|
pull_number: pullRequest,
|
|
15097
15130
|
commit_id: commitSha,
|
|
15098
|
-
path:
|
|
15131
|
+
path: path32,
|
|
15099
15132
|
line: startLine
|
|
15100
15133
|
});
|
|
15101
15134
|
const commentId = commentRes.data.id;
|
|
@@ -16617,8 +16650,8 @@ async function resolveSkillScanInput(skillInput) {
|
|
|
16617
16650
|
if (!fs11.existsSync(resolvedPath)) {
|
|
16618
16651
|
return skillInput;
|
|
16619
16652
|
}
|
|
16620
|
-
const
|
|
16621
|
-
if (!
|
|
16653
|
+
const stat3 = fs11.statSync(resolvedPath);
|
|
16654
|
+
if (!stat3.isDirectory()) {
|
|
16622
16655
|
throw new CliError(
|
|
16623
16656
|
"Local skill input must be a directory containing SKILL.md"
|
|
16624
16657
|
);
|
|
@@ -17016,8 +17049,10 @@ async function analyzeHandler(args) {
|
|
|
17016
17049
|
import { spawn } from "child_process";
|
|
17017
17050
|
|
|
17018
17051
|
// src/features/claude_code/daemon.ts
|
|
17019
|
-
import
|
|
17052
|
+
import { readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
17053
|
+
import path19 from "path";
|
|
17020
17054
|
import { setTimeout as sleep2 } from "timers/promises";
|
|
17055
|
+
import Configstore3 from "configstore";
|
|
17021
17056
|
|
|
17022
17057
|
// src/features/claude_code/daemon_pid_file.ts
|
|
17023
17058
|
import fs13 from "fs";
|
|
@@ -17109,10 +17144,455 @@ var DaemonPidFile = class {
|
|
|
17109
17144
|
|
|
17110
17145
|
// src/features/claude_code/data_collector.ts
|
|
17111
17146
|
import { execFile } from "child_process";
|
|
17112
|
-
import { createHash as
|
|
17113
|
-
import { access, open as open4, readdir, readFile, unlink } from "fs/promises";
|
|
17114
|
-
import
|
|
17147
|
+
import { createHash as createHash3 } from "crypto";
|
|
17148
|
+
import { access, open as open4, readdir, readFile as readFile2, unlink } from "fs/promises";
|
|
17149
|
+
import path16 from "path";
|
|
17115
17150
|
import { promisify } from "util";
|
|
17151
|
+
|
|
17152
|
+
// src/features/analysis/context_file_processor.ts
|
|
17153
|
+
import { createHash } from "crypto";
|
|
17154
|
+
import path14 from "path";
|
|
17155
|
+
import AdmZip3 from "adm-zip";
|
|
17156
|
+
import pLimit6 from "p-limit";
|
|
17157
|
+
var SANITIZE_CONCURRENCY = 5;
|
|
17158
|
+
function md5Hex(data) {
|
|
17159
|
+
return createHash("md5").update(typeof data === "string" ? Buffer.from(data, "utf-8") : data).digest("hex");
|
|
17160
|
+
}
|
|
17161
|
+
async function sanitizeFileContent(content) {
|
|
17162
|
+
const { sanitizedData } = await sanitizeDataWithCounts(content, {
|
|
17163
|
+
noSizeLimit: true
|
|
17164
|
+
});
|
|
17165
|
+
return sanitizedData;
|
|
17166
|
+
}
|
|
17167
|
+
async function processContextFiles(regularFiles, skillGroups) {
|
|
17168
|
+
const limit = pLimit6(SANITIZE_CONCURRENCY);
|
|
17169
|
+
const processedFiles = await Promise.all(
|
|
17170
|
+
regularFiles.map(
|
|
17171
|
+
(entry) => limit(async () => {
|
|
17172
|
+
const sanitizedContent = await sanitizeFileContent(entry.content);
|
|
17173
|
+
const md5 = md5Hex(sanitizedContent);
|
|
17174
|
+
const sizeBytes = Buffer.byteLength(sanitizedContent, "utf-8");
|
|
17175
|
+
return { entry, sanitizedContent, md5, sizeBytes };
|
|
17176
|
+
})
|
|
17177
|
+
)
|
|
17178
|
+
);
|
|
17179
|
+
const processedSkills = await Promise.all(
|
|
17180
|
+
skillGroups.filter((group) => group.files.length > 0).map(
|
|
17181
|
+
(group) => limit(async () => {
|
|
17182
|
+
const zip = new AdmZip3();
|
|
17183
|
+
const sortedFiles = [...group.files].sort(
|
|
17184
|
+
(a, b) => a.path.localeCompare(b.path)
|
|
17185
|
+
);
|
|
17186
|
+
for (const file of sortedFiles) {
|
|
17187
|
+
const sanitizedContent = await sanitizeFileContent(file.content);
|
|
17188
|
+
const zipEntryName = group.isFolder ? path14.relative(group.skillPath, file.path).replace(/\\/g, "/") : path14.basename(file.path);
|
|
17189
|
+
zip.addFile(zipEntryName, Buffer.from(sanitizedContent, "utf-8"));
|
|
17190
|
+
}
|
|
17191
|
+
const zipBuffer = zip.toBuffer();
|
|
17192
|
+
const md5 = md5Hex(zipBuffer);
|
|
17193
|
+
return { group, zipBuffer, md5, sizeBytes: zipBuffer.byteLength };
|
|
17194
|
+
})
|
|
17195
|
+
)
|
|
17196
|
+
);
|
|
17197
|
+
return { files: processedFiles, skills: processedSkills };
|
|
17198
|
+
}
|
|
17199
|
+
|
|
17200
|
+
// src/features/analysis/context_file_scanner.ts
|
|
17201
|
+
import { readFile, stat } from "fs/promises";
|
|
17202
|
+
import { homedir } from "os";
|
|
17203
|
+
import path15 from "path";
|
|
17204
|
+
import { globby as globby2 } from "globby";
|
|
17205
|
+
var MAX_CONTEXT_FILE_SIZE = 20 * 1024 * 1024;
|
|
17206
|
+
var SKILL_CATEGORY = "skill";
|
|
17207
|
+
var SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
17208
|
+
var sessionMtimes = /* @__PURE__ */ new Map();
|
|
17209
|
+
function markContextFilesUploaded(sessionId, files, skills) {
|
|
17210
|
+
let entry = sessionMtimes.get(sessionId);
|
|
17211
|
+
if (!entry) {
|
|
17212
|
+
entry = { files: /* @__PURE__ */ new Map(), skills: /* @__PURE__ */ new Map(), lastUpdatedAt: Date.now() };
|
|
17213
|
+
sessionMtimes.set(sessionId, entry);
|
|
17214
|
+
}
|
|
17215
|
+
for (const f of files) {
|
|
17216
|
+
entry.files.set(f.path, f.mtimeMs);
|
|
17217
|
+
}
|
|
17218
|
+
if (skills) {
|
|
17219
|
+
for (const sg of skills) {
|
|
17220
|
+
entry.skills.set(sg.sessionKey, sg.maxMtimeMs);
|
|
17221
|
+
}
|
|
17222
|
+
}
|
|
17223
|
+
entry.lastUpdatedAt = Date.now();
|
|
17224
|
+
}
|
|
17225
|
+
var SCAN_PATHS = {
|
|
17226
|
+
"claude-code": [
|
|
17227
|
+
{ glob: "CLAUDE.md", category: "rule", root: "workspace" },
|
|
17228
|
+
{ glob: "CLAUDE.local.md", category: "rule", root: "workspace" },
|
|
17229
|
+
{ glob: "INSIGHTS.md", category: "rule", root: "workspace" },
|
|
17230
|
+
{ glob: "AGENTS.md", category: "rule", root: "workspace" },
|
|
17231
|
+
{ glob: ".claude/rules/**/*.md", category: "rule", root: "workspace" },
|
|
17232
|
+
{ glob: ".claude/CLAUDE.md", category: "rule", root: "home" },
|
|
17233
|
+
{ glob: ".claude/INSIGHTS.md", category: "rule", root: "home" },
|
|
17234
|
+
{ glob: ".claude/rules/**/*.md", category: "rule", root: "home" },
|
|
17235
|
+
{
|
|
17236
|
+
glob: ".claude/projects/*/memory/*.md",
|
|
17237
|
+
category: "memory",
|
|
17238
|
+
root: "home"
|
|
17239
|
+
},
|
|
17240
|
+
{
|
|
17241
|
+
glob: ".claude/skills/**/*",
|
|
17242
|
+
category: SKILL_CATEGORY,
|
|
17243
|
+
root: "workspace"
|
|
17244
|
+
},
|
|
17245
|
+
{ glob: ".claude/commands/*.md", category: "command", root: "workspace" },
|
|
17246
|
+
{
|
|
17247
|
+
glob: ".claude/agents/*.md",
|
|
17248
|
+
category: "agent-config",
|
|
17249
|
+
root: "workspace"
|
|
17250
|
+
},
|
|
17251
|
+
{ glob: ".claude/skills/**/*", category: SKILL_CATEGORY, root: "home" },
|
|
17252
|
+
{ glob: ".claude/commands/*.md", category: "command", root: "home" },
|
|
17253
|
+
{ glob: ".claude/agents/*.md", category: "agent-config", root: "home" },
|
|
17254
|
+
{ glob: ".claude/settings.json", category: "config", root: "workspace" },
|
|
17255
|
+
{
|
|
17256
|
+
glob: ".claude/settings.local.json",
|
|
17257
|
+
category: "config",
|
|
17258
|
+
root: "workspace"
|
|
17259
|
+
},
|
|
17260
|
+
{ glob: ".mcp.json", category: "mcp-config", root: "workspace" },
|
|
17261
|
+
{ glob: ".claude/.mcp.json", category: "mcp-config", root: "workspace" },
|
|
17262
|
+
{ glob: ".claude/settings.json", category: "config", root: "home" },
|
|
17263
|
+
{ glob: ".claudeignore", category: "ignore", root: "workspace" }
|
|
17264
|
+
],
|
|
17265
|
+
cursor: [
|
|
17266
|
+
{ glob: ".cursorrules", category: "rule", root: "workspace" },
|
|
17267
|
+
{ glob: ".cursor/rules/**/*.mdc", category: "rule", root: "workspace" },
|
|
17268
|
+
{ glob: ".cursor/mcp.json", category: "mcp-config", root: "workspace" },
|
|
17269
|
+
{ glob: ".cursor/mcp.json", category: "mcp-config", root: "home" },
|
|
17270
|
+
{ glob: ".cursorignore", category: "ignore", root: "workspace" }
|
|
17271
|
+
],
|
|
17272
|
+
copilot: [
|
|
17273
|
+
{
|
|
17274
|
+
glob: ".github/copilot-instructions.md",
|
|
17275
|
+
category: "rule",
|
|
17276
|
+
root: "workspace"
|
|
17277
|
+
},
|
|
17278
|
+
{
|
|
17279
|
+
glob: ".github/instructions/*.instructions.md",
|
|
17280
|
+
category: "rule",
|
|
17281
|
+
root: "workspace"
|
|
17282
|
+
},
|
|
17283
|
+
{
|
|
17284
|
+
glob: ".github/prompts/*.prompt.md",
|
|
17285
|
+
category: SKILL_CATEGORY,
|
|
17286
|
+
root: "workspace"
|
|
17287
|
+
},
|
|
17288
|
+
{
|
|
17289
|
+
glob: ".github/chatmodes/*.chatmode.md",
|
|
17290
|
+
category: SKILL_CATEGORY,
|
|
17291
|
+
root: "workspace"
|
|
17292
|
+
},
|
|
17293
|
+
{
|
|
17294
|
+
glob: ".config/github-copilot/global-copilot-instructions.md",
|
|
17295
|
+
category: "rule",
|
|
17296
|
+
root: "home"
|
|
17297
|
+
}
|
|
17298
|
+
]
|
|
17299
|
+
};
|
|
17300
|
+
function groupSkills(files, root, baseDir) {
|
|
17301
|
+
const skillFiles = files.filter((f) => f.category === SKILL_CATEGORY);
|
|
17302
|
+
const folderMap = /* @__PURE__ */ new Map();
|
|
17303
|
+
const standalone = [];
|
|
17304
|
+
for (const f of skillFiles) {
|
|
17305
|
+
const rel = path15.relative(baseDir, f.path).replace(/\\/g, "/");
|
|
17306
|
+
const skillsMarker = "skills/";
|
|
17307
|
+
const skillsIdx = rel.indexOf(skillsMarker);
|
|
17308
|
+
if (skillsIdx === -1) {
|
|
17309
|
+
standalone.push(f);
|
|
17310
|
+
continue;
|
|
17311
|
+
}
|
|
17312
|
+
const relFromSkills = rel.slice(skillsIdx + skillsMarker.length);
|
|
17313
|
+
const slashIdx = relFromSkills.indexOf("/");
|
|
17314
|
+
if (slashIdx === -1) {
|
|
17315
|
+
standalone.push(f);
|
|
17316
|
+
} else {
|
|
17317
|
+
const folderName = relFromSkills.slice(0, slashIdx);
|
|
17318
|
+
if (!folderMap.has(folderName)) {
|
|
17319
|
+
folderMap.set(folderName, []);
|
|
17320
|
+
}
|
|
17321
|
+
folderMap.get(folderName).push(f);
|
|
17322
|
+
}
|
|
17323
|
+
}
|
|
17324
|
+
const groups = [];
|
|
17325
|
+
for (const f of standalone) {
|
|
17326
|
+
const name = path15.basename(f.path, path15.extname(f.path));
|
|
17327
|
+
const sessionKey = `skill:${root}:${name}`;
|
|
17328
|
+
groups.push({
|
|
17329
|
+
name,
|
|
17330
|
+
root,
|
|
17331
|
+
skillPath: f.path,
|
|
17332
|
+
files: [f],
|
|
17333
|
+
isFolder: false,
|
|
17334
|
+
maxMtimeMs: f.mtimeMs,
|
|
17335
|
+
sessionKey
|
|
17336
|
+
});
|
|
17337
|
+
}
|
|
17338
|
+
for (const [folderName, folderFiles] of folderMap) {
|
|
17339
|
+
const maxMtimeMs = Math.max(...folderFiles.map((f) => f.mtimeMs));
|
|
17340
|
+
const anyFile = folderFiles[0];
|
|
17341
|
+
const rel = path15.relative(baseDir, anyFile.path).replace(/\\/g, "/");
|
|
17342
|
+
const skillsIdx = rel.indexOf("skills/");
|
|
17343
|
+
const skillRelPath = rel.slice(
|
|
17344
|
+
0,
|
|
17345
|
+
skillsIdx + "skills/".length + folderName.length
|
|
17346
|
+
);
|
|
17347
|
+
const skillPath = path15.join(baseDir, skillRelPath);
|
|
17348
|
+
const sessionKey = `skill:${root}:${folderName}`;
|
|
17349
|
+
groups.push({
|
|
17350
|
+
name: folderName,
|
|
17351
|
+
root,
|
|
17352
|
+
skillPath,
|
|
17353
|
+
files: folderFiles,
|
|
17354
|
+
isFolder: true,
|
|
17355
|
+
maxMtimeMs,
|
|
17356
|
+
sessionKey
|
|
17357
|
+
});
|
|
17358
|
+
}
|
|
17359
|
+
return groups;
|
|
17360
|
+
}
|
|
17361
|
+
async function scanContextFiles(workspaceRoot, platform2, sessionId) {
|
|
17362
|
+
const entries = SCAN_PATHS[platform2];
|
|
17363
|
+
if (!entries || entries.length === 0) {
|
|
17364
|
+
return { regularFiles: [], skillGroups: [] };
|
|
17365
|
+
}
|
|
17366
|
+
const now = Date.now();
|
|
17367
|
+
for (const [sid, entry] of sessionMtimes) {
|
|
17368
|
+
if (now - entry.lastUpdatedAt > SESSION_TTL_MS) {
|
|
17369
|
+
sessionMtimes.delete(sid);
|
|
17370
|
+
}
|
|
17371
|
+
}
|
|
17372
|
+
const home = homedir();
|
|
17373
|
+
const sessionEntry = sessionId ? sessionMtimes.get(sessionId) : void 0;
|
|
17374
|
+
const allFiles = [];
|
|
17375
|
+
const skillFilesByRoot = /* @__PURE__ */ new Map();
|
|
17376
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
17377
|
+
for (const entry of entries) {
|
|
17378
|
+
const baseDir = entry.root === "home" ? home : workspaceRoot;
|
|
17379
|
+
const matchedFiles = await globby2(entry.glob, {
|
|
17380
|
+
cwd: baseDir,
|
|
17381
|
+
absolute: true,
|
|
17382
|
+
onlyFiles: true,
|
|
17383
|
+
dot: true
|
|
17384
|
+
});
|
|
17385
|
+
for (const filePath of matchedFiles) {
|
|
17386
|
+
if (seenPaths.has(filePath)) {
|
|
17387
|
+
continue;
|
|
17388
|
+
}
|
|
17389
|
+
seenPaths.add(filePath);
|
|
17390
|
+
try {
|
|
17391
|
+
const fileStat = await stat(filePath);
|
|
17392
|
+
if (fileStat.size === 0 || fileStat.size > MAX_CONTEXT_FILE_SIZE || !fileStat.isFile()) {
|
|
17393
|
+
continue;
|
|
17394
|
+
}
|
|
17395
|
+
const content = await readFile(filePath, "utf-8");
|
|
17396
|
+
const sizeBytes = Buffer.byteLength(content, "utf-8");
|
|
17397
|
+
const name = deriveIdentifier(filePath, baseDir);
|
|
17398
|
+
const fileEntry = {
|
|
17399
|
+
name,
|
|
17400
|
+
path: filePath,
|
|
17401
|
+
content,
|
|
17402
|
+
sizeBytes,
|
|
17403
|
+
category: entry.category,
|
|
17404
|
+
mtimeMs: fileStat.mtimeMs
|
|
17405
|
+
};
|
|
17406
|
+
if (entry.category === SKILL_CATEGORY) {
|
|
17407
|
+
let rootFiles = skillFilesByRoot.get(entry.root);
|
|
17408
|
+
if (!rootFiles) {
|
|
17409
|
+
rootFiles = [];
|
|
17410
|
+
skillFilesByRoot.set(entry.root, rootFiles);
|
|
17411
|
+
}
|
|
17412
|
+
rootFiles.push(fileEntry);
|
|
17413
|
+
} else {
|
|
17414
|
+
const prevMtime = sessionEntry?.files.get(filePath);
|
|
17415
|
+
if (prevMtime !== void 0 && fileStat.mtimeMs <= prevMtime) {
|
|
17416
|
+
continue;
|
|
17417
|
+
}
|
|
17418
|
+
allFiles.push(fileEntry);
|
|
17419
|
+
}
|
|
17420
|
+
} catch (_err) {
|
|
17421
|
+
}
|
|
17422
|
+
}
|
|
17423
|
+
}
|
|
17424
|
+
const allSkillGroups = [];
|
|
17425
|
+
for (const [root, files] of skillFilesByRoot) {
|
|
17426
|
+
const baseDir = root === "home" ? home : workspaceRoot;
|
|
17427
|
+
const groups = groupSkills(files, root, baseDir);
|
|
17428
|
+
for (const group of groups) {
|
|
17429
|
+
if (sessionEntry) {
|
|
17430
|
+
const prevMtime = sessionEntry.skills.get(group.sessionKey);
|
|
17431
|
+
if (prevMtime !== void 0 && group.maxMtimeMs <= prevMtime) {
|
|
17432
|
+
continue;
|
|
17433
|
+
}
|
|
17434
|
+
}
|
|
17435
|
+
allSkillGroups.push(group);
|
|
17436
|
+
}
|
|
17437
|
+
}
|
|
17438
|
+
return { regularFiles: allFiles, skillGroups: allSkillGroups };
|
|
17439
|
+
}
|
|
17440
|
+
function deriveIdentifier(filePath, baseDir) {
|
|
17441
|
+
const relative2 = path15.relative(baseDir, filePath);
|
|
17442
|
+
if (!relative2.startsWith("..")) {
|
|
17443
|
+
return relative2.replace(/\\/g, "/");
|
|
17444
|
+
}
|
|
17445
|
+
return path15.basename(filePath);
|
|
17446
|
+
}
|
|
17447
|
+
|
|
17448
|
+
// src/features/analysis/context_file_uploader.ts
|
|
17449
|
+
import pLimit7 from "p-limit";
|
|
17450
|
+
init_client_generates();
|
|
17451
|
+
var UPLOAD_CONCURRENCY = 5;
|
|
17452
|
+
async function uploadContextRecords(opts) {
|
|
17453
|
+
const {
|
|
17454
|
+
processedFiles,
|
|
17455
|
+
processedSkills,
|
|
17456
|
+
keyPrefix,
|
|
17457
|
+
url,
|
|
17458
|
+
uploadFields,
|
|
17459
|
+
sessionId,
|
|
17460
|
+
now,
|
|
17461
|
+
platform: platform2,
|
|
17462
|
+
repositoryUrl,
|
|
17463
|
+
clientVersion,
|
|
17464
|
+
onFileError,
|
|
17465
|
+
onSkillError
|
|
17466
|
+
} = opts;
|
|
17467
|
+
const records = [];
|
|
17468
|
+
const uploadedFiles = [];
|
|
17469
|
+
const uploadedSkillGroups = [];
|
|
17470
|
+
const limit = pLimit7(UPLOAD_CONCURRENCY);
|
|
17471
|
+
const extraFields = {
|
|
17472
|
+
...repositoryUrl !== void 0 && { repositoryUrl },
|
|
17473
|
+
...clientVersion !== void 0 && { clientVersion }
|
|
17474
|
+
};
|
|
17475
|
+
const tasks = [
|
|
17476
|
+
...processedFiles.map(
|
|
17477
|
+
(pf) => limit(async () => {
|
|
17478
|
+
const s3Key = `${keyPrefix}ctx-${pf.md5}.bin`;
|
|
17479
|
+
try {
|
|
17480
|
+
await uploadFile({
|
|
17481
|
+
file: Buffer.from(pf.sanitizedContent, "utf-8"),
|
|
17482
|
+
url,
|
|
17483
|
+
uploadKey: s3Key,
|
|
17484
|
+
uploadFields
|
|
17485
|
+
});
|
|
17486
|
+
} catch (err) {
|
|
17487
|
+
onFileError?.(pf.entry.name, err);
|
|
17488
|
+
return;
|
|
17489
|
+
}
|
|
17490
|
+
records.push({
|
|
17491
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17492
|
+
platform: platform2,
|
|
17493
|
+
recordId: `ctx:${sessionId}:${pf.md5}`,
|
|
17494
|
+
recordTimestamp: now,
|
|
17495
|
+
blameType: "CHAT" /* Chat */,
|
|
17496
|
+
rawDataS3Key: s3Key,
|
|
17497
|
+
...extraFields,
|
|
17498
|
+
context: {
|
|
17499
|
+
md5: pf.md5,
|
|
17500
|
+
category: pf.entry.category,
|
|
17501
|
+
name: pf.entry.name,
|
|
17502
|
+
sizeBytes: pf.sizeBytes,
|
|
17503
|
+
filePath: pf.entry.path,
|
|
17504
|
+
sessionId
|
|
17505
|
+
}
|
|
17506
|
+
});
|
|
17507
|
+
uploadedFiles.push(pf.entry);
|
|
17508
|
+
})
|
|
17509
|
+
),
|
|
17510
|
+
...processedSkills.map(
|
|
17511
|
+
(ps) => limit(async () => {
|
|
17512
|
+
const s3Key = `${keyPrefix}skill-${ps.md5}.zip`;
|
|
17513
|
+
try {
|
|
17514
|
+
await uploadFile({
|
|
17515
|
+
file: ps.zipBuffer,
|
|
17516
|
+
url,
|
|
17517
|
+
uploadKey: s3Key,
|
|
17518
|
+
uploadFields
|
|
17519
|
+
});
|
|
17520
|
+
} catch (err) {
|
|
17521
|
+
onSkillError?.(ps.group.name, err);
|
|
17522
|
+
return;
|
|
17523
|
+
}
|
|
17524
|
+
records.push({
|
|
17525
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17526
|
+
platform: platform2,
|
|
17527
|
+
recordId: `ctx:${sessionId}:${ps.md5}`,
|
|
17528
|
+
recordTimestamp: now,
|
|
17529
|
+
blameType: "CHAT" /* Chat */,
|
|
17530
|
+
rawDataS3Key: s3Key,
|
|
17531
|
+
...extraFields,
|
|
17532
|
+
context: {
|
|
17533
|
+
md5: ps.md5,
|
|
17534
|
+
category: SKILL_CATEGORY,
|
|
17535
|
+
name: ps.group.name,
|
|
17536
|
+
sizeBytes: ps.sizeBytes,
|
|
17537
|
+
filePath: ps.group.skillPath,
|
|
17538
|
+
sessionId
|
|
17539
|
+
}
|
|
17540
|
+
});
|
|
17541
|
+
uploadedSkillGroups.push(ps.group);
|
|
17542
|
+
})
|
|
17543
|
+
)
|
|
17544
|
+
];
|
|
17545
|
+
await Promise.allSettled(tasks);
|
|
17546
|
+
return { records, uploadedFiles, uploadedSkillGroups };
|
|
17547
|
+
}
|
|
17548
|
+
async function runContextFileUploadPipeline(opts) {
|
|
17549
|
+
const {
|
|
17550
|
+
processedFiles,
|
|
17551
|
+
processedSkills,
|
|
17552
|
+
sessionId,
|
|
17553
|
+
platform: platform2,
|
|
17554
|
+
url,
|
|
17555
|
+
uploadFieldsJSON,
|
|
17556
|
+
keyPrefix,
|
|
17557
|
+
repositoryUrl,
|
|
17558
|
+
clientVersion,
|
|
17559
|
+
submitRecords,
|
|
17560
|
+
onFileError,
|
|
17561
|
+
onSkillError
|
|
17562
|
+
} = opts;
|
|
17563
|
+
let uploadFields;
|
|
17564
|
+
try {
|
|
17565
|
+
uploadFields = JSON.parse(uploadFieldsJSON);
|
|
17566
|
+
} catch {
|
|
17567
|
+
return null;
|
|
17568
|
+
}
|
|
17569
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
17570
|
+
const { records, uploadedFiles, uploadedSkillGroups } = await uploadContextRecords({
|
|
17571
|
+
processedFiles,
|
|
17572
|
+
processedSkills,
|
|
17573
|
+
keyPrefix,
|
|
17574
|
+
url,
|
|
17575
|
+
uploadFields,
|
|
17576
|
+
sessionId,
|
|
17577
|
+
now,
|
|
17578
|
+
platform: platform2,
|
|
17579
|
+
repositoryUrl,
|
|
17580
|
+
clientVersion,
|
|
17581
|
+
onFileError,
|
|
17582
|
+
onSkillError
|
|
17583
|
+
});
|
|
17584
|
+
if (records.length === 0) {
|
|
17585
|
+
return { fileCount: 0, skillCount: 0 };
|
|
17586
|
+
}
|
|
17587
|
+
await submitRecords(records);
|
|
17588
|
+
markContextFilesUploaded(sessionId, uploadedFiles, uploadedSkillGroups);
|
|
17589
|
+
return {
|
|
17590
|
+
fileCount: uploadedFiles.length,
|
|
17591
|
+
skillCount: uploadedSkillGroups.length
|
|
17592
|
+
};
|
|
17593
|
+
}
|
|
17594
|
+
|
|
17595
|
+
// src/features/claude_code/data_collector.ts
|
|
17116
17596
|
init_client_generates();
|
|
17117
17597
|
|
|
17118
17598
|
// src/utils/shared-logger/create-logger.ts
|
|
@@ -17126,6 +17606,8 @@ var DEFAULT_MAX_LOGS = 1e3;
|
|
|
17126
17606
|
var DEFAULT_MAX_HEARTBEAT = 100;
|
|
17127
17607
|
var LOGS_KEY = "logs";
|
|
17128
17608
|
var HEARTBEAT_KEY = "heartbeat";
|
|
17609
|
+
var MAX_DATA_CHARS = 2048;
|
|
17610
|
+
var MAX_SCOPE_KEYS = 20;
|
|
17129
17611
|
function createConfigstoreStream(store, opts) {
|
|
17130
17612
|
const maxLogs = opts.maxLogs ?? DEFAULT_MAX_LOGS;
|
|
17131
17613
|
const maxHeartbeat = opts.maxHeartbeat ?? DEFAULT_MAX_HEARTBEAT;
|
|
@@ -17141,6 +17623,10 @@ function createConfigstoreStream(store, opts) {
|
|
|
17141
17623
|
existing.push(...entries);
|
|
17142
17624
|
const trimmed = existing.length > max ? existing.slice(-max) : existing;
|
|
17143
17625
|
store.set(key, trimmed);
|
|
17626
|
+
const prefix = key.includes(":") ? key.split(":")[0] : null;
|
|
17627
|
+
if (prefix) {
|
|
17628
|
+
pruneStaleScopes(prefix);
|
|
17629
|
+
}
|
|
17144
17630
|
} catch {
|
|
17145
17631
|
try {
|
|
17146
17632
|
const lines = `${entries.map((e) => JSON.stringify(e)).join("\n")}
|
|
@@ -17150,11 +17636,40 @@ function createConfigstoreStream(store, opts) {
|
|
|
17150
17636
|
}
|
|
17151
17637
|
}
|
|
17152
17638
|
}
|
|
17639
|
+
function pruneStaleScopes(prefix) {
|
|
17640
|
+
const allKeys = Object.keys(store.all);
|
|
17641
|
+
const scopedKeys = allKeys.filter(
|
|
17642
|
+
(k) => k.startsWith(`${prefix}:`) && Array.isArray(store.get(k))
|
|
17643
|
+
);
|
|
17644
|
+
if (scopedKeys.length <= MAX_SCOPE_KEYS) {
|
|
17645
|
+
return;
|
|
17646
|
+
}
|
|
17647
|
+
const withTimestamp = scopedKeys.map((k) => {
|
|
17648
|
+
const entries = store.get(k);
|
|
17649
|
+
const last = entries.length > 0 ? entries[entries.length - 1] : void 0;
|
|
17650
|
+
return { key: k, lastTs: last?.timestamp ?? "" };
|
|
17651
|
+
});
|
|
17652
|
+
withTimestamp.sort((a, b) => a.lastTs.localeCompare(b.lastTs));
|
|
17653
|
+
const toDelete = withTimestamp.slice(
|
|
17654
|
+
0,
|
|
17655
|
+
withTimestamp.length - MAX_SCOPE_KEYS
|
|
17656
|
+
);
|
|
17657
|
+
for (const { key: k } of toDelete) {
|
|
17658
|
+
store.delete(k);
|
|
17659
|
+
}
|
|
17660
|
+
}
|
|
17153
17661
|
const writable = new stream.Writable({
|
|
17154
17662
|
write(chunk, _encoding, callback) {
|
|
17155
17663
|
callback();
|
|
17156
17664
|
try {
|
|
17157
17665
|
const parsed = JSON.parse(chunk.toString());
|
|
17666
|
+
let data = parsed.data;
|
|
17667
|
+
if (data !== void 0) {
|
|
17668
|
+
const serialized = JSON.stringify(data);
|
|
17669
|
+
if (serialized.length > MAX_DATA_CHARS) {
|
|
17670
|
+
data = `${serialized.slice(0, MAX_DATA_CHARS)}... [truncated, ${serialized.length} chars]`;
|
|
17671
|
+
}
|
|
17672
|
+
}
|
|
17158
17673
|
const entry = {
|
|
17159
17674
|
timestamp: parsed.time ? new Date(parsed.time).toISOString() : (/* @__PURE__ */ new Date()).toISOString(),
|
|
17160
17675
|
level: parsed.level ?? "info",
|
|
@@ -17162,7 +17677,7 @@ function createConfigstoreStream(store, opts) {
|
|
|
17162
17677
|
...parsed.durationMs !== void 0 && {
|
|
17163
17678
|
durationMs: parsed.durationMs
|
|
17164
17679
|
},
|
|
17165
|
-
...
|
|
17680
|
+
...data !== void 0 && { data }
|
|
17166
17681
|
};
|
|
17167
17682
|
const isHeartbeat = parsed.heartbeat === true;
|
|
17168
17683
|
if (opts.buffered) {
|
|
@@ -17192,8 +17707,8 @@ function createConfigstoreStream(store, opts) {
|
|
|
17192
17707
|
heartbeatBuffer.length = 0;
|
|
17193
17708
|
}
|
|
17194
17709
|
}
|
|
17195
|
-
function setScopePath(
|
|
17196
|
-
scopePath =
|
|
17710
|
+
function setScopePath(path32) {
|
|
17711
|
+
scopePath = path32;
|
|
17197
17712
|
}
|
|
17198
17713
|
return { writable, flush, setScopePath };
|
|
17199
17714
|
}
|
|
@@ -17274,10 +17789,10 @@ function createDdBatch(config2) {
|
|
|
17274
17789
|
}
|
|
17275
17790
|
|
|
17276
17791
|
// src/utils/shared-logger/hostname.ts
|
|
17277
|
-
import { createHash } from "crypto";
|
|
17792
|
+
import { createHash as createHash2 } from "crypto";
|
|
17278
17793
|
import os5 from "os";
|
|
17279
17794
|
function hashString(input) {
|
|
17280
|
-
return
|
|
17795
|
+
return createHash2("sha256").update(input).digest("hex").slice(0, 16);
|
|
17281
17796
|
}
|
|
17282
17797
|
function getPlainHostname() {
|
|
17283
17798
|
try {
|
|
@@ -17417,7 +17932,7 @@ function createLogger(config2) {
|
|
|
17417
17932
|
|
|
17418
17933
|
// src/features/claude_code/hook_logger.ts
|
|
17419
17934
|
var DD_RUM_TOKEN = true ? "pubf59c0182545bfb4c299175119f1abf9b" : "";
|
|
17420
|
-
var CLI_VERSION = true ? "1.3.
|
|
17935
|
+
var CLI_VERSION = true ? "1.3.7" : "unknown";
|
|
17421
17936
|
var NAMESPACE = "mobbdev-claude-code-hook-logs";
|
|
17422
17937
|
var claudeCodeVersion;
|
|
17423
17938
|
function buildDdTags() {
|
|
@@ -17495,11 +18010,11 @@ async function detectClaudeCodeVersion() {
|
|
|
17495
18010
|
}
|
|
17496
18011
|
function generateSyntheticId(sessionId, timestamp, type2, lineIndex) {
|
|
17497
18012
|
const input = `${sessionId ?? ""}:${timestamp ?? ""}:${type2 ?? ""}:${lineIndex}`;
|
|
17498
|
-
const hash =
|
|
18013
|
+
const hash = createHash3("sha256").update(input).digest("hex").slice(0, 16);
|
|
17499
18014
|
return `synth:${hash}`;
|
|
17500
18015
|
}
|
|
17501
18016
|
function getCursorKey(transcriptPath) {
|
|
17502
|
-
const hash =
|
|
18017
|
+
const hash = createHash3("sha256").update(transcriptPath).digest("hex").slice(0, 12);
|
|
17503
18018
|
return `cursor.${hash}`;
|
|
17504
18019
|
}
|
|
17505
18020
|
async function resolveTranscriptPath(transcriptPath, sessionId) {
|
|
@@ -17508,12 +18023,12 @@ async function resolveTranscriptPath(transcriptPath, sessionId) {
|
|
|
17508
18023
|
return transcriptPath;
|
|
17509
18024
|
} catch {
|
|
17510
18025
|
}
|
|
17511
|
-
const filename =
|
|
17512
|
-
const dirName =
|
|
17513
|
-
const projectsDir =
|
|
18026
|
+
const filename = path16.basename(transcriptPath);
|
|
18027
|
+
const dirName = path16.basename(path16.dirname(transcriptPath));
|
|
18028
|
+
const projectsDir = path16.dirname(path16.dirname(transcriptPath));
|
|
17514
18029
|
const baseDirName = dirName.replace(/[-.]claude-worktrees-.+$/, "");
|
|
17515
18030
|
if (baseDirName !== dirName) {
|
|
17516
|
-
const candidate =
|
|
18031
|
+
const candidate = path16.join(projectsDir, baseDirName, filename);
|
|
17517
18032
|
try {
|
|
17518
18033
|
await access(candidate);
|
|
17519
18034
|
hookLog.info(
|
|
@@ -17535,7 +18050,7 @@ async function resolveTranscriptPath(transcriptPath, sessionId) {
|
|
|
17535
18050
|
const dirs = await readdir(projectsDir);
|
|
17536
18051
|
for (const dir of dirs) {
|
|
17537
18052
|
if (dir === dirName) continue;
|
|
17538
|
-
const candidate =
|
|
18053
|
+
const candidate = path16.join(projectsDir, dir, filename);
|
|
17539
18054
|
try {
|
|
17540
18055
|
await access(candidate);
|
|
17541
18056
|
hookLog.info(
|
|
@@ -17566,9 +18081,9 @@ async function readNewTranscriptEntries(transcriptPath, sessionId, sessionStore,
|
|
|
17566
18081
|
if (cursor?.byteOffset) {
|
|
17567
18082
|
const fh = await open4(transcriptPath, "r");
|
|
17568
18083
|
try {
|
|
17569
|
-
const
|
|
17570
|
-
fileSize =
|
|
17571
|
-
if (cursor.byteOffset >=
|
|
18084
|
+
const stat3 = await fh.stat();
|
|
18085
|
+
fileSize = stat3.size;
|
|
18086
|
+
if (cursor.byteOffset >= stat3.size) {
|
|
17572
18087
|
hookLog.info({ data: { sessionId } }, "No new data in transcript file");
|
|
17573
18088
|
return {
|
|
17574
18089
|
entries: [],
|
|
@@ -17576,7 +18091,7 @@ async function readNewTranscriptEntries(transcriptPath, sessionId, sessionStore,
|
|
|
17576
18091
|
resolvedTranscriptPath: transcriptPath
|
|
17577
18092
|
};
|
|
17578
18093
|
}
|
|
17579
|
-
const buf = Buffer.alloc(
|
|
18094
|
+
const buf = Buffer.alloc(stat3.size - cursor.byteOffset);
|
|
17580
18095
|
await fh.read(buf, 0, buf.length, cursor.byteOffset);
|
|
17581
18096
|
content = buf.toString("utf-8");
|
|
17582
18097
|
} finally {
|
|
@@ -17594,7 +18109,7 @@ async function readNewTranscriptEntries(transcriptPath, sessionId, sessionStore,
|
|
|
17594
18109
|
"Read transcript file from offset"
|
|
17595
18110
|
);
|
|
17596
18111
|
} else {
|
|
17597
|
-
content = await
|
|
18112
|
+
content = await readFile2(transcriptPath, "utf-8");
|
|
17598
18113
|
fileSize = Buffer.byteLength(content, "utf-8");
|
|
17599
18114
|
lineIndexOffset = 0;
|
|
17600
18115
|
hookLog.debug(
|
|
@@ -17693,14 +18208,6 @@ var FILTERED_ENTRY_TYPES = /* @__PURE__ */ new Set([
|
|
|
17693
18208
|
// Redundant — the actual user prompt is already captured in the 'user' entry.
|
|
17694
18209
|
"last-prompt"
|
|
17695
18210
|
]);
|
|
17696
|
-
var FILTERED_ASSISTANT_TOOLS = /* @__PURE__ */ new Set([
|
|
17697
|
-
// Polls for a sub-agent result. The input is just task_id + boilerplate
|
|
17698
|
-
// (block, timeout). The actual result is captured in the user:tool_result.
|
|
17699
|
-
"TaskOutput",
|
|
17700
|
-
// Discovers available deferred/MCP tools. The input is just a search query.
|
|
17701
|
-
// The discovered tools are captured in the user:tool_result.
|
|
17702
|
-
"ToolSearch"
|
|
17703
|
-
]);
|
|
17704
18211
|
function filterEntries(entries) {
|
|
17705
18212
|
const filtered = entries.filter((entry) => {
|
|
17706
18213
|
const entryType = entry.type ?? "";
|
|
@@ -17712,16 +18219,6 @@ function filterEntries(entries) {
|
|
|
17712
18219
|
const subtype = typeof data?.["type"] === "string" ? data["type"] : "";
|
|
17713
18220
|
return !FILTERED_PROGRESS_SUBTYPES.has(subtype);
|
|
17714
18221
|
}
|
|
17715
|
-
if (entryType === "assistant") {
|
|
17716
|
-
const message = entry["message"];
|
|
17717
|
-
const content = message?.["content"];
|
|
17718
|
-
if (Array.isArray(content) && content.length > 0) {
|
|
17719
|
-
const block = content[0];
|
|
17720
|
-
if (block["type"] === "tool_use" && typeof block["name"] === "string" && FILTERED_ASSISTANT_TOOLS.has(block["name"])) {
|
|
17721
|
-
return false;
|
|
17722
|
-
}
|
|
17723
|
-
}
|
|
17724
|
-
}
|
|
17725
18222
|
return true;
|
|
17726
18223
|
});
|
|
17727
18224
|
return { filtered, filteredOut: entries.length - filtered.length };
|
|
@@ -17738,9 +18235,9 @@ async function cleanupStaleSessions(configDir) {
|
|
|
17738
18235
|
let deletedCount = 0;
|
|
17739
18236
|
for (const file of files) {
|
|
17740
18237
|
if (!file.startsWith(prefix) || !file.endsWith(".json")) continue;
|
|
17741
|
-
const filePath =
|
|
18238
|
+
const filePath = path16.join(configDir, file);
|
|
17742
18239
|
try {
|
|
17743
|
-
const content = JSON.parse(await
|
|
18240
|
+
const content = JSON.parse(await readFile2(filePath, "utf-8"));
|
|
17744
18241
|
let newest = 0;
|
|
17745
18242
|
const cursors = content["cursor"];
|
|
17746
18243
|
if (cursors && typeof cursors === "object") {
|
|
@@ -17890,6 +18387,16 @@ async function processTranscript(input, sessionStore, log2, maxEntries = DAEMON_
|
|
|
17890
18387
|
entriesSkipped: filteredOut,
|
|
17891
18388
|
claudeCodeVersion: getClaudeCodeVersion()
|
|
17892
18389
|
});
|
|
18390
|
+
if (input.cwd) {
|
|
18391
|
+
uploadContextFilesIfNeeded(
|
|
18392
|
+
input.session_id,
|
|
18393
|
+
input.cwd,
|
|
18394
|
+
gqlClient,
|
|
18395
|
+
log2
|
|
18396
|
+
).catch((err) => {
|
|
18397
|
+
log2.error({ data: { err } }, "uploadContextFilesIfNeeded failed");
|
|
18398
|
+
});
|
|
18399
|
+
}
|
|
17893
18400
|
return {
|
|
17894
18401
|
entriesUploaded: entries.length,
|
|
17895
18402
|
entriesSkipped: filteredOut,
|
|
@@ -17906,19 +18413,84 @@ async function processTranscript(input, sessionStore, log2, maxEntries = DAEMON_
|
|
|
17906
18413
|
errors: entries.length
|
|
17907
18414
|
};
|
|
17908
18415
|
}
|
|
18416
|
+
async function uploadContextFilesIfNeeded(sessionId, cwd, gqlClient, log2) {
|
|
18417
|
+
const { regularFiles, skillGroups } = await scanContextFiles(
|
|
18418
|
+
cwd,
|
|
18419
|
+
"claude-code",
|
|
18420
|
+
sessionId
|
|
18421
|
+
);
|
|
18422
|
+
if (regularFiles.length === 0 && skillGroups.length === 0) {
|
|
18423
|
+
return;
|
|
18424
|
+
}
|
|
18425
|
+
const { files: processedFiles, skills: processedSkills } = await processContextFiles(regularFiles, skillGroups);
|
|
18426
|
+
if (processedFiles.length === 0 && processedSkills.length === 0) {
|
|
18427
|
+
return;
|
|
18428
|
+
}
|
|
18429
|
+
const uploadUrlResult = await gqlClient.getTracyRawDataUploadUrl();
|
|
18430
|
+
const { url, uploadFieldsJSON, keyPrefix } = uploadUrlResult.getTracyRawDataUploadUrl;
|
|
18431
|
+
if (!url || !uploadFieldsJSON || !keyPrefix) {
|
|
18432
|
+
log2.error(
|
|
18433
|
+
{ data: { sessionId } },
|
|
18434
|
+
"Failed to get S3 upload URL for context files"
|
|
18435
|
+
);
|
|
18436
|
+
return;
|
|
18437
|
+
}
|
|
18438
|
+
const pipelineResult = await runContextFileUploadPipeline({
|
|
18439
|
+
processedFiles,
|
|
18440
|
+
processedSkills,
|
|
18441
|
+
sessionId,
|
|
18442
|
+
platform: "CLAUDE_CODE" /* ClaudeCode */,
|
|
18443
|
+
url,
|
|
18444
|
+
uploadFieldsJSON,
|
|
18445
|
+
keyPrefix,
|
|
18446
|
+
submitRecords: async (records) => {
|
|
18447
|
+
const r = await prepareAndSendTracyRecords(gqlClient, records, cwd);
|
|
18448
|
+
if (!r.ok) {
|
|
18449
|
+
throw new Error(r.errors?.join(", ") ?? "batch upload failed");
|
|
18450
|
+
}
|
|
18451
|
+
},
|
|
18452
|
+
onFileError: (name, err) => log2.error(
|
|
18453
|
+
{ data: { sessionId, name, err } },
|
|
18454
|
+
"Failed to upload context file to S3"
|
|
18455
|
+
),
|
|
18456
|
+
onSkillError: (name, err) => log2.error(
|
|
18457
|
+
{ data: { sessionId, name, err } },
|
|
18458
|
+
"Failed to upload skill zip to S3"
|
|
18459
|
+
)
|
|
18460
|
+
});
|
|
18461
|
+
if (pipelineResult === null) {
|
|
18462
|
+
log2.error(
|
|
18463
|
+
{ data: { sessionId } },
|
|
18464
|
+
"Malformed uploadFieldsJSON for context files"
|
|
18465
|
+
);
|
|
18466
|
+
return;
|
|
18467
|
+
}
|
|
18468
|
+
if (pipelineResult.fileCount > 0 || pipelineResult.skillCount > 0) {
|
|
18469
|
+
log2.info(
|
|
18470
|
+
{
|
|
18471
|
+
data: {
|
|
18472
|
+
sessionId,
|
|
18473
|
+
fileCount: pipelineResult.fileCount,
|
|
18474
|
+
skillCount: pipelineResult.skillCount
|
|
18475
|
+
}
|
|
18476
|
+
},
|
|
18477
|
+
"Uploaded context files and skills for session"
|
|
18478
|
+
);
|
|
18479
|
+
}
|
|
18480
|
+
}
|
|
17909
18481
|
|
|
17910
18482
|
// src/features/claude_code/install_hook.ts
|
|
17911
18483
|
import fs14 from "fs";
|
|
17912
18484
|
import fsPromises4 from "fs/promises";
|
|
17913
18485
|
import os6 from "os";
|
|
17914
|
-
import
|
|
18486
|
+
import path17 from "path";
|
|
17915
18487
|
import chalk11 from "chalk";
|
|
17916
18488
|
|
|
17917
18489
|
// src/features/claude_code/daemon-check-shim.tmpl.js
|
|
17918
18490
|
var daemon_check_shim_tmpl_default = "// Mobb daemon shim \u2014 checks if daemon is alive, spawns if dead.\n// Auto-generated by mobbdev CLI. Do not edit.\nvar fs = require('fs')\nvar spawn = require('child_process').spawn\nvar path = require('path')\nvar os = require('os')\n\nvar pidFile = path.join(os.homedir(), '.mobbdev', 'daemon.pid')\nvar HEARTBEAT_STALE_MS = __HEARTBEAT_STALE_MS__\n\ntry {\n var data = JSON.parse(fs.readFileSync(pidFile, 'utf8'))\n if (Date.now() - data.heartbeat > HEARTBEAT_STALE_MS) throw new Error('stale')\n process.kill(data.pid, 0) // throws ESRCH if the process is gone\n} catch (e) {\n var localCli = process.env.MOBBDEV_LOCAL_CLI\n var child = localCli\n ? spawn('node', [localCli, 'claude-code-daemon'], { detached: true, stdio: 'ignore', windowsHide: true })\n : spawn('npx', ['--yes', 'mobbdev@latest', 'claude-code-daemon'], { detached: true, stdio: 'ignore', shell: true, windowsHide: true })\n child.unref()\n}\n";
|
|
17919
18491
|
|
|
17920
18492
|
// src/features/claude_code/install_hook.ts
|
|
17921
|
-
var CLAUDE_SETTINGS_PATH =
|
|
18493
|
+
var CLAUDE_SETTINGS_PATH = path17.join(os6.homedir(), ".claude", "settings.json");
|
|
17922
18494
|
var RECOMMENDED_MATCHER = "*";
|
|
17923
18495
|
async function claudeSettingsExists() {
|
|
17924
18496
|
try {
|
|
@@ -18064,18 +18636,18 @@ async function installMobbHooks(options = {}) {
|
|
|
18064
18636
|
}
|
|
18065
18637
|
|
|
18066
18638
|
// src/features/claude_code/transcript_scanner.ts
|
|
18067
|
-
import { open as open5, readdir as readdir2, stat } from "fs/promises";
|
|
18639
|
+
import { open as open5, readdir as readdir2, stat as stat2 } from "fs/promises";
|
|
18068
18640
|
import os7 from "os";
|
|
18069
|
-
import
|
|
18641
|
+
import path18 from "path";
|
|
18070
18642
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
18071
18643
|
function getClaudeProjectsDirs() {
|
|
18072
18644
|
const dirs = [];
|
|
18073
18645
|
const configDir = process.env["CLAUDE_CONFIG_DIR"];
|
|
18074
18646
|
if (configDir) {
|
|
18075
|
-
dirs.push(
|
|
18647
|
+
dirs.push(path18.join(configDir, "projects"));
|
|
18076
18648
|
}
|
|
18077
|
-
dirs.push(
|
|
18078
|
-
dirs.push(
|
|
18649
|
+
dirs.push(path18.join(os7.homedir(), ".config", "claude", "projects"));
|
|
18650
|
+
dirs.push(path18.join(os7.homedir(), ".claude", "projects"));
|
|
18079
18651
|
return dirs;
|
|
18080
18652
|
}
|
|
18081
18653
|
async function collectJsonlFiles(files, dir, projectDir, seen, now, results) {
|
|
@@ -18083,12 +18655,12 @@ async function collectJsonlFiles(files, dir, projectDir, seen, now, results) {
|
|
|
18083
18655
|
if (!file.endsWith(".jsonl")) continue;
|
|
18084
18656
|
const sessionId = file.replace(".jsonl", "");
|
|
18085
18657
|
if (!UUID_RE.test(sessionId)) continue;
|
|
18086
|
-
const filePath =
|
|
18658
|
+
const filePath = path18.join(dir, file);
|
|
18087
18659
|
if (seen.has(filePath)) continue;
|
|
18088
18660
|
seen.add(filePath);
|
|
18089
18661
|
let fileStat;
|
|
18090
18662
|
try {
|
|
18091
|
-
fileStat = await
|
|
18663
|
+
fileStat = await stat2(filePath);
|
|
18092
18664
|
} catch {
|
|
18093
18665
|
continue;
|
|
18094
18666
|
}
|
|
@@ -18114,10 +18686,10 @@ async function scanForTranscripts(projectsDirs = getClaudeProjectsDirs()) {
|
|
|
18114
18686
|
continue;
|
|
18115
18687
|
}
|
|
18116
18688
|
for (const projName of projectDirs) {
|
|
18117
|
-
const projPath =
|
|
18689
|
+
const projPath = path18.join(projectsDir, projName);
|
|
18118
18690
|
let projStat;
|
|
18119
18691
|
try {
|
|
18120
|
-
projStat = await
|
|
18692
|
+
projStat = await stat2(projPath);
|
|
18121
18693
|
} catch {
|
|
18122
18694
|
continue;
|
|
18123
18695
|
}
|
|
@@ -18131,9 +18703,9 @@ async function scanForTranscripts(projectsDirs = getClaudeProjectsDirs()) {
|
|
|
18131
18703
|
await collectJsonlFiles(files, projPath, projPath, seen, now, results);
|
|
18132
18704
|
for (const entry of files) {
|
|
18133
18705
|
if (!UUID_RE.test(entry)) continue;
|
|
18134
|
-
const subagentsDir =
|
|
18706
|
+
const subagentsDir = path18.join(projPath, entry, "subagents");
|
|
18135
18707
|
try {
|
|
18136
|
-
const s = await
|
|
18708
|
+
const s = await stat2(subagentsDir);
|
|
18137
18709
|
if (!s.isDirectory()) continue;
|
|
18138
18710
|
const subFiles = await readdir2(subagentsDir);
|
|
18139
18711
|
await collectJsonlFiles(
|
|
@@ -18185,6 +18757,7 @@ async function extractCwdFromTranscript(filePath) {
|
|
|
18185
18757
|
// src/features/claude_code/daemon.ts
|
|
18186
18758
|
async function startDaemon() {
|
|
18187
18759
|
hookLog.info("Daemon starting");
|
|
18760
|
+
pruneHookLogFile();
|
|
18188
18761
|
const pidFile = await acquirePidFile();
|
|
18189
18762
|
async function gracefulExit(code, reason) {
|
|
18190
18763
|
hookLog.info({ data: { code } }, `Daemon exiting: ${reason}`);
|
|
@@ -18231,7 +18804,7 @@ async function startDaemon() {
|
|
|
18231
18804
|
for (const transcript of changed) {
|
|
18232
18805
|
const sessionStore = createSessionConfigStore(transcript.sessionId);
|
|
18233
18806
|
if (!cleanupConfigDir) {
|
|
18234
|
-
cleanupConfigDir =
|
|
18807
|
+
cleanupConfigDir = path19.dirname(sessionStore.path);
|
|
18235
18808
|
}
|
|
18236
18809
|
await drainTranscript(transcript, sessionStore, gqlClient);
|
|
18237
18810
|
}
|
|
@@ -18346,6 +18919,53 @@ async function tryAutoUpgradeHooks() {
|
|
|
18346
18919
|
hookLog.warn({ err }, "Failed to auto-upgrade hook matcher");
|
|
18347
18920
|
}
|
|
18348
18921
|
}
|
|
18922
|
+
var HOOK_LOG_MAX_SCOPE_KEYS = 20;
|
|
18923
|
+
var HOOK_LOG_MAX_ENTRIES_PER_KEY = 200;
|
|
18924
|
+
function pruneHookLogFile() {
|
|
18925
|
+
const logFilePath = new Configstore3("mobbdev-claude-code-hook-logs").path;
|
|
18926
|
+
try {
|
|
18927
|
+
const raw = readFileSync(logFilePath, "utf-8");
|
|
18928
|
+
const data = JSON.parse(raw);
|
|
18929
|
+
const prefixes = /* @__PURE__ */ new Map();
|
|
18930
|
+
for (const key of Object.keys(data)) {
|
|
18931
|
+
const colonIdx = key.indexOf(":");
|
|
18932
|
+
const prefix = colonIdx > 0 ? key.slice(0, colonIdx) : key;
|
|
18933
|
+
const group = prefixes.get(prefix) ?? [];
|
|
18934
|
+
group.push(key);
|
|
18935
|
+
prefixes.set(prefix, group);
|
|
18936
|
+
}
|
|
18937
|
+
let changed = false;
|
|
18938
|
+
for (const [, keys] of prefixes) {
|
|
18939
|
+
if (keys.length <= HOOK_LOG_MAX_SCOPE_KEYS) {
|
|
18940
|
+
continue;
|
|
18941
|
+
}
|
|
18942
|
+
const withTs = keys.map((k) => {
|
|
18943
|
+
const val = data[k];
|
|
18944
|
+
if (!Array.isArray(val) || val.length === 0) {
|
|
18945
|
+
return { key: k, lastTs: "" };
|
|
18946
|
+
}
|
|
18947
|
+
const last = val[val.length - 1];
|
|
18948
|
+
return { key: k, lastTs: last?.timestamp ?? "" };
|
|
18949
|
+
}).sort((a, b) => a.lastTs.localeCompare(b.lastTs));
|
|
18950
|
+
const toDelete = withTs.slice(0, withTs.length - HOOK_LOG_MAX_SCOPE_KEYS);
|
|
18951
|
+
for (const { key } of toDelete) {
|
|
18952
|
+
delete data[key];
|
|
18953
|
+
changed = true;
|
|
18954
|
+
}
|
|
18955
|
+
}
|
|
18956
|
+
for (const [key, val] of Object.entries(data)) {
|
|
18957
|
+
if (Array.isArray(val) && val.length > HOOK_LOG_MAX_ENTRIES_PER_KEY) {
|
|
18958
|
+
data[key] = val.slice(-HOOK_LOG_MAX_ENTRIES_PER_KEY);
|
|
18959
|
+
changed = true;
|
|
18960
|
+
}
|
|
18961
|
+
}
|
|
18962
|
+
if (changed) {
|
|
18963
|
+
writeFileSync2(logFilePath, JSON.stringify(data, null, " "));
|
|
18964
|
+
hookLog.info("Pruned hook log file");
|
|
18965
|
+
}
|
|
18966
|
+
} catch {
|
|
18967
|
+
}
|
|
18968
|
+
}
|
|
18349
18969
|
|
|
18350
18970
|
// src/args/commands/claude_code.ts
|
|
18351
18971
|
var claudeCodeInstallHookBuilder = (yargs2) => {
|
|
@@ -18434,7 +19054,7 @@ import {
|
|
|
18434
19054
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
18435
19055
|
|
|
18436
19056
|
// src/mcp/Logger.ts
|
|
18437
|
-
import
|
|
19057
|
+
import Configstore4 from "configstore";
|
|
18438
19058
|
|
|
18439
19059
|
// src/mcp/services/WorkspaceService.ts
|
|
18440
19060
|
var WorkspaceService = class {
|
|
@@ -18442,8 +19062,8 @@ var WorkspaceService = class {
|
|
|
18442
19062
|
* Sets a known workspace path that was discovered through successful validation
|
|
18443
19063
|
* @param path The validated workspace path to store
|
|
18444
19064
|
*/
|
|
18445
|
-
static setKnownWorkspacePath(
|
|
18446
|
-
this.knownWorkspacePath =
|
|
19065
|
+
static setKnownWorkspacePath(path32) {
|
|
19066
|
+
this.knownWorkspacePath = path32;
|
|
18447
19067
|
}
|
|
18448
19068
|
/**
|
|
18449
19069
|
* Gets the known workspace path that was previously validated
|
|
@@ -18520,7 +19140,7 @@ var Logger = class {
|
|
|
18520
19140
|
__publicField(this, "lastKnownPath", null);
|
|
18521
19141
|
this.host = WorkspaceService.getHost();
|
|
18522
19142
|
this.unknownPathSuffix = Math.floor(1e3 + Math.random() * 9e3).toString();
|
|
18523
|
-
this.mobbConfigStore = new
|
|
19143
|
+
this.mobbConfigStore = new Configstore4("mobb-logs", {});
|
|
18524
19144
|
this.mobbConfigStore.set("version", packageJson.version);
|
|
18525
19145
|
}
|
|
18526
19146
|
/**
|
|
@@ -19304,7 +19924,7 @@ async function createAuthenticatedMcpGQLClient({
|
|
|
19304
19924
|
import { execSync as execSync2 } from "child_process";
|
|
19305
19925
|
import fs15 from "fs";
|
|
19306
19926
|
import os8 from "os";
|
|
19307
|
-
import
|
|
19927
|
+
import path20 from "path";
|
|
19308
19928
|
var IDEs = ["cursor", "windsurf", "webstorm", "vscode", "claude"];
|
|
19309
19929
|
var runCommand = (cmd) => {
|
|
19310
19930
|
try {
|
|
@@ -19319,7 +19939,7 @@ var gitInfo = {
|
|
|
19319
19939
|
};
|
|
19320
19940
|
var getClaudeWorkspacePaths = () => {
|
|
19321
19941
|
const home = os8.homedir();
|
|
19322
|
-
const claudeIdePath =
|
|
19942
|
+
const claudeIdePath = path20.join(home, ".claude", "ide");
|
|
19323
19943
|
const workspacePaths = [];
|
|
19324
19944
|
if (!fs15.existsSync(claudeIdePath)) {
|
|
19325
19945
|
return workspacePaths;
|
|
@@ -19327,7 +19947,7 @@ var getClaudeWorkspacePaths = () => {
|
|
|
19327
19947
|
try {
|
|
19328
19948
|
const lockFiles = fs15.readdirSync(claudeIdePath).filter((file) => file.endsWith(".lock"));
|
|
19329
19949
|
for (const lockFile of lockFiles) {
|
|
19330
|
-
const lockFilePath =
|
|
19950
|
+
const lockFilePath = path20.join(claudeIdePath, lockFile);
|
|
19331
19951
|
try {
|
|
19332
19952
|
const lockContent = JSON.parse(fs15.readFileSync(lockFilePath, "utf8"));
|
|
19333
19953
|
if (lockContent.workspaceFolders && Array.isArray(lockContent.workspaceFolders)) {
|
|
@@ -19352,24 +19972,24 @@ var getMCPConfigPaths = (hostName) => {
|
|
|
19352
19972
|
switch (hostName.toLowerCase()) {
|
|
19353
19973
|
case "cursor":
|
|
19354
19974
|
return [
|
|
19355
|
-
|
|
19975
|
+
path20.join(currentDir, ".cursor", "mcp.json"),
|
|
19356
19976
|
// local first
|
|
19357
|
-
|
|
19977
|
+
path20.join(home, ".cursor", "mcp.json")
|
|
19358
19978
|
];
|
|
19359
19979
|
case "windsurf":
|
|
19360
19980
|
return [
|
|
19361
|
-
|
|
19981
|
+
path20.join(currentDir, ".codeium", "mcp_config.json"),
|
|
19362
19982
|
// local first
|
|
19363
|
-
|
|
19983
|
+
path20.join(home, ".codeium", "windsurf", "mcp_config.json")
|
|
19364
19984
|
];
|
|
19365
19985
|
case "webstorm":
|
|
19366
19986
|
return [];
|
|
19367
19987
|
case "visualstudiocode":
|
|
19368
19988
|
case "vscode":
|
|
19369
19989
|
return [
|
|
19370
|
-
|
|
19990
|
+
path20.join(currentDir, ".vscode", "mcp.json"),
|
|
19371
19991
|
// local first
|
|
19372
|
-
process.platform === "win32" ?
|
|
19992
|
+
process.platform === "win32" ? path20.join(home, "AppData", "Roaming", "Code", "User", "mcp.json") : path20.join(
|
|
19373
19993
|
home,
|
|
19374
19994
|
"Library",
|
|
19375
19995
|
"Application Support",
|
|
@@ -19380,13 +20000,13 @@ var getMCPConfigPaths = (hostName) => {
|
|
|
19380
20000
|
];
|
|
19381
20001
|
case "claude": {
|
|
19382
20002
|
const claudePaths = [
|
|
19383
|
-
|
|
20003
|
+
path20.join(currentDir, ".claude.json"),
|
|
19384
20004
|
// local first
|
|
19385
|
-
|
|
20005
|
+
path20.join(home, ".claude.json")
|
|
19386
20006
|
];
|
|
19387
20007
|
const workspacePaths = getClaudeWorkspacePaths();
|
|
19388
20008
|
for (const workspacePath of workspacePaths) {
|
|
19389
|
-
claudePaths.push(
|
|
20009
|
+
claudePaths.push(path20.join(workspacePath, ".mcp.json"));
|
|
19390
20010
|
}
|
|
19391
20011
|
return claudePaths;
|
|
19392
20012
|
}
|
|
@@ -19547,10 +20167,10 @@ var getHostInfo = (additionalMcpList) => {
|
|
|
19547
20167
|
const ideConfigPaths = /* @__PURE__ */ new Set();
|
|
19548
20168
|
for (const ide of IDEs) {
|
|
19549
20169
|
const configPaths = getMCPConfigPaths(ide);
|
|
19550
|
-
configPaths.forEach((
|
|
20170
|
+
configPaths.forEach((path32) => ideConfigPaths.add(path32));
|
|
19551
20171
|
}
|
|
19552
20172
|
const uniqueAdditionalPaths = additionalMcpList.filter(
|
|
19553
|
-
(
|
|
20173
|
+
(path32) => !ideConfigPaths.has(path32)
|
|
19554
20174
|
);
|
|
19555
20175
|
for (const ide of IDEs) {
|
|
19556
20176
|
const cfg = readMCPConfig(ide);
|
|
@@ -19672,7 +20292,7 @@ init_configs();
|
|
|
19672
20292
|
init_configs();
|
|
19673
20293
|
import fs16 from "fs";
|
|
19674
20294
|
import os9 from "os";
|
|
19675
|
-
import
|
|
20295
|
+
import path21 from "path";
|
|
19676
20296
|
var MAX_DEPTH = 2;
|
|
19677
20297
|
var patterns = ["mcp", "claude"];
|
|
19678
20298
|
var isFileMatch = (fileName) => {
|
|
@@ -19692,7 +20312,7 @@ var searchDir = async (dir, depth = 0) => {
|
|
|
19692
20312
|
if (depth > MAX_DEPTH) return results;
|
|
19693
20313
|
const entries = await fs16.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
19694
20314
|
for (const entry of entries) {
|
|
19695
|
-
const fullPath =
|
|
20315
|
+
const fullPath = path21.join(dir, entry.name);
|
|
19696
20316
|
if (entry.isFile() && isFileMatch(entry.name)) {
|
|
19697
20317
|
results.push(fullPath);
|
|
19698
20318
|
} else if (entry.isDirectory()) {
|
|
@@ -19709,14 +20329,14 @@ var findSystemMCPConfigs = async () => {
|
|
|
19709
20329
|
const home = os9.homedir();
|
|
19710
20330
|
const platform2 = os9.platform();
|
|
19711
20331
|
const knownDirs = platform2 === "win32" ? [
|
|
19712
|
-
|
|
19713
|
-
|
|
19714
|
-
|
|
20332
|
+
path21.join(home, ".cursor"),
|
|
20333
|
+
path21.join(home, "Documents"),
|
|
20334
|
+
path21.join(home, "Downloads")
|
|
19715
20335
|
] : [
|
|
19716
|
-
|
|
19717
|
-
process.env["XDG_CONFIG_HOME"] ||
|
|
19718
|
-
|
|
19719
|
-
|
|
20336
|
+
path21.join(home, ".cursor"),
|
|
20337
|
+
process.env["XDG_CONFIG_HOME"] || path21.join(home, ".config"),
|
|
20338
|
+
path21.join(home, "Documents"),
|
|
20339
|
+
path21.join(home, "Downloads")
|
|
19720
20340
|
];
|
|
19721
20341
|
const timeoutPromise = new Promise(
|
|
19722
20342
|
(resolve) => setTimeout(() => {
|
|
@@ -22132,13 +22752,13 @@ For a complete security audit workflow, use the \`full-security-audit\` prompt.
|
|
|
22132
22752
|
// src/mcp/services/McpDetectionService/CursorMcpDetectionService.ts
|
|
22133
22753
|
import * as fs19 from "fs";
|
|
22134
22754
|
import * as os12 from "os";
|
|
22135
|
-
import * as
|
|
22755
|
+
import * as path23 from "path";
|
|
22136
22756
|
|
|
22137
22757
|
// src/mcp/services/McpDetectionService/BaseMcpDetectionService.ts
|
|
22138
22758
|
init_configs();
|
|
22139
22759
|
import * as fs18 from "fs";
|
|
22140
22760
|
import fetch7 from "node-fetch";
|
|
22141
|
-
import * as
|
|
22761
|
+
import * as path22 from "path";
|
|
22142
22762
|
|
|
22143
22763
|
// src/mcp/services/McpDetectionService/McpDetectionServiceUtils.ts
|
|
22144
22764
|
import * as fs17 from "fs";
|
|
@@ -22147,14 +22767,14 @@ import * as os11 from "os";
|
|
|
22147
22767
|
// src/mcp/services/McpDetectionService/VscodeMcpDetectionService.ts
|
|
22148
22768
|
import * as fs20 from "fs";
|
|
22149
22769
|
import * as os13 from "os";
|
|
22150
|
-
import * as
|
|
22770
|
+
import * as path24 from "path";
|
|
22151
22771
|
|
|
22152
22772
|
// src/mcp/tools/checkForNewAvailableFixes/CheckForNewAvailableFixesTool.ts
|
|
22153
22773
|
import { z as z42 } from "zod";
|
|
22154
22774
|
|
|
22155
22775
|
// src/mcp/services/PathValidation.ts
|
|
22156
22776
|
import fs21 from "fs";
|
|
22157
|
-
import
|
|
22777
|
+
import path25 from "path";
|
|
22158
22778
|
async function validatePath(inputPath) {
|
|
22159
22779
|
logDebug("Validating MCP path", { inputPath });
|
|
22160
22780
|
if (/^\/[a-zA-Z]:\//.test(inputPath)) {
|
|
@@ -22186,7 +22806,7 @@ async function validatePath(inputPath) {
|
|
|
22186
22806
|
logError(error);
|
|
22187
22807
|
return { isValid: false, error, path: inputPath };
|
|
22188
22808
|
}
|
|
22189
|
-
const normalizedPath =
|
|
22809
|
+
const normalizedPath = path25.normalize(inputPath);
|
|
22190
22810
|
if (normalizedPath.includes("..")) {
|
|
22191
22811
|
const error = `Normalized path contains path traversal patterns: ${inputPath}`;
|
|
22192
22812
|
logError(error);
|
|
@@ -22838,7 +23458,7 @@ init_configs();
|
|
|
22838
23458
|
import fs22 from "fs/promises";
|
|
22839
23459
|
import nodePath from "path";
|
|
22840
23460
|
var getLocalFiles = async ({
|
|
22841
|
-
path:
|
|
23461
|
+
path: path32,
|
|
22842
23462
|
maxFileSize = MCP_MAX_FILE_SIZE,
|
|
22843
23463
|
maxFiles,
|
|
22844
23464
|
isAllFilesScan,
|
|
@@ -22846,17 +23466,17 @@ var getLocalFiles = async ({
|
|
|
22846
23466
|
scanRecentlyChangedFiles
|
|
22847
23467
|
}) => {
|
|
22848
23468
|
logDebug(`[${scanContext}] Starting getLocalFiles`, {
|
|
22849
|
-
path:
|
|
23469
|
+
path: path32,
|
|
22850
23470
|
maxFileSize,
|
|
22851
23471
|
maxFiles,
|
|
22852
23472
|
isAllFilesScan,
|
|
22853
23473
|
scanRecentlyChangedFiles
|
|
22854
23474
|
});
|
|
22855
23475
|
try {
|
|
22856
|
-
const resolvedRepoPath = await fs22.realpath(
|
|
23476
|
+
const resolvedRepoPath = await fs22.realpath(path32);
|
|
22857
23477
|
logDebug(`[${scanContext}] Resolved repository path`, {
|
|
22858
23478
|
resolvedRepoPath,
|
|
22859
|
-
originalPath:
|
|
23479
|
+
originalPath: path32
|
|
22860
23480
|
});
|
|
22861
23481
|
const gitService = new GitService(resolvedRepoPath, log);
|
|
22862
23482
|
const gitValidation = await gitService.validateRepository();
|
|
@@ -22869,7 +23489,7 @@ var getLocalFiles = async ({
|
|
|
22869
23489
|
if (!gitValidation.isValid || isAllFilesScan) {
|
|
22870
23490
|
try {
|
|
22871
23491
|
files = await FileUtils.getLastChangedFiles({
|
|
22872
|
-
dir:
|
|
23492
|
+
dir: path32,
|
|
22873
23493
|
maxFileSize,
|
|
22874
23494
|
maxFiles,
|
|
22875
23495
|
isAllFilesScan
|
|
@@ -22961,7 +23581,7 @@ var getLocalFiles = async ({
|
|
|
22961
23581
|
logError(`${scanContext}Unexpected error in getLocalFiles`, {
|
|
22962
23582
|
error: error instanceof Error ? error.message : String(error),
|
|
22963
23583
|
stack: error instanceof Error ? error.stack : void 0,
|
|
22964
|
-
path:
|
|
23584
|
+
path: path32
|
|
22965
23585
|
});
|
|
22966
23586
|
throw error;
|
|
22967
23587
|
}
|
|
@@ -22971,7 +23591,7 @@ var getLocalFiles = async ({
|
|
|
22971
23591
|
init_client_generates();
|
|
22972
23592
|
init_GitService();
|
|
22973
23593
|
import fs23 from "fs";
|
|
22974
|
-
import
|
|
23594
|
+
import path26 from "path";
|
|
22975
23595
|
import { z as z41 } from "zod";
|
|
22976
23596
|
function extractPathFromPatch(patch) {
|
|
22977
23597
|
const match = patch?.match(/diff --git a\/([^\s]+) b\//);
|
|
@@ -23057,7 +23677,7 @@ var LocalMobbFolderService = class {
|
|
|
23057
23677
|
"[LocalMobbFolderService] Non-git repository detected, skipping .gitignore operations"
|
|
23058
23678
|
);
|
|
23059
23679
|
}
|
|
23060
|
-
const mobbFolderPath =
|
|
23680
|
+
const mobbFolderPath = path26.join(
|
|
23061
23681
|
this.repoPath,
|
|
23062
23682
|
this.defaultMobbFolderName
|
|
23063
23683
|
);
|
|
@@ -23229,7 +23849,7 @@ var LocalMobbFolderService = class {
|
|
|
23229
23849
|
mobbFolderPath,
|
|
23230
23850
|
baseFileName
|
|
23231
23851
|
);
|
|
23232
|
-
const filePath =
|
|
23852
|
+
const filePath = path26.join(mobbFolderPath, uniqueFileName);
|
|
23233
23853
|
await fs23.promises.writeFile(filePath, patch, "utf8");
|
|
23234
23854
|
logInfo("[LocalMobbFolderService] Patch saved successfully", {
|
|
23235
23855
|
filePath,
|
|
@@ -23287,11 +23907,11 @@ var LocalMobbFolderService = class {
|
|
|
23287
23907
|
* @returns Unique filename that doesn't conflict with existing files
|
|
23288
23908
|
*/
|
|
23289
23909
|
getUniqueFileName(folderPath, baseFileName) {
|
|
23290
|
-
const baseName =
|
|
23291
|
-
const extension =
|
|
23910
|
+
const baseName = path26.parse(baseFileName).name;
|
|
23911
|
+
const extension = path26.parse(baseFileName).ext;
|
|
23292
23912
|
let uniqueFileName = baseFileName;
|
|
23293
23913
|
let index = 1;
|
|
23294
|
-
while (fs23.existsSync(
|
|
23914
|
+
while (fs23.existsSync(path26.join(folderPath, uniqueFileName))) {
|
|
23295
23915
|
uniqueFileName = `${baseName}-${index}${extension}`;
|
|
23296
23916
|
index++;
|
|
23297
23917
|
if (index > 1e3) {
|
|
@@ -23322,7 +23942,7 @@ var LocalMobbFolderService = class {
|
|
|
23322
23942
|
logDebug("[LocalMobbFolderService] Logging patch info", { fixId: fix.id });
|
|
23323
23943
|
try {
|
|
23324
23944
|
const mobbFolderPath = await this.getFolder();
|
|
23325
|
-
const patchInfoPath =
|
|
23945
|
+
const patchInfoPath = path26.join(mobbFolderPath, "patchInfo.md");
|
|
23326
23946
|
const markdownContent = this.generateFixMarkdown(fix, savedPatchFileName);
|
|
23327
23947
|
let existingContent = "";
|
|
23328
23948
|
if (fs23.existsSync(patchInfoPath)) {
|
|
@@ -23364,7 +23984,7 @@ var LocalMobbFolderService = class {
|
|
|
23364
23984
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
23365
23985
|
const patch = this.extractPatchFromFix(fix);
|
|
23366
23986
|
const relativePatchedFilePath = patch ? extractPathFromPatch(patch) : null;
|
|
23367
|
-
const patchedFilePath = relativePatchedFilePath ?
|
|
23987
|
+
const patchedFilePath = relativePatchedFilePath ? path26.resolve(this.repoPath, relativePatchedFilePath) : null;
|
|
23368
23988
|
const fixIdentifier = savedPatchFileName ? savedPatchFileName.replace(".patch", "") : fix.id;
|
|
23369
23989
|
let markdown = `# Fix ${fixIdentifier}
|
|
23370
23990
|
|
|
@@ -23702,20 +24322,20 @@ init_configs();
|
|
|
23702
24322
|
import {
|
|
23703
24323
|
existsSync as existsSync6,
|
|
23704
24324
|
mkdirSync,
|
|
23705
|
-
readFileSync as
|
|
24325
|
+
readFileSync as readFileSync4,
|
|
23706
24326
|
unlinkSync,
|
|
23707
|
-
writeFileSync as
|
|
24327
|
+
writeFileSync as writeFileSync3
|
|
23708
24328
|
} from "fs";
|
|
23709
24329
|
import fs24 from "fs/promises";
|
|
23710
24330
|
import parseDiff2 from "parse-diff";
|
|
23711
|
-
import
|
|
24331
|
+
import path27 from "path";
|
|
23712
24332
|
var PatchApplicationService = class {
|
|
23713
24333
|
/**
|
|
23714
24334
|
* Gets the appropriate comment syntax for a file based on its extension
|
|
23715
24335
|
*/
|
|
23716
24336
|
static getCommentSyntax(filePath) {
|
|
23717
|
-
const ext =
|
|
23718
|
-
const basename2 =
|
|
24337
|
+
const ext = path27.extname(filePath).toLowerCase();
|
|
24338
|
+
const basename2 = path27.basename(filePath);
|
|
23719
24339
|
const commentMap = {
|
|
23720
24340
|
// C-style languages (single line comments)
|
|
23721
24341
|
".js": "//",
|
|
@@ -23923,18 +24543,18 @@ var PatchApplicationService = class {
|
|
|
23923
24543
|
}
|
|
23924
24544
|
);
|
|
23925
24545
|
}
|
|
23926
|
-
const dirPath =
|
|
24546
|
+
const dirPath = path27.dirname(normalizedFilePath);
|
|
23927
24547
|
mkdirSync(dirPath, { recursive: true });
|
|
23928
|
-
|
|
24548
|
+
writeFileSync3(normalizedFilePath, finalContent, "utf8");
|
|
23929
24549
|
return normalizedFilePath;
|
|
23930
24550
|
}
|
|
23931
24551
|
static resolvePathWithinRepo({
|
|
23932
24552
|
repositoryPath,
|
|
23933
24553
|
targetPath
|
|
23934
24554
|
}) {
|
|
23935
|
-
const repoRoot =
|
|
23936
|
-
const normalizedPath =
|
|
23937
|
-
const repoRootWithSep = repoRoot.endsWith(
|
|
24555
|
+
const repoRoot = path27.resolve(repositoryPath);
|
|
24556
|
+
const normalizedPath = path27.resolve(repoRoot, targetPath);
|
|
24557
|
+
const repoRootWithSep = repoRoot.endsWith(path27.sep) ? repoRoot : `${repoRoot}${path27.sep}`;
|
|
23938
24558
|
if (normalizedPath !== repoRoot && !normalizedPath.startsWith(repoRootWithSep)) {
|
|
23939
24559
|
throw new Error(
|
|
23940
24560
|
`Security violation: target path ${targetPath} resolves outside repository`
|
|
@@ -23943,7 +24563,7 @@ var PatchApplicationService = class {
|
|
|
23943
24563
|
return {
|
|
23944
24564
|
repoRoot,
|
|
23945
24565
|
normalizedPath,
|
|
23946
|
-
relativePath:
|
|
24566
|
+
relativePath: path27.relative(repoRoot, normalizedPath)
|
|
23947
24567
|
};
|
|
23948
24568
|
}
|
|
23949
24569
|
/**
|
|
@@ -24225,7 +24845,7 @@ var PatchApplicationService = class {
|
|
|
24225
24845
|
continue;
|
|
24226
24846
|
}
|
|
24227
24847
|
try {
|
|
24228
|
-
const absolutePath =
|
|
24848
|
+
const absolutePath = path27.resolve(repositoryPath, targetFile);
|
|
24229
24849
|
if (existsSync6(absolutePath)) {
|
|
24230
24850
|
const stats = await fs24.stat(absolutePath);
|
|
24231
24851
|
const fileModTime = stats.mtime.getTime();
|
|
@@ -24451,7 +25071,7 @@ var PatchApplicationService = class {
|
|
|
24451
25071
|
fix,
|
|
24452
25072
|
scanContext
|
|
24453
25073
|
});
|
|
24454
|
-
appliedFiles.push(
|
|
25074
|
+
appliedFiles.push(path27.relative(repositoryPath, actualPath));
|
|
24455
25075
|
logDebug(`[${scanContext}] Created new file: ${relativePath}`);
|
|
24456
25076
|
}
|
|
24457
25077
|
/**
|
|
@@ -24487,7 +25107,7 @@ var PatchApplicationService = class {
|
|
|
24487
25107
|
`Target file does not exist: ${targetFile} (resolved to: ${absoluteFilePath})`
|
|
24488
25108
|
);
|
|
24489
25109
|
}
|
|
24490
|
-
const originalContent =
|
|
25110
|
+
const originalContent = readFileSync4(absoluteFilePath, "utf8");
|
|
24491
25111
|
const modifiedContent = this.applyHunksToFile(
|
|
24492
25112
|
originalContent,
|
|
24493
25113
|
fileDiff.chunks
|
|
@@ -24500,7 +25120,7 @@ var PatchApplicationService = class {
|
|
|
24500
25120
|
fix,
|
|
24501
25121
|
scanContext
|
|
24502
25122
|
});
|
|
24503
|
-
appliedFiles.push(
|
|
25123
|
+
appliedFiles.push(path27.relative(repositoryPath, actualPath));
|
|
24504
25124
|
logDebug(`[${scanContext}] Modified file: ${relativePath}`);
|
|
24505
25125
|
}
|
|
24506
25126
|
}
|
|
@@ -24697,8 +25317,8 @@ init_configs();
|
|
|
24697
25317
|
// src/mcp/services/FileOperations.ts
|
|
24698
25318
|
init_FileUtils();
|
|
24699
25319
|
import fs25 from "fs";
|
|
24700
|
-
import
|
|
24701
|
-
import
|
|
25320
|
+
import path28 from "path";
|
|
25321
|
+
import AdmZip4 from "adm-zip";
|
|
24702
25322
|
var FileOperations = class {
|
|
24703
25323
|
/**
|
|
24704
25324
|
* Creates a ZIP archive containing the specified source files
|
|
@@ -24713,14 +25333,14 @@ var FileOperations = class {
|
|
|
24713
25333
|
maxFileSize
|
|
24714
25334
|
}) {
|
|
24715
25335
|
logDebug("[FileOperations] Packing files");
|
|
24716
|
-
const zip = new
|
|
25336
|
+
const zip = new AdmZip4();
|
|
24717
25337
|
let packedFilesCount = 0;
|
|
24718
25338
|
const packedFiles = [];
|
|
24719
25339
|
const excludedFiles = [];
|
|
24720
|
-
const resolvedRepoPath =
|
|
25340
|
+
const resolvedRepoPath = path28.resolve(repositoryPath);
|
|
24721
25341
|
for (const filepath of fileList) {
|
|
24722
|
-
const absoluteFilepath =
|
|
24723
|
-
const resolvedFilePath =
|
|
25342
|
+
const absoluteFilepath = path28.join(repositoryPath, filepath);
|
|
25343
|
+
const resolvedFilePath = path28.resolve(absoluteFilepath);
|
|
24724
25344
|
if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
|
|
24725
25345
|
const reason = "potential path traversal security risk";
|
|
24726
25346
|
logDebug(`[FileOperations] Skipping ${filepath} due to ${reason}`);
|
|
@@ -24767,11 +25387,11 @@ var FileOperations = class {
|
|
|
24767
25387
|
fileList,
|
|
24768
25388
|
repositoryPath
|
|
24769
25389
|
}) {
|
|
24770
|
-
const resolvedRepoPath =
|
|
25390
|
+
const resolvedRepoPath = path28.resolve(repositoryPath);
|
|
24771
25391
|
const validatedPaths = [];
|
|
24772
25392
|
for (const filepath of fileList) {
|
|
24773
|
-
const absoluteFilepath =
|
|
24774
|
-
const resolvedFilePath =
|
|
25393
|
+
const absoluteFilepath = path28.join(repositoryPath, filepath);
|
|
25394
|
+
const resolvedFilePath = path28.resolve(absoluteFilepath);
|
|
24775
25395
|
if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
|
|
24776
25396
|
logDebug(
|
|
24777
25397
|
`[FileOperations] Rejecting ${filepath} - path traversal attempt detected`
|
|
@@ -24799,7 +25419,7 @@ var FileOperations = class {
|
|
|
24799
25419
|
for (const absolutePath of filePaths) {
|
|
24800
25420
|
try {
|
|
24801
25421
|
const content = await fs25.promises.readFile(absolutePath);
|
|
24802
|
-
const relativePath =
|
|
25422
|
+
const relativePath = path28.basename(absolutePath);
|
|
24803
25423
|
fileDataArray.push({
|
|
24804
25424
|
relativePath,
|
|
24805
25425
|
absolutePath,
|
|
@@ -25111,14 +25731,14 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
25111
25731
|
* since the last scan.
|
|
25112
25732
|
*/
|
|
25113
25733
|
async scanForSecurityVulnerabilities({
|
|
25114
|
-
path:
|
|
25734
|
+
path: path32,
|
|
25115
25735
|
isAllDetectionRulesScan,
|
|
25116
25736
|
isAllFilesScan,
|
|
25117
25737
|
scanContext
|
|
25118
25738
|
}) {
|
|
25119
25739
|
this.hasAuthenticationFailed = false;
|
|
25120
25740
|
logDebug(`[${scanContext}] Scanning for new security vulnerabilities`, {
|
|
25121
|
-
path:
|
|
25741
|
+
path: path32
|
|
25122
25742
|
});
|
|
25123
25743
|
if (!this.gqlClient) {
|
|
25124
25744
|
logInfo(`[${scanContext}] No GQL client found, skipping scan`);
|
|
@@ -25134,11 +25754,11 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
25134
25754
|
}
|
|
25135
25755
|
logDebug(
|
|
25136
25756
|
`[${scanContext}] Connected to the API, assembling list of files to scan`,
|
|
25137
|
-
{ path:
|
|
25757
|
+
{ path: path32 }
|
|
25138
25758
|
);
|
|
25139
25759
|
const isBackgroundScan = scanContext === ScanContext.BACKGROUND_INITIAL || scanContext === ScanContext.BACKGROUND_PERIODIC;
|
|
25140
25760
|
const files = await getLocalFiles({
|
|
25141
|
-
path:
|
|
25761
|
+
path: path32,
|
|
25142
25762
|
isAllFilesScan,
|
|
25143
25763
|
scanContext,
|
|
25144
25764
|
scanRecentlyChangedFiles: !isBackgroundScan
|
|
@@ -25164,13 +25784,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
25164
25784
|
});
|
|
25165
25785
|
const { fixReportId, projectId } = await scanFiles({
|
|
25166
25786
|
fileList: filesToScan.map((file) => file.relativePath),
|
|
25167
|
-
repositoryPath:
|
|
25787
|
+
repositoryPath: path32,
|
|
25168
25788
|
gqlClient: this.gqlClient,
|
|
25169
25789
|
isAllDetectionRulesScan,
|
|
25170
25790
|
scanContext
|
|
25171
25791
|
});
|
|
25172
25792
|
logInfo(
|
|
25173
|
-
`[${scanContext}] Security scan completed for ${
|
|
25793
|
+
`[${scanContext}] Security scan completed for ${path32} reportId: ${fixReportId} projectId: ${projectId}`
|
|
25174
25794
|
);
|
|
25175
25795
|
if (isAllFilesScan) {
|
|
25176
25796
|
return;
|
|
@@ -25464,13 +26084,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
25464
26084
|
});
|
|
25465
26085
|
return scannedFiles.some((file) => file.relativePath === fixFile);
|
|
25466
26086
|
}
|
|
25467
|
-
async getFreshFixes({ path:
|
|
26087
|
+
async getFreshFixes({ path: path32 }) {
|
|
25468
26088
|
const scanContext = ScanContext.USER_REQUEST;
|
|
25469
|
-
logDebug(`[${scanContext}] Getting fresh fixes`, { path:
|
|
25470
|
-
if (this.path !==
|
|
25471
|
-
this.path =
|
|
26089
|
+
logDebug(`[${scanContext}] Getting fresh fixes`, { path: path32 });
|
|
26090
|
+
if (this.path !== path32) {
|
|
26091
|
+
this.path = path32;
|
|
25472
26092
|
this.reset();
|
|
25473
|
-
logInfo(`[${scanContext}] Reset service state for new path`, { path:
|
|
26093
|
+
logInfo(`[${scanContext}] Reset service state for new path`, { path: path32 });
|
|
25474
26094
|
}
|
|
25475
26095
|
try {
|
|
25476
26096
|
const loginContext = createMcpLoginContext("check_new_fixes");
|
|
@@ -25489,7 +26109,7 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
25489
26109
|
}
|
|
25490
26110
|
throw error;
|
|
25491
26111
|
}
|
|
25492
|
-
this.triggerScan({ path:
|
|
26112
|
+
this.triggerScan({ path: path32, gqlClient: this.gqlClient });
|
|
25493
26113
|
let isMvsAutoFixEnabled = null;
|
|
25494
26114
|
try {
|
|
25495
26115
|
isMvsAutoFixEnabled = await this.gqlClient.getMvsAutoFixSettings();
|
|
@@ -25523,33 +26143,33 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
25523
26143
|
return noFreshFixesPrompt;
|
|
25524
26144
|
}
|
|
25525
26145
|
triggerScan({
|
|
25526
|
-
path:
|
|
26146
|
+
path: path32,
|
|
25527
26147
|
gqlClient
|
|
25528
26148
|
}) {
|
|
25529
|
-
if (this.path !==
|
|
25530
|
-
this.path =
|
|
26149
|
+
if (this.path !== path32) {
|
|
26150
|
+
this.path = path32;
|
|
25531
26151
|
this.reset();
|
|
25532
|
-
logInfo(`Reset service state for new path in triggerScan`, { path:
|
|
26152
|
+
logInfo(`Reset service state for new path in triggerScan`, { path: path32 });
|
|
25533
26153
|
}
|
|
25534
26154
|
this.gqlClient = gqlClient;
|
|
25535
26155
|
if (!this.intervalId) {
|
|
25536
|
-
this.startPeriodicScanning(
|
|
25537
|
-
this.executeInitialScan(
|
|
25538
|
-
void this.executeInitialFullScan(
|
|
26156
|
+
this.startPeriodicScanning(path32);
|
|
26157
|
+
this.executeInitialScan(path32);
|
|
26158
|
+
void this.executeInitialFullScan(path32);
|
|
25539
26159
|
}
|
|
25540
26160
|
}
|
|
25541
|
-
startPeriodicScanning(
|
|
26161
|
+
startPeriodicScanning(path32) {
|
|
25542
26162
|
const scanContext = ScanContext.BACKGROUND_PERIODIC;
|
|
25543
26163
|
logDebug(
|
|
25544
26164
|
`[${scanContext}] Starting periodic scan for new security vulnerabilities`,
|
|
25545
26165
|
{
|
|
25546
|
-
path:
|
|
26166
|
+
path: path32
|
|
25547
26167
|
}
|
|
25548
26168
|
);
|
|
25549
26169
|
this.intervalId = setInterval(() => {
|
|
25550
|
-
logDebug(`[${scanContext}] Triggering periodic security scan`, { path:
|
|
26170
|
+
logDebug(`[${scanContext}] Triggering periodic security scan`, { path: path32 });
|
|
25551
26171
|
this.scanForSecurityVulnerabilities({
|
|
25552
|
-
path:
|
|
26172
|
+
path: path32,
|
|
25553
26173
|
scanContext
|
|
25554
26174
|
}).catch((error) => {
|
|
25555
26175
|
logError(`[${scanContext}] Error during periodic security scan`, {
|
|
@@ -25558,45 +26178,45 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
25558
26178
|
});
|
|
25559
26179
|
}, MCP_PERIODIC_CHECK_INTERVAL);
|
|
25560
26180
|
}
|
|
25561
|
-
async executeInitialFullScan(
|
|
26181
|
+
async executeInitialFullScan(path32) {
|
|
25562
26182
|
const scanContext = ScanContext.FULL_SCAN;
|
|
25563
|
-
logDebug(`[${scanContext}] Triggering initial full security scan`, { path:
|
|
26183
|
+
logDebug(`[${scanContext}] Triggering initial full security scan`, { path: path32 });
|
|
25564
26184
|
logDebug(`[${scanContext}] Full scan paths scanned`, {
|
|
25565
26185
|
fullScanPathsScanned: this.fullScanPathsScanned
|
|
25566
26186
|
});
|
|
25567
|
-
if (this.fullScanPathsScanned.includes(
|
|
26187
|
+
if (this.fullScanPathsScanned.includes(path32)) {
|
|
25568
26188
|
logDebug(`[${scanContext}] Full scan already executed for this path`, {
|
|
25569
|
-
path:
|
|
26189
|
+
path: path32
|
|
25570
26190
|
});
|
|
25571
26191
|
return;
|
|
25572
26192
|
}
|
|
25573
26193
|
configStore.set("fullScanPathsScanned", [
|
|
25574
26194
|
...this.fullScanPathsScanned,
|
|
25575
|
-
|
|
26195
|
+
path32
|
|
25576
26196
|
]);
|
|
25577
26197
|
try {
|
|
25578
26198
|
await this.scanForSecurityVulnerabilities({
|
|
25579
|
-
path:
|
|
26199
|
+
path: path32,
|
|
25580
26200
|
isAllFilesScan: true,
|
|
25581
26201
|
isAllDetectionRulesScan: true,
|
|
25582
26202
|
scanContext: ScanContext.FULL_SCAN
|
|
25583
26203
|
});
|
|
25584
|
-
if (!this.fullScanPathsScanned.includes(
|
|
25585
|
-
this.fullScanPathsScanned.push(
|
|
26204
|
+
if (!this.fullScanPathsScanned.includes(path32)) {
|
|
26205
|
+
this.fullScanPathsScanned.push(path32);
|
|
25586
26206
|
configStore.set("fullScanPathsScanned", this.fullScanPathsScanned);
|
|
25587
26207
|
}
|
|
25588
|
-
logInfo(`[${scanContext}] Full scan completed`, { path:
|
|
26208
|
+
logInfo(`[${scanContext}] Full scan completed`, { path: path32 });
|
|
25589
26209
|
} catch (error) {
|
|
25590
26210
|
logError(`[${scanContext}] Error during initial full security scan`, {
|
|
25591
26211
|
error
|
|
25592
26212
|
});
|
|
25593
26213
|
}
|
|
25594
26214
|
}
|
|
25595
|
-
executeInitialScan(
|
|
26215
|
+
executeInitialScan(path32) {
|
|
25596
26216
|
const scanContext = ScanContext.BACKGROUND_INITIAL;
|
|
25597
|
-
logDebug(`[${scanContext}] Triggering initial security scan`, { path:
|
|
26217
|
+
logDebug(`[${scanContext}] Triggering initial security scan`, { path: path32 });
|
|
25598
26218
|
this.scanForSecurityVulnerabilities({
|
|
25599
|
-
path:
|
|
26219
|
+
path: path32,
|
|
25600
26220
|
scanContext: ScanContext.BACKGROUND_INITIAL
|
|
25601
26221
|
}).catch((error) => {
|
|
25602
26222
|
logError(`[${scanContext}] Error during initial security scan`, { error });
|
|
@@ -25693,9 +26313,9 @@ Example payload:
|
|
|
25693
26313
|
`Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
|
|
25694
26314
|
);
|
|
25695
26315
|
}
|
|
25696
|
-
const
|
|
26316
|
+
const path32 = pathValidationResult.path;
|
|
25697
26317
|
const resultText = await this.newFixesService.getFreshFixes({
|
|
25698
|
-
path:
|
|
26318
|
+
path: path32
|
|
25699
26319
|
});
|
|
25700
26320
|
logInfo("CheckForNewAvailableFixesTool execution completed", {
|
|
25701
26321
|
resultText
|
|
@@ -25873,8 +26493,8 @@ Call this tool instead of ${MCP_TOOL_SCAN_AND_FIX_VULNERABILITIES} when you only
|
|
|
25873
26493
|
`Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
|
|
25874
26494
|
);
|
|
25875
26495
|
}
|
|
25876
|
-
const
|
|
25877
|
-
const gitService = new GitService(
|
|
26496
|
+
const path32 = pathValidationResult.path;
|
|
26497
|
+
const gitService = new GitService(path32, log);
|
|
25878
26498
|
const gitValidation = await gitService.validateRepository();
|
|
25879
26499
|
if (!gitValidation.isValid) {
|
|
25880
26500
|
throw new Error(`Invalid git repository: ${gitValidation.error}`);
|
|
@@ -26259,9 +26879,9 @@ Example payload:
|
|
|
26259
26879
|
`Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
|
|
26260
26880
|
);
|
|
26261
26881
|
}
|
|
26262
|
-
const
|
|
26882
|
+
const path32 = pathValidationResult.path;
|
|
26263
26883
|
const files = await getLocalFiles({
|
|
26264
|
-
path:
|
|
26884
|
+
path: path32,
|
|
26265
26885
|
maxFileSize: MCP_MAX_FILE_SIZE,
|
|
26266
26886
|
maxFiles: args.maxFiles,
|
|
26267
26887
|
scanContext: ScanContext.USER_REQUEST,
|
|
@@ -26281,7 +26901,7 @@ Example payload:
|
|
|
26281
26901
|
try {
|
|
26282
26902
|
const fixResult = await this.vulnerabilityFixService.processVulnerabilities({
|
|
26283
26903
|
fileList: files.map((file) => file.relativePath),
|
|
26284
|
-
repositoryPath:
|
|
26904
|
+
repositoryPath: path32,
|
|
26285
26905
|
offset: args.offset,
|
|
26286
26906
|
limit: args.limit,
|
|
26287
26907
|
isRescan: args.rescan || !!args.maxFiles
|
|
@@ -26582,10 +27202,10 @@ init_client_generates();
|
|
|
26582
27202
|
init_urlParser2();
|
|
26583
27203
|
|
|
26584
27204
|
// src/features/codeium_intellij/codeium_language_server_grpc_client.ts
|
|
26585
|
-
import
|
|
27205
|
+
import path29 from "path";
|
|
26586
27206
|
import * as grpc from "@grpc/grpc-js";
|
|
26587
27207
|
import * as protoLoader from "@grpc/proto-loader";
|
|
26588
|
-
var PROTO_PATH =
|
|
27208
|
+
var PROTO_PATH = path29.join(
|
|
26589
27209
|
getModuleRootDir(),
|
|
26590
27210
|
"src/features/codeium_intellij/proto/exa/language_server_pb/language_server.proto"
|
|
26591
27211
|
);
|
|
@@ -26597,7 +27217,7 @@ function loadProto() {
|
|
|
26597
27217
|
defaults: true,
|
|
26598
27218
|
oneofs: true,
|
|
26599
27219
|
includeDirs: [
|
|
26600
|
-
|
|
27220
|
+
path29.join(getModuleRootDir(), "src/features/codeium_intellij/proto")
|
|
26601
27221
|
]
|
|
26602
27222
|
});
|
|
26603
27223
|
return grpc.loadPackageDefinition(
|
|
@@ -26653,28 +27273,28 @@ async function getGrpcClient(port, csrf3) {
|
|
|
26653
27273
|
// src/features/codeium_intellij/parse_intellij_logs.ts
|
|
26654
27274
|
import fs27 from "fs";
|
|
26655
27275
|
import os14 from "os";
|
|
26656
|
-
import
|
|
27276
|
+
import path30 from "path";
|
|
26657
27277
|
function getLogsDir() {
|
|
26658
27278
|
if (process.platform === "darwin") {
|
|
26659
|
-
return
|
|
27279
|
+
return path30.join(os14.homedir(), "Library/Logs/JetBrains");
|
|
26660
27280
|
} else if (process.platform === "win32") {
|
|
26661
|
-
return
|
|
26662
|
-
process.env["LOCALAPPDATA"] ||
|
|
27281
|
+
return path30.join(
|
|
27282
|
+
process.env["LOCALAPPDATA"] || path30.join(os14.homedir(), "AppData/Local"),
|
|
26663
27283
|
"JetBrains"
|
|
26664
27284
|
);
|
|
26665
27285
|
} else {
|
|
26666
|
-
return
|
|
27286
|
+
return path30.join(os14.homedir(), ".cache/JetBrains");
|
|
26667
27287
|
}
|
|
26668
27288
|
}
|
|
26669
27289
|
function parseIdeLogDir(ideLogDir) {
|
|
26670
27290
|
const logFiles = fs27.readdirSync(ideLogDir).filter((f) => /^idea(\.\d+)?\.log$/.test(f)).map((f) => ({
|
|
26671
27291
|
name: f,
|
|
26672
|
-
mtime: fs27.statSync(
|
|
27292
|
+
mtime: fs27.statSync(path30.join(ideLogDir, f)).mtimeMs
|
|
26673
27293
|
})).sort((a, b) => a.mtime - b.mtime).map((f) => f.name);
|
|
26674
27294
|
let latestCsrf = null;
|
|
26675
27295
|
let latestPort = null;
|
|
26676
27296
|
for (const logFile of logFiles) {
|
|
26677
|
-
const lines = fs27.readFileSync(
|
|
27297
|
+
const lines = fs27.readFileSync(path30.join(ideLogDir, logFile), "utf-8").split("\n");
|
|
26678
27298
|
for (const line of lines) {
|
|
26679
27299
|
if (!line.includes(
|
|
26680
27300
|
"com.codeium.intellij.language_server.LanguageServerProcessHandler"
|
|
@@ -26702,9 +27322,9 @@ function findRunningCodeiumLanguageServers() {
|
|
|
26702
27322
|
const logsDir = getLogsDir();
|
|
26703
27323
|
if (!fs27.existsSync(logsDir)) return results;
|
|
26704
27324
|
for (const ide of fs27.readdirSync(logsDir)) {
|
|
26705
|
-
let ideLogDir =
|
|
27325
|
+
let ideLogDir = path30.join(logsDir, ide);
|
|
26706
27326
|
if (process.platform !== "darwin") {
|
|
26707
|
-
ideLogDir =
|
|
27327
|
+
ideLogDir = path30.join(ideLogDir, "log");
|
|
26708
27328
|
}
|
|
26709
27329
|
if (!fs27.existsSync(ideLogDir) || !fs27.statSync(ideLogDir).isDirectory()) {
|
|
26710
27330
|
continue;
|
|
@@ -26887,10 +27507,10 @@ function processChatStepCodeAction(step) {
|
|
|
26887
27507
|
// src/features/codeium_intellij/install_hook.ts
|
|
26888
27508
|
import fsPromises5 from "fs/promises";
|
|
26889
27509
|
import os15 from "os";
|
|
26890
|
-
import
|
|
27510
|
+
import path31 from "path";
|
|
26891
27511
|
import chalk14 from "chalk";
|
|
26892
27512
|
function getCodeiumHooksPath() {
|
|
26893
|
-
return
|
|
27513
|
+
return path31.join(os15.homedir(), ".codeium", "hooks.json");
|
|
26894
27514
|
}
|
|
26895
27515
|
async function readCodeiumHooks() {
|
|
26896
27516
|
const hooksPath = getCodeiumHooksPath();
|
|
@@ -26903,7 +27523,7 @@ async function readCodeiumHooks() {
|
|
|
26903
27523
|
}
|
|
26904
27524
|
async function writeCodeiumHooks(config2) {
|
|
26905
27525
|
const hooksPath = getCodeiumHooksPath();
|
|
26906
|
-
const dir =
|
|
27526
|
+
const dir = path31.dirname(hooksPath);
|
|
26907
27527
|
await fsPromises5.mkdir(dir, { recursive: true });
|
|
26908
27528
|
await fsPromises5.writeFile(
|
|
26909
27529
|
hooksPath,
|