mobbdev 1.3.4 → 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 +33 -39
- package/dist/args/commands/upload_ai_blame.mjs +29 -18
- package/dist/index.mjs +874 -268
- 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;
|
|
@@ -14016,18 +14049,6 @@ async function getRepositoryUrl(workingDir) {
|
|
|
14016
14049
|
return null;
|
|
14017
14050
|
}
|
|
14018
14051
|
}
|
|
14019
|
-
async function getRepoGitRoot(workingDir) {
|
|
14020
|
-
try {
|
|
14021
|
-
const gitService = new GitService(workingDir ?? process.cwd());
|
|
14022
|
-
const isRepo = await gitService.isGitRepository();
|
|
14023
|
-
if (!isRepo) {
|
|
14024
|
-
return null;
|
|
14025
|
-
}
|
|
14026
|
-
return await gitService.getGitRoot();
|
|
14027
|
-
} catch {
|
|
14028
|
-
return null;
|
|
14029
|
-
}
|
|
14030
|
-
}
|
|
14031
14052
|
function getSystemInfo() {
|
|
14032
14053
|
let userName;
|
|
14033
14054
|
try {
|
|
@@ -14360,7 +14381,6 @@ async function prepareAndSendTracyRecords(client, rawRecords, workingDir, option
|
|
|
14360
14381
|
const defaultClientVersion = packageJson.version;
|
|
14361
14382
|
const shouldSanitize = options?.sanitize ?? true;
|
|
14362
14383
|
const defaultRepoUrl = rawRecords[0]?.repositoryUrl ? void 0 : await getRepositoryUrl(workingDir) ?? void 0;
|
|
14363
|
-
const defaultGitRoot = rawRecords[0]?.gitRoot ? void 0 : await getRepoGitRoot(workingDir) ?? void 0;
|
|
14364
14384
|
debug10(
|
|
14365
14385
|
"[step:sanitize] %s %d records",
|
|
14366
14386
|
shouldSanitize ? "Sanitizing" : "Serializing",
|
|
@@ -14371,7 +14391,7 @@ async function prepareAndSendTracyRecords(client, rawRecords, workingDir, option
|
|
|
14371
14391
|
`${shouldSanitize ? "sanitize" : "serialize"} ${rawRecords.length} records`,
|
|
14372
14392
|
() => Promise.all(
|
|
14373
14393
|
rawRecords.map(async (record, index) => {
|
|
14374
|
-
if (record.rawData != null) {
|
|
14394
|
+
if (record.rawData != null && record.rawDataS3Key == null) {
|
|
14375
14395
|
const serialized = shouldSanitize ? await sanitizeRawData(record.rawData) : JSON.stringify(record.rawData);
|
|
14376
14396
|
serializedRawDataByIndex.set(index, serialized);
|
|
14377
14397
|
}
|
|
@@ -14379,7 +14399,6 @@ async function prepareAndSendTracyRecords(client, rawRecords, workingDir, option
|
|
|
14379
14399
|
return {
|
|
14380
14400
|
...rest,
|
|
14381
14401
|
repositoryUrl: record.repositoryUrl ?? defaultRepoUrl,
|
|
14382
|
-
gitRoot: record.gitRoot ?? defaultGitRoot,
|
|
14383
14402
|
computerName,
|
|
14384
14403
|
userName,
|
|
14385
14404
|
clientVersion: record.clientVersion ?? defaultClientVersion
|
|
@@ -15042,7 +15061,7 @@ async function postIssueComment(params) {
|
|
|
15042
15061
|
fpDescription
|
|
15043
15062
|
} = params;
|
|
15044
15063
|
const {
|
|
15045
|
-
path:
|
|
15064
|
+
path: path32,
|
|
15046
15065
|
startLine,
|
|
15047
15066
|
vulnerabilityReportIssue: {
|
|
15048
15067
|
vulnerabilityReportIssueTags,
|
|
@@ -15057,7 +15076,7 @@ async function postIssueComment(params) {
|
|
|
15057
15076
|
Refresh the page in order to see the changes.`,
|
|
15058
15077
|
pull_number: pullRequest,
|
|
15059
15078
|
commit_id: commitSha,
|
|
15060
|
-
path:
|
|
15079
|
+
path: path32,
|
|
15061
15080
|
line: startLine
|
|
15062
15081
|
});
|
|
15063
15082
|
const commentId = commentRes.data.id;
|
|
@@ -15091,7 +15110,7 @@ async function postFixComment(params) {
|
|
|
15091
15110
|
scanner
|
|
15092
15111
|
} = params;
|
|
15093
15112
|
const {
|
|
15094
|
-
path:
|
|
15113
|
+
path: path32,
|
|
15095
15114
|
startLine,
|
|
15096
15115
|
vulnerabilityReportIssue: { fixId, vulnerabilityReportIssueTags, category },
|
|
15097
15116
|
vulnerabilityReportIssueId
|
|
@@ -15109,7 +15128,7 @@ async function postFixComment(params) {
|
|
|
15109
15128
|
Refresh the page in order to see the changes.`,
|
|
15110
15129
|
pull_number: pullRequest,
|
|
15111
15130
|
commit_id: commitSha,
|
|
15112
|
-
path:
|
|
15131
|
+
path: path32,
|
|
15113
15132
|
line: startLine
|
|
15114
15133
|
});
|
|
15115
15134
|
const commentId = commentRes.data.id;
|
|
@@ -16631,8 +16650,8 @@ async function resolveSkillScanInput(skillInput) {
|
|
|
16631
16650
|
if (!fs11.existsSync(resolvedPath)) {
|
|
16632
16651
|
return skillInput;
|
|
16633
16652
|
}
|
|
16634
|
-
const
|
|
16635
|
-
if (!
|
|
16653
|
+
const stat3 = fs11.statSync(resolvedPath);
|
|
16654
|
+
if (!stat3.isDirectory()) {
|
|
16636
16655
|
throw new CliError(
|
|
16637
16656
|
"Local skill input must be a directory containing SKILL.md"
|
|
16638
16657
|
);
|
|
@@ -17030,8 +17049,10 @@ async function analyzeHandler(args) {
|
|
|
17030
17049
|
import { spawn } from "child_process";
|
|
17031
17050
|
|
|
17032
17051
|
// src/features/claude_code/daemon.ts
|
|
17033
|
-
import
|
|
17052
|
+
import { readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
17053
|
+
import path19 from "path";
|
|
17034
17054
|
import { setTimeout as sleep2 } from "timers/promises";
|
|
17055
|
+
import Configstore3 from "configstore";
|
|
17035
17056
|
|
|
17036
17057
|
// src/features/claude_code/daemon_pid_file.ts
|
|
17037
17058
|
import fs13 from "fs";
|
|
@@ -17123,10 +17144,455 @@ var DaemonPidFile = class {
|
|
|
17123
17144
|
|
|
17124
17145
|
// src/features/claude_code/data_collector.ts
|
|
17125
17146
|
import { execFile } from "child_process";
|
|
17126
|
-
import { createHash as
|
|
17127
|
-
import { access, open as open4, readdir, readFile, unlink } from "fs/promises";
|
|
17128
|
-
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";
|
|
17129
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
|
|
17130
17596
|
init_client_generates();
|
|
17131
17597
|
|
|
17132
17598
|
// src/utils/shared-logger/create-logger.ts
|
|
@@ -17140,6 +17606,8 @@ var DEFAULT_MAX_LOGS = 1e3;
|
|
|
17140
17606
|
var DEFAULT_MAX_HEARTBEAT = 100;
|
|
17141
17607
|
var LOGS_KEY = "logs";
|
|
17142
17608
|
var HEARTBEAT_KEY = "heartbeat";
|
|
17609
|
+
var MAX_DATA_CHARS = 2048;
|
|
17610
|
+
var MAX_SCOPE_KEYS = 20;
|
|
17143
17611
|
function createConfigstoreStream(store, opts) {
|
|
17144
17612
|
const maxLogs = opts.maxLogs ?? DEFAULT_MAX_LOGS;
|
|
17145
17613
|
const maxHeartbeat = opts.maxHeartbeat ?? DEFAULT_MAX_HEARTBEAT;
|
|
@@ -17155,6 +17623,10 @@ function createConfigstoreStream(store, opts) {
|
|
|
17155
17623
|
existing.push(...entries);
|
|
17156
17624
|
const trimmed = existing.length > max ? existing.slice(-max) : existing;
|
|
17157
17625
|
store.set(key, trimmed);
|
|
17626
|
+
const prefix = key.includes(":") ? key.split(":")[0] : null;
|
|
17627
|
+
if (prefix) {
|
|
17628
|
+
pruneStaleScopes(prefix);
|
|
17629
|
+
}
|
|
17158
17630
|
} catch {
|
|
17159
17631
|
try {
|
|
17160
17632
|
const lines = `${entries.map((e) => JSON.stringify(e)).join("\n")}
|
|
@@ -17164,11 +17636,40 @@ function createConfigstoreStream(store, opts) {
|
|
|
17164
17636
|
}
|
|
17165
17637
|
}
|
|
17166
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
|
+
}
|
|
17167
17661
|
const writable = new stream.Writable({
|
|
17168
17662
|
write(chunk, _encoding, callback) {
|
|
17169
17663
|
callback();
|
|
17170
17664
|
try {
|
|
17171
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
|
+
}
|
|
17172
17673
|
const entry = {
|
|
17173
17674
|
timestamp: parsed.time ? new Date(parsed.time).toISOString() : (/* @__PURE__ */ new Date()).toISOString(),
|
|
17174
17675
|
level: parsed.level ?? "info",
|
|
@@ -17176,7 +17677,7 @@ function createConfigstoreStream(store, opts) {
|
|
|
17176
17677
|
...parsed.durationMs !== void 0 && {
|
|
17177
17678
|
durationMs: parsed.durationMs
|
|
17178
17679
|
},
|
|
17179
|
-
...
|
|
17680
|
+
...data !== void 0 && { data }
|
|
17180
17681
|
};
|
|
17181
17682
|
const isHeartbeat = parsed.heartbeat === true;
|
|
17182
17683
|
if (opts.buffered) {
|
|
@@ -17206,8 +17707,8 @@ function createConfigstoreStream(store, opts) {
|
|
|
17206
17707
|
heartbeatBuffer.length = 0;
|
|
17207
17708
|
}
|
|
17208
17709
|
}
|
|
17209
|
-
function setScopePath(
|
|
17210
|
-
scopePath =
|
|
17710
|
+
function setScopePath(path32) {
|
|
17711
|
+
scopePath = path32;
|
|
17211
17712
|
}
|
|
17212
17713
|
return { writable, flush, setScopePath };
|
|
17213
17714
|
}
|
|
@@ -17288,10 +17789,10 @@ function createDdBatch(config2) {
|
|
|
17288
17789
|
}
|
|
17289
17790
|
|
|
17290
17791
|
// src/utils/shared-logger/hostname.ts
|
|
17291
|
-
import { createHash } from "crypto";
|
|
17792
|
+
import { createHash as createHash2 } from "crypto";
|
|
17292
17793
|
import os5 from "os";
|
|
17293
17794
|
function hashString(input) {
|
|
17294
|
-
return
|
|
17795
|
+
return createHash2("sha256").update(input).digest("hex").slice(0, 16);
|
|
17295
17796
|
}
|
|
17296
17797
|
function getPlainHostname() {
|
|
17297
17798
|
try {
|
|
@@ -17431,7 +17932,7 @@ function createLogger(config2) {
|
|
|
17431
17932
|
|
|
17432
17933
|
// src/features/claude_code/hook_logger.ts
|
|
17433
17934
|
var DD_RUM_TOKEN = true ? "pubf59c0182545bfb4c299175119f1abf9b" : "";
|
|
17434
|
-
var CLI_VERSION = true ? "1.3.
|
|
17935
|
+
var CLI_VERSION = true ? "1.3.7" : "unknown";
|
|
17435
17936
|
var NAMESPACE = "mobbdev-claude-code-hook-logs";
|
|
17436
17937
|
var claudeCodeVersion;
|
|
17437
17938
|
function buildDdTags() {
|
|
@@ -17509,11 +18010,11 @@ async function detectClaudeCodeVersion() {
|
|
|
17509
18010
|
}
|
|
17510
18011
|
function generateSyntheticId(sessionId, timestamp, type2, lineIndex) {
|
|
17511
18012
|
const input = `${sessionId ?? ""}:${timestamp ?? ""}:${type2 ?? ""}:${lineIndex}`;
|
|
17512
|
-
const hash =
|
|
18013
|
+
const hash = createHash3("sha256").update(input).digest("hex").slice(0, 16);
|
|
17513
18014
|
return `synth:${hash}`;
|
|
17514
18015
|
}
|
|
17515
18016
|
function getCursorKey(transcriptPath) {
|
|
17516
|
-
const hash =
|
|
18017
|
+
const hash = createHash3("sha256").update(transcriptPath).digest("hex").slice(0, 12);
|
|
17517
18018
|
return `cursor.${hash}`;
|
|
17518
18019
|
}
|
|
17519
18020
|
async function resolveTranscriptPath(transcriptPath, sessionId) {
|
|
@@ -17522,12 +18023,12 @@ async function resolveTranscriptPath(transcriptPath, sessionId) {
|
|
|
17522
18023
|
return transcriptPath;
|
|
17523
18024
|
} catch {
|
|
17524
18025
|
}
|
|
17525
|
-
const filename =
|
|
17526
|
-
const dirName =
|
|
17527
|
-
const projectsDir =
|
|
18026
|
+
const filename = path16.basename(transcriptPath);
|
|
18027
|
+
const dirName = path16.basename(path16.dirname(transcriptPath));
|
|
18028
|
+
const projectsDir = path16.dirname(path16.dirname(transcriptPath));
|
|
17528
18029
|
const baseDirName = dirName.replace(/[-.]claude-worktrees-.+$/, "");
|
|
17529
18030
|
if (baseDirName !== dirName) {
|
|
17530
|
-
const candidate =
|
|
18031
|
+
const candidate = path16.join(projectsDir, baseDirName, filename);
|
|
17531
18032
|
try {
|
|
17532
18033
|
await access(candidate);
|
|
17533
18034
|
hookLog.info(
|
|
@@ -17549,7 +18050,7 @@ async function resolveTranscriptPath(transcriptPath, sessionId) {
|
|
|
17549
18050
|
const dirs = await readdir(projectsDir);
|
|
17550
18051
|
for (const dir of dirs) {
|
|
17551
18052
|
if (dir === dirName) continue;
|
|
17552
|
-
const candidate =
|
|
18053
|
+
const candidate = path16.join(projectsDir, dir, filename);
|
|
17553
18054
|
try {
|
|
17554
18055
|
await access(candidate);
|
|
17555
18056
|
hookLog.info(
|
|
@@ -17580,9 +18081,9 @@ async function readNewTranscriptEntries(transcriptPath, sessionId, sessionStore,
|
|
|
17580
18081
|
if (cursor?.byteOffset) {
|
|
17581
18082
|
const fh = await open4(transcriptPath, "r");
|
|
17582
18083
|
try {
|
|
17583
|
-
const
|
|
17584
|
-
fileSize =
|
|
17585
|
-
if (cursor.byteOffset >=
|
|
18084
|
+
const stat3 = await fh.stat();
|
|
18085
|
+
fileSize = stat3.size;
|
|
18086
|
+
if (cursor.byteOffset >= stat3.size) {
|
|
17586
18087
|
hookLog.info({ data: { sessionId } }, "No new data in transcript file");
|
|
17587
18088
|
return {
|
|
17588
18089
|
entries: [],
|
|
@@ -17590,7 +18091,7 @@ async function readNewTranscriptEntries(transcriptPath, sessionId, sessionStore,
|
|
|
17590
18091
|
resolvedTranscriptPath: transcriptPath
|
|
17591
18092
|
};
|
|
17592
18093
|
}
|
|
17593
|
-
const buf = Buffer.alloc(
|
|
18094
|
+
const buf = Buffer.alloc(stat3.size - cursor.byteOffset);
|
|
17594
18095
|
await fh.read(buf, 0, buf.length, cursor.byteOffset);
|
|
17595
18096
|
content = buf.toString("utf-8");
|
|
17596
18097
|
} finally {
|
|
@@ -17608,7 +18109,7 @@ async function readNewTranscriptEntries(transcriptPath, sessionId, sessionStore,
|
|
|
17608
18109
|
"Read transcript file from offset"
|
|
17609
18110
|
);
|
|
17610
18111
|
} else {
|
|
17611
|
-
content = await
|
|
18112
|
+
content = await readFile2(transcriptPath, "utf-8");
|
|
17612
18113
|
fileSize = Buffer.byteLength(content, "utf-8");
|
|
17613
18114
|
lineIndexOffset = 0;
|
|
17614
18115
|
hookLog.debug(
|
|
@@ -17707,14 +18208,6 @@ var FILTERED_ENTRY_TYPES = /* @__PURE__ */ new Set([
|
|
|
17707
18208
|
// Redundant — the actual user prompt is already captured in the 'user' entry.
|
|
17708
18209
|
"last-prompt"
|
|
17709
18210
|
]);
|
|
17710
|
-
var FILTERED_ASSISTANT_TOOLS = /* @__PURE__ */ new Set([
|
|
17711
|
-
// Polls for a sub-agent result. The input is just task_id + boilerplate
|
|
17712
|
-
// (block, timeout). The actual result is captured in the user:tool_result.
|
|
17713
|
-
"TaskOutput",
|
|
17714
|
-
// Discovers available deferred/MCP tools. The input is just a search query.
|
|
17715
|
-
// The discovered tools are captured in the user:tool_result.
|
|
17716
|
-
"ToolSearch"
|
|
17717
|
-
]);
|
|
17718
18211
|
function filterEntries(entries) {
|
|
17719
18212
|
const filtered = entries.filter((entry) => {
|
|
17720
18213
|
const entryType = entry.type ?? "";
|
|
@@ -17726,16 +18219,6 @@ function filterEntries(entries) {
|
|
|
17726
18219
|
const subtype = typeof data?.["type"] === "string" ? data["type"] : "";
|
|
17727
18220
|
return !FILTERED_PROGRESS_SUBTYPES.has(subtype);
|
|
17728
18221
|
}
|
|
17729
|
-
if (entryType === "assistant") {
|
|
17730
|
-
const message = entry["message"];
|
|
17731
|
-
const content = message?.["content"];
|
|
17732
|
-
if (Array.isArray(content) && content.length > 0) {
|
|
17733
|
-
const block = content[0];
|
|
17734
|
-
if (block["type"] === "tool_use" && typeof block["name"] === "string" && FILTERED_ASSISTANT_TOOLS.has(block["name"])) {
|
|
17735
|
-
return false;
|
|
17736
|
-
}
|
|
17737
|
-
}
|
|
17738
|
-
}
|
|
17739
18222
|
return true;
|
|
17740
18223
|
});
|
|
17741
18224
|
return { filtered, filteredOut: entries.length - filtered.length };
|
|
@@ -17752,9 +18235,9 @@ async function cleanupStaleSessions(configDir) {
|
|
|
17752
18235
|
let deletedCount = 0;
|
|
17753
18236
|
for (const file of files) {
|
|
17754
18237
|
if (!file.startsWith(prefix) || !file.endsWith(".json")) continue;
|
|
17755
|
-
const filePath =
|
|
18238
|
+
const filePath = path16.join(configDir, file);
|
|
17756
18239
|
try {
|
|
17757
|
-
const content = JSON.parse(await
|
|
18240
|
+
const content = JSON.parse(await readFile2(filePath, "utf-8"));
|
|
17758
18241
|
let newest = 0;
|
|
17759
18242
|
const cursors = content["cursor"];
|
|
17760
18243
|
if (cursors && typeof cursors === "object") {
|
|
@@ -17904,6 +18387,16 @@ async function processTranscript(input, sessionStore, log2, maxEntries = DAEMON_
|
|
|
17904
18387
|
entriesSkipped: filteredOut,
|
|
17905
18388
|
claudeCodeVersion: getClaudeCodeVersion()
|
|
17906
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
|
+
}
|
|
17907
18400
|
return {
|
|
17908
18401
|
entriesUploaded: entries.length,
|
|
17909
18402
|
entriesSkipped: filteredOut,
|
|
@@ -17920,19 +18413,84 @@ async function processTranscript(input, sessionStore, log2, maxEntries = DAEMON_
|
|
|
17920
18413
|
errors: entries.length
|
|
17921
18414
|
};
|
|
17922
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
|
+
}
|
|
17923
18481
|
|
|
17924
18482
|
// src/features/claude_code/install_hook.ts
|
|
17925
18483
|
import fs14 from "fs";
|
|
17926
18484
|
import fsPromises4 from "fs/promises";
|
|
17927
18485
|
import os6 from "os";
|
|
17928
|
-
import
|
|
18486
|
+
import path17 from "path";
|
|
17929
18487
|
import chalk11 from "chalk";
|
|
17930
18488
|
|
|
17931
18489
|
// src/features/claude_code/daemon-check-shim.tmpl.js
|
|
17932
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";
|
|
17933
18491
|
|
|
17934
18492
|
// src/features/claude_code/install_hook.ts
|
|
17935
|
-
var CLAUDE_SETTINGS_PATH =
|
|
18493
|
+
var CLAUDE_SETTINGS_PATH = path17.join(os6.homedir(), ".claude", "settings.json");
|
|
17936
18494
|
var RECOMMENDED_MATCHER = "*";
|
|
17937
18495
|
async function claudeSettingsExists() {
|
|
17938
18496
|
try {
|
|
@@ -18078,18 +18636,18 @@ async function installMobbHooks(options = {}) {
|
|
|
18078
18636
|
}
|
|
18079
18637
|
|
|
18080
18638
|
// src/features/claude_code/transcript_scanner.ts
|
|
18081
|
-
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";
|
|
18082
18640
|
import os7 from "os";
|
|
18083
|
-
import
|
|
18641
|
+
import path18 from "path";
|
|
18084
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;
|
|
18085
18643
|
function getClaudeProjectsDirs() {
|
|
18086
18644
|
const dirs = [];
|
|
18087
18645
|
const configDir = process.env["CLAUDE_CONFIG_DIR"];
|
|
18088
18646
|
if (configDir) {
|
|
18089
|
-
dirs.push(
|
|
18647
|
+
dirs.push(path18.join(configDir, "projects"));
|
|
18090
18648
|
}
|
|
18091
|
-
dirs.push(
|
|
18092
|
-
dirs.push(
|
|
18649
|
+
dirs.push(path18.join(os7.homedir(), ".config", "claude", "projects"));
|
|
18650
|
+
dirs.push(path18.join(os7.homedir(), ".claude", "projects"));
|
|
18093
18651
|
return dirs;
|
|
18094
18652
|
}
|
|
18095
18653
|
async function collectJsonlFiles(files, dir, projectDir, seen, now, results) {
|
|
@@ -18097,12 +18655,12 @@ async function collectJsonlFiles(files, dir, projectDir, seen, now, results) {
|
|
|
18097
18655
|
if (!file.endsWith(".jsonl")) continue;
|
|
18098
18656
|
const sessionId = file.replace(".jsonl", "");
|
|
18099
18657
|
if (!UUID_RE.test(sessionId)) continue;
|
|
18100
|
-
const filePath =
|
|
18658
|
+
const filePath = path18.join(dir, file);
|
|
18101
18659
|
if (seen.has(filePath)) continue;
|
|
18102
18660
|
seen.add(filePath);
|
|
18103
18661
|
let fileStat;
|
|
18104
18662
|
try {
|
|
18105
|
-
fileStat = await
|
|
18663
|
+
fileStat = await stat2(filePath);
|
|
18106
18664
|
} catch {
|
|
18107
18665
|
continue;
|
|
18108
18666
|
}
|
|
@@ -18128,10 +18686,10 @@ async function scanForTranscripts(projectsDirs = getClaudeProjectsDirs()) {
|
|
|
18128
18686
|
continue;
|
|
18129
18687
|
}
|
|
18130
18688
|
for (const projName of projectDirs) {
|
|
18131
|
-
const projPath =
|
|
18689
|
+
const projPath = path18.join(projectsDir, projName);
|
|
18132
18690
|
let projStat;
|
|
18133
18691
|
try {
|
|
18134
|
-
projStat = await
|
|
18692
|
+
projStat = await stat2(projPath);
|
|
18135
18693
|
} catch {
|
|
18136
18694
|
continue;
|
|
18137
18695
|
}
|
|
@@ -18145,9 +18703,9 @@ async function scanForTranscripts(projectsDirs = getClaudeProjectsDirs()) {
|
|
|
18145
18703
|
await collectJsonlFiles(files, projPath, projPath, seen, now, results);
|
|
18146
18704
|
for (const entry of files) {
|
|
18147
18705
|
if (!UUID_RE.test(entry)) continue;
|
|
18148
|
-
const subagentsDir =
|
|
18706
|
+
const subagentsDir = path18.join(projPath, entry, "subagents");
|
|
18149
18707
|
try {
|
|
18150
|
-
const s = await
|
|
18708
|
+
const s = await stat2(subagentsDir);
|
|
18151
18709
|
if (!s.isDirectory()) continue;
|
|
18152
18710
|
const subFiles = await readdir2(subagentsDir);
|
|
18153
18711
|
await collectJsonlFiles(
|
|
@@ -18199,6 +18757,7 @@ async function extractCwdFromTranscript(filePath) {
|
|
|
18199
18757
|
// src/features/claude_code/daemon.ts
|
|
18200
18758
|
async function startDaemon() {
|
|
18201
18759
|
hookLog.info("Daemon starting");
|
|
18760
|
+
pruneHookLogFile();
|
|
18202
18761
|
const pidFile = await acquirePidFile();
|
|
18203
18762
|
async function gracefulExit(code, reason) {
|
|
18204
18763
|
hookLog.info({ data: { code } }, `Daemon exiting: ${reason}`);
|
|
@@ -18245,7 +18804,7 @@ async function startDaemon() {
|
|
|
18245
18804
|
for (const transcript of changed) {
|
|
18246
18805
|
const sessionStore = createSessionConfigStore(transcript.sessionId);
|
|
18247
18806
|
if (!cleanupConfigDir) {
|
|
18248
|
-
cleanupConfigDir =
|
|
18807
|
+
cleanupConfigDir = path19.dirname(sessionStore.path);
|
|
18249
18808
|
}
|
|
18250
18809
|
await drainTranscript(transcript, sessionStore, gqlClient);
|
|
18251
18810
|
}
|
|
@@ -18360,6 +18919,53 @@ async function tryAutoUpgradeHooks() {
|
|
|
18360
18919
|
hookLog.warn({ err }, "Failed to auto-upgrade hook matcher");
|
|
18361
18920
|
}
|
|
18362
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
|
+
}
|
|
18363
18969
|
|
|
18364
18970
|
// src/args/commands/claude_code.ts
|
|
18365
18971
|
var claudeCodeInstallHookBuilder = (yargs2) => {
|
|
@@ -18448,7 +19054,7 @@ import {
|
|
|
18448
19054
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
18449
19055
|
|
|
18450
19056
|
// src/mcp/Logger.ts
|
|
18451
|
-
import
|
|
19057
|
+
import Configstore4 from "configstore";
|
|
18452
19058
|
|
|
18453
19059
|
// src/mcp/services/WorkspaceService.ts
|
|
18454
19060
|
var WorkspaceService = class {
|
|
@@ -18456,8 +19062,8 @@ var WorkspaceService = class {
|
|
|
18456
19062
|
* Sets a known workspace path that was discovered through successful validation
|
|
18457
19063
|
* @param path The validated workspace path to store
|
|
18458
19064
|
*/
|
|
18459
|
-
static setKnownWorkspacePath(
|
|
18460
|
-
this.knownWorkspacePath =
|
|
19065
|
+
static setKnownWorkspacePath(path32) {
|
|
19066
|
+
this.knownWorkspacePath = path32;
|
|
18461
19067
|
}
|
|
18462
19068
|
/**
|
|
18463
19069
|
* Gets the known workspace path that was previously validated
|
|
@@ -18534,7 +19140,7 @@ var Logger = class {
|
|
|
18534
19140
|
__publicField(this, "lastKnownPath", null);
|
|
18535
19141
|
this.host = WorkspaceService.getHost();
|
|
18536
19142
|
this.unknownPathSuffix = Math.floor(1e3 + Math.random() * 9e3).toString();
|
|
18537
|
-
this.mobbConfigStore = new
|
|
19143
|
+
this.mobbConfigStore = new Configstore4("mobb-logs", {});
|
|
18538
19144
|
this.mobbConfigStore.set("version", packageJson.version);
|
|
18539
19145
|
}
|
|
18540
19146
|
/**
|
|
@@ -19318,7 +19924,7 @@ async function createAuthenticatedMcpGQLClient({
|
|
|
19318
19924
|
import { execSync as execSync2 } from "child_process";
|
|
19319
19925
|
import fs15 from "fs";
|
|
19320
19926
|
import os8 from "os";
|
|
19321
|
-
import
|
|
19927
|
+
import path20 from "path";
|
|
19322
19928
|
var IDEs = ["cursor", "windsurf", "webstorm", "vscode", "claude"];
|
|
19323
19929
|
var runCommand = (cmd) => {
|
|
19324
19930
|
try {
|
|
@@ -19333,7 +19939,7 @@ var gitInfo = {
|
|
|
19333
19939
|
};
|
|
19334
19940
|
var getClaudeWorkspacePaths = () => {
|
|
19335
19941
|
const home = os8.homedir();
|
|
19336
|
-
const claudeIdePath =
|
|
19942
|
+
const claudeIdePath = path20.join(home, ".claude", "ide");
|
|
19337
19943
|
const workspacePaths = [];
|
|
19338
19944
|
if (!fs15.existsSync(claudeIdePath)) {
|
|
19339
19945
|
return workspacePaths;
|
|
@@ -19341,7 +19947,7 @@ var getClaudeWorkspacePaths = () => {
|
|
|
19341
19947
|
try {
|
|
19342
19948
|
const lockFiles = fs15.readdirSync(claudeIdePath).filter((file) => file.endsWith(".lock"));
|
|
19343
19949
|
for (const lockFile of lockFiles) {
|
|
19344
|
-
const lockFilePath =
|
|
19950
|
+
const lockFilePath = path20.join(claudeIdePath, lockFile);
|
|
19345
19951
|
try {
|
|
19346
19952
|
const lockContent = JSON.parse(fs15.readFileSync(lockFilePath, "utf8"));
|
|
19347
19953
|
if (lockContent.workspaceFolders && Array.isArray(lockContent.workspaceFolders)) {
|
|
@@ -19366,24 +19972,24 @@ var getMCPConfigPaths = (hostName) => {
|
|
|
19366
19972
|
switch (hostName.toLowerCase()) {
|
|
19367
19973
|
case "cursor":
|
|
19368
19974
|
return [
|
|
19369
|
-
|
|
19975
|
+
path20.join(currentDir, ".cursor", "mcp.json"),
|
|
19370
19976
|
// local first
|
|
19371
|
-
|
|
19977
|
+
path20.join(home, ".cursor", "mcp.json")
|
|
19372
19978
|
];
|
|
19373
19979
|
case "windsurf":
|
|
19374
19980
|
return [
|
|
19375
|
-
|
|
19981
|
+
path20.join(currentDir, ".codeium", "mcp_config.json"),
|
|
19376
19982
|
// local first
|
|
19377
|
-
|
|
19983
|
+
path20.join(home, ".codeium", "windsurf", "mcp_config.json")
|
|
19378
19984
|
];
|
|
19379
19985
|
case "webstorm":
|
|
19380
19986
|
return [];
|
|
19381
19987
|
case "visualstudiocode":
|
|
19382
19988
|
case "vscode":
|
|
19383
19989
|
return [
|
|
19384
|
-
|
|
19990
|
+
path20.join(currentDir, ".vscode", "mcp.json"),
|
|
19385
19991
|
// local first
|
|
19386
|
-
process.platform === "win32" ?
|
|
19992
|
+
process.platform === "win32" ? path20.join(home, "AppData", "Roaming", "Code", "User", "mcp.json") : path20.join(
|
|
19387
19993
|
home,
|
|
19388
19994
|
"Library",
|
|
19389
19995
|
"Application Support",
|
|
@@ -19394,13 +20000,13 @@ var getMCPConfigPaths = (hostName) => {
|
|
|
19394
20000
|
];
|
|
19395
20001
|
case "claude": {
|
|
19396
20002
|
const claudePaths = [
|
|
19397
|
-
|
|
20003
|
+
path20.join(currentDir, ".claude.json"),
|
|
19398
20004
|
// local first
|
|
19399
|
-
|
|
20005
|
+
path20.join(home, ".claude.json")
|
|
19400
20006
|
];
|
|
19401
20007
|
const workspacePaths = getClaudeWorkspacePaths();
|
|
19402
20008
|
for (const workspacePath of workspacePaths) {
|
|
19403
|
-
claudePaths.push(
|
|
20009
|
+
claudePaths.push(path20.join(workspacePath, ".mcp.json"));
|
|
19404
20010
|
}
|
|
19405
20011
|
return claudePaths;
|
|
19406
20012
|
}
|
|
@@ -19561,10 +20167,10 @@ var getHostInfo = (additionalMcpList) => {
|
|
|
19561
20167
|
const ideConfigPaths = /* @__PURE__ */ new Set();
|
|
19562
20168
|
for (const ide of IDEs) {
|
|
19563
20169
|
const configPaths = getMCPConfigPaths(ide);
|
|
19564
|
-
configPaths.forEach((
|
|
20170
|
+
configPaths.forEach((path32) => ideConfigPaths.add(path32));
|
|
19565
20171
|
}
|
|
19566
20172
|
const uniqueAdditionalPaths = additionalMcpList.filter(
|
|
19567
|
-
(
|
|
20173
|
+
(path32) => !ideConfigPaths.has(path32)
|
|
19568
20174
|
);
|
|
19569
20175
|
for (const ide of IDEs) {
|
|
19570
20176
|
const cfg = readMCPConfig(ide);
|
|
@@ -19686,7 +20292,7 @@ init_configs();
|
|
|
19686
20292
|
init_configs();
|
|
19687
20293
|
import fs16 from "fs";
|
|
19688
20294
|
import os9 from "os";
|
|
19689
|
-
import
|
|
20295
|
+
import path21 from "path";
|
|
19690
20296
|
var MAX_DEPTH = 2;
|
|
19691
20297
|
var patterns = ["mcp", "claude"];
|
|
19692
20298
|
var isFileMatch = (fileName) => {
|
|
@@ -19706,7 +20312,7 @@ var searchDir = async (dir, depth = 0) => {
|
|
|
19706
20312
|
if (depth > MAX_DEPTH) return results;
|
|
19707
20313
|
const entries = await fs16.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
19708
20314
|
for (const entry of entries) {
|
|
19709
|
-
const fullPath =
|
|
20315
|
+
const fullPath = path21.join(dir, entry.name);
|
|
19710
20316
|
if (entry.isFile() && isFileMatch(entry.name)) {
|
|
19711
20317
|
results.push(fullPath);
|
|
19712
20318
|
} else if (entry.isDirectory()) {
|
|
@@ -19723,14 +20329,14 @@ var findSystemMCPConfigs = async () => {
|
|
|
19723
20329
|
const home = os9.homedir();
|
|
19724
20330
|
const platform2 = os9.platform();
|
|
19725
20331
|
const knownDirs = platform2 === "win32" ? [
|
|
19726
|
-
|
|
19727
|
-
|
|
19728
|
-
|
|
20332
|
+
path21.join(home, ".cursor"),
|
|
20333
|
+
path21.join(home, "Documents"),
|
|
20334
|
+
path21.join(home, "Downloads")
|
|
19729
20335
|
] : [
|
|
19730
|
-
|
|
19731
|
-
process.env["XDG_CONFIG_HOME"] ||
|
|
19732
|
-
|
|
19733
|
-
|
|
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")
|
|
19734
20340
|
];
|
|
19735
20341
|
const timeoutPromise = new Promise(
|
|
19736
20342
|
(resolve) => setTimeout(() => {
|
|
@@ -22146,13 +22752,13 @@ For a complete security audit workflow, use the \`full-security-audit\` prompt.
|
|
|
22146
22752
|
// src/mcp/services/McpDetectionService/CursorMcpDetectionService.ts
|
|
22147
22753
|
import * as fs19 from "fs";
|
|
22148
22754
|
import * as os12 from "os";
|
|
22149
|
-
import * as
|
|
22755
|
+
import * as path23 from "path";
|
|
22150
22756
|
|
|
22151
22757
|
// src/mcp/services/McpDetectionService/BaseMcpDetectionService.ts
|
|
22152
22758
|
init_configs();
|
|
22153
22759
|
import * as fs18 from "fs";
|
|
22154
22760
|
import fetch7 from "node-fetch";
|
|
22155
|
-
import * as
|
|
22761
|
+
import * as path22 from "path";
|
|
22156
22762
|
|
|
22157
22763
|
// src/mcp/services/McpDetectionService/McpDetectionServiceUtils.ts
|
|
22158
22764
|
import * as fs17 from "fs";
|
|
@@ -22161,14 +22767,14 @@ import * as os11 from "os";
|
|
|
22161
22767
|
// src/mcp/services/McpDetectionService/VscodeMcpDetectionService.ts
|
|
22162
22768
|
import * as fs20 from "fs";
|
|
22163
22769
|
import * as os13 from "os";
|
|
22164
|
-
import * as
|
|
22770
|
+
import * as path24 from "path";
|
|
22165
22771
|
|
|
22166
22772
|
// src/mcp/tools/checkForNewAvailableFixes/CheckForNewAvailableFixesTool.ts
|
|
22167
22773
|
import { z as z42 } from "zod";
|
|
22168
22774
|
|
|
22169
22775
|
// src/mcp/services/PathValidation.ts
|
|
22170
22776
|
import fs21 from "fs";
|
|
22171
|
-
import
|
|
22777
|
+
import path25 from "path";
|
|
22172
22778
|
async function validatePath(inputPath) {
|
|
22173
22779
|
logDebug("Validating MCP path", { inputPath });
|
|
22174
22780
|
if (/^\/[a-zA-Z]:\//.test(inputPath)) {
|
|
@@ -22200,7 +22806,7 @@ async function validatePath(inputPath) {
|
|
|
22200
22806
|
logError(error);
|
|
22201
22807
|
return { isValid: false, error, path: inputPath };
|
|
22202
22808
|
}
|
|
22203
|
-
const normalizedPath =
|
|
22809
|
+
const normalizedPath = path25.normalize(inputPath);
|
|
22204
22810
|
if (normalizedPath.includes("..")) {
|
|
22205
22811
|
const error = `Normalized path contains path traversal patterns: ${inputPath}`;
|
|
22206
22812
|
logError(error);
|
|
@@ -22852,7 +23458,7 @@ init_configs();
|
|
|
22852
23458
|
import fs22 from "fs/promises";
|
|
22853
23459
|
import nodePath from "path";
|
|
22854
23460
|
var getLocalFiles = async ({
|
|
22855
|
-
path:
|
|
23461
|
+
path: path32,
|
|
22856
23462
|
maxFileSize = MCP_MAX_FILE_SIZE,
|
|
22857
23463
|
maxFiles,
|
|
22858
23464
|
isAllFilesScan,
|
|
@@ -22860,17 +23466,17 @@ var getLocalFiles = async ({
|
|
|
22860
23466
|
scanRecentlyChangedFiles
|
|
22861
23467
|
}) => {
|
|
22862
23468
|
logDebug(`[${scanContext}] Starting getLocalFiles`, {
|
|
22863
|
-
path:
|
|
23469
|
+
path: path32,
|
|
22864
23470
|
maxFileSize,
|
|
22865
23471
|
maxFiles,
|
|
22866
23472
|
isAllFilesScan,
|
|
22867
23473
|
scanRecentlyChangedFiles
|
|
22868
23474
|
});
|
|
22869
23475
|
try {
|
|
22870
|
-
const resolvedRepoPath = await fs22.realpath(
|
|
23476
|
+
const resolvedRepoPath = await fs22.realpath(path32);
|
|
22871
23477
|
logDebug(`[${scanContext}] Resolved repository path`, {
|
|
22872
23478
|
resolvedRepoPath,
|
|
22873
|
-
originalPath:
|
|
23479
|
+
originalPath: path32
|
|
22874
23480
|
});
|
|
22875
23481
|
const gitService = new GitService(resolvedRepoPath, log);
|
|
22876
23482
|
const gitValidation = await gitService.validateRepository();
|
|
@@ -22883,7 +23489,7 @@ var getLocalFiles = async ({
|
|
|
22883
23489
|
if (!gitValidation.isValid || isAllFilesScan) {
|
|
22884
23490
|
try {
|
|
22885
23491
|
files = await FileUtils.getLastChangedFiles({
|
|
22886
|
-
dir:
|
|
23492
|
+
dir: path32,
|
|
22887
23493
|
maxFileSize,
|
|
22888
23494
|
maxFiles,
|
|
22889
23495
|
isAllFilesScan
|
|
@@ -22975,7 +23581,7 @@ var getLocalFiles = async ({
|
|
|
22975
23581
|
logError(`${scanContext}Unexpected error in getLocalFiles`, {
|
|
22976
23582
|
error: error instanceof Error ? error.message : String(error),
|
|
22977
23583
|
stack: error instanceof Error ? error.stack : void 0,
|
|
22978
|
-
path:
|
|
23584
|
+
path: path32
|
|
22979
23585
|
});
|
|
22980
23586
|
throw error;
|
|
22981
23587
|
}
|
|
@@ -22985,7 +23591,7 @@ var getLocalFiles = async ({
|
|
|
22985
23591
|
init_client_generates();
|
|
22986
23592
|
init_GitService();
|
|
22987
23593
|
import fs23 from "fs";
|
|
22988
|
-
import
|
|
23594
|
+
import path26 from "path";
|
|
22989
23595
|
import { z as z41 } from "zod";
|
|
22990
23596
|
function extractPathFromPatch(patch) {
|
|
22991
23597
|
const match = patch?.match(/diff --git a\/([^\s]+) b\//);
|
|
@@ -23071,7 +23677,7 @@ var LocalMobbFolderService = class {
|
|
|
23071
23677
|
"[LocalMobbFolderService] Non-git repository detected, skipping .gitignore operations"
|
|
23072
23678
|
);
|
|
23073
23679
|
}
|
|
23074
|
-
const mobbFolderPath =
|
|
23680
|
+
const mobbFolderPath = path26.join(
|
|
23075
23681
|
this.repoPath,
|
|
23076
23682
|
this.defaultMobbFolderName
|
|
23077
23683
|
);
|
|
@@ -23243,7 +23849,7 @@ var LocalMobbFolderService = class {
|
|
|
23243
23849
|
mobbFolderPath,
|
|
23244
23850
|
baseFileName
|
|
23245
23851
|
);
|
|
23246
|
-
const filePath =
|
|
23852
|
+
const filePath = path26.join(mobbFolderPath, uniqueFileName);
|
|
23247
23853
|
await fs23.promises.writeFile(filePath, patch, "utf8");
|
|
23248
23854
|
logInfo("[LocalMobbFolderService] Patch saved successfully", {
|
|
23249
23855
|
filePath,
|
|
@@ -23301,11 +23907,11 @@ var LocalMobbFolderService = class {
|
|
|
23301
23907
|
* @returns Unique filename that doesn't conflict with existing files
|
|
23302
23908
|
*/
|
|
23303
23909
|
getUniqueFileName(folderPath, baseFileName) {
|
|
23304
|
-
const baseName =
|
|
23305
|
-
const extension =
|
|
23910
|
+
const baseName = path26.parse(baseFileName).name;
|
|
23911
|
+
const extension = path26.parse(baseFileName).ext;
|
|
23306
23912
|
let uniqueFileName = baseFileName;
|
|
23307
23913
|
let index = 1;
|
|
23308
|
-
while (fs23.existsSync(
|
|
23914
|
+
while (fs23.existsSync(path26.join(folderPath, uniqueFileName))) {
|
|
23309
23915
|
uniqueFileName = `${baseName}-${index}${extension}`;
|
|
23310
23916
|
index++;
|
|
23311
23917
|
if (index > 1e3) {
|
|
@@ -23336,7 +23942,7 @@ var LocalMobbFolderService = class {
|
|
|
23336
23942
|
logDebug("[LocalMobbFolderService] Logging patch info", { fixId: fix.id });
|
|
23337
23943
|
try {
|
|
23338
23944
|
const mobbFolderPath = await this.getFolder();
|
|
23339
|
-
const patchInfoPath =
|
|
23945
|
+
const patchInfoPath = path26.join(mobbFolderPath, "patchInfo.md");
|
|
23340
23946
|
const markdownContent = this.generateFixMarkdown(fix, savedPatchFileName);
|
|
23341
23947
|
let existingContent = "";
|
|
23342
23948
|
if (fs23.existsSync(patchInfoPath)) {
|
|
@@ -23378,7 +23984,7 @@ var LocalMobbFolderService = class {
|
|
|
23378
23984
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
23379
23985
|
const patch = this.extractPatchFromFix(fix);
|
|
23380
23986
|
const relativePatchedFilePath = patch ? extractPathFromPatch(patch) : null;
|
|
23381
|
-
const patchedFilePath = relativePatchedFilePath ?
|
|
23987
|
+
const patchedFilePath = relativePatchedFilePath ? path26.resolve(this.repoPath, relativePatchedFilePath) : null;
|
|
23382
23988
|
const fixIdentifier = savedPatchFileName ? savedPatchFileName.replace(".patch", "") : fix.id;
|
|
23383
23989
|
let markdown = `# Fix ${fixIdentifier}
|
|
23384
23990
|
|
|
@@ -23716,20 +24322,20 @@ init_configs();
|
|
|
23716
24322
|
import {
|
|
23717
24323
|
existsSync as existsSync6,
|
|
23718
24324
|
mkdirSync,
|
|
23719
|
-
readFileSync as
|
|
24325
|
+
readFileSync as readFileSync4,
|
|
23720
24326
|
unlinkSync,
|
|
23721
|
-
writeFileSync as
|
|
24327
|
+
writeFileSync as writeFileSync3
|
|
23722
24328
|
} from "fs";
|
|
23723
24329
|
import fs24 from "fs/promises";
|
|
23724
24330
|
import parseDiff2 from "parse-diff";
|
|
23725
|
-
import
|
|
24331
|
+
import path27 from "path";
|
|
23726
24332
|
var PatchApplicationService = class {
|
|
23727
24333
|
/**
|
|
23728
24334
|
* Gets the appropriate comment syntax for a file based on its extension
|
|
23729
24335
|
*/
|
|
23730
24336
|
static getCommentSyntax(filePath) {
|
|
23731
|
-
const ext =
|
|
23732
|
-
const basename2 =
|
|
24337
|
+
const ext = path27.extname(filePath).toLowerCase();
|
|
24338
|
+
const basename2 = path27.basename(filePath);
|
|
23733
24339
|
const commentMap = {
|
|
23734
24340
|
// C-style languages (single line comments)
|
|
23735
24341
|
".js": "//",
|
|
@@ -23937,18 +24543,18 @@ var PatchApplicationService = class {
|
|
|
23937
24543
|
}
|
|
23938
24544
|
);
|
|
23939
24545
|
}
|
|
23940
|
-
const dirPath =
|
|
24546
|
+
const dirPath = path27.dirname(normalizedFilePath);
|
|
23941
24547
|
mkdirSync(dirPath, { recursive: true });
|
|
23942
|
-
|
|
24548
|
+
writeFileSync3(normalizedFilePath, finalContent, "utf8");
|
|
23943
24549
|
return normalizedFilePath;
|
|
23944
24550
|
}
|
|
23945
24551
|
static resolvePathWithinRepo({
|
|
23946
24552
|
repositoryPath,
|
|
23947
24553
|
targetPath
|
|
23948
24554
|
}) {
|
|
23949
|
-
const repoRoot =
|
|
23950
|
-
const normalizedPath =
|
|
23951
|
-
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}`;
|
|
23952
24558
|
if (normalizedPath !== repoRoot && !normalizedPath.startsWith(repoRootWithSep)) {
|
|
23953
24559
|
throw new Error(
|
|
23954
24560
|
`Security violation: target path ${targetPath} resolves outside repository`
|
|
@@ -23957,7 +24563,7 @@ var PatchApplicationService = class {
|
|
|
23957
24563
|
return {
|
|
23958
24564
|
repoRoot,
|
|
23959
24565
|
normalizedPath,
|
|
23960
|
-
relativePath:
|
|
24566
|
+
relativePath: path27.relative(repoRoot, normalizedPath)
|
|
23961
24567
|
};
|
|
23962
24568
|
}
|
|
23963
24569
|
/**
|
|
@@ -24239,7 +24845,7 @@ var PatchApplicationService = class {
|
|
|
24239
24845
|
continue;
|
|
24240
24846
|
}
|
|
24241
24847
|
try {
|
|
24242
|
-
const absolutePath =
|
|
24848
|
+
const absolutePath = path27.resolve(repositoryPath, targetFile);
|
|
24243
24849
|
if (existsSync6(absolutePath)) {
|
|
24244
24850
|
const stats = await fs24.stat(absolutePath);
|
|
24245
24851
|
const fileModTime = stats.mtime.getTime();
|
|
@@ -24465,7 +25071,7 @@ var PatchApplicationService = class {
|
|
|
24465
25071
|
fix,
|
|
24466
25072
|
scanContext
|
|
24467
25073
|
});
|
|
24468
|
-
appliedFiles.push(
|
|
25074
|
+
appliedFiles.push(path27.relative(repositoryPath, actualPath));
|
|
24469
25075
|
logDebug(`[${scanContext}] Created new file: ${relativePath}`);
|
|
24470
25076
|
}
|
|
24471
25077
|
/**
|
|
@@ -24501,7 +25107,7 @@ var PatchApplicationService = class {
|
|
|
24501
25107
|
`Target file does not exist: ${targetFile} (resolved to: ${absoluteFilePath})`
|
|
24502
25108
|
);
|
|
24503
25109
|
}
|
|
24504
|
-
const originalContent =
|
|
25110
|
+
const originalContent = readFileSync4(absoluteFilePath, "utf8");
|
|
24505
25111
|
const modifiedContent = this.applyHunksToFile(
|
|
24506
25112
|
originalContent,
|
|
24507
25113
|
fileDiff.chunks
|
|
@@ -24514,7 +25120,7 @@ var PatchApplicationService = class {
|
|
|
24514
25120
|
fix,
|
|
24515
25121
|
scanContext
|
|
24516
25122
|
});
|
|
24517
|
-
appliedFiles.push(
|
|
25123
|
+
appliedFiles.push(path27.relative(repositoryPath, actualPath));
|
|
24518
25124
|
logDebug(`[${scanContext}] Modified file: ${relativePath}`);
|
|
24519
25125
|
}
|
|
24520
25126
|
}
|
|
@@ -24711,8 +25317,8 @@ init_configs();
|
|
|
24711
25317
|
// src/mcp/services/FileOperations.ts
|
|
24712
25318
|
init_FileUtils();
|
|
24713
25319
|
import fs25 from "fs";
|
|
24714
|
-
import
|
|
24715
|
-
import
|
|
25320
|
+
import path28 from "path";
|
|
25321
|
+
import AdmZip4 from "adm-zip";
|
|
24716
25322
|
var FileOperations = class {
|
|
24717
25323
|
/**
|
|
24718
25324
|
* Creates a ZIP archive containing the specified source files
|
|
@@ -24727,14 +25333,14 @@ var FileOperations = class {
|
|
|
24727
25333
|
maxFileSize
|
|
24728
25334
|
}) {
|
|
24729
25335
|
logDebug("[FileOperations] Packing files");
|
|
24730
|
-
const zip = new
|
|
25336
|
+
const zip = new AdmZip4();
|
|
24731
25337
|
let packedFilesCount = 0;
|
|
24732
25338
|
const packedFiles = [];
|
|
24733
25339
|
const excludedFiles = [];
|
|
24734
|
-
const resolvedRepoPath =
|
|
25340
|
+
const resolvedRepoPath = path28.resolve(repositoryPath);
|
|
24735
25341
|
for (const filepath of fileList) {
|
|
24736
|
-
const absoluteFilepath =
|
|
24737
|
-
const resolvedFilePath =
|
|
25342
|
+
const absoluteFilepath = path28.join(repositoryPath, filepath);
|
|
25343
|
+
const resolvedFilePath = path28.resolve(absoluteFilepath);
|
|
24738
25344
|
if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
|
|
24739
25345
|
const reason = "potential path traversal security risk";
|
|
24740
25346
|
logDebug(`[FileOperations] Skipping ${filepath} due to ${reason}`);
|
|
@@ -24781,11 +25387,11 @@ var FileOperations = class {
|
|
|
24781
25387
|
fileList,
|
|
24782
25388
|
repositoryPath
|
|
24783
25389
|
}) {
|
|
24784
|
-
const resolvedRepoPath =
|
|
25390
|
+
const resolvedRepoPath = path28.resolve(repositoryPath);
|
|
24785
25391
|
const validatedPaths = [];
|
|
24786
25392
|
for (const filepath of fileList) {
|
|
24787
|
-
const absoluteFilepath =
|
|
24788
|
-
const resolvedFilePath =
|
|
25393
|
+
const absoluteFilepath = path28.join(repositoryPath, filepath);
|
|
25394
|
+
const resolvedFilePath = path28.resolve(absoluteFilepath);
|
|
24789
25395
|
if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
|
|
24790
25396
|
logDebug(
|
|
24791
25397
|
`[FileOperations] Rejecting ${filepath} - path traversal attempt detected`
|
|
@@ -24813,7 +25419,7 @@ var FileOperations = class {
|
|
|
24813
25419
|
for (const absolutePath of filePaths) {
|
|
24814
25420
|
try {
|
|
24815
25421
|
const content = await fs25.promises.readFile(absolutePath);
|
|
24816
|
-
const relativePath =
|
|
25422
|
+
const relativePath = path28.basename(absolutePath);
|
|
24817
25423
|
fileDataArray.push({
|
|
24818
25424
|
relativePath,
|
|
24819
25425
|
absolutePath,
|
|
@@ -25125,14 +25731,14 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
25125
25731
|
* since the last scan.
|
|
25126
25732
|
*/
|
|
25127
25733
|
async scanForSecurityVulnerabilities({
|
|
25128
|
-
path:
|
|
25734
|
+
path: path32,
|
|
25129
25735
|
isAllDetectionRulesScan,
|
|
25130
25736
|
isAllFilesScan,
|
|
25131
25737
|
scanContext
|
|
25132
25738
|
}) {
|
|
25133
25739
|
this.hasAuthenticationFailed = false;
|
|
25134
25740
|
logDebug(`[${scanContext}] Scanning for new security vulnerabilities`, {
|
|
25135
|
-
path:
|
|
25741
|
+
path: path32
|
|
25136
25742
|
});
|
|
25137
25743
|
if (!this.gqlClient) {
|
|
25138
25744
|
logInfo(`[${scanContext}] No GQL client found, skipping scan`);
|
|
@@ -25148,11 +25754,11 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
25148
25754
|
}
|
|
25149
25755
|
logDebug(
|
|
25150
25756
|
`[${scanContext}] Connected to the API, assembling list of files to scan`,
|
|
25151
|
-
{ path:
|
|
25757
|
+
{ path: path32 }
|
|
25152
25758
|
);
|
|
25153
25759
|
const isBackgroundScan = scanContext === ScanContext.BACKGROUND_INITIAL || scanContext === ScanContext.BACKGROUND_PERIODIC;
|
|
25154
25760
|
const files = await getLocalFiles({
|
|
25155
|
-
path:
|
|
25761
|
+
path: path32,
|
|
25156
25762
|
isAllFilesScan,
|
|
25157
25763
|
scanContext,
|
|
25158
25764
|
scanRecentlyChangedFiles: !isBackgroundScan
|
|
@@ -25178,13 +25784,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
25178
25784
|
});
|
|
25179
25785
|
const { fixReportId, projectId } = await scanFiles({
|
|
25180
25786
|
fileList: filesToScan.map((file) => file.relativePath),
|
|
25181
|
-
repositoryPath:
|
|
25787
|
+
repositoryPath: path32,
|
|
25182
25788
|
gqlClient: this.gqlClient,
|
|
25183
25789
|
isAllDetectionRulesScan,
|
|
25184
25790
|
scanContext
|
|
25185
25791
|
});
|
|
25186
25792
|
logInfo(
|
|
25187
|
-
`[${scanContext}] Security scan completed for ${
|
|
25793
|
+
`[${scanContext}] Security scan completed for ${path32} reportId: ${fixReportId} projectId: ${projectId}`
|
|
25188
25794
|
);
|
|
25189
25795
|
if (isAllFilesScan) {
|
|
25190
25796
|
return;
|
|
@@ -25478,13 +26084,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
25478
26084
|
});
|
|
25479
26085
|
return scannedFiles.some((file) => file.relativePath === fixFile);
|
|
25480
26086
|
}
|
|
25481
|
-
async getFreshFixes({ path:
|
|
26087
|
+
async getFreshFixes({ path: path32 }) {
|
|
25482
26088
|
const scanContext = ScanContext.USER_REQUEST;
|
|
25483
|
-
logDebug(`[${scanContext}] Getting fresh fixes`, { path:
|
|
25484
|
-
if (this.path !==
|
|
25485
|
-
this.path =
|
|
26089
|
+
logDebug(`[${scanContext}] Getting fresh fixes`, { path: path32 });
|
|
26090
|
+
if (this.path !== path32) {
|
|
26091
|
+
this.path = path32;
|
|
25486
26092
|
this.reset();
|
|
25487
|
-
logInfo(`[${scanContext}] Reset service state for new path`, { path:
|
|
26093
|
+
logInfo(`[${scanContext}] Reset service state for new path`, { path: path32 });
|
|
25488
26094
|
}
|
|
25489
26095
|
try {
|
|
25490
26096
|
const loginContext = createMcpLoginContext("check_new_fixes");
|
|
@@ -25503,7 +26109,7 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
25503
26109
|
}
|
|
25504
26110
|
throw error;
|
|
25505
26111
|
}
|
|
25506
|
-
this.triggerScan({ path:
|
|
26112
|
+
this.triggerScan({ path: path32, gqlClient: this.gqlClient });
|
|
25507
26113
|
let isMvsAutoFixEnabled = null;
|
|
25508
26114
|
try {
|
|
25509
26115
|
isMvsAutoFixEnabled = await this.gqlClient.getMvsAutoFixSettings();
|
|
@@ -25537,33 +26143,33 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
25537
26143
|
return noFreshFixesPrompt;
|
|
25538
26144
|
}
|
|
25539
26145
|
triggerScan({
|
|
25540
|
-
path:
|
|
26146
|
+
path: path32,
|
|
25541
26147
|
gqlClient
|
|
25542
26148
|
}) {
|
|
25543
|
-
if (this.path !==
|
|
25544
|
-
this.path =
|
|
26149
|
+
if (this.path !== path32) {
|
|
26150
|
+
this.path = path32;
|
|
25545
26151
|
this.reset();
|
|
25546
|
-
logInfo(`Reset service state for new path in triggerScan`, { path:
|
|
26152
|
+
logInfo(`Reset service state for new path in triggerScan`, { path: path32 });
|
|
25547
26153
|
}
|
|
25548
26154
|
this.gqlClient = gqlClient;
|
|
25549
26155
|
if (!this.intervalId) {
|
|
25550
|
-
this.startPeriodicScanning(
|
|
25551
|
-
this.executeInitialScan(
|
|
25552
|
-
void this.executeInitialFullScan(
|
|
26156
|
+
this.startPeriodicScanning(path32);
|
|
26157
|
+
this.executeInitialScan(path32);
|
|
26158
|
+
void this.executeInitialFullScan(path32);
|
|
25553
26159
|
}
|
|
25554
26160
|
}
|
|
25555
|
-
startPeriodicScanning(
|
|
26161
|
+
startPeriodicScanning(path32) {
|
|
25556
26162
|
const scanContext = ScanContext.BACKGROUND_PERIODIC;
|
|
25557
26163
|
logDebug(
|
|
25558
26164
|
`[${scanContext}] Starting periodic scan for new security vulnerabilities`,
|
|
25559
26165
|
{
|
|
25560
|
-
path:
|
|
26166
|
+
path: path32
|
|
25561
26167
|
}
|
|
25562
26168
|
);
|
|
25563
26169
|
this.intervalId = setInterval(() => {
|
|
25564
|
-
logDebug(`[${scanContext}] Triggering periodic security scan`, { path:
|
|
26170
|
+
logDebug(`[${scanContext}] Triggering periodic security scan`, { path: path32 });
|
|
25565
26171
|
this.scanForSecurityVulnerabilities({
|
|
25566
|
-
path:
|
|
26172
|
+
path: path32,
|
|
25567
26173
|
scanContext
|
|
25568
26174
|
}).catch((error) => {
|
|
25569
26175
|
logError(`[${scanContext}] Error during periodic security scan`, {
|
|
@@ -25572,45 +26178,45 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
25572
26178
|
});
|
|
25573
26179
|
}, MCP_PERIODIC_CHECK_INTERVAL);
|
|
25574
26180
|
}
|
|
25575
|
-
async executeInitialFullScan(
|
|
26181
|
+
async executeInitialFullScan(path32) {
|
|
25576
26182
|
const scanContext = ScanContext.FULL_SCAN;
|
|
25577
|
-
logDebug(`[${scanContext}] Triggering initial full security scan`, { path:
|
|
26183
|
+
logDebug(`[${scanContext}] Triggering initial full security scan`, { path: path32 });
|
|
25578
26184
|
logDebug(`[${scanContext}] Full scan paths scanned`, {
|
|
25579
26185
|
fullScanPathsScanned: this.fullScanPathsScanned
|
|
25580
26186
|
});
|
|
25581
|
-
if (this.fullScanPathsScanned.includes(
|
|
26187
|
+
if (this.fullScanPathsScanned.includes(path32)) {
|
|
25582
26188
|
logDebug(`[${scanContext}] Full scan already executed for this path`, {
|
|
25583
|
-
path:
|
|
26189
|
+
path: path32
|
|
25584
26190
|
});
|
|
25585
26191
|
return;
|
|
25586
26192
|
}
|
|
25587
26193
|
configStore.set("fullScanPathsScanned", [
|
|
25588
26194
|
...this.fullScanPathsScanned,
|
|
25589
|
-
|
|
26195
|
+
path32
|
|
25590
26196
|
]);
|
|
25591
26197
|
try {
|
|
25592
26198
|
await this.scanForSecurityVulnerabilities({
|
|
25593
|
-
path:
|
|
26199
|
+
path: path32,
|
|
25594
26200
|
isAllFilesScan: true,
|
|
25595
26201
|
isAllDetectionRulesScan: true,
|
|
25596
26202
|
scanContext: ScanContext.FULL_SCAN
|
|
25597
26203
|
});
|
|
25598
|
-
if (!this.fullScanPathsScanned.includes(
|
|
25599
|
-
this.fullScanPathsScanned.push(
|
|
26204
|
+
if (!this.fullScanPathsScanned.includes(path32)) {
|
|
26205
|
+
this.fullScanPathsScanned.push(path32);
|
|
25600
26206
|
configStore.set("fullScanPathsScanned", this.fullScanPathsScanned);
|
|
25601
26207
|
}
|
|
25602
|
-
logInfo(`[${scanContext}] Full scan completed`, { path:
|
|
26208
|
+
logInfo(`[${scanContext}] Full scan completed`, { path: path32 });
|
|
25603
26209
|
} catch (error) {
|
|
25604
26210
|
logError(`[${scanContext}] Error during initial full security scan`, {
|
|
25605
26211
|
error
|
|
25606
26212
|
});
|
|
25607
26213
|
}
|
|
25608
26214
|
}
|
|
25609
|
-
executeInitialScan(
|
|
26215
|
+
executeInitialScan(path32) {
|
|
25610
26216
|
const scanContext = ScanContext.BACKGROUND_INITIAL;
|
|
25611
|
-
logDebug(`[${scanContext}] Triggering initial security scan`, { path:
|
|
26217
|
+
logDebug(`[${scanContext}] Triggering initial security scan`, { path: path32 });
|
|
25612
26218
|
this.scanForSecurityVulnerabilities({
|
|
25613
|
-
path:
|
|
26219
|
+
path: path32,
|
|
25614
26220
|
scanContext: ScanContext.BACKGROUND_INITIAL
|
|
25615
26221
|
}).catch((error) => {
|
|
25616
26222
|
logError(`[${scanContext}] Error during initial security scan`, { error });
|
|
@@ -25707,9 +26313,9 @@ Example payload:
|
|
|
25707
26313
|
`Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
|
|
25708
26314
|
);
|
|
25709
26315
|
}
|
|
25710
|
-
const
|
|
26316
|
+
const path32 = pathValidationResult.path;
|
|
25711
26317
|
const resultText = await this.newFixesService.getFreshFixes({
|
|
25712
|
-
path:
|
|
26318
|
+
path: path32
|
|
25713
26319
|
});
|
|
25714
26320
|
logInfo("CheckForNewAvailableFixesTool execution completed", {
|
|
25715
26321
|
resultText
|
|
@@ -25887,8 +26493,8 @@ Call this tool instead of ${MCP_TOOL_SCAN_AND_FIX_VULNERABILITIES} when you only
|
|
|
25887
26493
|
`Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
|
|
25888
26494
|
);
|
|
25889
26495
|
}
|
|
25890
|
-
const
|
|
25891
|
-
const gitService = new GitService(
|
|
26496
|
+
const path32 = pathValidationResult.path;
|
|
26497
|
+
const gitService = new GitService(path32, log);
|
|
25892
26498
|
const gitValidation = await gitService.validateRepository();
|
|
25893
26499
|
if (!gitValidation.isValid) {
|
|
25894
26500
|
throw new Error(`Invalid git repository: ${gitValidation.error}`);
|
|
@@ -26273,9 +26879,9 @@ Example payload:
|
|
|
26273
26879
|
`Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
|
|
26274
26880
|
);
|
|
26275
26881
|
}
|
|
26276
|
-
const
|
|
26882
|
+
const path32 = pathValidationResult.path;
|
|
26277
26883
|
const files = await getLocalFiles({
|
|
26278
|
-
path:
|
|
26884
|
+
path: path32,
|
|
26279
26885
|
maxFileSize: MCP_MAX_FILE_SIZE,
|
|
26280
26886
|
maxFiles: args.maxFiles,
|
|
26281
26887
|
scanContext: ScanContext.USER_REQUEST,
|
|
@@ -26295,7 +26901,7 @@ Example payload:
|
|
|
26295
26901
|
try {
|
|
26296
26902
|
const fixResult = await this.vulnerabilityFixService.processVulnerabilities({
|
|
26297
26903
|
fileList: files.map((file) => file.relativePath),
|
|
26298
|
-
repositoryPath:
|
|
26904
|
+
repositoryPath: path32,
|
|
26299
26905
|
offset: args.offset,
|
|
26300
26906
|
limit: args.limit,
|
|
26301
26907
|
isRescan: args.rescan || !!args.maxFiles
|
|
@@ -26596,10 +27202,10 @@ init_client_generates();
|
|
|
26596
27202
|
init_urlParser2();
|
|
26597
27203
|
|
|
26598
27204
|
// src/features/codeium_intellij/codeium_language_server_grpc_client.ts
|
|
26599
|
-
import
|
|
27205
|
+
import path29 from "path";
|
|
26600
27206
|
import * as grpc from "@grpc/grpc-js";
|
|
26601
27207
|
import * as protoLoader from "@grpc/proto-loader";
|
|
26602
|
-
var PROTO_PATH =
|
|
27208
|
+
var PROTO_PATH = path29.join(
|
|
26603
27209
|
getModuleRootDir(),
|
|
26604
27210
|
"src/features/codeium_intellij/proto/exa/language_server_pb/language_server.proto"
|
|
26605
27211
|
);
|
|
@@ -26611,7 +27217,7 @@ function loadProto() {
|
|
|
26611
27217
|
defaults: true,
|
|
26612
27218
|
oneofs: true,
|
|
26613
27219
|
includeDirs: [
|
|
26614
|
-
|
|
27220
|
+
path29.join(getModuleRootDir(), "src/features/codeium_intellij/proto")
|
|
26615
27221
|
]
|
|
26616
27222
|
});
|
|
26617
27223
|
return grpc.loadPackageDefinition(
|
|
@@ -26667,28 +27273,28 @@ async function getGrpcClient(port, csrf3) {
|
|
|
26667
27273
|
// src/features/codeium_intellij/parse_intellij_logs.ts
|
|
26668
27274
|
import fs27 from "fs";
|
|
26669
27275
|
import os14 from "os";
|
|
26670
|
-
import
|
|
27276
|
+
import path30 from "path";
|
|
26671
27277
|
function getLogsDir() {
|
|
26672
27278
|
if (process.platform === "darwin") {
|
|
26673
|
-
return
|
|
27279
|
+
return path30.join(os14.homedir(), "Library/Logs/JetBrains");
|
|
26674
27280
|
} else if (process.platform === "win32") {
|
|
26675
|
-
return
|
|
26676
|
-
process.env["LOCALAPPDATA"] ||
|
|
27281
|
+
return path30.join(
|
|
27282
|
+
process.env["LOCALAPPDATA"] || path30.join(os14.homedir(), "AppData/Local"),
|
|
26677
27283
|
"JetBrains"
|
|
26678
27284
|
);
|
|
26679
27285
|
} else {
|
|
26680
|
-
return
|
|
27286
|
+
return path30.join(os14.homedir(), ".cache/JetBrains");
|
|
26681
27287
|
}
|
|
26682
27288
|
}
|
|
26683
27289
|
function parseIdeLogDir(ideLogDir) {
|
|
26684
27290
|
const logFiles = fs27.readdirSync(ideLogDir).filter((f) => /^idea(\.\d+)?\.log$/.test(f)).map((f) => ({
|
|
26685
27291
|
name: f,
|
|
26686
|
-
mtime: fs27.statSync(
|
|
27292
|
+
mtime: fs27.statSync(path30.join(ideLogDir, f)).mtimeMs
|
|
26687
27293
|
})).sort((a, b) => a.mtime - b.mtime).map((f) => f.name);
|
|
26688
27294
|
let latestCsrf = null;
|
|
26689
27295
|
let latestPort = null;
|
|
26690
27296
|
for (const logFile of logFiles) {
|
|
26691
|
-
const lines = fs27.readFileSync(
|
|
27297
|
+
const lines = fs27.readFileSync(path30.join(ideLogDir, logFile), "utf-8").split("\n");
|
|
26692
27298
|
for (const line of lines) {
|
|
26693
27299
|
if (!line.includes(
|
|
26694
27300
|
"com.codeium.intellij.language_server.LanguageServerProcessHandler"
|
|
@@ -26716,9 +27322,9 @@ function findRunningCodeiumLanguageServers() {
|
|
|
26716
27322
|
const logsDir = getLogsDir();
|
|
26717
27323
|
if (!fs27.existsSync(logsDir)) return results;
|
|
26718
27324
|
for (const ide of fs27.readdirSync(logsDir)) {
|
|
26719
|
-
let ideLogDir =
|
|
27325
|
+
let ideLogDir = path30.join(logsDir, ide);
|
|
26720
27326
|
if (process.platform !== "darwin") {
|
|
26721
|
-
ideLogDir =
|
|
27327
|
+
ideLogDir = path30.join(ideLogDir, "log");
|
|
26722
27328
|
}
|
|
26723
27329
|
if (!fs27.existsSync(ideLogDir) || !fs27.statSync(ideLogDir).isDirectory()) {
|
|
26724
27330
|
continue;
|
|
@@ -26901,10 +27507,10 @@ function processChatStepCodeAction(step) {
|
|
|
26901
27507
|
// src/features/codeium_intellij/install_hook.ts
|
|
26902
27508
|
import fsPromises5 from "fs/promises";
|
|
26903
27509
|
import os15 from "os";
|
|
26904
|
-
import
|
|
27510
|
+
import path31 from "path";
|
|
26905
27511
|
import chalk14 from "chalk";
|
|
26906
27512
|
function getCodeiumHooksPath() {
|
|
26907
|
-
return
|
|
27513
|
+
return path31.join(os15.homedir(), ".codeium", "hooks.json");
|
|
26908
27514
|
}
|
|
26909
27515
|
async function readCodeiumHooks() {
|
|
26910
27516
|
const hooksPath = getCodeiumHooksPath();
|
|
@@ -26917,7 +27523,7 @@ async function readCodeiumHooks() {
|
|
|
26917
27523
|
}
|
|
26918
27524
|
async function writeCodeiumHooks(config2) {
|
|
26919
27525
|
const hooksPath = getCodeiumHooksPath();
|
|
26920
|
-
const dir =
|
|
27526
|
+
const dir = path31.dirname(hooksPath);
|
|
26921
27527
|
await fsPromises5.mkdir(dir, { recursive: true });
|
|
26922
27528
|
await fsPromises5.writeFile(
|
|
26923
27529
|
hooksPath,
|