mobbdev 1.4.0 → 1.4.2
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 +40 -40
- package/dist/args/commands/upload_ai_blame.mjs +37 -4
- package/dist/index.mjs +552 -360
- package/package.json +3 -1
package/dist/index.mjs
CHANGED
|
@@ -263,10 +263,12 @@ var init_client_generates = __esm({
|
|
|
263
263
|
IssueType_Enum2["ImproperExceptionHandling"] = "IMPROPER_EXCEPTION_HANDLING";
|
|
264
264
|
IssueType_Enum2["ImproperResourceShutdownOrRelease"] = "IMPROPER_RESOURCE_SHUTDOWN_OR_RELEASE";
|
|
265
265
|
IssueType_Enum2["ImproperStringFormatting"] = "IMPROPER_STRING_FORMATTING";
|
|
266
|
+
IssueType_Enum2["ImproperValidationOfArrayIndex"] = "IMPROPER_VALIDATION_OF_ARRAY_INDEX";
|
|
266
267
|
IssueType_Enum2["IncompleteHostnameRegex"] = "INCOMPLETE_HOSTNAME_REGEX";
|
|
267
268
|
IssueType_Enum2["IncompleteSanitization"] = "INCOMPLETE_SANITIZATION";
|
|
268
269
|
IssueType_Enum2["IncompleteUrlSanitization"] = "INCOMPLETE_URL_SANITIZATION";
|
|
269
270
|
IssueType_Enum2["IncompleteUrlSchemeCheck"] = "INCOMPLETE_URL_SCHEME_CHECK";
|
|
271
|
+
IssueType_Enum2["IncorrectIntegerConversion"] = "INCORRECT_INTEGER_CONVERSION";
|
|
270
272
|
IssueType_Enum2["IncorrectSqlApiUsage"] = "INCORRECT_SQL_API_USAGE";
|
|
271
273
|
IssueType_Enum2["InformationExposureViaHeaders"] = "INFORMATION_EXPOSURE_VIA_HEADERS";
|
|
272
274
|
IssueType_Enum2["InsecureBinderConfiguration"] = "INSECURE_BINDER_CONFIGURATION";
|
|
@@ -291,6 +293,7 @@ var init_client_generates = __esm({
|
|
|
291
293
|
IssueType_Enum2["MissingUser"] = "MISSING_USER";
|
|
292
294
|
IssueType_Enum2["MissingWhitespace"] = "MISSING_WHITESPACE";
|
|
293
295
|
IssueType_Enum2["MissingWorkflowPermissions"] = "MISSING_WORKFLOW_PERMISSIONS";
|
|
296
|
+
IssueType_Enum2["MissingXFrameOptions"] = "MISSING_X_FRAME_OPTIONS";
|
|
294
297
|
IssueType_Enum2["ModifiedDefaultParam"] = "MODIFIED_DEFAULT_PARAM";
|
|
295
298
|
IssueType_Enum2["NonFinalPublicStaticField"] = "NON_FINAL_PUBLIC_STATIC_FIELD";
|
|
296
299
|
IssueType_Enum2["NonReadonlyField"] = "NON_READONLY_FIELD";
|
|
@@ -408,6 +411,7 @@ var init_client_generates = __esm({
|
|
|
408
411
|
return Vulnerability_Report_Issue_Tag_Enum3;
|
|
409
412
|
})(Vulnerability_Report_Issue_Tag_Enum || {});
|
|
410
413
|
Vulnerability_Report_Vendor_Enum = /* @__PURE__ */ ((Vulnerability_Report_Vendor_Enum3) => {
|
|
414
|
+
Vulnerability_Report_Vendor_Enum3["BlackDuck"] = "blackDuck";
|
|
411
415
|
Vulnerability_Report_Vendor_Enum3["Checkmarx"] = "checkmarx";
|
|
412
416
|
Vulnerability_Report_Vendor_Enum3["CheckmarxXml"] = "checkmarxXml";
|
|
413
417
|
Vulnerability_Report_Vendor_Enum3["Codeql"] = "codeql";
|
|
@@ -1467,7 +1471,10 @@ var init_getIssueType = __esm({
|
|
|
1467
1471
|
["REDUNDANT_NIL_ERROR_CHECK" /* RedundantNilErrorCheck */]: "Redundant Nil Error Check",
|
|
1468
1472
|
["MISSING_WORKFLOW_PERMISSIONS" /* MissingWorkflowPermissions */]: "Missing Workflow Permissions",
|
|
1469
1473
|
["EXCESSIVE_SECRETS_EXPOSURE" /* ExcessiveSecretsExposure */]: "Excessive Secrets Exposure",
|
|
1470
|
-
["TAINTED_NUMERIC_CAST" /* TaintedNumericCast */]: "Tainted Numeric Cast"
|
|
1474
|
+
["TAINTED_NUMERIC_CAST" /* TaintedNumericCast */]: "Tainted Numeric Cast",
|
|
1475
|
+
["MISSING_X_FRAME_OPTIONS" /* MissingXFrameOptions */]: "Missing X-Frame-Options Header",
|
|
1476
|
+
["IMPROPER_VALIDATION_OF_ARRAY_INDEX" /* ImproperValidationOfArrayIndex */]: "Improper Validation of Array Index",
|
|
1477
|
+
["INCORRECT_INTEGER_CONVERSION" /* IncorrectIntegerConversion */]: "Incorrect Integer Conversion"
|
|
1471
1478
|
};
|
|
1472
1479
|
issueTypeZ = z.nativeEnum(IssueType_Enum);
|
|
1473
1480
|
getIssueTypeFriendlyString = (issueType) => {
|
|
@@ -4141,11 +4148,11 @@ ${rootContent}`;
|
|
|
4141
4148
|
try {
|
|
4142
4149
|
const gitRoot = await this.getGitRoot();
|
|
4143
4150
|
const gitignorePath = path2.join(gitRoot, ".gitignore");
|
|
4144
|
-
const
|
|
4151
|
+
const exists2 = fs2.existsSync(gitignorePath);
|
|
4145
4152
|
this.log("[GitService] .gitignore existence check complete", "debug", {
|
|
4146
|
-
exists
|
|
4153
|
+
exists: exists2
|
|
4147
4154
|
});
|
|
4148
|
-
return
|
|
4155
|
+
return exists2;
|
|
4149
4156
|
} catch (error) {
|
|
4150
4157
|
const errorMessage = `Failed to check .gitignore existence: ${error.message}`;
|
|
4151
4158
|
this.log(`[GitService] ${errorMessage}`, "error", { error });
|
|
@@ -4660,7 +4667,10 @@ var fixDetailsData = {
|
|
|
4660
4667
|
["REDUNDANT_NIL_ERROR_CHECK" /* RedundantNilErrorCheck */]: void 0,
|
|
4661
4668
|
["MISSING_WORKFLOW_PERMISSIONS" /* MissingWorkflowPermissions */]: void 0,
|
|
4662
4669
|
["EXCESSIVE_SECRETS_EXPOSURE" /* ExcessiveSecretsExposure */]: void 0,
|
|
4663
|
-
["TAINTED_NUMERIC_CAST" /* TaintedNumericCast */]: void 0
|
|
4670
|
+
["TAINTED_NUMERIC_CAST" /* TaintedNumericCast */]: void 0,
|
|
4671
|
+
["MISSING_X_FRAME_OPTIONS" /* MissingXFrameOptions */]: void 0,
|
|
4672
|
+
["IMPROPER_VALIDATION_OF_ARRAY_INDEX" /* ImproperValidationOfArrayIndex */]: void 0,
|
|
4673
|
+
["INCORRECT_INTEGER_CONVERSION" /* IncorrectIntegerConversion */]: void 0
|
|
4664
4674
|
};
|
|
4665
4675
|
|
|
4666
4676
|
// src/features/analysis/scm/shared/src/commitDescriptionMarkup.ts
|
|
@@ -5997,6 +6007,19 @@ var headerMaxAge = {
|
|
|
5997
6007
|
}
|
|
5998
6008
|
};
|
|
5999
6009
|
|
|
6010
|
+
// src/features/analysis/scm/shared/src/storedQuestionData/js/missingXFrameOptions.ts
|
|
6011
|
+
var xFrameOptionsValue = {
|
|
6012
|
+
xFrameOptionsValue: {
|
|
6013
|
+
content: () => "Please provide the value for the X-Frame-Options header",
|
|
6014
|
+
description: () => `The \`X-Frame-Options\` HTTP response header tells the browser whether the page is allowed to be rendered inside a \`<frame>\`, \`<iframe>\`, \`<embed>\` or \`<object>\`. Without it, attackers can embed your application in an invisible iframe and trick users into clicking on it \u2014 a class of attacks known as clickjacking (UI redressing).
|
|
6015
|
+
|
|
6016
|
+
**Allowed values:**
|
|
6017
|
+
- \`DENY\` \u2014 the page cannot be framed by any site, including your own. Recommended default for any page that does not need to be embedded.
|
|
6018
|
+
- \`SAMEORIGIN\` \u2014 the page can only be framed by pages served from the same origin. Use this only if your own application legitimately embeds this page in an iframe.`,
|
|
6019
|
+
guidance: () => ``
|
|
6020
|
+
}
|
|
6021
|
+
};
|
|
6022
|
+
|
|
6000
6023
|
// src/features/analysis/scm/shared/src/storedQuestionData/js/noLimitsOrThrottling.ts
|
|
6001
6024
|
var noLimitsOrThrottling2 = {
|
|
6002
6025
|
setGlobalLimiter: {
|
|
@@ -6141,6 +6164,7 @@ var vulnerabilities13 = {
|
|
|
6141
6164
|
["UNCHECKED_LOOP_CONDITION" /* UncheckedLoopCondition */]: uncheckedLoopCondition2,
|
|
6142
6165
|
["NO_LIMITS_OR_THROTTLING" /* NoLimitsOrThrottling */]: noLimitsOrThrottling2,
|
|
6143
6166
|
["MISSING_CSP_HEADER" /* MissingCspHeader */]: cspHeaderValue,
|
|
6167
|
+
["MISSING_X_FRAME_OPTIONS" /* MissingXFrameOptions */]: xFrameOptionsValue,
|
|
6144
6168
|
["HARDCODED_DOMAIN_IN_HTML" /* HardcodedDomainInHtml */]: hardcodedDomainInHtml,
|
|
6145
6169
|
["CSRF" /* Csrf */]: csrf2
|
|
6146
6170
|
};
|
|
@@ -6461,6 +6485,13 @@ var ReferenceType = /* @__PURE__ */ ((ReferenceType2) => {
|
|
|
6461
6485
|
ReferenceType2["TAG"] = "TAG";
|
|
6462
6486
|
return ReferenceType2;
|
|
6463
6487
|
})(ReferenceType || {});
|
|
6488
|
+
var GithubFullShaZ = z13.string().regex(/^[a-f0-9]{40}$/);
|
|
6489
|
+
var MergedPrSurvivalMetadataZ = z13.object({
|
|
6490
|
+
mergeCommitShas: z13.array(GithubFullShaZ).min(1).refine((shas) => new Set(shas).size === shas.length, {
|
|
6491
|
+
message: "mergeCommitShas must contain unique SHAs"
|
|
6492
|
+
}),
|
|
6493
|
+
targetBranch: z13.string().min(1)
|
|
6494
|
+
});
|
|
6464
6495
|
var ScmLibScmType = /* @__PURE__ */ ((ScmLibScmType2) => {
|
|
6465
6496
|
ScmLibScmType2["GITHUB"] = "GITHUB";
|
|
6466
6497
|
ScmLibScmType2["GITLAB"] = "GITLAB";
|
|
@@ -7147,7 +7178,7 @@ async function getAdoSdk(params) {
|
|
|
7147
7178
|
const url = new URL(repoUrl);
|
|
7148
7179
|
const origin = url.origin.toLowerCase().endsWith(".visualstudio.com") ? DEFUALT_ADO_ORIGIN : url.origin.toLowerCase();
|
|
7149
7180
|
const params2 = `path=/&versionDescriptor[versionOptions]=0&versionDescriptor[versionType]=commit&versionDescriptor[version]=${branch}&resolveLfs=true&$format=zip&api-version=5.0&download=true`;
|
|
7150
|
-
const
|
|
7181
|
+
const path36 = [
|
|
7151
7182
|
prefixPath,
|
|
7152
7183
|
owner,
|
|
7153
7184
|
projectName,
|
|
@@ -7158,7 +7189,7 @@ async function getAdoSdk(params) {
|
|
|
7158
7189
|
"items",
|
|
7159
7190
|
"items"
|
|
7160
7191
|
].filter(Boolean).join("/");
|
|
7161
|
-
return new URL(`${
|
|
7192
|
+
return new URL(`${path36}?${params2}`, origin).toString();
|
|
7162
7193
|
},
|
|
7163
7194
|
async getAdoBranchList({ repoUrl }) {
|
|
7164
7195
|
try {
|
|
@@ -7247,8 +7278,8 @@ async function getAdoSdk(params) {
|
|
|
7247
7278
|
const changeType = entry.changeType;
|
|
7248
7279
|
return changeType !== 16 && entry.item?.path;
|
|
7249
7280
|
}).map((entry) => {
|
|
7250
|
-
const
|
|
7251
|
-
return
|
|
7281
|
+
const path36 = entry.item.path;
|
|
7282
|
+
return path36.startsWith("/") ? path36.slice(1) : path36;
|
|
7252
7283
|
});
|
|
7253
7284
|
},
|
|
7254
7285
|
async searchAdoPullRequests({
|
|
@@ -7649,6 +7680,12 @@ var SCMLib = class {
|
|
|
7649
7680
|
async getPrDataBatch(_repoUrl, _prNumbers) {
|
|
7650
7681
|
throw new Error("getPrDataBatch not implemented for this SCM provider");
|
|
7651
7682
|
}
|
|
7683
|
+
/**
|
|
7684
|
+
* GitHub: merge detection for main-branch survival. Other providers return null.
|
|
7685
|
+
*/
|
|
7686
|
+
async getMergedPrSurvivalMetadata(_prNumber) {
|
|
7687
|
+
return null;
|
|
7688
|
+
}
|
|
7652
7689
|
getAccessToken() {
|
|
7653
7690
|
return this.accessToken || "";
|
|
7654
7691
|
}
|
|
@@ -8910,6 +8947,24 @@ async function encryptSecret(secret, key) {
|
|
|
8910
8947
|
return sodium.to_base64(encBytes, sodium.base64_variants.ORIGINAL);
|
|
8911
8948
|
}
|
|
8912
8949
|
|
|
8950
|
+
// src/features/analysis/scm/github/utils/mergeCommitShas.ts
|
|
8951
|
+
async function commitShasBetweenBaseAndMerge(githubSdk, args) {
|
|
8952
|
+
let compare;
|
|
8953
|
+
try {
|
|
8954
|
+
compare = await githubSdk.compareCommitsBasehead({
|
|
8955
|
+
owner: args.owner,
|
|
8956
|
+
repo: args.repo,
|
|
8957
|
+
basehead: `${args.baseSha}...${args.mergeCommitSha}`
|
|
8958
|
+
});
|
|
8959
|
+
} catch (err) {
|
|
8960
|
+
throw new Error(
|
|
8961
|
+
`Failed to compare commits ${args.baseSha}...${args.mergeCommitSha}: ${err instanceof Error ? err.message : String(err)}`
|
|
8962
|
+
);
|
|
8963
|
+
}
|
|
8964
|
+
const shas = compare.data.commits.map((c) => c.sha);
|
|
8965
|
+
return shas.length > 0 ? shas : [args.mergeCommitSha];
|
|
8966
|
+
}
|
|
8967
|
+
|
|
8913
8968
|
// src/features/analysis/scm/github/utils/utils.ts
|
|
8914
8969
|
import { Octokit } from "octokit";
|
|
8915
8970
|
import { fetch as fetch2, ProxyAgent } from "undici";
|
|
@@ -9642,6 +9697,12 @@ function getGithubSdk(params = {}) {
|
|
|
9642
9697
|
);
|
|
9643
9698
|
return res;
|
|
9644
9699
|
},
|
|
9700
|
+
async listPullRequestCommits(params2) {
|
|
9701
|
+
return octokit.rest.pulls.listCommits(params2);
|
|
9702
|
+
},
|
|
9703
|
+
async compareCommitsBasehead(params2) {
|
|
9704
|
+
return octokit.rest.repos.compareCommitsWithBasehead(params2);
|
|
9705
|
+
},
|
|
9645
9706
|
/**
|
|
9646
9707
|
* List PRs using GitHub's REST `/repos/{owner}/{repo}/pulls` endpoint.
|
|
9647
9708
|
* https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#list-pull-requests
|
|
@@ -10456,6 +10517,34 @@ var GithubSCMLib = class extends SCMLib {
|
|
|
10456
10517
|
commentIds
|
|
10457
10518
|
};
|
|
10458
10519
|
}
|
|
10520
|
+
/**
|
|
10521
|
+
* Detect merge strategy and SHAs on the target branch for main-branch survival (GitHub only).
|
|
10522
|
+
*/
|
|
10523
|
+
async getMergedPrSurvivalMetadata(prNumber) {
|
|
10524
|
+
this._validateAccessTokenAndUrl();
|
|
10525
|
+
const { owner, repo } = parseGithubOwnerAndRepo(this.url);
|
|
10526
|
+
const pr = await this.githubSdk.getPr({
|
|
10527
|
+
owner,
|
|
10528
|
+
repo,
|
|
10529
|
+
pull_number: prNumber
|
|
10530
|
+
});
|
|
10531
|
+
if (pr.data.merged !== true || !pr.data.merge_commit_sha) {
|
|
10532
|
+
return null;
|
|
10533
|
+
}
|
|
10534
|
+
const mergeCommitSha = pr.data.merge_commit_sha;
|
|
10535
|
+
const targetBranch = pr.data.base.ref;
|
|
10536
|
+
const baseSha = pr.data.base.sha;
|
|
10537
|
+
const mergeCommitShas = await commitShasBetweenBaseAndMerge(
|
|
10538
|
+
this.githubSdk,
|
|
10539
|
+
{
|
|
10540
|
+
owner,
|
|
10541
|
+
repo,
|
|
10542
|
+
baseSha,
|
|
10543
|
+
mergeCommitSha
|
|
10544
|
+
}
|
|
10545
|
+
);
|
|
10546
|
+
return { mergeCommitShas, targetBranch };
|
|
10547
|
+
}
|
|
10459
10548
|
};
|
|
10460
10549
|
|
|
10461
10550
|
// src/features/analysis/scm/gitlab/gitlab.ts
|
|
@@ -12462,7 +12551,8 @@ var SCANNERS = {
|
|
|
12462
12551
|
Snyk: "snyk",
|
|
12463
12552
|
Sonarqube: "sonarqube",
|
|
12464
12553
|
Semgrep: "semgrep",
|
|
12465
|
-
Datadog: "datadog"
|
|
12554
|
+
Datadog: "datadog",
|
|
12555
|
+
BlackDuck: "blackduck"
|
|
12466
12556
|
};
|
|
12467
12557
|
var scannerToVulnerabilityReportVendorEnum = {
|
|
12468
12558
|
[SCANNERS.Checkmarx]: "checkmarx" /* Checkmarx */,
|
|
@@ -12471,7 +12561,8 @@ var scannerToVulnerabilityReportVendorEnum = {
|
|
|
12471
12561
|
[SCANNERS.Codeql]: "codeql" /* Codeql */,
|
|
12472
12562
|
[SCANNERS.Fortify]: "fortify" /* Fortify */,
|
|
12473
12563
|
[SCANNERS.Semgrep]: "semgrep" /* Semgrep */,
|
|
12474
|
-
[SCANNERS.Datadog]: "datadog" /* Datadog
|
|
12564
|
+
[SCANNERS.Datadog]: "datadog" /* Datadog */,
|
|
12565
|
+
[SCANNERS.BlackDuck]: "blackDuck" /* BlackDuck */
|
|
12475
12566
|
};
|
|
12476
12567
|
var SupportedScannersZ = z25.enum([SCANNERS.Checkmarx, SCANNERS.Snyk]);
|
|
12477
12568
|
var envVariablesSchema = z25.object({
|
|
@@ -14890,7 +14981,8 @@ var scannerToFriendlyString = {
|
|
|
14890
14981
|
snyk: "Snyk",
|
|
14891
14982
|
sonarqube: "Sonarqube",
|
|
14892
14983
|
semgrep: "Semgrep",
|
|
14893
|
-
datadog: "Datadog"
|
|
14984
|
+
datadog: "Datadog",
|
|
14985
|
+
blackduck: "Black Duck"
|
|
14894
14986
|
};
|
|
14895
14987
|
|
|
14896
14988
|
// src/features/analysis/add_fix_comments_for_pr/utils/buildCommentBody.ts
|
|
@@ -15080,7 +15172,7 @@ async function postIssueComment(params) {
|
|
|
15080
15172
|
fpDescription
|
|
15081
15173
|
} = params;
|
|
15082
15174
|
const {
|
|
15083
|
-
path:
|
|
15175
|
+
path: path36,
|
|
15084
15176
|
startLine,
|
|
15085
15177
|
vulnerabilityReportIssue: {
|
|
15086
15178
|
vulnerabilityReportIssueTags,
|
|
@@ -15095,7 +15187,7 @@ async function postIssueComment(params) {
|
|
|
15095
15187
|
Refresh the page in order to see the changes.`,
|
|
15096
15188
|
pull_number: pullRequest,
|
|
15097
15189
|
commit_id: commitSha,
|
|
15098
|
-
path:
|
|
15190
|
+
path: path36,
|
|
15099
15191
|
line: startLine
|
|
15100
15192
|
});
|
|
15101
15193
|
const commentId = commentRes.data.id;
|
|
@@ -15129,7 +15221,7 @@ async function postFixComment(params) {
|
|
|
15129
15221
|
scanner
|
|
15130
15222
|
} = params;
|
|
15131
15223
|
const {
|
|
15132
|
-
path:
|
|
15224
|
+
path: path36,
|
|
15133
15225
|
startLine,
|
|
15134
15226
|
vulnerabilityReportIssue: { fixId, vulnerabilityReportIssueTags, category },
|
|
15135
15227
|
vulnerabilityReportIssueId
|
|
@@ -15147,7 +15239,7 @@ async function postFixComment(params) {
|
|
|
15147
15239
|
Refresh the page in order to see the changes.`,
|
|
15148
15240
|
pull_number: pullRequest,
|
|
15149
15241
|
commit_id: commitSha,
|
|
15150
|
-
path:
|
|
15242
|
+
path: path36,
|
|
15151
15243
|
line: startLine
|
|
15152
15244
|
});
|
|
15153
15245
|
const commentId = commentRes.data.id;
|
|
@@ -16998,7 +17090,7 @@ function analyzeBuilder(yargs2) {
|
|
|
16998
17090
|
alias: "scan-file",
|
|
16999
17091
|
type: "string",
|
|
17000
17092
|
describe: chalk10.bold(
|
|
17001
|
-
"Select the vulnerability report to analyze (Checkmarx, Snyk, Fortify, CodeQL, Sonarqube, Semgrep, Datadog)"
|
|
17093
|
+
"Select the vulnerability report to analyze (Checkmarx, Snyk, Fortify, CodeQL, Sonarqube, Semgrep, Datadog, Black Duck)"
|
|
17002
17094
|
)
|
|
17003
17095
|
}).option("repo", repoOption).option("p", {
|
|
17004
17096
|
alias: "src-path",
|
|
@@ -17069,7 +17161,7 @@ import { spawn } from "child_process";
|
|
|
17069
17161
|
|
|
17070
17162
|
// src/features/claude_code/daemon.ts
|
|
17071
17163
|
import { readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
17072
|
-
import
|
|
17164
|
+
import path23 from "path";
|
|
17073
17165
|
import { setTimeout as sleep2 } from "timers/promises";
|
|
17074
17166
|
import Configstore3 from "configstore";
|
|
17075
17167
|
|
|
@@ -17081,7 +17173,12 @@ var HEARTBEAT_DEBOUNCE_MS = (() => {
|
|
|
17081
17173
|
})();
|
|
17082
17174
|
var KILL_SWITCH_ENV = "MOBB_TRACY_SKILL_QUARANTINE_DISABLE";
|
|
17083
17175
|
var MALICIOUS_VERDICT = "MALICIOUS";
|
|
17084
|
-
var
|
|
17176
|
+
var PARTIAL_SWEEP_GRACE_MS = 10 * 60 * 1e3;
|
|
17177
|
+
var STUB_MARKER = "\u26D4 QUARANTINED BY TRACY";
|
|
17178
|
+
|
|
17179
|
+
// src/features/analysis/skill_quarantine/enumerateInstalledSkills.ts
|
|
17180
|
+
import { homedir as homedir2 } from "os";
|
|
17181
|
+
import path15 from "path";
|
|
17085
17182
|
|
|
17086
17183
|
// src/features/analysis/context_file_processor.ts
|
|
17087
17184
|
import { createHash } from "crypto";
|
|
@@ -17136,7 +17233,7 @@ async function processContextFiles(regularFiles, skillGroups) {
|
|
|
17136
17233
|
}
|
|
17137
17234
|
|
|
17138
17235
|
// src/features/analysis/context_file_scanner.ts
|
|
17139
|
-
import { lstat, readFile, stat } from "fs/promises";
|
|
17236
|
+
import { lstat, readdir, readFile, realpath, stat } from "fs/promises";
|
|
17140
17237
|
import { homedir } from "os";
|
|
17141
17238
|
import path14 from "path";
|
|
17142
17239
|
import { globby as globby2 } from "globby";
|
|
@@ -17160,15 +17257,15 @@ var SCAN_PATHS = {
|
|
|
17160
17257
|
root: "home"
|
|
17161
17258
|
},
|
|
17162
17259
|
{ kind: "skill-bundle", skillsRoot: ".claude/skills", root: "workspace" },
|
|
17163
|
-
{ glob: ".claude/commands/*.md", category: "
|
|
17260
|
+
{ glob: ".claude/commands/*.md", category: "skill", root: "workspace" },
|
|
17164
17261
|
{
|
|
17165
17262
|
glob: ".claude/agents/*.md",
|
|
17166
|
-
category:
|
|
17263
|
+
category: SKILL_CATEGORY,
|
|
17167
17264
|
root: "workspace"
|
|
17168
17265
|
},
|
|
17169
17266
|
{ kind: "skill-bundle", skillsRoot: ".claude/skills", root: "home" },
|
|
17170
|
-
{ glob: ".claude/commands/*.md", category: "
|
|
17171
|
-
{ glob: ".claude/agents/*.md", category:
|
|
17267
|
+
{ glob: ".claude/commands/*.md", category: "skill", root: "home" },
|
|
17268
|
+
{ glob: ".claude/agents/*.md", category: SKILL_CATEGORY, root: "home" },
|
|
17172
17269
|
{ glob: ".claude/settings.json", category: "config", root: "workspace" },
|
|
17173
17270
|
{
|
|
17174
17271
|
glob: ".claude/settings.local.json",
|
|
@@ -17247,7 +17344,7 @@ var SCAN_PATHS = {
|
|
|
17247
17344
|
},
|
|
17248
17345
|
{
|
|
17249
17346
|
glob: ".claude/agents/*.md",
|
|
17250
|
-
category:
|
|
17347
|
+
category: SKILL_CATEGORY,
|
|
17251
17348
|
root: "workspace"
|
|
17252
17349
|
},
|
|
17253
17350
|
// Agent skills — Copilot discovers skills in all three roots (VS Code docs:
|
|
@@ -17279,7 +17376,7 @@ var SCAN_PATHS = {
|
|
|
17279
17376
|
// Cross-compat home paths (Copilot reads Claude / generic agent dirs too)
|
|
17280
17377
|
{ glob: ".claude/CLAUDE.md", category: "rule", root: "home" },
|
|
17281
17378
|
{ glob: ".claude/rules/**/*.md", category: "rule", root: "home" },
|
|
17282
|
-
{ glob: ".claude/agents/*.md", category:
|
|
17379
|
+
{ glob: ".claude/agents/*.md", category: SKILL_CATEGORY, root: "home" },
|
|
17283
17380
|
{ kind: "skill-bundle", skillsRoot: ".claude/skills", root: "home" },
|
|
17284
17381
|
{ kind: "skill-bundle", skillsRoot: ".agents/skills", root: "home" }
|
|
17285
17382
|
]
|
|
@@ -17450,11 +17547,11 @@ async function readJsoncSettings(settingsPath) {
|
|
|
17450
17547
|
putSettingsCache(settingsPath, { mtimeMs, parsed: payload });
|
|
17451
17548
|
return payload;
|
|
17452
17549
|
}
|
|
17453
|
-
function putSettingsCache(
|
|
17454
|
-
if (!settingsCache.has(
|
|
17550
|
+
function putSettingsCache(path36, entry) {
|
|
17551
|
+
if (!settingsCache.has(path36) && settingsCache.size >= MAX_SETTINGS_CACHE_SIZE) {
|
|
17455
17552
|
settingsCache.delete(settingsCache.keys().next().value);
|
|
17456
17553
|
}
|
|
17457
|
-
settingsCache.set(
|
|
17554
|
+
settingsCache.set(path36, entry);
|
|
17458
17555
|
}
|
|
17459
17556
|
async function readCopilotCustomLocations(workspaceRoot) {
|
|
17460
17557
|
const parsed = await readJsoncSettings(
|
|
@@ -17704,7 +17801,7 @@ async function enumerateGlob(pattern, cwd, category, isDynamic) {
|
|
|
17704
17801
|
} catch {
|
|
17705
17802
|
return [];
|
|
17706
17803
|
}
|
|
17707
|
-
return files.map((
|
|
17804
|
+
return files.map((path36) => ({ path: path36, category }));
|
|
17708
17805
|
}
|
|
17709
17806
|
async function enumerateSkillBundle(baseDir, skillsRoot) {
|
|
17710
17807
|
const skillsDir = path14.resolve(baseDir, skillsRoot);
|
|
@@ -17738,7 +17835,68 @@ async function enumerateSkillBundle(baseDir, skillsRoot) {
|
|
|
17738
17835
|
}
|
|
17739
17836
|
})
|
|
17740
17837
|
);
|
|
17741
|
-
|
|
17838
|
+
const standaloneFiles = await enumerateStandaloneSkills(skillsDir);
|
|
17839
|
+
const symlinkResults = await enumerateSymlinkedSkills(skillsDir);
|
|
17840
|
+
return [
|
|
17841
|
+
...perSkillResults.flat().map((p) => ({ path: p, category: "skill" })),
|
|
17842
|
+
...standaloneFiles.map((p) => ({ path: p, category: "skill" })),
|
|
17843
|
+
...symlinkResults
|
|
17844
|
+
];
|
|
17845
|
+
}
|
|
17846
|
+
async function enumerateStandaloneSkills(skillsDir) {
|
|
17847
|
+
try {
|
|
17848
|
+
return await globby2("*.md", {
|
|
17849
|
+
cwd: skillsDir,
|
|
17850
|
+
absolute: true,
|
|
17851
|
+
onlyFiles: true,
|
|
17852
|
+
dot: false,
|
|
17853
|
+
followSymbolicLinks: false
|
|
17854
|
+
});
|
|
17855
|
+
} catch {
|
|
17856
|
+
return [];
|
|
17857
|
+
}
|
|
17858
|
+
}
|
|
17859
|
+
async function enumerateSymlinkedSkills(skillsDir) {
|
|
17860
|
+
let dirEntries;
|
|
17861
|
+
try {
|
|
17862
|
+
dirEntries = await readdir(skillsDir, {
|
|
17863
|
+
withFileTypes: true,
|
|
17864
|
+
encoding: "utf8"
|
|
17865
|
+
});
|
|
17866
|
+
} catch {
|
|
17867
|
+
return [];
|
|
17868
|
+
}
|
|
17869
|
+
const results = [];
|
|
17870
|
+
for (const entry of dirEntries) {
|
|
17871
|
+
if (!entry.isSymbolicLink()) continue;
|
|
17872
|
+
const entryPath = path14.join(skillsDir, entry.name);
|
|
17873
|
+
try {
|
|
17874
|
+
const st = await stat(entryPath);
|
|
17875
|
+
if (st.isDirectory()) {
|
|
17876
|
+
const hasManifest = await stat(path14.join(entryPath, "SKILL.md")).then(() => true).catch(() => false);
|
|
17877
|
+
if (!hasManifest) continue;
|
|
17878
|
+
const realDir = await realpath(entryPath);
|
|
17879
|
+
const realFiles = await globby2("**/*", {
|
|
17880
|
+
cwd: realDir,
|
|
17881
|
+
absolute: true,
|
|
17882
|
+
onlyFiles: true,
|
|
17883
|
+
dot: true,
|
|
17884
|
+
followSymbolicLinks: false,
|
|
17885
|
+
deep: SKILL_BUNDLE_MAX_DEPTH
|
|
17886
|
+
}).catch(() => []);
|
|
17887
|
+
for (const f of realFiles) {
|
|
17888
|
+
results.push({
|
|
17889
|
+
path: path14.join(entryPath, path14.relative(realDir, f)),
|
|
17890
|
+
category: "skill"
|
|
17891
|
+
});
|
|
17892
|
+
}
|
|
17893
|
+
} else if (st.isFile() && entry.name.endsWith(".md")) {
|
|
17894
|
+
results.push({ path: entryPath, category: "skill" });
|
|
17895
|
+
}
|
|
17896
|
+
} catch {
|
|
17897
|
+
}
|
|
17898
|
+
}
|
|
17899
|
+
return results;
|
|
17742
17900
|
}
|
|
17743
17901
|
var DYNAMIC_SCAN_MAX_DEPTH = 6;
|
|
17744
17902
|
var SKILL_MANIFEST_SCAN_DEPTH = 2;
|
|
@@ -17769,15 +17927,26 @@ function deriveIdentifier(filePath, baseDir) {
|
|
|
17769
17927
|
|
|
17770
17928
|
// src/features/analysis/skill_quarantine/enumerateInstalledSkills.ts
|
|
17771
17929
|
async function enumerateInstalledSkills(workspaceRoot) {
|
|
17772
|
-
const { skillGroups } = await scanContextFiles(
|
|
17930
|
+
const { skillGroups, regularFiles } = await scanContextFiles(
|
|
17773
17931
|
workspaceRoot,
|
|
17774
17932
|
"claude-code",
|
|
17775
17933
|
void 0
|
|
17776
17934
|
);
|
|
17777
|
-
|
|
17935
|
+
const home = homedir2();
|
|
17936
|
+
const agentGroups = regularFiles.filter((f) => f.category === "agent-config").map((f) => ({
|
|
17937
|
+
name: path15.basename(f.path, path15.extname(f.path)),
|
|
17938
|
+
root: f.path.startsWith(home + path15.sep) ? "home" : "workspace",
|
|
17939
|
+
skillPath: f.path,
|
|
17940
|
+
files: [f],
|
|
17941
|
+
isFolder: false,
|
|
17942
|
+
maxMtimeMs: f.mtimeMs,
|
|
17943
|
+
sessionKey: `agent-config:${f.path}`
|
|
17944
|
+
}));
|
|
17945
|
+
const allGroups = [...skillGroups, ...agentGroups];
|
|
17946
|
+
if (allGroups.length === 0) {
|
|
17778
17947
|
return [];
|
|
17779
17948
|
}
|
|
17780
|
-
const { skills } = await processContextFiles([],
|
|
17949
|
+
const { skills } = await processContextFiles([], allGroups);
|
|
17781
17950
|
return skills.map((s) => {
|
|
17782
17951
|
const parts = s.group.skillPath.split(/[\\/]/);
|
|
17783
17952
|
const origName = parts[parts.length - 1] || s.group.name;
|
|
@@ -17798,64 +17967,73 @@ var Metric = {
|
|
|
17798
17967
|
CHECK_DISABLED_ENV: "skill_quarantine.check_disabled_env",
|
|
17799
17968
|
/** Verdict-query call failed. Fail-open. */
|
|
17800
17969
|
QUERY_ERROR: "skill_quarantine.query_error",
|
|
17801
|
-
/** Count of skills enumerated in this run
|
|
17970
|
+
/** Count of skills enumerated in this run. */
|
|
17802
17971
|
SKILLS_CHECKED: "skill_quarantine.skills_checked",
|
|
17803
|
-
/** A skill was freshly quarantined.
|
|
17972
|
+
/** A skill was freshly quarantined. */
|
|
17804
17973
|
QUARANTINED: "skill_quarantine.quarantined",
|
|
17805
|
-
/** Presence check hit; skill already quarantined. */
|
|
17974
|
+
/** Presence check hit (`<md5>.zip` exists); skill already quarantined. */
|
|
17806
17975
|
ALREADY_QUARANTINED: "skill_quarantine.already_quarantined",
|
|
17807
|
-
/**
|
|
17808
|
-
|
|
17809
|
-
/** Stub
|
|
17976
|
+
/** Zip build or partial→tmp rename failed (phase 1). */
|
|
17977
|
+
ZIP_ERROR: "skill_quarantine.zip_error",
|
|
17978
|
+
/** Stub write failed (phase 2); tmp preserved for reconcile. */
|
|
17810
17979
|
STUB_ERROR: "skill_quarantine.stub_error",
|
|
17811
|
-
/**
|
|
17812
|
-
|
|
17980
|
+
/** Tmp→published rename failed (phase 3); reconcile will retry. */
|
|
17981
|
+
PUBLISH_ERROR: "skill_quarantine.publish_error",
|
|
17982
|
+
/** Reconcile published a leftover tmp whose `<md5>.zip` was missing. */
|
|
17983
|
+
RECONCILED: "skill_quarantine.reconciled",
|
|
17984
|
+
/** Tmp removed because `<md5>.zip` already existed. */
|
|
17985
|
+
SWEPT_REDUNDANT_TMP: "skill_quarantine.swept_redundant_tmp",
|
|
17986
|
+
/** Stale partial zip swept (older than grace window). */
|
|
17987
|
+
SWEPT_PARTIAL: "skill_quarantine.swept_partial",
|
|
17813
17988
|
/** Total run duration including I/O. */
|
|
17814
17989
|
DURATION_MS: "skill_quarantine.duration_ms"
|
|
17815
17990
|
};
|
|
17816
17991
|
|
|
17817
17992
|
// src/features/analysis/skill_quarantine/quarantineSkill.ts
|
|
17818
17993
|
import { randomUUID } from "crypto";
|
|
17819
|
-
import { existsSync as existsSync2 } from "fs";
|
|
17820
17994
|
import {
|
|
17995
|
+
access,
|
|
17821
17996
|
mkdir,
|
|
17822
|
-
readdir,
|
|
17997
|
+
readdir as readdir2,
|
|
17823
17998
|
readFile as readFile2,
|
|
17824
17999
|
rename,
|
|
17825
18000
|
rm,
|
|
17826
18001
|
stat as stat2,
|
|
18002
|
+
unlink,
|
|
17827
18003
|
writeFile
|
|
17828
18004
|
} from "fs/promises";
|
|
17829
|
-
import
|
|
17830
|
-
import
|
|
18005
|
+
import path18 from "path";
|
|
18006
|
+
import AdmZip4 from "adm-zip";
|
|
17831
18007
|
|
|
17832
18008
|
// src/features/analysis/skill_quarantine/paths.ts
|
|
17833
|
-
import { homedir as
|
|
17834
|
-
import
|
|
18009
|
+
import { homedir as homedir3 } from "os";
|
|
18010
|
+
import path16 from "path";
|
|
17835
18011
|
function getQuarantineRoot() {
|
|
17836
|
-
return
|
|
17837
|
-
}
|
|
17838
|
-
function getQuarantinedHashDir(md5) {
|
|
17839
|
-
return path15.join(getQuarantineRoot(), md5);
|
|
18012
|
+
return path16.join(homedir3(), ".tracy", "quarantine", "claude", "skills");
|
|
17840
18013
|
}
|
|
17841
|
-
function
|
|
17842
|
-
return
|
|
18014
|
+
function getQuarantineZipPath(md5) {
|
|
18015
|
+
return path16.join(getQuarantineRoot(), `${md5}.zip`);
|
|
17843
18016
|
}
|
|
17844
|
-
function
|
|
17845
|
-
return
|
|
18017
|
+
function getTmpZipPath(md5, uuid) {
|
|
18018
|
+
return path16.join(getQuarantineRoot(), `${md5}_tmp_${uuid}.zip`);
|
|
17846
18019
|
}
|
|
17847
|
-
var
|
|
18020
|
+
var TMP_ZIP_REGEX = /^([0-9a-f]{32})_tmp_[0-9a-f-]+\.zip$/;
|
|
18021
|
+
var COMMITTED_ZIP_REGEX = /^([0-9a-f]{32})\.zip$/;
|
|
17848
18022
|
|
|
17849
18023
|
// src/features/analysis/skill_quarantine/stubTemplate.ts
|
|
18024
|
+
import path17 from "path";
|
|
18025
|
+
import { quote } from "shell-quote";
|
|
17850
18026
|
var LEGACY_SUMMARY_FALLBACK = "not available (scan predates current schema)";
|
|
17851
18027
|
function renderStub(params) {
|
|
17852
18028
|
const folderOrFile = params.isFolder ? "skill folder" : "skill file";
|
|
17853
18029
|
const reason = params.summary ?? LEGACY_SUMMARY_FALLBACK;
|
|
17854
|
-
|
|
18030
|
+
const extractParent = path17.dirname(params.origPath);
|
|
18031
|
+
const recoverCommand = `rm -rf ${quote([params.origPath])} && unzip -o ${quote([params.quarantinedZipPath])} -d ${quote([extractParent])}`;
|
|
18032
|
+
return `# ${STUB_MARKER}
|
|
17855
18033
|
|
|
17856
18034
|
This skill was flagged **MALICIOUS** by the Mobb security scanner and has been
|
|
17857
|
-
|
|
17858
|
-
stub is in place.
|
|
18035
|
+
archived out of your skills folder. **Claude Code will not execute it** while
|
|
18036
|
+
this stub is in place.
|
|
17859
18037
|
|
|
17860
18038
|
## Why this skill was flagged
|
|
17861
18039
|
|
|
@@ -17866,23 +18044,22 @@ stub is in place.
|
|
|
17866
18044
|
|
|
17867
18045
|
## Where the original is now
|
|
17868
18046
|
|
|
17869
|
-
The original ${folderOrFile} has been
|
|
18047
|
+
The original ${folderOrFile} has been archived to:
|
|
17870
18048
|
|
|
17871
|
-
${params.
|
|
18049
|
+
${params.quarantinedZipPath}
|
|
17872
18050
|
|
|
17873
|
-
Nothing has been deleted. The
|
|
18051
|
+
Nothing has been deleted. The archive preserves the skill exactly as it was,
|
|
18052
|
+
including any secrets or local-only edits.
|
|
17874
18053
|
|
|
17875
18054
|
## If this is a false positive \u2014 how to recover
|
|
17876
18055
|
|
|
17877
18056
|
If you're confident this skill is safe and want to restore it:
|
|
17878
18057
|
|
|
17879
|
-
|
|
18058
|
+
${recoverCommand}
|
|
17880
18059
|
|
|
17881
|
-
Tracy will not re-quarantine it as long as
|
|
17882
|
-
|
|
17883
|
-
|
|
17884
|
-
that directory entirely, the next heartbeat will re-evaluate the skill from
|
|
17885
|
-
scratch.
|
|
18060
|
+
Tracy will not re-quarantine it as long as \`${params.md5}.zip\` remains in
|
|
18061
|
+
the quarantine folder. If you delete the archive, the next heartbeat will
|
|
18062
|
+
re-evaluate the skill from scratch.
|
|
17886
18063
|
|
|
17887
18064
|
## How to report a false positive
|
|
17888
18065
|
|
|
@@ -17899,77 +18076,50 @@ Your report helps tune the scanner for everyone.
|
|
|
17899
18076
|
// src/features/analysis/skill_quarantine/quarantineSkill.ts
|
|
17900
18077
|
async function quarantineSkill(params) {
|
|
17901
18078
|
const { skillPath, isFolder, md5, origName, verdict, log: log2 } = params;
|
|
17902
|
-
const
|
|
17903
|
-
if (
|
|
18079
|
+
const finalZip = getQuarantineZipPath(md5);
|
|
18080
|
+
if (await exists(finalZip)) {
|
|
17904
18081
|
log2.debug(
|
|
17905
18082
|
{ md5, metric: Metric.ALREADY_QUARANTINED },
|
|
17906
|
-
"skill_quarantine: already quarantined
|
|
18083
|
+
"skill_quarantine: already quarantined"
|
|
17907
18084
|
);
|
|
17908
18085
|
return { status: "already_quarantined" };
|
|
17909
18086
|
}
|
|
17910
|
-
const
|
|
17911
|
-
const stagingTarget = path16.join(stagingDir, origName);
|
|
17912
|
-
const finalTarget = getQuarantinedTargetPath(md5, origName);
|
|
18087
|
+
const tmpZip = getTmpZipPath(md5, randomUUID());
|
|
17913
18088
|
try {
|
|
17914
|
-
await mkdir(
|
|
18089
|
+
await mkdir(getQuarantineRoot(), { recursive: true });
|
|
18090
|
+
const zip = new AdmZip4();
|
|
18091
|
+
if (isFolder) {
|
|
18092
|
+
await addFolderAsync(zip, skillPath, origName);
|
|
18093
|
+
} else {
|
|
18094
|
+
zip.addFile(origName, await readFile2(skillPath));
|
|
18095
|
+
}
|
|
18096
|
+
await writeFile(tmpZip, zip.toBuffer());
|
|
17915
18097
|
} catch (err) {
|
|
18098
|
+
await unlink(tmpZip).catch(ignoreErr);
|
|
17916
18099
|
log2.error(
|
|
17917
|
-
{ err, md5, metric: Metric.
|
|
17918
|
-
"skill_quarantine:
|
|
18100
|
+
{ err, md5, metric: Metric.ZIP_ERROR },
|
|
18101
|
+
"skill_quarantine: phase-1 zip write failed"
|
|
17919
18102
|
);
|
|
17920
|
-
return { status: "
|
|
18103
|
+
return { status: "zip_error", err };
|
|
17921
18104
|
}
|
|
17922
18105
|
try {
|
|
17923
|
-
await
|
|
18106
|
+
await writeStub(params);
|
|
17924
18107
|
} catch (err) {
|
|
17925
|
-
await tryRm(stagingDir);
|
|
17926
18108
|
log2.error(
|
|
17927
|
-
{ err, md5, metric: Metric.
|
|
17928
|
-
"skill_quarantine:
|
|
18109
|
+
{ err, md5, skillPath, metric: Metric.STUB_ERROR },
|
|
18110
|
+
"skill_quarantine: stub write failed; tmp zip preserved for reconcile"
|
|
17929
18111
|
);
|
|
17930
|
-
return { status: "
|
|
18112
|
+
return { status: "stub_error", err };
|
|
17931
18113
|
}
|
|
17932
18114
|
try {
|
|
17933
|
-
await rename(
|
|
18115
|
+
await rename(tmpZip, finalZip);
|
|
17934
18116
|
} catch (err) {
|
|
17935
18117
|
log2.error(
|
|
17936
|
-
{
|
|
17937
|
-
|
|
17938
|
-
md5,
|
|
17939
|
-
stagingDir,
|
|
17940
|
-
metric: Metric.MOVE_ERROR,
|
|
17941
|
-
phase: "publish"
|
|
17942
|
-
},
|
|
17943
|
-
"skill_quarantine: phase-2 publish failed; staging dir preserved for manual recovery"
|
|
18118
|
+
{ err, md5, tmpZip, metric: Metric.PUBLISH_ERROR },
|
|
18119
|
+
"skill_quarantine: phase-3 publish failed; reconcile will retry"
|
|
17944
18120
|
);
|
|
17945
|
-
return { status: "
|
|
18121
|
+
return { status: "publish_error", err };
|
|
17946
18122
|
}
|
|
17947
|
-
const quarantinedPath = finalTarget;
|
|
17948
|
-
const stubContent = renderStub({
|
|
17949
|
-
md5,
|
|
17950
|
-
isFolder,
|
|
17951
|
-
quarantinedPath,
|
|
17952
|
-
origPath: skillPath,
|
|
17953
|
-
summary: verdict.summary,
|
|
17954
|
-
scannerName: verdict.scannerName,
|
|
17955
|
-
scannerVersion: verdict.scannerVersion,
|
|
17956
|
-
scannedAt: verdict.scannedAt
|
|
17957
|
-
});
|
|
17958
|
-
try {
|
|
17959
|
-
if (isFolder) {
|
|
17960
|
-
await mkdir(skillPath, { recursive: true });
|
|
17961
|
-
await writeFile(path16.join(skillPath, "SKILL.md"), stubContent, "utf8");
|
|
17962
|
-
} else {
|
|
17963
|
-
await writeFile(skillPath, stubContent, "utf8");
|
|
17964
|
-
}
|
|
17965
|
-
} catch (err) {
|
|
17966
|
-
log2.error(
|
|
17967
|
-
{ err, md5, skillPath, metric: Metric.STUB_ERROR },
|
|
17968
|
-
"skill_quarantine: stub write failed; quarantine is still in place"
|
|
17969
|
-
);
|
|
17970
|
-
return { status: "stub_error", err };
|
|
17971
|
-
}
|
|
17972
|
-
await preRegisterStubMd5(skillPath, isFolder, log2);
|
|
17973
18123
|
log2.info(
|
|
17974
18124
|
{
|
|
17975
18125
|
md5,
|
|
@@ -17983,87 +18133,110 @@ async function quarantineSkill(params) {
|
|
|
17983
18133
|
);
|
|
17984
18134
|
return { status: "quarantined" };
|
|
17985
18135
|
}
|
|
17986
|
-
async function
|
|
17987
|
-
|
|
17988
|
-
|
|
17989
|
-
|
|
17990
|
-
|
|
17991
|
-
|
|
17992
|
-
|
|
17993
|
-
|
|
17994
|
-
|
|
17995
|
-
|
|
17996
|
-
|
|
17997
|
-
|
|
17998
|
-
|
|
17999
|
-
|
|
18000
|
-
|
|
18001
|
-
await
|
|
18002
|
-
}
|
|
18003
|
-
|
|
18004
|
-
{ err, skillPath },
|
|
18005
|
-
"skill_quarantine: failed to pre-register stub md5"
|
|
18006
|
-
);
|
|
18136
|
+
async function writeStub(params) {
|
|
18137
|
+
const { skillPath, isFolder, md5, verdict } = params;
|
|
18138
|
+
const stubContent = renderStub({
|
|
18139
|
+
md5,
|
|
18140
|
+
isFolder,
|
|
18141
|
+
quarantinedZipPath: getQuarantineZipPath(md5),
|
|
18142
|
+
origPath: skillPath,
|
|
18143
|
+
summary: verdict.summary,
|
|
18144
|
+
scannerName: verdict.scannerName,
|
|
18145
|
+
scannerVersion: verdict.scannerVersion,
|
|
18146
|
+
scannedAt: verdict.scannedAt
|
|
18147
|
+
});
|
|
18148
|
+
if (isFolder) {
|
|
18149
|
+
await rm(skillPath, { recursive: true, force: true });
|
|
18150
|
+
await mkdir(skillPath, { recursive: true });
|
|
18151
|
+
await writeFile(path18.join(skillPath, "SKILL.md"), stubContent, "utf8");
|
|
18152
|
+
} else {
|
|
18153
|
+
await writeFile(skillPath, stubContent, "utf8");
|
|
18007
18154
|
}
|
|
18008
18155
|
}
|
|
18009
|
-
async function
|
|
18010
|
-
const now = Date.now();
|
|
18011
|
-
const target = isFolder ? path16.join(skillPath, "SKILL.md") : skillPath;
|
|
18012
|
-
const [st, content] = await Promise.all([
|
|
18013
|
-
stat2(target),
|
|
18014
|
-
readFile2(target, "utf8")
|
|
18015
|
-
]);
|
|
18016
|
-
return [
|
|
18017
|
-
{
|
|
18018
|
-
name: isFolder ? "SKILL.md" : path16.basename(skillPath),
|
|
18019
|
-
path: target,
|
|
18020
|
-
content,
|
|
18021
|
-
sizeBytes: st.size,
|
|
18022
|
-
category: "skill",
|
|
18023
|
-
mtimeMs: now
|
|
18024
|
-
}
|
|
18025
|
-
];
|
|
18026
|
-
}
|
|
18027
|
-
async function sweepOrphanStagingDirs(log2) {
|
|
18156
|
+
async function reconcileAndSweep(log2) {
|
|
18028
18157
|
const root = getQuarantineRoot();
|
|
18029
18158
|
let entries;
|
|
18030
18159
|
try {
|
|
18031
|
-
entries = await
|
|
18160
|
+
entries = await readdir2(root);
|
|
18032
18161
|
} catch (err) {
|
|
18033
|
-
if (err.code === "ENOENT") return
|
|
18034
|
-
log2.warn({ err, root }, "skill_quarantine:
|
|
18035
|
-
return
|
|
18162
|
+
if (err.code === "ENOENT") return;
|
|
18163
|
+
log2.warn({ err, root }, "skill_quarantine: reconcile readdir failed");
|
|
18164
|
+
return;
|
|
18036
18165
|
}
|
|
18166
|
+
const committed = new Set(
|
|
18167
|
+
entries.map((e) => COMMITTED_ZIP_REGEX.exec(e)?.[1]).filter((m) => m !== void 0)
|
|
18168
|
+
);
|
|
18037
18169
|
const now = Date.now();
|
|
18038
|
-
let swept = 0;
|
|
18039
18170
|
for (const entry of entries) {
|
|
18040
|
-
|
|
18041
|
-
|
|
18042
|
-
|
|
18043
|
-
|
|
18044
|
-
|
|
18045
|
-
|
|
18171
|
+
const md5 = TMP_ZIP_REGEX.exec(entry)?.[1];
|
|
18172
|
+
if (md5 === void 0) continue;
|
|
18173
|
+
const full = path18.join(root, entry);
|
|
18174
|
+
if (committed.has(md5)) {
|
|
18175
|
+
await unlink(full).catch(
|
|
18176
|
+
(err) => log2.warn(
|
|
18177
|
+
{ err, path: full, md5 },
|
|
18178
|
+
"skill_quarantine: redundant tmp unlink failed"
|
|
18179
|
+
)
|
|
18180
|
+
);
|
|
18181
|
+
log2.info(
|
|
18182
|
+
{ path: full, md5, metric: Metric.SWEPT_REDUNDANT_TMP },
|
|
18183
|
+
"skill_quarantine: swept redundant tmp"
|
|
18184
|
+
);
|
|
18046
18185
|
continue;
|
|
18047
18186
|
}
|
|
18048
|
-
|
|
18187
|
+
let valid;
|
|
18049
18188
|
try {
|
|
18050
|
-
|
|
18051
|
-
|
|
18052
|
-
|
|
18053
|
-
|
|
18054
|
-
"skill_quarantine: orphan swept"
|
|
18055
|
-
);
|
|
18056
|
-
} catch (err) {
|
|
18057
|
-
log2.warn({ err, path: full }, "skill_quarantine: orphan sweep rm failed");
|
|
18189
|
+
new AdmZip4(full).getEntries();
|
|
18190
|
+
valid = true;
|
|
18191
|
+
} catch {
|
|
18192
|
+
valid = false;
|
|
18058
18193
|
}
|
|
18194
|
+
if (valid) {
|
|
18195
|
+
try {
|
|
18196
|
+
await rename(full, getQuarantineZipPath(md5));
|
|
18197
|
+
committed.add(md5);
|
|
18198
|
+
log2.info(
|
|
18199
|
+
{ path: full, md5, metric: Metric.RECONCILED },
|
|
18200
|
+
"skill_quarantine: reconciled tmp \u2192 published"
|
|
18201
|
+
);
|
|
18202
|
+
} catch (err) {
|
|
18203
|
+
log2.warn(
|
|
18204
|
+
{ err, path: full, md5 },
|
|
18205
|
+
"skill_quarantine: reconcile rename failed"
|
|
18206
|
+
);
|
|
18207
|
+
}
|
|
18208
|
+
continue;
|
|
18209
|
+
}
|
|
18210
|
+
const { mtimeMs } = await stat2(full).catch(() => ({ mtimeMs: now }));
|
|
18211
|
+
if (now - mtimeMs < PARTIAL_SWEEP_GRACE_MS) continue;
|
|
18212
|
+
await unlink(full).catch(
|
|
18213
|
+
(err) => log2.warn({ err, path: full }, "skill_quarantine: partial unlink failed")
|
|
18214
|
+
);
|
|
18215
|
+
log2.info(
|
|
18216
|
+
{ path: full, metric: Metric.SWEPT_PARTIAL },
|
|
18217
|
+
"skill_quarantine: swept broken tmp (partial write)"
|
|
18218
|
+
);
|
|
18059
18219
|
}
|
|
18060
|
-
return swept;
|
|
18061
18220
|
}
|
|
18062
|
-
|
|
18063
|
-
|
|
18064
|
-
|
|
18065
|
-
|
|
18221
|
+
function exists(p) {
|
|
18222
|
+
return access(p).then(
|
|
18223
|
+
() => true,
|
|
18224
|
+
() => false
|
|
18225
|
+
);
|
|
18226
|
+
}
|
|
18227
|
+
function ignoreErr() {
|
|
18228
|
+
}
|
|
18229
|
+
async function addFolderAsync(zip, root, prefix) {
|
|
18230
|
+
async function walk(dir, relPrefix) {
|
|
18231
|
+
const entries = await readdir2(dir, { withFileTypes: true });
|
|
18232
|
+
for (const e of entries) {
|
|
18233
|
+
const full = path18.join(dir, e.name);
|
|
18234
|
+
const rel = path18.posix.join(relPrefix, e.name);
|
|
18235
|
+
if (e.isDirectory()) await walk(full, rel);
|
|
18236
|
+
else if (e.isFile()) zip.addFile(rel, await readFile2(full));
|
|
18237
|
+
}
|
|
18066
18238
|
}
|
|
18239
|
+
await walk(root, prefix);
|
|
18067
18240
|
}
|
|
18068
18241
|
|
|
18069
18242
|
// src/features/analysis/skill_quarantine/queryVerdicts.ts
|
|
@@ -18121,7 +18294,7 @@ async function runQuarantineCheckIfNeeded(opts) {
|
|
|
18121
18294
|
);
|
|
18122
18295
|
const t0 = Date.now();
|
|
18123
18296
|
try {
|
|
18124
|
-
await
|
|
18297
|
+
await reconcileAndSweep(log2);
|
|
18125
18298
|
const installed = await enumerateInstalledSkills(cwd);
|
|
18126
18299
|
log2.info(
|
|
18127
18300
|
{ sessionId, count: installed.length, metric: Metric.SKILLS_CHECKED },
|
|
@@ -18171,7 +18344,7 @@ async function runQuarantineCheckIfNeeded(opts) {
|
|
|
18171
18344
|
// src/features/claude_code/daemon_pid_file.ts
|
|
18172
18345
|
import fs13 from "fs";
|
|
18173
18346
|
import os4 from "os";
|
|
18174
|
-
import
|
|
18347
|
+
import path19 from "path";
|
|
18175
18348
|
|
|
18176
18349
|
// src/features/claude_code/data_collector_constants.ts
|
|
18177
18350
|
var CC_VERSION_CACHE_KEY = "claudeCode.detectedCCVersion";
|
|
@@ -18188,20 +18361,21 @@ var DAEMON_POLL_INTERVAL_MS = (() => {
|
|
|
18188
18361
|
var HEARTBEAT_STALE_MS = 3e4;
|
|
18189
18362
|
var TRANSCRIPT_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
18190
18363
|
var DAEMON_CHUNK_SIZE = 50;
|
|
18364
|
+
var CONTEXT_SCAN_INTERVAL_MS = 5e3;
|
|
18191
18365
|
|
|
18192
18366
|
// src/features/claude_code/daemon_pid_file.ts
|
|
18193
18367
|
function getMobbdevDir() {
|
|
18194
|
-
return
|
|
18368
|
+
return path19.join(os4.homedir(), ".mobbdev");
|
|
18195
18369
|
}
|
|
18196
18370
|
function getDaemonCheckScriptPath() {
|
|
18197
|
-
return
|
|
18371
|
+
return path19.join(getMobbdevDir(), "daemon-check.js");
|
|
18198
18372
|
}
|
|
18199
18373
|
var DaemonPidFile = class {
|
|
18200
18374
|
constructor() {
|
|
18201
18375
|
__publicField(this, "data", null);
|
|
18202
18376
|
}
|
|
18203
18377
|
get filePath() {
|
|
18204
|
-
return
|
|
18378
|
+
return path19.join(getMobbdevDir(), "daemon.pid");
|
|
18205
18379
|
}
|
|
18206
18380
|
/** Ensure ~/.mobbdev/ directory exists. */
|
|
18207
18381
|
ensureDir() {
|
|
@@ -18263,8 +18437,8 @@ var DaemonPidFile = class {
|
|
|
18263
18437
|
// src/features/claude_code/data_collector.ts
|
|
18264
18438
|
import { execFile } from "child_process";
|
|
18265
18439
|
import { createHash as createHash3 } from "crypto";
|
|
18266
|
-
import { access, open as open4, readdir as
|
|
18267
|
-
import
|
|
18440
|
+
import { access as access2, open as open4, readdir as readdir3, readFile as readFile3, unlink as unlink2 } from "fs/promises";
|
|
18441
|
+
import path20 from "path";
|
|
18268
18442
|
import { promisify } from "util";
|
|
18269
18443
|
|
|
18270
18444
|
// src/features/analysis/context_file_uploader.ts
|
|
@@ -18529,8 +18703,8 @@ function createConfigstoreStream(store, opts) {
|
|
|
18529
18703
|
heartbeatBuffer.length = 0;
|
|
18530
18704
|
}
|
|
18531
18705
|
}
|
|
18532
|
-
function setScopePath(
|
|
18533
|
-
scopePath =
|
|
18706
|
+
function setScopePath(path36) {
|
|
18707
|
+
scopePath = path36;
|
|
18534
18708
|
}
|
|
18535
18709
|
return { writable, flush, setScopePath };
|
|
18536
18710
|
}
|
|
@@ -18754,7 +18928,7 @@ function createLogger(config2) {
|
|
|
18754
18928
|
|
|
18755
18929
|
// src/features/claude_code/hook_logger.ts
|
|
18756
18930
|
var DD_RUM_TOKEN = true ? "pubf59c0182545bfb4c299175119f1abf9b" : "";
|
|
18757
|
-
var CLI_VERSION = true ? "1.4.
|
|
18931
|
+
var CLI_VERSION = true ? "1.4.2" : "unknown";
|
|
18758
18932
|
var NAMESPACE = "mobbdev-claude-code-hook-logs";
|
|
18759
18933
|
var claudeCodeVersion;
|
|
18760
18934
|
function buildDdTags() {
|
|
@@ -18841,18 +19015,18 @@ function getCursorKey(transcriptPath) {
|
|
|
18841
19015
|
}
|
|
18842
19016
|
async function resolveTranscriptPath(transcriptPath, sessionId) {
|
|
18843
19017
|
try {
|
|
18844
|
-
await
|
|
19018
|
+
await access2(transcriptPath);
|
|
18845
19019
|
return transcriptPath;
|
|
18846
19020
|
} catch {
|
|
18847
19021
|
}
|
|
18848
|
-
const filename =
|
|
18849
|
-
const dirName =
|
|
18850
|
-
const projectsDir =
|
|
19022
|
+
const filename = path20.basename(transcriptPath);
|
|
19023
|
+
const dirName = path20.basename(path20.dirname(transcriptPath));
|
|
19024
|
+
const projectsDir = path20.dirname(path20.dirname(transcriptPath));
|
|
18851
19025
|
const baseDirName = dirName.replace(/[-.]claude-worktrees-.+$/, "");
|
|
18852
19026
|
if (baseDirName !== dirName) {
|
|
18853
|
-
const candidate =
|
|
19027
|
+
const candidate = path20.join(projectsDir, baseDirName, filename);
|
|
18854
19028
|
try {
|
|
18855
|
-
await
|
|
19029
|
+
await access2(candidate);
|
|
18856
19030
|
hookLog.info(
|
|
18857
19031
|
{
|
|
18858
19032
|
data: {
|
|
@@ -18869,12 +19043,12 @@ async function resolveTranscriptPath(transcriptPath, sessionId) {
|
|
|
18869
19043
|
}
|
|
18870
19044
|
}
|
|
18871
19045
|
try {
|
|
18872
|
-
const dirs = await
|
|
19046
|
+
const dirs = await readdir3(projectsDir);
|
|
18873
19047
|
for (const dir of dirs) {
|
|
18874
19048
|
if (dir === dirName) continue;
|
|
18875
|
-
const candidate =
|
|
19049
|
+
const candidate = path20.join(projectsDir, dir, filename);
|
|
18876
19050
|
try {
|
|
18877
|
-
await
|
|
19051
|
+
await access2(candidate);
|
|
18878
19052
|
hookLog.info(
|
|
18879
19053
|
{
|
|
18880
19054
|
data: {
|
|
@@ -19053,11 +19227,11 @@ async function cleanupStaleSessions(configDir) {
|
|
|
19053
19227
|
const now = Date.now();
|
|
19054
19228
|
const prefix = getSessionFilePrefix();
|
|
19055
19229
|
try {
|
|
19056
|
-
const files = await
|
|
19230
|
+
const files = await readdir3(configDir);
|
|
19057
19231
|
let deletedCount = 0;
|
|
19058
19232
|
for (const file of files) {
|
|
19059
19233
|
if (!file.startsWith(prefix) || !file.endsWith(".json")) continue;
|
|
19060
|
-
const filePath =
|
|
19234
|
+
const filePath = path20.join(configDir, file);
|
|
19061
19235
|
try {
|
|
19062
19236
|
const content = JSON.parse(await readFile3(filePath, "utf-8"));
|
|
19063
19237
|
let newest = 0;
|
|
@@ -19069,7 +19243,7 @@ async function cleanupStaleSessions(configDir) {
|
|
|
19069
19243
|
}
|
|
19070
19244
|
}
|
|
19071
19245
|
if (newest > 0 && now - newest > STALE_KEY_MAX_AGE_MS) {
|
|
19072
|
-
await
|
|
19246
|
+
await unlink2(filePath);
|
|
19073
19247
|
deletedCount++;
|
|
19074
19248
|
}
|
|
19075
19249
|
} catch {
|
|
@@ -19209,16 +19383,6 @@ async function processTranscript(input, sessionStore, log2, maxEntries = DAEMON_
|
|
|
19209
19383
|
entriesSkipped: filteredOut,
|
|
19210
19384
|
claudeCodeVersion: getClaudeCodeVersion()
|
|
19211
19385
|
});
|
|
19212
|
-
if (input.cwd) {
|
|
19213
|
-
uploadContextFilesIfNeeded(
|
|
19214
|
-
input.session_id,
|
|
19215
|
-
input.cwd,
|
|
19216
|
-
gqlClient,
|
|
19217
|
-
log2
|
|
19218
|
-
).catch((err) => {
|
|
19219
|
-
log2.error({ data: { err } }, "uploadContextFilesIfNeeded failed");
|
|
19220
|
-
});
|
|
19221
|
-
}
|
|
19222
19386
|
return {
|
|
19223
19387
|
entriesUploaded: entries.length,
|
|
19224
19388
|
entriesSkipped: filteredOut,
|
|
@@ -19305,14 +19469,14 @@ async function uploadContextFilesIfNeeded(sessionId, cwd, gqlClient, log2) {
|
|
|
19305
19469
|
import fs14 from "fs";
|
|
19306
19470
|
import fsPromises4 from "fs/promises";
|
|
19307
19471
|
import os6 from "os";
|
|
19308
|
-
import
|
|
19472
|
+
import path21 from "path";
|
|
19309
19473
|
import chalk11 from "chalk";
|
|
19310
19474
|
|
|
19311
19475
|
// src/features/claude_code/daemon-check-shim.tmpl.js
|
|
19312
19476
|
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";
|
|
19313
19477
|
|
|
19314
19478
|
// src/features/claude_code/install_hook.ts
|
|
19315
|
-
var CLAUDE_SETTINGS_PATH =
|
|
19479
|
+
var CLAUDE_SETTINGS_PATH = path21.join(os6.homedir(), ".claude", "settings.json");
|
|
19316
19480
|
var RECOMMENDED_MATCHER = "*";
|
|
19317
19481
|
async function claudeSettingsExists() {
|
|
19318
19482
|
try {
|
|
@@ -19458,18 +19622,18 @@ async function installMobbHooks(options = {}) {
|
|
|
19458
19622
|
}
|
|
19459
19623
|
|
|
19460
19624
|
// src/features/claude_code/transcript_scanner.ts
|
|
19461
|
-
import { open as open5, readdir as
|
|
19625
|
+
import { open as open5, readdir as readdir4, stat as stat3 } from "fs/promises";
|
|
19462
19626
|
import os7 from "os";
|
|
19463
|
-
import
|
|
19627
|
+
import path22 from "path";
|
|
19464
19628
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
19465
19629
|
function getClaudeProjectsDirs() {
|
|
19466
19630
|
const dirs = [];
|
|
19467
19631
|
const configDir = process.env["CLAUDE_CONFIG_DIR"];
|
|
19468
19632
|
if (configDir) {
|
|
19469
|
-
dirs.push(
|
|
19633
|
+
dirs.push(path22.join(configDir, "projects"));
|
|
19470
19634
|
}
|
|
19471
|
-
dirs.push(
|
|
19472
|
-
dirs.push(
|
|
19635
|
+
dirs.push(path22.join(os7.homedir(), ".config", "claude", "projects"));
|
|
19636
|
+
dirs.push(path22.join(os7.homedir(), ".claude", "projects"));
|
|
19473
19637
|
return dirs;
|
|
19474
19638
|
}
|
|
19475
19639
|
async function collectJsonlFiles(files, dir, projectDir, seen, now, results) {
|
|
@@ -19477,7 +19641,7 @@ async function collectJsonlFiles(files, dir, projectDir, seen, now, results) {
|
|
|
19477
19641
|
if (!file.endsWith(".jsonl")) continue;
|
|
19478
19642
|
const sessionId = file.replace(".jsonl", "");
|
|
19479
19643
|
if (!UUID_RE.test(sessionId)) continue;
|
|
19480
|
-
const filePath =
|
|
19644
|
+
const filePath = path22.join(dir, file);
|
|
19481
19645
|
if (seen.has(filePath)) continue;
|
|
19482
19646
|
seen.add(filePath);
|
|
19483
19647
|
let fileStat;
|
|
@@ -19503,12 +19667,12 @@ async function scanForTranscripts(projectsDirs = getClaudeProjectsDirs()) {
|
|
|
19503
19667
|
for (const projectsDir of projectsDirs) {
|
|
19504
19668
|
let projectDirs;
|
|
19505
19669
|
try {
|
|
19506
|
-
projectDirs = await
|
|
19670
|
+
projectDirs = await readdir4(projectsDir);
|
|
19507
19671
|
} catch {
|
|
19508
19672
|
continue;
|
|
19509
19673
|
}
|
|
19510
19674
|
for (const projName of projectDirs) {
|
|
19511
|
-
const projPath =
|
|
19675
|
+
const projPath = path22.join(projectsDir, projName);
|
|
19512
19676
|
let projStat;
|
|
19513
19677
|
try {
|
|
19514
19678
|
projStat = await stat3(projPath);
|
|
@@ -19518,18 +19682,18 @@ async function scanForTranscripts(projectsDirs = getClaudeProjectsDirs()) {
|
|
|
19518
19682
|
if (!projStat.isDirectory()) continue;
|
|
19519
19683
|
let files;
|
|
19520
19684
|
try {
|
|
19521
|
-
files = await
|
|
19685
|
+
files = await readdir4(projPath);
|
|
19522
19686
|
} catch {
|
|
19523
19687
|
continue;
|
|
19524
19688
|
}
|
|
19525
19689
|
await collectJsonlFiles(files, projPath, projPath, seen, now, results);
|
|
19526
19690
|
for (const entry of files) {
|
|
19527
19691
|
if (!UUID_RE.test(entry)) continue;
|
|
19528
|
-
const subagentsDir =
|
|
19692
|
+
const subagentsDir = path22.join(projPath, entry, "subagents");
|
|
19529
19693
|
try {
|
|
19530
19694
|
const s = await stat3(subagentsDir);
|
|
19531
19695
|
if (!s.isDirectory()) continue;
|
|
19532
|
-
const subFiles = await
|
|
19696
|
+
const subFiles = await readdir4(subagentsDir);
|
|
19533
19697
|
await collectJsonlFiles(
|
|
19534
19698
|
subFiles,
|
|
19535
19699
|
subagentsDir,
|
|
@@ -19613,6 +19777,8 @@ async function startDaemon() {
|
|
|
19613
19777
|
const startedAt = Date.now();
|
|
19614
19778
|
const lastSeen = /* @__PURE__ */ new Map();
|
|
19615
19779
|
let cleanupConfigDir;
|
|
19780
|
+
const sessionCwdCache = /* @__PURE__ */ new Map();
|
|
19781
|
+
let lastContextScanMs = 0;
|
|
19616
19782
|
while (true) {
|
|
19617
19783
|
if (shuttingDown) {
|
|
19618
19784
|
await gracefulExit(0, "signal");
|
|
@@ -19626,9 +19792,29 @@ async function startDaemon() {
|
|
|
19626
19792
|
for (const transcript of changed) {
|
|
19627
19793
|
const sessionStore = createSessionConfigStore(transcript.sessionId);
|
|
19628
19794
|
if (!cleanupConfigDir) {
|
|
19629
|
-
cleanupConfigDir =
|
|
19795
|
+
cleanupConfigDir = path23.dirname(sessionStore.path);
|
|
19796
|
+
}
|
|
19797
|
+
await drainTranscript(
|
|
19798
|
+
transcript,
|
|
19799
|
+
sessionStore,
|
|
19800
|
+
gqlClient,
|
|
19801
|
+
sessionCwdCache
|
|
19802
|
+
);
|
|
19803
|
+
}
|
|
19804
|
+
if (lastSeen.size > 0) {
|
|
19805
|
+
for (const filePath of sessionCwdCache.keys()) {
|
|
19806
|
+
if (!lastSeen.has(filePath)) sessionCwdCache.delete(filePath);
|
|
19807
|
+
}
|
|
19808
|
+
}
|
|
19809
|
+
const now = Date.now();
|
|
19810
|
+
if (now - lastContextScanMs >= CONTEXT_SCAN_INTERVAL_MS) {
|
|
19811
|
+
lastContextScanMs = now;
|
|
19812
|
+
for (const { sessionId, cwd } of sessionCwdCache.values()) {
|
|
19813
|
+
const log2 = createScopedHookLog(cwd, { daemonMode: true });
|
|
19814
|
+
uploadContextFilesIfNeeded(sessionId, cwd, gqlClient, log2).catch(
|
|
19815
|
+
(err) => log2.warn({ err }, "Context file scan failed")
|
|
19816
|
+
);
|
|
19630
19817
|
}
|
|
19631
|
-
await drainTranscript(transcript, sessionStore, gqlClient);
|
|
19632
19818
|
}
|
|
19633
19819
|
if (cleanupConfigDir) {
|
|
19634
19820
|
await cleanupStaleSessions(cleanupConfigDir);
|
|
@@ -19669,11 +19855,17 @@ async function authenticateOrExit(exit) {
|
|
|
19669
19855
|
return exit(1, "auth failed");
|
|
19670
19856
|
}
|
|
19671
19857
|
}
|
|
19672
|
-
async function drainTranscript(transcript, sessionStore, gqlClient) {
|
|
19858
|
+
async function drainTranscript(transcript, sessionStore, gqlClient, sessionCwdCache) {
|
|
19673
19859
|
const cwd = await extractCwdFromTranscript(transcript.filePath);
|
|
19674
19860
|
const log2 = createScopedHookLog(cwd ?? transcript.projectDir, {
|
|
19675
19861
|
daemonMode: true
|
|
19676
19862
|
});
|
|
19863
|
+
if (cwd) {
|
|
19864
|
+
sessionCwdCache.set(transcript.filePath, {
|
|
19865
|
+
sessionId: transcript.sessionId,
|
|
19866
|
+
cwd
|
|
19867
|
+
});
|
|
19868
|
+
}
|
|
19677
19869
|
try {
|
|
19678
19870
|
let hasMore = true;
|
|
19679
19871
|
while (hasMore) {
|
|
@@ -19897,8 +20089,8 @@ var WorkspaceService = class {
|
|
|
19897
20089
|
* Sets a known workspace path that was discovered through successful validation
|
|
19898
20090
|
* @param path The validated workspace path to store
|
|
19899
20091
|
*/
|
|
19900
|
-
static setKnownWorkspacePath(
|
|
19901
|
-
this.knownWorkspacePath =
|
|
20092
|
+
static setKnownWorkspacePath(path36) {
|
|
20093
|
+
this.knownWorkspacePath = path36;
|
|
19902
20094
|
}
|
|
19903
20095
|
/**
|
|
19904
20096
|
* Gets the known workspace path that was previously validated
|
|
@@ -20759,7 +20951,7 @@ async function createAuthenticatedMcpGQLClient({
|
|
|
20759
20951
|
import { execSync as execSync2 } from "child_process";
|
|
20760
20952
|
import fs15 from "fs";
|
|
20761
20953
|
import os8 from "os";
|
|
20762
|
-
import
|
|
20954
|
+
import path24 from "path";
|
|
20763
20955
|
var IDEs = ["cursor", "windsurf", "webstorm", "vscode", "claude"];
|
|
20764
20956
|
var runCommand = (cmd) => {
|
|
20765
20957
|
try {
|
|
@@ -20774,7 +20966,7 @@ var gitInfo = {
|
|
|
20774
20966
|
};
|
|
20775
20967
|
var getClaudeWorkspacePaths = () => {
|
|
20776
20968
|
const home = os8.homedir();
|
|
20777
|
-
const claudeIdePath =
|
|
20969
|
+
const claudeIdePath = path24.join(home, ".claude", "ide");
|
|
20778
20970
|
const workspacePaths = [];
|
|
20779
20971
|
if (!fs15.existsSync(claudeIdePath)) {
|
|
20780
20972
|
return workspacePaths;
|
|
@@ -20782,7 +20974,7 @@ var getClaudeWorkspacePaths = () => {
|
|
|
20782
20974
|
try {
|
|
20783
20975
|
const lockFiles = fs15.readdirSync(claudeIdePath).filter((file) => file.endsWith(".lock"));
|
|
20784
20976
|
for (const lockFile of lockFiles) {
|
|
20785
|
-
const lockFilePath =
|
|
20977
|
+
const lockFilePath = path24.join(claudeIdePath, lockFile);
|
|
20786
20978
|
try {
|
|
20787
20979
|
const lockContent = JSON.parse(fs15.readFileSync(lockFilePath, "utf8"));
|
|
20788
20980
|
if (lockContent.workspaceFolders && Array.isArray(lockContent.workspaceFolders)) {
|
|
@@ -20807,24 +20999,24 @@ var getMCPConfigPaths = (hostName) => {
|
|
|
20807
20999
|
switch (hostName.toLowerCase()) {
|
|
20808
21000
|
case "cursor":
|
|
20809
21001
|
return [
|
|
20810
|
-
|
|
21002
|
+
path24.join(currentDir, ".cursor", "mcp.json"),
|
|
20811
21003
|
// local first
|
|
20812
|
-
|
|
21004
|
+
path24.join(home, ".cursor", "mcp.json")
|
|
20813
21005
|
];
|
|
20814
21006
|
case "windsurf":
|
|
20815
21007
|
return [
|
|
20816
|
-
|
|
21008
|
+
path24.join(currentDir, ".codeium", "mcp_config.json"),
|
|
20817
21009
|
// local first
|
|
20818
|
-
|
|
21010
|
+
path24.join(home, ".codeium", "windsurf", "mcp_config.json")
|
|
20819
21011
|
];
|
|
20820
21012
|
case "webstorm":
|
|
20821
21013
|
return [];
|
|
20822
21014
|
case "visualstudiocode":
|
|
20823
21015
|
case "vscode":
|
|
20824
21016
|
return [
|
|
20825
|
-
|
|
21017
|
+
path24.join(currentDir, ".vscode", "mcp.json"),
|
|
20826
21018
|
// local first
|
|
20827
|
-
process.platform === "win32" ?
|
|
21019
|
+
process.platform === "win32" ? path24.join(home, "AppData", "Roaming", "Code", "User", "mcp.json") : path24.join(
|
|
20828
21020
|
home,
|
|
20829
21021
|
"Library",
|
|
20830
21022
|
"Application Support",
|
|
@@ -20835,13 +21027,13 @@ var getMCPConfigPaths = (hostName) => {
|
|
|
20835
21027
|
];
|
|
20836
21028
|
case "claude": {
|
|
20837
21029
|
const claudePaths = [
|
|
20838
|
-
|
|
21030
|
+
path24.join(currentDir, ".claude.json"),
|
|
20839
21031
|
// local first
|
|
20840
|
-
|
|
21032
|
+
path24.join(home, ".claude.json")
|
|
20841
21033
|
];
|
|
20842
21034
|
const workspacePaths = getClaudeWorkspacePaths();
|
|
20843
21035
|
for (const workspacePath of workspacePaths) {
|
|
20844
|
-
claudePaths.push(
|
|
21036
|
+
claudePaths.push(path24.join(workspacePath, ".mcp.json"));
|
|
20845
21037
|
}
|
|
20846
21038
|
return claudePaths;
|
|
20847
21039
|
}
|
|
@@ -21002,10 +21194,10 @@ var getHostInfo = (additionalMcpList) => {
|
|
|
21002
21194
|
const ideConfigPaths = /* @__PURE__ */ new Set();
|
|
21003
21195
|
for (const ide of IDEs) {
|
|
21004
21196
|
const configPaths = getMCPConfigPaths(ide);
|
|
21005
|
-
configPaths.forEach((
|
|
21197
|
+
configPaths.forEach((path36) => ideConfigPaths.add(path36));
|
|
21006
21198
|
}
|
|
21007
21199
|
const uniqueAdditionalPaths = additionalMcpList.filter(
|
|
21008
|
-
(
|
|
21200
|
+
(path36) => !ideConfigPaths.has(path36)
|
|
21009
21201
|
);
|
|
21010
21202
|
for (const ide of IDEs) {
|
|
21011
21203
|
const cfg = readMCPConfig(ide);
|
|
@@ -21127,7 +21319,7 @@ init_configs();
|
|
|
21127
21319
|
init_configs();
|
|
21128
21320
|
import fs16 from "fs";
|
|
21129
21321
|
import os9 from "os";
|
|
21130
|
-
import
|
|
21322
|
+
import path25 from "path";
|
|
21131
21323
|
var MAX_DEPTH = 2;
|
|
21132
21324
|
var patterns = ["mcp", "claude"];
|
|
21133
21325
|
var isFileMatch = (fileName) => {
|
|
@@ -21147,7 +21339,7 @@ var searchDir = async (dir, depth = 0) => {
|
|
|
21147
21339
|
if (depth > MAX_DEPTH) return results;
|
|
21148
21340
|
const entries = await fs16.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
21149
21341
|
for (const entry of entries) {
|
|
21150
|
-
const fullPath =
|
|
21342
|
+
const fullPath = path25.join(dir, entry.name);
|
|
21151
21343
|
if (entry.isFile() && isFileMatch(entry.name)) {
|
|
21152
21344
|
results.push(fullPath);
|
|
21153
21345
|
} else if (entry.isDirectory()) {
|
|
@@ -21164,14 +21356,14 @@ var findSystemMCPConfigs = async () => {
|
|
|
21164
21356
|
const home = os9.homedir();
|
|
21165
21357
|
const platform2 = os9.platform();
|
|
21166
21358
|
const knownDirs = platform2 === "win32" ? [
|
|
21167
|
-
|
|
21168
|
-
|
|
21169
|
-
|
|
21359
|
+
path25.join(home, ".cursor"),
|
|
21360
|
+
path25.join(home, "Documents"),
|
|
21361
|
+
path25.join(home, "Downloads")
|
|
21170
21362
|
] : [
|
|
21171
|
-
|
|
21172
|
-
process.env["XDG_CONFIG_HOME"] ||
|
|
21173
|
-
|
|
21174
|
-
|
|
21363
|
+
path25.join(home, ".cursor"),
|
|
21364
|
+
process.env["XDG_CONFIG_HOME"] || path25.join(home, ".config"),
|
|
21365
|
+
path25.join(home, "Documents"),
|
|
21366
|
+
path25.join(home, "Downloads")
|
|
21175
21367
|
];
|
|
21176
21368
|
const timeoutPromise = new Promise(
|
|
21177
21369
|
(resolve) => setTimeout(() => {
|
|
@@ -23587,13 +23779,13 @@ For a complete security audit workflow, use the \`full-security-audit\` prompt.
|
|
|
23587
23779
|
// src/mcp/services/McpDetectionService/CursorMcpDetectionService.ts
|
|
23588
23780
|
import * as fs19 from "fs";
|
|
23589
23781
|
import * as os12 from "os";
|
|
23590
|
-
import * as
|
|
23782
|
+
import * as path27 from "path";
|
|
23591
23783
|
|
|
23592
23784
|
// src/mcp/services/McpDetectionService/BaseMcpDetectionService.ts
|
|
23593
23785
|
init_configs();
|
|
23594
23786
|
import * as fs18 from "fs";
|
|
23595
23787
|
import fetch7 from "node-fetch";
|
|
23596
|
-
import * as
|
|
23788
|
+
import * as path26 from "path";
|
|
23597
23789
|
|
|
23598
23790
|
// src/mcp/services/McpDetectionService/McpDetectionServiceUtils.ts
|
|
23599
23791
|
import * as fs17 from "fs";
|
|
@@ -23602,14 +23794,14 @@ import * as os11 from "os";
|
|
|
23602
23794
|
// src/mcp/services/McpDetectionService/VscodeMcpDetectionService.ts
|
|
23603
23795
|
import * as fs20 from "fs";
|
|
23604
23796
|
import * as os13 from "os";
|
|
23605
|
-
import * as
|
|
23797
|
+
import * as path28 from "path";
|
|
23606
23798
|
|
|
23607
23799
|
// src/mcp/tools/checkForNewAvailableFixes/CheckForNewAvailableFixesTool.ts
|
|
23608
23800
|
import { z as z42 } from "zod";
|
|
23609
23801
|
|
|
23610
23802
|
// src/mcp/services/PathValidation.ts
|
|
23611
23803
|
import fs21 from "fs";
|
|
23612
|
-
import
|
|
23804
|
+
import path29 from "path";
|
|
23613
23805
|
async function validatePath(inputPath) {
|
|
23614
23806
|
logDebug("Validating MCP path", { inputPath });
|
|
23615
23807
|
if (/^\/[a-zA-Z]:\//.test(inputPath)) {
|
|
@@ -23641,7 +23833,7 @@ async function validatePath(inputPath) {
|
|
|
23641
23833
|
logError(error);
|
|
23642
23834
|
return { isValid: false, error, path: inputPath };
|
|
23643
23835
|
}
|
|
23644
|
-
const normalizedPath =
|
|
23836
|
+
const normalizedPath = path29.normalize(inputPath);
|
|
23645
23837
|
if (normalizedPath.includes("..")) {
|
|
23646
23838
|
const error = `Normalized path contains path traversal patterns: ${inputPath}`;
|
|
23647
23839
|
logError(error);
|
|
@@ -24293,7 +24485,7 @@ init_configs();
|
|
|
24293
24485
|
import fs22 from "fs/promises";
|
|
24294
24486
|
import nodePath from "path";
|
|
24295
24487
|
var getLocalFiles = async ({
|
|
24296
|
-
path:
|
|
24488
|
+
path: path36,
|
|
24297
24489
|
maxFileSize = MCP_MAX_FILE_SIZE,
|
|
24298
24490
|
maxFiles,
|
|
24299
24491
|
isAllFilesScan,
|
|
@@ -24301,17 +24493,17 @@ var getLocalFiles = async ({
|
|
|
24301
24493
|
scanRecentlyChangedFiles
|
|
24302
24494
|
}) => {
|
|
24303
24495
|
logDebug(`[${scanContext}] Starting getLocalFiles`, {
|
|
24304
|
-
path:
|
|
24496
|
+
path: path36,
|
|
24305
24497
|
maxFileSize,
|
|
24306
24498
|
maxFiles,
|
|
24307
24499
|
isAllFilesScan,
|
|
24308
24500
|
scanRecentlyChangedFiles
|
|
24309
24501
|
});
|
|
24310
24502
|
try {
|
|
24311
|
-
const resolvedRepoPath = await fs22.realpath(
|
|
24503
|
+
const resolvedRepoPath = await fs22.realpath(path36);
|
|
24312
24504
|
logDebug(`[${scanContext}] Resolved repository path`, {
|
|
24313
24505
|
resolvedRepoPath,
|
|
24314
|
-
originalPath:
|
|
24506
|
+
originalPath: path36
|
|
24315
24507
|
});
|
|
24316
24508
|
const gitService = new GitService(resolvedRepoPath, log);
|
|
24317
24509
|
const gitValidation = await gitService.validateRepository();
|
|
@@ -24324,7 +24516,7 @@ var getLocalFiles = async ({
|
|
|
24324
24516
|
if (!gitValidation.isValid || isAllFilesScan) {
|
|
24325
24517
|
try {
|
|
24326
24518
|
files = await FileUtils.getLastChangedFiles({
|
|
24327
|
-
dir:
|
|
24519
|
+
dir: path36,
|
|
24328
24520
|
maxFileSize,
|
|
24329
24521
|
maxFiles,
|
|
24330
24522
|
isAllFilesScan
|
|
@@ -24416,7 +24608,7 @@ var getLocalFiles = async ({
|
|
|
24416
24608
|
logError(`${scanContext}Unexpected error in getLocalFiles`, {
|
|
24417
24609
|
error: error instanceof Error ? error.message : String(error),
|
|
24418
24610
|
stack: error instanceof Error ? error.stack : void 0,
|
|
24419
|
-
path:
|
|
24611
|
+
path: path36
|
|
24420
24612
|
});
|
|
24421
24613
|
throw error;
|
|
24422
24614
|
}
|
|
@@ -24426,7 +24618,7 @@ var getLocalFiles = async ({
|
|
|
24426
24618
|
init_client_generates();
|
|
24427
24619
|
init_GitService();
|
|
24428
24620
|
import fs23 from "fs";
|
|
24429
|
-
import
|
|
24621
|
+
import path30 from "path";
|
|
24430
24622
|
import { z as z41 } from "zod";
|
|
24431
24623
|
function extractPathFromPatch(patch) {
|
|
24432
24624
|
const match = patch?.match(/diff --git a\/([^\s]+) b\//);
|
|
@@ -24512,7 +24704,7 @@ var LocalMobbFolderService = class {
|
|
|
24512
24704
|
"[LocalMobbFolderService] Non-git repository detected, skipping .gitignore operations"
|
|
24513
24705
|
);
|
|
24514
24706
|
}
|
|
24515
|
-
const mobbFolderPath =
|
|
24707
|
+
const mobbFolderPath = path30.join(
|
|
24516
24708
|
this.repoPath,
|
|
24517
24709
|
this.defaultMobbFolderName
|
|
24518
24710
|
);
|
|
@@ -24684,7 +24876,7 @@ var LocalMobbFolderService = class {
|
|
|
24684
24876
|
mobbFolderPath,
|
|
24685
24877
|
baseFileName
|
|
24686
24878
|
);
|
|
24687
|
-
const filePath =
|
|
24879
|
+
const filePath = path30.join(mobbFolderPath, uniqueFileName);
|
|
24688
24880
|
await fs23.promises.writeFile(filePath, patch, "utf8");
|
|
24689
24881
|
logInfo("[LocalMobbFolderService] Patch saved successfully", {
|
|
24690
24882
|
filePath,
|
|
@@ -24742,11 +24934,11 @@ var LocalMobbFolderService = class {
|
|
|
24742
24934
|
* @returns Unique filename that doesn't conflict with existing files
|
|
24743
24935
|
*/
|
|
24744
24936
|
getUniqueFileName(folderPath, baseFileName) {
|
|
24745
|
-
const baseName =
|
|
24746
|
-
const extension =
|
|
24937
|
+
const baseName = path30.parse(baseFileName).name;
|
|
24938
|
+
const extension = path30.parse(baseFileName).ext;
|
|
24747
24939
|
let uniqueFileName = baseFileName;
|
|
24748
24940
|
let index = 1;
|
|
24749
|
-
while (fs23.existsSync(
|
|
24941
|
+
while (fs23.existsSync(path30.join(folderPath, uniqueFileName))) {
|
|
24750
24942
|
uniqueFileName = `${baseName}-${index}${extension}`;
|
|
24751
24943
|
index++;
|
|
24752
24944
|
if (index > 1e3) {
|
|
@@ -24777,7 +24969,7 @@ var LocalMobbFolderService = class {
|
|
|
24777
24969
|
logDebug("[LocalMobbFolderService] Logging patch info", { fixId: fix.id });
|
|
24778
24970
|
try {
|
|
24779
24971
|
const mobbFolderPath = await this.getFolder();
|
|
24780
|
-
const patchInfoPath =
|
|
24972
|
+
const patchInfoPath = path30.join(mobbFolderPath, "patchInfo.md");
|
|
24781
24973
|
const markdownContent = this.generateFixMarkdown(fix, savedPatchFileName);
|
|
24782
24974
|
let existingContent = "";
|
|
24783
24975
|
if (fs23.existsSync(patchInfoPath)) {
|
|
@@ -24819,7 +25011,7 @@ var LocalMobbFolderService = class {
|
|
|
24819
25011
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
24820
25012
|
const patch = this.extractPatchFromFix(fix);
|
|
24821
25013
|
const relativePatchedFilePath = patch ? extractPathFromPatch(patch) : null;
|
|
24822
|
-
const patchedFilePath = relativePatchedFilePath ?
|
|
25014
|
+
const patchedFilePath = relativePatchedFilePath ? path30.resolve(this.repoPath, relativePatchedFilePath) : null;
|
|
24823
25015
|
const fixIdentifier = savedPatchFileName ? savedPatchFileName.replace(".patch", "") : fix.id;
|
|
24824
25016
|
let markdown = `# Fix ${fixIdentifier}
|
|
24825
25017
|
|
|
@@ -25155,7 +25347,7 @@ var LocalMobbFolderService = class {
|
|
|
25155
25347
|
// src/mcp/services/PatchApplicationService.ts
|
|
25156
25348
|
init_configs();
|
|
25157
25349
|
import {
|
|
25158
|
-
existsSync as
|
|
25350
|
+
existsSync as existsSync6,
|
|
25159
25351
|
mkdirSync,
|
|
25160
25352
|
readFileSync as readFileSync4,
|
|
25161
25353
|
unlinkSync,
|
|
@@ -25163,14 +25355,14 @@ import {
|
|
|
25163
25355
|
} from "fs";
|
|
25164
25356
|
import fs24 from "fs/promises";
|
|
25165
25357
|
import parseDiff2 from "parse-diff";
|
|
25166
|
-
import
|
|
25358
|
+
import path31 from "path";
|
|
25167
25359
|
var PatchApplicationService = class {
|
|
25168
25360
|
/**
|
|
25169
25361
|
* Gets the appropriate comment syntax for a file based on its extension
|
|
25170
25362
|
*/
|
|
25171
25363
|
static getCommentSyntax(filePath) {
|
|
25172
|
-
const ext =
|
|
25173
|
-
const basename2 =
|
|
25364
|
+
const ext = path31.extname(filePath).toLowerCase();
|
|
25365
|
+
const basename2 = path31.basename(filePath);
|
|
25174
25366
|
const commentMap = {
|
|
25175
25367
|
// C-style languages (single line comments)
|
|
25176
25368
|
".js": "//",
|
|
@@ -25378,7 +25570,7 @@ var PatchApplicationService = class {
|
|
|
25378
25570
|
}
|
|
25379
25571
|
);
|
|
25380
25572
|
}
|
|
25381
|
-
const dirPath =
|
|
25573
|
+
const dirPath = path31.dirname(normalizedFilePath);
|
|
25382
25574
|
mkdirSync(dirPath, { recursive: true });
|
|
25383
25575
|
writeFileSync3(normalizedFilePath, finalContent, "utf8");
|
|
25384
25576
|
return normalizedFilePath;
|
|
@@ -25387,9 +25579,9 @@ var PatchApplicationService = class {
|
|
|
25387
25579
|
repositoryPath,
|
|
25388
25580
|
targetPath
|
|
25389
25581
|
}) {
|
|
25390
|
-
const repoRoot =
|
|
25391
|
-
const normalizedPath =
|
|
25392
|
-
const repoRootWithSep = repoRoot.endsWith(
|
|
25582
|
+
const repoRoot = path31.resolve(repositoryPath);
|
|
25583
|
+
const normalizedPath = path31.resolve(repoRoot, targetPath);
|
|
25584
|
+
const repoRootWithSep = repoRoot.endsWith(path31.sep) ? repoRoot : `${repoRoot}${path31.sep}`;
|
|
25393
25585
|
if (normalizedPath !== repoRoot && !normalizedPath.startsWith(repoRootWithSep)) {
|
|
25394
25586
|
throw new Error(
|
|
25395
25587
|
`Security violation: target path ${targetPath} resolves outside repository`
|
|
@@ -25398,7 +25590,7 @@ var PatchApplicationService = class {
|
|
|
25398
25590
|
return {
|
|
25399
25591
|
repoRoot,
|
|
25400
25592
|
normalizedPath,
|
|
25401
|
-
relativePath:
|
|
25593
|
+
relativePath: path31.relative(repoRoot, normalizedPath)
|
|
25402
25594
|
};
|
|
25403
25595
|
}
|
|
25404
25596
|
/**
|
|
@@ -25680,8 +25872,8 @@ var PatchApplicationService = class {
|
|
|
25680
25872
|
continue;
|
|
25681
25873
|
}
|
|
25682
25874
|
try {
|
|
25683
|
-
const absolutePath =
|
|
25684
|
-
if (
|
|
25875
|
+
const absolutePath = path31.resolve(repositoryPath, targetFile);
|
|
25876
|
+
if (existsSync6(absolutePath)) {
|
|
25685
25877
|
const stats = await fs24.stat(absolutePath);
|
|
25686
25878
|
const fileModTime = stats.mtime.getTime();
|
|
25687
25879
|
if (fileModTime > scanStartTime) {
|
|
@@ -25882,7 +26074,7 @@ var PatchApplicationService = class {
|
|
|
25882
26074
|
targetFile,
|
|
25883
26075
|
absoluteFilePath,
|
|
25884
26076
|
relativePath,
|
|
25885
|
-
exists:
|
|
26077
|
+
exists: existsSync6(absoluteFilePath)
|
|
25886
26078
|
});
|
|
25887
26079
|
return { absoluteFilePath, relativePath };
|
|
25888
26080
|
}
|
|
@@ -25906,7 +26098,7 @@ var PatchApplicationService = class {
|
|
|
25906
26098
|
fix,
|
|
25907
26099
|
scanContext
|
|
25908
26100
|
});
|
|
25909
|
-
appliedFiles.push(
|
|
26101
|
+
appliedFiles.push(path31.relative(repositoryPath, actualPath));
|
|
25910
26102
|
logDebug(`[${scanContext}] Created new file: ${relativePath}`);
|
|
25911
26103
|
}
|
|
25912
26104
|
/**
|
|
@@ -25918,7 +26110,7 @@ var PatchApplicationService = class {
|
|
|
25918
26110
|
appliedFiles,
|
|
25919
26111
|
scanContext
|
|
25920
26112
|
}) {
|
|
25921
|
-
if (
|
|
26113
|
+
if (existsSync6(absoluteFilePath)) {
|
|
25922
26114
|
unlinkSync(absoluteFilePath);
|
|
25923
26115
|
appliedFiles.push(relativePath);
|
|
25924
26116
|
logDebug(`[${scanContext}] Deleted file: ${relativePath}`);
|
|
@@ -25937,7 +26129,7 @@ var PatchApplicationService = class {
|
|
|
25937
26129
|
appliedFiles,
|
|
25938
26130
|
scanContext
|
|
25939
26131
|
}) {
|
|
25940
|
-
if (!
|
|
26132
|
+
if (!existsSync6(absoluteFilePath)) {
|
|
25941
26133
|
throw new Error(
|
|
25942
26134
|
`Target file does not exist: ${targetFile} (resolved to: ${absoluteFilePath})`
|
|
25943
26135
|
);
|
|
@@ -25955,7 +26147,7 @@ var PatchApplicationService = class {
|
|
|
25955
26147
|
fix,
|
|
25956
26148
|
scanContext
|
|
25957
26149
|
});
|
|
25958
|
-
appliedFiles.push(
|
|
26150
|
+
appliedFiles.push(path31.relative(repositoryPath, actualPath));
|
|
25959
26151
|
logDebug(`[${scanContext}] Modified file: ${relativePath}`);
|
|
25960
26152
|
}
|
|
25961
26153
|
}
|
|
@@ -26152,8 +26344,8 @@ init_configs();
|
|
|
26152
26344
|
// src/mcp/services/FileOperations.ts
|
|
26153
26345
|
init_FileUtils();
|
|
26154
26346
|
import fs25 from "fs";
|
|
26155
|
-
import
|
|
26156
|
-
import
|
|
26347
|
+
import path32 from "path";
|
|
26348
|
+
import AdmZip5 from "adm-zip";
|
|
26157
26349
|
var FileOperations = class {
|
|
26158
26350
|
/**
|
|
26159
26351
|
* Creates a ZIP archive containing the specified source files
|
|
@@ -26168,14 +26360,14 @@ var FileOperations = class {
|
|
|
26168
26360
|
maxFileSize
|
|
26169
26361
|
}) {
|
|
26170
26362
|
logDebug("[FileOperations] Packing files");
|
|
26171
|
-
const zip = new
|
|
26363
|
+
const zip = new AdmZip5();
|
|
26172
26364
|
let packedFilesCount = 0;
|
|
26173
26365
|
const packedFiles = [];
|
|
26174
26366
|
const excludedFiles = [];
|
|
26175
|
-
const resolvedRepoPath =
|
|
26367
|
+
const resolvedRepoPath = path32.resolve(repositoryPath);
|
|
26176
26368
|
for (const filepath of fileList) {
|
|
26177
|
-
const absoluteFilepath =
|
|
26178
|
-
const resolvedFilePath =
|
|
26369
|
+
const absoluteFilepath = path32.join(repositoryPath, filepath);
|
|
26370
|
+
const resolvedFilePath = path32.resolve(absoluteFilepath);
|
|
26179
26371
|
if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
|
|
26180
26372
|
const reason = "potential path traversal security risk";
|
|
26181
26373
|
logDebug(`[FileOperations] Skipping ${filepath} due to ${reason}`);
|
|
@@ -26222,11 +26414,11 @@ var FileOperations = class {
|
|
|
26222
26414
|
fileList,
|
|
26223
26415
|
repositoryPath
|
|
26224
26416
|
}) {
|
|
26225
|
-
const resolvedRepoPath =
|
|
26417
|
+
const resolvedRepoPath = path32.resolve(repositoryPath);
|
|
26226
26418
|
const validatedPaths = [];
|
|
26227
26419
|
for (const filepath of fileList) {
|
|
26228
|
-
const absoluteFilepath =
|
|
26229
|
-
const resolvedFilePath =
|
|
26420
|
+
const absoluteFilepath = path32.join(repositoryPath, filepath);
|
|
26421
|
+
const resolvedFilePath = path32.resolve(absoluteFilepath);
|
|
26230
26422
|
if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
|
|
26231
26423
|
logDebug(
|
|
26232
26424
|
`[FileOperations] Rejecting ${filepath} - path traversal attempt detected`
|
|
@@ -26254,7 +26446,7 @@ var FileOperations = class {
|
|
|
26254
26446
|
for (const absolutePath of filePaths) {
|
|
26255
26447
|
try {
|
|
26256
26448
|
const content = await fs25.promises.readFile(absolutePath);
|
|
26257
|
-
const relativePath =
|
|
26449
|
+
const relativePath = path32.basename(absolutePath);
|
|
26258
26450
|
fileDataArray.push({
|
|
26259
26451
|
relativePath,
|
|
26260
26452
|
absolutePath,
|
|
@@ -26566,14 +26758,14 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
26566
26758
|
* since the last scan.
|
|
26567
26759
|
*/
|
|
26568
26760
|
async scanForSecurityVulnerabilities({
|
|
26569
|
-
path:
|
|
26761
|
+
path: path36,
|
|
26570
26762
|
isAllDetectionRulesScan,
|
|
26571
26763
|
isAllFilesScan,
|
|
26572
26764
|
scanContext
|
|
26573
26765
|
}) {
|
|
26574
26766
|
this.hasAuthenticationFailed = false;
|
|
26575
26767
|
logDebug(`[${scanContext}] Scanning for new security vulnerabilities`, {
|
|
26576
|
-
path:
|
|
26768
|
+
path: path36
|
|
26577
26769
|
});
|
|
26578
26770
|
if (!this.gqlClient) {
|
|
26579
26771
|
logInfo(`[${scanContext}] No GQL client found, skipping scan`);
|
|
@@ -26589,11 +26781,11 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
26589
26781
|
}
|
|
26590
26782
|
logDebug(
|
|
26591
26783
|
`[${scanContext}] Connected to the API, assembling list of files to scan`,
|
|
26592
|
-
{ path:
|
|
26784
|
+
{ path: path36 }
|
|
26593
26785
|
);
|
|
26594
26786
|
const isBackgroundScan = scanContext === ScanContext.BACKGROUND_INITIAL || scanContext === ScanContext.BACKGROUND_PERIODIC;
|
|
26595
26787
|
const files = await getLocalFiles({
|
|
26596
|
-
path:
|
|
26788
|
+
path: path36,
|
|
26597
26789
|
isAllFilesScan,
|
|
26598
26790
|
scanContext,
|
|
26599
26791
|
scanRecentlyChangedFiles: !isBackgroundScan
|
|
@@ -26619,13 +26811,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
26619
26811
|
});
|
|
26620
26812
|
const { fixReportId, projectId } = await scanFiles({
|
|
26621
26813
|
fileList: filesToScan.map((file) => file.relativePath),
|
|
26622
|
-
repositoryPath:
|
|
26814
|
+
repositoryPath: path36,
|
|
26623
26815
|
gqlClient: this.gqlClient,
|
|
26624
26816
|
isAllDetectionRulesScan,
|
|
26625
26817
|
scanContext
|
|
26626
26818
|
});
|
|
26627
26819
|
logInfo(
|
|
26628
|
-
`[${scanContext}] Security scan completed for ${
|
|
26820
|
+
`[${scanContext}] Security scan completed for ${path36} reportId: ${fixReportId} projectId: ${projectId}`
|
|
26629
26821
|
);
|
|
26630
26822
|
if (isAllFilesScan) {
|
|
26631
26823
|
return;
|
|
@@ -26919,13 +27111,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
26919
27111
|
});
|
|
26920
27112
|
return scannedFiles.some((file) => file.relativePath === fixFile);
|
|
26921
27113
|
}
|
|
26922
|
-
async getFreshFixes({ path:
|
|
27114
|
+
async getFreshFixes({ path: path36 }) {
|
|
26923
27115
|
const scanContext = ScanContext.USER_REQUEST;
|
|
26924
|
-
logDebug(`[${scanContext}] Getting fresh fixes`, { path:
|
|
26925
|
-
if (this.path !==
|
|
26926
|
-
this.path =
|
|
27116
|
+
logDebug(`[${scanContext}] Getting fresh fixes`, { path: path36 });
|
|
27117
|
+
if (this.path !== path36) {
|
|
27118
|
+
this.path = path36;
|
|
26927
27119
|
this.reset();
|
|
26928
|
-
logInfo(`[${scanContext}] Reset service state for new path`, { path:
|
|
27120
|
+
logInfo(`[${scanContext}] Reset service state for new path`, { path: path36 });
|
|
26929
27121
|
}
|
|
26930
27122
|
try {
|
|
26931
27123
|
const loginContext = createMcpLoginContext("check_new_fixes");
|
|
@@ -26944,7 +27136,7 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
26944
27136
|
}
|
|
26945
27137
|
throw error;
|
|
26946
27138
|
}
|
|
26947
|
-
this.triggerScan({ path:
|
|
27139
|
+
this.triggerScan({ path: path36, gqlClient: this.gqlClient });
|
|
26948
27140
|
let isMvsAutoFixEnabled = null;
|
|
26949
27141
|
try {
|
|
26950
27142
|
isMvsAutoFixEnabled = await this.gqlClient.getMvsAutoFixSettings();
|
|
@@ -26978,33 +27170,33 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
26978
27170
|
return noFreshFixesPrompt;
|
|
26979
27171
|
}
|
|
26980
27172
|
triggerScan({
|
|
26981
|
-
path:
|
|
27173
|
+
path: path36,
|
|
26982
27174
|
gqlClient
|
|
26983
27175
|
}) {
|
|
26984
|
-
if (this.path !==
|
|
26985
|
-
this.path =
|
|
27176
|
+
if (this.path !== path36) {
|
|
27177
|
+
this.path = path36;
|
|
26986
27178
|
this.reset();
|
|
26987
|
-
logInfo(`Reset service state for new path in triggerScan`, { path:
|
|
27179
|
+
logInfo(`Reset service state for new path in triggerScan`, { path: path36 });
|
|
26988
27180
|
}
|
|
26989
27181
|
this.gqlClient = gqlClient;
|
|
26990
27182
|
if (!this.intervalId) {
|
|
26991
|
-
this.startPeriodicScanning(
|
|
26992
|
-
this.executeInitialScan(
|
|
26993
|
-
void this.executeInitialFullScan(
|
|
27183
|
+
this.startPeriodicScanning(path36);
|
|
27184
|
+
this.executeInitialScan(path36);
|
|
27185
|
+
void this.executeInitialFullScan(path36);
|
|
26994
27186
|
}
|
|
26995
27187
|
}
|
|
26996
|
-
startPeriodicScanning(
|
|
27188
|
+
startPeriodicScanning(path36) {
|
|
26997
27189
|
const scanContext = ScanContext.BACKGROUND_PERIODIC;
|
|
26998
27190
|
logDebug(
|
|
26999
27191
|
`[${scanContext}] Starting periodic scan for new security vulnerabilities`,
|
|
27000
27192
|
{
|
|
27001
|
-
path:
|
|
27193
|
+
path: path36
|
|
27002
27194
|
}
|
|
27003
27195
|
);
|
|
27004
27196
|
this.intervalId = setInterval(() => {
|
|
27005
|
-
logDebug(`[${scanContext}] Triggering periodic security scan`, { path:
|
|
27197
|
+
logDebug(`[${scanContext}] Triggering periodic security scan`, { path: path36 });
|
|
27006
27198
|
this.scanForSecurityVulnerabilities({
|
|
27007
|
-
path:
|
|
27199
|
+
path: path36,
|
|
27008
27200
|
scanContext
|
|
27009
27201
|
}).catch((error) => {
|
|
27010
27202
|
logError(`[${scanContext}] Error during periodic security scan`, {
|
|
@@ -27013,45 +27205,45 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
27013
27205
|
});
|
|
27014
27206
|
}, MCP_PERIODIC_CHECK_INTERVAL);
|
|
27015
27207
|
}
|
|
27016
|
-
async executeInitialFullScan(
|
|
27208
|
+
async executeInitialFullScan(path36) {
|
|
27017
27209
|
const scanContext = ScanContext.FULL_SCAN;
|
|
27018
|
-
logDebug(`[${scanContext}] Triggering initial full security scan`, { path:
|
|
27210
|
+
logDebug(`[${scanContext}] Triggering initial full security scan`, { path: path36 });
|
|
27019
27211
|
logDebug(`[${scanContext}] Full scan paths scanned`, {
|
|
27020
27212
|
fullScanPathsScanned: this.fullScanPathsScanned
|
|
27021
27213
|
});
|
|
27022
|
-
if (this.fullScanPathsScanned.includes(
|
|
27214
|
+
if (this.fullScanPathsScanned.includes(path36)) {
|
|
27023
27215
|
logDebug(`[${scanContext}] Full scan already executed for this path`, {
|
|
27024
|
-
path:
|
|
27216
|
+
path: path36
|
|
27025
27217
|
});
|
|
27026
27218
|
return;
|
|
27027
27219
|
}
|
|
27028
27220
|
configStore.set("fullScanPathsScanned", [
|
|
27029
27221
|
...this.fullScanPathsScanned,
|
|
27030
|
-
|
|
27222
|
+
path36
|
|
27031
27223
|
]);
|
|
27032
27224
|
try {
|
|
27033
27225
|
await this.scanForSecurityVulnerabilities({
|
|
27034
|
-
path:
|
|
27226
|
+
path: path36,
|
|
27035
27227
|
isAllFilesScan: true,
|
|
27036
27228
|
isAllDetectionRulesScan: true,
|
|
27037
27229
|
scanContext: ScanContext.FULL_SCAN
|
|
27038
27230
|
});
|
|
27039
|
-
if (!this.fullScanPathsScanned.includes(
|
|
27040
|
-
this.fullScanPathsScanned.push(
|
|
27231
|
+
if (!this.fullScanPathsScanned.includes(path36)) {
|
|
27232
|
+
this.fullScanPathsScanned.push(path36);
|
|
27041
27233
|
configStore.set("fullScanPathsScanned", this.fullScanPathsScanned);
|
|
27042
27234
|
}
|
|
27043
|
-
logInfo(`[${scanContext}] Full scan completed`, { path:
|
|
27235
|
+
logInfo(`[${scanContext}] Full scan completed`, { path: path36 });
|
|
27044
27236
|
} catch (error) {
|
|
27045
27237
|
logError(`[${scanContext}] Error during initial full security scan`, {
|
|
27046
27238
|
error
|
|
27047
27239
|
});
|
|
27048
27240
|
}
|
|
27049
27241
|
}
|
|
27050
|
-
executeInitialScan(
|
|
27242
|
+
executeInitialScan(path36) {
|
|
27051
27243
|
const scanContext = ScanContext.BACKGROUND_INITIAL;
|
|
27052
|
-
logDebug(`[${scanContext}] Triggering initial security scan`, { path:
|
|
27244
|
+
logDebug(`[${scanContext}] Triggering initial security scan`, { path: path36 });
|
|
27053
27245
|
this.scanForSecurityVulnerabilities({
|
|
27054
|
-
path:
|
|
27246
|
+
path: path36,
|
|
27055
27247
|
scanContext: ScanContext.BACKGROUND_INITIAL
|
|
27056
27248
|
}).catch((error) => {
|
|
27057
27249
|
logError(`[${scanContext}] Error during initial security scan`, { error });
|
|
@@ -27148,9 +27340,9 @@ Example payload:
|
|
|
27148
27340
|
`Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
|
|
27149
27341
|
);
|
|
27150
27342
|
}
|
|
27151
|
-
const
|
|
27343
|
+
const path36 = pathValidationResult.path;
|
|
27152
27344
|
const resultText = await this.newFixesService.getFreshFixes({
|
|
27153
|
-
path:
|
|
27345
|
+
path: path36
|
|
27154
27346
|
});
|
|
27155
27347
|
logInfo("CheckForNewAvailableFixesTool execution completed", {
|
|
27156
27348
|
resultText
|
|
@@ -27328,8 +27520,8 @@ Call this tool instead of ${MCP_TOOL_SCAN_AND_FIX_VULNERABILITIES} when you only
|
|
|
27328
27520
|
`Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
|
|
27329
27521
|
);
|
|
27330
27522
|
}
|
|
27331
|
-
const
|
|
27332
|
-
const gitService = new GitService(
|
|
27523
|
+
const path36 = pathValidationResult.path;
|
|
27524
|
+
const gitService = new GitService(path36, log);
|
|
27333
27525
|
const gitValidation = await gitService.validateRepository();
|
|
27334
27526
|
if (!gitValidation.isValid) {
|
|
27335
27527
|
throw new Error(`Invalid git repository: ${gitValidation.error}`);
|
|
@@ -27714,9 +27906,9 @@ Example payload:
|
|
|
27714
27906
|
`Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
|
|
27715
27907
|
);
|
|
27716
27908
|
}
|
|
27717
|
-
const
|
|
27909
|
+
const path36 = pathValidationResult.path;
|
|
27718
27910
|
const files = await getLocalFiles({
|
|
27719
|
-
path:
|
|
27911
|
+
path: path36,
|
|
27720
27912
|
maxFileSize: MCP_MAX_FILE_SIZE,
|
|
27721
27913
|
maxFiles: args.maxFiles,
|
|
27722
27914
|
scanContext: ScanContext.USER_REQUEST,
|
|
@@ -27736,7 +27928,7 @@ Example payload:
|
|
|
27736
27928
|
try {
|
|
27737
27929
|
const fixResult = await this.vulnerabilityFixService.processVulnerabilities({
|
|
27738
27930
|
fileList: files.map((file) => file.relativePath),
|
|
27739
|
-
repositoryPath:
|
|
27931
|
+
repositoryPath: path36,
|
|
27740
27932
|
offset: args.offset,
|
|
27741
27933
|
limit: args.limit,
|
|
27742
27934
|
isRescan: args.rescan || !!args.maxFiles
|
|
@@ -28037,10 +28229,10 @@ init_client_generates();
|
|
|
28037
28229
|
init_urlParser2();
|
|
28038
28230
|
|
|
28039
28231
|
// src/features/codeium_intellij/codeium_language_server_grpc_client.ts
|
|
28040
|
-
import
|
|
28232
|
+
import path33 from "path";
|
|
28041
28233
|
import * as grpc from "@grpc/grpc-js";
|
|
28042
28234
|
import * as protoLoader from "@grpc/proto-loader";
|
|
28043
|
-
var PROTO_PATH =
|
|
28235
|
+
var PROTO_PATH = path33.join(
|
|
28044
28236
|
getModuleRootDir(),
|
|
28045
28237
|
"src/features/codeium_intellij/proto/exa/language_server_pb/language_server.proto"
|
|
28046
28238
|
);
|
|
@@ -28052,7 +28244,7 @@ function loadProto() {
|
|
|
28052
28244
|
defaults: true,
|
|
28053
28245
|
oneofs: true,
|
|
28054
28246
|
includeDirs: [
|
|
28055
|
-
|
|
28247
|
+
path33.join(getModuleRootDir(), "src/features/codeium_intellij/proto")
|
|
28056
28248
|
]
|
|
28057
28249
|
});
|
|
28058
28250
|
return grpc.loadPackageDefinition(
|
|
@@ -28108,28 +28300,28 @@ async function getGrpcClient(port, csrf3) {
|
|
|
28108
28300
|
// src/features/codeium_intellij/parse_intellij_logs.ts
|
|
28109
28301
|
import fs27 from "fs";
|
|
28110
28302
|
import os14 from "os";
|
|
28111
|
-
import
|
|
28303
|
+
import path34 from "path";
|
|
28112
28304
|
function getLogsDir() {
|
|
28113
28305
|
if (process.platform === "darwin") {
|
|
28114
|
-
return
|
|
28306
|
+
return path34.join(os14.homedir(), "Library/Logs/JetBrains");
|
|
28115
28307
|
} else if (process.platform === "win32") {
|
|
28116
|
-
return
|
|
28117
|
-
process.env["LOCALAPPDATA"] ||
|
|
28308
|
+
return path34.join(
|
|
28309
|
+
process.env["LOCALAPPDATA"] || path34.join(os14.homedir(), "AppData/Local"),
|
|
28118
28310
|
"JetBrains"
|
|
28119
28311
|
);
|
|
28120
28312
|
} else {
|
|
28121
|
-
return
|
|
28313
|
+
return path34.join(os14.homedir(), ".cache/JetBrains");
|
|
28122
28314
|
}
|
|
28123
28315
|
}
|
|
28124
28316
|
function parseIdeLogDir(ideLogDir) {
|
|
28125
28317
|
const logFiles = fs27.readdirSync(ideLogDir).filter((f) => /^idea(\.\d+)?\.log$/.test(f)).map((f) => ({
|
|
28126
28318
|
name: f,
|
|
28127
|
-
mtime: fs27.statSync(
|
|
28319
|
+
mtime: fs27.statSync(path34.join(ideLogDir, f)).mtimeMs
|
|
28128
28320
|
})).sort((a, b) => a.mtime - b.mtime).map((f) => f.name);
|
|
28129
28321
|
let latestCsrf = null;
|
|
28130
28322
|
let latestPort = null;
|
|
28131
28323
|
for (const logFile of logFiles) {
|
|
28132
|
-
const lines = fs27.readFileSync(
|
|
28324
|
+
const lines = fs27.readFileSync(path34.join(ideLogDir, logFile), "utf-8").split("\n");
|
|
28133
28325
|
for (const line of lines) {
|
|
28134
28326
|
if (!line.includes(
|
|
28135
28327
|
"com.codeium.intellij.language_server.LanguageServerProcessHandler"
|
|
@@ -28157,9 +28349,9 @@ function findRunningCodeiumLanguageServers() {
|
|
|
28157
28349
|
const logsDir = getLogsDir();
|
|
28158
28350
|
if (!fs27.existsSync(logsDir)) return results;
|
|
28159
28351
|
for (const ide of fs27.readdirSync(logsDir)) {
|
|
28160
|
-
let ideLogDir =
|
|
28352
|
+
let ideLogDir = path34.join(logsDir, ide);
|
|
28161
28353
|
if (process.platform !== "darwin") {
|
|
28162
|
-
ideLogDir =
|
|
28354
|
+
ideLogDir = path34.join(ideLogDir, "log");
|
|
28163
28355
|
}
|
|
28164
28356
|
if (!fs27.existsSync(ideLogDir) || !fs27.statSync(ideLogDir).isDirectory()) {
|
|
28165
28357
|
continue;
|
|
@@ -28342,10 +28534,10 @@ function processChatStepCodeAction(step) {
|
|
|
28342
28534
|
// src/features/codeium_intellij/install_hook.ts
|
|
28343
28535
|
import fsPromises5 from "fs/promises";
|
|
28344
28536
|
import os15 from "os";
|
|
28345
|
-
import
|
|
28537
|
+
import path35 from "path";
|
|
28346
28538
|
import chalk14 from "chalk";
|
|
28347
28539
|
function getCodeiumHooksPath() {
|
|
28348
|
-
return
|
|
28540
|
+
return path35.join(os15.homedir(), ".codeium", "hooks.json");
|
|
28349
28541
|
}
|
|
28350
28542
|
async function readCodeiumHooks() {
|
|
28351
28543
|
const hooksPath = getCodeiumHooksPath();
|
|
@@ -28358,7 +28550,7 @@ async function readCodeiumHooks() {
|
|
|
28358
28550
|
}
|
|
28359
28551
|
async function writeCodeiumHooks(config2) {
|
|
28360
28552
|
const hooksPath = getCodeiumHooksPath();
|
|
28361
|
-
const dir =
|
|
28553
|
+
const dir = path35.dirname(hooksPath);
|
|
28362
28554
|
await fsPromises5.mkdir(dir, { recursive: true });
|
|
28363
28555
|
await fsPromises5.writeFile(
|
|
28364
28556
|
hooksPath,
|