mobbdev 1.4.47 → 1.4.49
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.mjs +32 -3
- package/dist/index.mjs +512 -341
- package/package.json +7 -3
package/dist/index.mjs
CHANGED
|
@@ -150,7 +150,7 @@ function getSdk(client, withWrapper = defaultWrapper) {
|
|
|
150
150
|
}
|
|
151
151
|
};
|
|
152
152
|
}
|
|
153
|
-
var AiBlameInferenceType, FixQuestionInputType, Language, ManifestAction, Effort_To_Apply_Fix_Enum, Fix_Rating_Tag_Enum, Fix_Report_State_Enum, Fix_State_Enum, IssueLanguage_Enum, Pr_Status_Enum, Project_Role_Type_Enum, Vulnerability_Report_Issue_Category_Enum, Vulnerability_Report_Issue_State_Enum, Vulnerability_Report_Issue_Tag_Enum, Vulnerability_Report_Vendor_Enum, Vulnerability_Severity_Enum, FixDetailsFragmentDoc, FixReportSummaryFieldsFragmentDoc, MeDocument, GetLastOrgAndNamedProjectDocument, GetLastOrgDocument, GetEncryptedApiTokenDocument, FixReportStateDocument, GetVulnerabilityReportPathsDocument, GetAnalysisSubscriptionDocument, GetAnalysisDocument, GetFixesDocument, GetVulByNodesMetadataDocument, GetFalsePositiveDocument, UpdateScmTokenDocument, UploadS3BucketInfoDocument, GetTracyDiffUploadUrlDocument, AnalyzeCommitForExtensionAiBlameDocument, GetAiBlameInferenceDocument, GetAiBlameAttributionPromptDocument, GetPromptSummaryDocument, UploadAiBlameInferencesInitDocument, FinalizeAiBlameInferencesUploadDocument, UploadTracyRecordsDocument, GetTracyRawDataUploadUrlDocument, DigestVulnerabilityReportDocument, SubmitVulnerabilityReportDocument, CreateCommunityUserDocument, CreateCliLoginDocument, PerformCliLoginDocument, SetQuarantineEnabledDocument, CreateProjectDocument, ValidateRepoUrlDocument, GitReferenceDocument, AutoPrAnalysisDocument, GetFixWithAnswersDocument, GetFixReportsByRepoUrlDocument, GetReportFixesDocument, GetLatestReportByRepoUrlDocument, UpdateDownloadedFixDataDocument, GetUserMvsAutoFixDocument, StreamBlameAiAnalysisRequestsDocument, StreamCommitBlameRequestsDocument, ScanSkillDocument, SkillVerdictsByMd5Document, LogMvsEventDocument, GetMvsProjectDocument, IssueTypesDocument, defaultWrapper;
|
|
153
|
+
var AiBlameInferenceType, FixQuestionInputType, Language, ManifestAction, Effort_To_Apply_Fix_Enum, Fix_Rating_Tag_Enum, Fix_Report_State_Enum, Fix_State_Enum, IssueLanguage_Enum, Pr_Status_Enum, Project_Role_Type_Enum, Scan_Source_Enum, Vulnerability_Report_Issue_Category_Enum, Vulnerability_Report_Issue_State_Enum, Vulnerability_Report_Issue_Tag_Enum, Vulnerability_Report_Vendor_Enum, Vulnerability_Severity_Enum, FixDetailsFragmentDoc, FixReportSummaryFieldsFragmentDoc, MeDocument, GetLastOrgAndNamedProjectDocument, GetLastOrgDocument, GetEncryptedApiTokenDocument, FixReportStateDocument, GetVulnerabilityReportPathsDocument, GetAnalysisSubscriptionDocument, GetAnalysisDocument, GetFixesDocument, GetVulByNodesMetadataDocument, GetFalsePositiveDocument, UpdateScmTokenDocument, UploadS3BucketInfoDocument, GetTracyDiffUploadUrlDocument, AnalyzeCommitForExtensionAiBlameDocument, GetAiBlameInferenceDocument, GetAiBlameAttributionPromptDocument, GetPromptSummaryDocument, UploadAiBlameInferencesInitDocument, FinalizeAiBlameInferencesUploadDocument, UploadTracyRecordsDocument, GetTracyRawDataUploadUrlDocument, DigestVulnerabilityReportDocument, SubmitVulnerabilityReportDocument, CreateCommunityUserDocument, CreateCliLoginDocument, PerformCliLoginDocument, SetQuarantineEnabledDocument, CreateProjectDocument, ValidateRepoUrlDocument, GitReferenceDocument, AutoPrAnalysisDocument, GetFixWithAnswersDocument, GetFixReportsByRepoUrlDocument, GetReportFixesDocument, GetLatestReportByRepoUrlDocument, UpdateDownloadedFixDataDocument, GetUserMvsAutoFixDocument, StreamBlameAiAnalysisRequestsDocument, StreamCommitBlameRequestsDocument, ScanSkillDocument, SkillVerdictsByMd5Document, LogMvsEventDocument, GetMvsProjectDocument, IssueTypesDocument, defaultWrapper;
|
|
154
154
|
var init_client_generates = __esm({
|
|
155
155
|
"src/features/analysis/scm/generates/client_generates.ts"() {
|
|
156
156
|
"use strict";
|
|
@@ -250,6 +250,25 @@ var init_client_generates = __esm({
|
|
|
250
250
|
Project_Role_Type_Enum2["Writer"] = "writer";
|
|
251
251
|
return Project_Role_Type_Enum2;
|
|
252
252
|
})(Project_Role_Type_Enum || {});
|
|
253
|
+
Scan_Source_Enum = /* @__PURE__ */ ((Scan_Source_Enum2) => {
|
|
254
|
+
Scan_Source_Enum2["Aspm"] = "ASPM";
|
|
255
|
+
Scan_Source_Enum2["AutoFixer"] = "AUTO_FIXER";
|
|
256
|
+
Scan_Source_Enum2["CiAzure"] = "CI_AZURE";
|
|
257
|
+
Scan_Source_Enum2["CiBamboo"] = "CI_BAMBOO";
|
|
258
|
+
Scan_Source_Enum2["CiCircleci"] = "CI_CIRCLECI";
|
|
259
|
+
Scan_Source_Enum2["CiGithub"] = "CI_GITHUB";
|
|
260
|
+
Scan_Source_Enum2["CiGitlab"] = "CI_GITLAB";
|
|
261
|
+
Scan_Source_Enum2["CiJenkins"] = "CI_JENKINS";
|
|
262
|
+
Scan_Source_Enum2["CiUnknown"] = "CI_UNKNOWN";
|
|
263
|
+
Scan_Source_Enum2["Cli"] = "CLI";
|
|
264
|
+
Scan_Source_Enum2["Mcp"] = "MCP";
|
|
265
|
+
Scan_Source_Enum2["WebUiCheckmarxIntegration"] = "WEB_UI_CHECKMARX_INTEGRATION";
|
|
266
|
+
Scan_Source_Enum2["WebUiFixOwnCode"] = "WEB_UI_FIX_OWN_CODE";
|
|
267
|
+
Scan_Source_Enum2["WebUiOnboarding"] = "WEB_UI_ONBOARDING";
|
|
268
|
+
Scan_Source_Enum2["WebUiRerunAnalysis"] = "WEB_UI_RERUN_ANALYSIS";
|
|
269
|
+
Scan_Source_Enum2["WebUiTryNow"] = "WEB_UI_TRY_NOW";
|
|
270
|
+
return Scan_Source_Enum2;
|
|
271
|
+
})(Scan_Source_Enum || {});
|
|
253
272
|
Vulnerability_Report_Issue_Category_Enum = /* @__PURE__ */ ((Vulnerability_Report_Issue_Category_Enum2) => {
|
|
254
273
|
Vulnerability_Report_Issue_Category_Enum2["FalsePositive"] = "FalsePositive";
|
|
255
274
|
Vulnerability_Report_Issue_Category_Enum2["Filtered"] = "Filtered";
|
|
@@ -481,6 +500,7 @@ var init_client_generates = __esm({
|
|
|
481
500
|
organization {
|
|
482
501
|
id
|
|
483
502
|
enableV2Fixes
|
|
503
|
+
isFixEnabled
|
|
484
504
|
projects(where: {name: {_eq: $projectName}}) {
|
|
485
505
|
name
|
|
486
506
|
id
|
|
@@ -500,6 +520,7 @@ var init_client_generates = __esm({
|
|
|
500
520
|
organization {
|
|
501
521
|
id
|
|
502
522
|
enableV2Fixes
|
|
523
|
+
isFixEnabled
|
|
503
524
|
}
|
|
504
525
|
}
|
|
505
526
|
}
|
|
@@ -1940,6 +1961,8 @@ var init_types = __esm({
|
|
|
1940
1961
|
reportSummaryUrl: z7.url().nullish(),
|
|
1941
1962
|
computedVendor: z7.enum(Vulnerability_Report_Vendor_Enum).nullable(),
|
|
1942
1963
|
vendor: z7.enum(Vulnerability_Report_Vendor_Enum).nullable(),
|
|
1964
|
+
// Lets the waiting screen distinguish a first scan from a rescan.
|
|
1965
|
+
scanSource: z7.enum(Scan_Source_Enum).nullable(),
|
|
1943
1966
|
issuesWithKnownLanguage: z7.number().nullable(),
|
|
1944
1967
|
scanDate: z7.string().nullable(),
|
|
1945
1968
|
vendorReportId: z7.guid().nullable(),
|
|
@@ -5204,7 +5227,7 @@ async function getAdoSdk(params) {
|
|
|
5204
5227
|
const url = new URL(repoUrl);
|
|
5205
5228
|
const origin = url.origin.toLowerCase().endsWith(".visualstudio.com") ? DEFUALT_ADO_ORIGIN : url.origin.toLowerCase();
|
|
5206
5229
|
const params2 = `path=/&versionDescriptor[versionOptions]=0&versionDescriptor[versionType]=commit&versionDescriptor[version]=${branch}&resolveLfs=true&$format=zip&api-version=5.0&download=true`;
|
|
5207
|
-
const
|
|
5230
|
+
const path38 = [
|
|
5208
5231
|
prefixPath,
|
|
5209
5232
|
owner,
|
|
5210
5233
|
projectName,
|
|
@@ -5215,7 +5238,7 @@ async function getAdoSdk(params) {
|
|
|
5215
5238
|
"items",
|
|
5216
5239
|
"items"
|
|
5217
5240
|
].filter(Boolean).join("/");
|
|
5218
|
-
return new URL(`${
|
|
5241
|
+
return new URL(`${path38}?${params2}`, origin).toString();
|
|
5219
5242
|
},
|
|
5220
5243
|
async getAdoBranchList({ repoUrl }) {
|
|
5221
5244
|
try {
|
|
@@ -5304,8 +5327,8 @@ async function getAdoSdk(params) {
|
|
|
5304
5327
|
const changeType = entry.changeType;
|
|
5305
5328
|
return changeType !== 16 && entry.item?.path;
|
|
5306
5329
|
}).map((entry) => {
|
|
5307
|
-
const
|
|
5308
|
-
return
|
|
5330
|
+
const path38 = entry.item.path;
|
|
5331
|
+
return path38.startsWith("/") ? path38.slice(1) : path38;
|
|
5309
5332
|
});
|
|
5310
5333
|
},
|
|
5311
5334
|
async searchAdoPullRequests({
|
|
@@ -6242,7 +6265,6 @@ var AdoSCMLib = class extends SCMLib {
|
|
|
6242
6265
|
// src/features/analysis/scm/bitbucket/bitbucket.ts
|
|
6243
6266
|
import querystring2 from "querystring";
|
|
6244
6267
|
import * as bitbucketPkgNode from "bitbucket";
|
|
6245
|
-
import bitbucketPkg from "bitbucket";
|
|
6246
6268
|
import Debug2 from "debug";
|
|
6247
6269
|
import { z as z16 } from "zod";
|
|
6248
6270
|
|
|
@@ -6255,6 +6277,7 @@ var BitbucketAuthResultZ = z15.object({
|
|
|
6255
6277
|
});
|
|
6256
6278
|
|
|
6257
6279
|
// src/features/analysis/scm/bitbucket/bitbucket.ts
|
|
6280
|
+
var bitbucketPkg = bitbucketPkgNode.default;
|
|
6258
6281
|
var debug3 = Debug2("scm:bitbucket");
|
|
6259
6282
|
function isBitbucketPermissionDenied(e) {
|
|
6260
6283
|
const status = e?.status;
|
|
@@ -10394,6 +10417,7 @@ __export(utils_exports, {
|
|
|
10394
10417
|
getDirName: () => getDirName,
|
|
10395
10418
|
getModuleRootDir: () => getModuleRootDir,
|
|
10396
10419
|
getTopLevelDirName: () => getTopLevelDirName,
|
|
10420
|
+
isStandaloneBinary: () => isStandaloneBinary,
|
|
10397
10421
|
keypress: () => keypress,
|
|
10398
10422
|
packageJson: () => packageJson,
|
|
10399
10423
|
sleep: () => sleep
|
|
@@ -10404,6 +10428,10 @@ import fs3 from "fs";
|
|
|
10404
10428
|
import path3 from "path";
|
|
10405
10429
|
import { fileURLToPath } from "url";
|
|
10406
10430
|
function getModuleRootDir() {
|
|
10431
|
+
const seaRoot = process.env["MOBBDEV_MODULE_ROOT"];
|
|
10432
|
+
if (seaRoot) {
|
|
10433
|
+
return seaRoot;
|
|
10434
|
+
}
|
|
10407
10435
|
let manifestDir = getDirName();
|
|
10408
10436
|
for (let i = 0; i < 10; i++) {
|
|
10409
10437
|
const manifestPath = path3.join(manifestDir, "package.json");
|
|
@@ -10500,6 +10528,16 @@ function Spinner({ ci = false } = {}) {
|
|
|
10500
10528
|
};
|
|
10501
10529
|
}
|
|
10502
10530
|
|
|
10531
|
+
// src/utils/standalone_binary.ts
|
|
10532
|
+
function isStandaloneBinary() {
|
|
10533
|
+
try {
|
|
10534
|
+
const getBuiltin = process.getBuiltinModule;
|
|
10535
|
+
return Boolean(getBuiltin?.("node:sea")?.isSea());
|
|
10536
|
+
} catch {
|
|
10537
|
+
return false;
|
|
10538
|
+
}
|
|
10539
|
+
}
|
|
10540
|
+
|
|
10503
10541
|
// src/utils/check_node_version.ts
|
|
10504
10542
|
import fs4 from "fs";
|
|
10505
10543
|
import path4 from "path";
|
|
@@ -11283,7 +11321,7 @@ var ScanContext = {
|
|
|
11283
11321
|
};
|
|
11284
11322
|
|
|
11285
11323
|
// src/args/commands/analyze.ts
|
|
11286
|
-
import
|
|
11324
|
+
import fs13 from "fs";
|
|
11287
11325
|
import chalk10 from "chalk";
|
|
11288
11326
|
|
|
11289
11327
|
// src/commands/index.ts
|
|
@@ -11291,11 +11329,11 @@ import chalk8 from "chalk";
|
|
|
11291
11329
|
import chalkAnimation from "chalk-animation";
|
|
11292
11330
|
|
|
11293
11331
|
// src/features/analysis/index.ts
|
|
11294
|
-
import
|
|
11332
|
+
import fs11 from "fs";
|
|
11295
11333
|
import fsPromises3 from "fs/promises";
|
|
11296
|
-
import
|
|
11334
|
+
import path11 from "path";
|
|
11297
11335
|
import { env as env2 } from "process";
|
|
11298
|
-
import { pipeline } from "stream/promises";
|
|
11336
|
+
import { pipeline as pipeline2 } from "stream/promises";
|
|
11299
11337
|
import chalk7 from "chalk";
|
|
11300
11338
|
import Debug22 from "debug";
|
|
11301
11339
|
import extract from "extract-zip";
|
|
@@ -11847,7 +11885,10 @@ var GQLClient = class {
|
|
|
11847
11885
|
return {
|
|
11848
11886
|
organizationId: organization.id,
|
|
11849
11887
|
projectId,
|
|
11850
|
-
enableV2Fixes: organization.enableV2Fixes === true
|
|
11888
|
+
enableV2Fixes: organization.enableV2Fixes === true,
|
|
11889
|
+
// NULL means "unset" and keeps the fix-enabled behaviour; only an
|
|
11890
|
+
// explicit false puts the org in SAST-only mode.
|
|
11891
|
+
isFixEnabled: organization.isFixEnabled !== false
|
|
11851
11892
|
};
|
|
11852
11893
|
}
|
|
11853
11894
|
async getEncryptedApiToken(variables) {
|
|
@@ -13993,7 +14034,7 @@ async function postIssueComment(params) {
|
|
|
13993
14034
|
fpDescription
|
|
13994
14035
|
} = params;
|
|
13995
14036
|
const {
|
|
13996
|
-
path:
|
|
14037
|
+
path: path38,
|
|
13997
14038
|
startLine,
|
|
13998
14039
|
vulnerabilityReportIssue: {
|
|
13999
14040
|
vulnerabilityReportIssueTags,
|
|
@@ -14008,7 +14049,7 @@ async function postIssueComment(params) {
|
|
|
14008
14049
|
Refresh the page in order to see the changes.`,
|
|
14009
14050
|
pull_number: pullRequest,
|
|
14010
14051
|
commit_id: commitSha,
|
|
14011
|
-
path:
|
|
14052
|
+
path: path38,
|
|
14012
14053
|
line: startLine
|
|
14013
14054
|
});
|
|
14014
14055
|
const commentId = commentRes.data.id;
|
|
@@ -14042,7 +14083,7 @@ async function postFixComment(params) {
|
|
|
14042
14083
|
scanner
|
|
14043
14084
|
} = params;
|
|
14044
14085
|
const {
|
|
14045
|
-
path:
|
|
14086
|
+
path: path38,
|
|
14046
14087
|
startLine,
|
|
14047
14088
|
vulnerabilityReportIssue: { fixId, vulnerabilityReportIssueTags, category },
|
|
14048
14089
|
vulnerabilityReportIssueId
|
|
@@ -14060,7 +14101,7 @@ async function postFixComment(params) {
|
|
|
14060
14101
|
Refresh the page in order to see the changes.`,
|
|
14061
14102
|
pull_number: pullRequest,
|
|
14062
14103
|
commit_id: commitSha,
|
|
14063
|
-
path:
|
|
14104
|
+
path: path38,
|
|
14064
14105
|
line: startLine
|
|
14065
14106
|
});
|
|
14066
14107
|
const commentId = commentRes.data.id;
|
|
@@ -14610,7 +14651,7 @@ function createFork({ args, processPath, name }, options) {
|
|
|
14610
14651
|
}
|
|
14611
14652
|
function createSpawn({ args, processPath, name, cwd, env: env3 }, options) {
|
|
14612
14653
|
const child = cp.spawn(processPath, args, {
|
|
14613
|
-
stdio: ["inherit", "pipe", "pipe"
|
|
14654
|
+
stdio: ["inherit", "pipe", "pipe"],
|
|
14614
14655
|
env: { ...process2.env, ...env3 },
|
|
14615
14656
|
cwd
|
|
14616
14657
|
});
|
|
@@ -14668,11 +14709,16 @@ if (typeof __filename !== "undefined") {
|
|
|
14668
14709
|
}
|
|
14669
14710
|
var costumeRequire = createRequire(moduleUrl);
|
|
14670
14711
|
var getCheckmarxPath = () => {
|
|
14671
|
-
const
|
|
14672
|
-
const cxFileName =
|
|
14712
|
+
const os20 = type();
|
|
14713
|
+
const cxFileName = os20 === "Windows_NT" ? "cx.exe" : "cx";
|
|
14673
14714
|
try {
|
|
14674
14715
|
return costumeRequire.resolve(`.bin/${cxFileName}`);
|
|
14675
14716
|
} catch (e) {
|
|
14717
|
+
if (isStandaloneBinary()) {
|
|
14718
|
+
throw new CliError(
|
|
14719
|
+
"The Checkmarx scanner is not available in the standalone mobbdev binary yet. Use the npm package instead: npx mobbdev@latest"
|
|
14720
|
+
);
|
|
14721
|
+
}
|
|
14676
14722
|
throw new CliError(cxOperatingSystemSupportMessage);
|
|
14677
14723
|
}
|
|
14678
14724
|
};
|
|
@@ -14779,7 +14825,13 @@ async function validateCheckamxCredentials() {
|
|
|
14779
14825
|
}
|
|
14780
14826
|
|
|
14781
14827
|
// src/features/analysis/scanners/snyk.ts
|
|
14828
|
+
import crypto2 from "crypto";
|
|
14829
|
+
import fs10 from "fs";
|
|
14782
14830
|
import { createRequire as createRequire2 } from "module";
|
|
14831
|
+
import os4 from "os";
|
|
14832
|
+
import path10 from "path";
|
|
14833
|
+
import { Readable } from "stream";
|
|
14834
|
+
import { pipeline } from "stream/promises";
|
|
14783
14835
|
import chalk6 from "chalk";
|
|
14784
14836
|
import Debug21 from "debug";
|
|
14785
14837
|
import { createSpinner as createSpinner3 } from "nanospinner";
|
|
@@ -14804,31 +14856,130 @@ if (typeof __filename !== "undefined") {
|
|
|
14804
14856
|
}
|
|
14805
14857
|
}
|
|
14806
14858
|
var costumeRequire2 = createRequire2(moduleUrl2);
|
|
14807
|
-
var SNYK_PATH = costumeRequire2.resolve("snyk/bin/snyk");
|
|
14808
14859
|
var SNYK_ARTICLE_URL = "https://docs.snyk.io/scan-using-snyk/snyk-code/configure-snyk-code#enable-snyk-code";
|
|
14809
|
-
|
|
14860
|
+
var SNYK_VERSION = packageJson.dependencies.snyk;
|
|
14861
|
+
var SNYK_DOWNLOAD_BASE = process.env["MOBB_SNYK_DOWNLOAD_BASE"] || "https://downloads.snyk.io/cli";
|
|
14862
|
+
var SNYK_BINARY_ASSETS = {
|
|
14863
|
+
"darwin-x64": "snyk-macos",
|
|
14864
|
+
"darwin-arm64": "snyk-macos-arm64",
|
|
14865
|
+
"linux-x64": "snyk-linux",
|
|
14866
|
+
"linux-arm64": "snyk-linux-arm64",
|
|
14867
|
+
"win32-x64": "snyk-win.exe"
|
|
14868
|
+
};
|
|
14869
|
+
async function downloadSnykBinary(asset, dest) {
|
|
14870
|
+
if (!/^\d+\.\d+\.\d+$/.test(SNYK_VERSION)) {
|
|
14871
|
+
throw new CliError(
|
|
14872
|
+
`Cannot build the Snyk CLI download URL: expected an exact version in the "snyk" dependency of package.json, got "${SNYK_VERSION}".`
|
|
14873
|
+
);
|
|
14874
|
+
}
|
|
14875
|
+
const url = `${SNYK_DOWNLOAD_BASE}/v${SNYK_VERSION}/${asset}`;
|
|
14876
|
+
const tmpPath = `${dest}.download.${process.pid}`;
|
|
14877
|
+
const spinner = createSpinner3(
|
|
14878
|
+
`\u2B07\uFE0F Downloading Snyk CLI v${SNYK_VERSION}`
|
|
14879
|
+
).start();
|
|
14880
|
+
try {
|
|
14881
|
+
fs10.mkdirSync(path10.dirname(dest), { recursive: true });
|
|
14882
|
+
const timeout = () => AbortSignal.timeout(10 * 60 * 1e3);
|
|
14883
|
+
const [binRes, shaRes] = await Promise.all([
|
|
14884
|
+
fetchWithProxy(url, { signal: timeout() }),
|
|
14885
|
+
fetchWithProxy(`${url}.sha256`, { signal: timeout() })
|
|
14886
|
+
]);
|
|
14887
|
+
if (!binRes.ok || !shaRes.ok || !binRes.body) {
|
|
14888
|
+
throw new CliError(
|
|
14889
|
+
`Snyk CLI download failed with HTTP ${binRes.ok ? shaRes.status : binRes.status}: ${url}
|
|
14890
|
+
If downloads.snyk.io is unreachable from this network, point MOBB_SNYK_DOWNLOAD_BASE at a mirror.`
|
|
14891
|
+
);
|
|
14892
|
+
}
|
|
14893
|
+
const hash = crypto2.createHash("sha256");
|
|
14894
|
+
await pipeline(
|
|
14895
|
+
Readable.fromWeb(binRes.body),
|
|
14896
|
+
async function* (source) {
|
|
14897
|
+
for await (const chunk of source) {
|
|
14898
|
+
hash.update(chunk);
|
|
14899
|
+
yield chunk;
|
|
14900
|
+
}
|
|
14901
|
+
},
|
|
14902
|
+
fs10.createWriteStream(tmpPath, { mode: 493 })
|
|
14903
|
+
);
|
|
14904
|
+
const expected = (await shaRes.text()).trim().split(/\s+/)[0];
|
|
14905
|
+
const actual = hash.digest("hex");
|
|
14906
|
+
if (actual !== expected) {
|
|
14907
|
+
throw new CliError(
|
|
14908
|
+
`Snyk CLI download checksum mismatch (expected ${expected}, got ${actual}) \u2014 refusing to install it. Please try again.`
|
|
14909
|
+
);
|
|
14910
|
+
}
|
|
14911
|
+
fs10.renameSync(tmpPath, dest);
|
|
14912
|
+
spinner.success({ text: `\u2B07\uFE0F Snyk CLI v${SNYK_VERSION} installed` });
|
|
14913
|
+
} catch (e) {
|
|
14914
|
+
fs10.rmSync(tmpPath, { force: true });
|
|
14915
|
+
spinner.error({ text: "\u2B07\uFE0F Snyk CLI download failed" });
|
|
14916
|
+
throw e;
|
|
14917
|
+
}
|
|
14918
|
+
}
|
|
14919
|
+
async function getSnykExecutable() {
|
|
14920
|
+
try {
|
|
14921
|
+
return {
|
|
14922
|
+
processPath: costumeRequire2.resolve("snyk/bin/snyk"),
|
|
14923
|
+
isNodeScript: true
|
|
14924
|
+
};
|
|
14925
|
+
} catch (e) {
|
|
14926
|
+
if (!isStandaloneBinary()) {
|
|
14927
|
+
throw e;
|
|
14928
|
+
}
|
|
14929
|
+
debug21("running as standalone binary, using standalone snyk binary");
|
|
14930
|
+
}
|
|
14931
|
+
const asset = SNYK_BINARY_ASSETS[`${process.platform}-${process.arch}`];
|
|
14932
|
+
if (!asset) {
|
|
14933
|
+
throw new CliError(
|
|
14934
|
+
`Snyk scan is not supported on this platform (${process.platform}-${process.arch}) when running the standalone mobbdev binary. Use the npm package instead: npx mobbdev@latest`
|
|
14935
|
+
);
|
|
14936
|
+
}
|
|
14937
|
+
const dest = path10.join(
|
|
14938
|
+
os4.homedir(),
|
|
14939
|
+
".mobb",
|
|
14940
|
+
"tools",
|
|
14941
|
+
"snyk",
|
|
14942
|
+
`v${SNYK_VERSION}`,
|
|
14943
|
+
asset
|
|
14944
|
+
);
|
|
14945
|
+
if (!fs10.existsSync(dest)) {
|
|
14946
|
+
await downloadSnykBinary(asset, dest);
|
|
14947
|
+
}
|
|
14948
|
+
return { processPath: dest, isNodeScript: false };
|
|
14949
|
+
}
|
|
14810
14950
|
async function forkSnyk(args, { display }) {
|
|
14811
14951
|
debug21("fork snyk with args %o %s", args, display);
|
|
14812
|
-
|
|
14952
|
+
const { processPath, isNodeScript } = await getSnykExecutable();
|
|
14953
|
+
const create = isNodeScript ? createFork : createSpawn;
|
|
14954
|
+
return create({ args, processPath, name: "snyk" }, { display });
|
|
14813
14955
|
}
|
|
14814
14956
|
async function getSnykReport(reportPath, repoRoot, { skipPrompts = false }) {
|
|
14815
14957
|
debug21("get snyk report start %s %s", reportPath, repoRoot);
|
|
14816
14958
|
const config2 = await forkSnyk(["config"], { display: false });
|
|
14817
14959
|
const { message: configMessage } = config2;
|
|
14818
|
-
|
|
14819
|
-
|
|
14820
|
-
|
|
14960
|
+
const hasStoredCredential = configMessage.includes("api: ") || configMessage.includes("INTERNAL_OAUTH_TOKEN_STORAGE");
|
|
14961
|
+
if (!hasStoredCredential) {
|
|
14962
|
+
const hasSnykToken = !!process.env["SNYK_TOKEN"];
|
|
14963
|
+
if (skipPrompts) {
|
|
14964
|
+
if (!hasSnykToken) {
|
|
14965
|
+
throw new CliError(
|
|
14966
|
+
"Snyk authentication is required. In CI / non-interactive mode, set the SNYK_TOKEN environment variable (Snyk API token). Interactive `snyk auth` (browser) is not available with --ci."
|
|
14967
|
+
);
|
|
14968
|
+
}
|
|
14969
|
+
debug21("skipPrompts: using SNYK_TOKEN for snyk authentication");
|
|
14970
|
+
} else {
|
|
14971
|
+
const snykLoginSpinner = createSpinner3().start();
|
|
14821
14972
|
snykLoginSpinner.update({
|
|
14822
14973
|
text: "\u{1F513} Login to Snyk is required, press any key to continue"
|
|
14823
14974
|
});
|
|
14824
14975
|
await keypress();
|
|
14976
|
+
snykLoginSpinner.update({
|
|
14977
|
+
text: "\u{1F513} Waiting for Snyk login to complete"
|
|
14978
|
+
});
|
|
14979
|
+
debug21("no token in the config %s", config2);
|
|
14980
|
+
await forkSnyk(["auth"], { display: true });
|
|
14981
|
+
snykLoginSpinner.success({ text: "\u{1F513} Login to Snyk Successful" });
|
|
14825
14982
|
}
|
|
14826
|
-
snykLoginSpinner.update({
|
|
14827
|
-
text: "\u{1F513} Waiting for Snyk login to complete"
|
|
14828
|
-
});
|
|
14829
|
-
debug21("no token in the config %s", config2);
|
|
14830
|
-
await forkSnyk(["auth"], { display: true });
|
|
14831
|
-
snykLoginSpinner.success({ text: "\u{1F513} Login to Snyk Successful" });
|
|
14832
14983
|
}
|
|
14833
14984
|
const snykSpinner = createSpinner3("\u{1F50D} Scanning your repo with Snyk ").start();
|
|
14834
14985
|
const { message: scanOutput } = await forkSnyk(
|
|
@@ -14890,7 +15041,7 @@ async function downloadRepo({
|
|
|
14890
15041
|
const { createSpinner: createSpinner5 } = Spinner2({ ci });
|
|
14891
15042
|
const repoSpinner = createSpinner5("\u{1F4BE} Downloading Repo").start();
|
|
14892
15043
|
debug22("download repo %s %s %s", repoUrl, dirname);
|
|
14893
|
-
const zipFilePath =
|
|
15044
|
+
const zipFilePath = path11.join(dirname, "repo.zip");
|
|
14894
15045
|
debug22("download URL: %s auth headers: %o", downloadUrl, authHeaders);
|
|
14895
15046
|
const response = await fetch4(downloadUrl, {
|
|
14896
15047
|
method: "GET",
|
|
@@ -14903,19 +15054,19 @@ async function downloadRepo({
|
|
|
14903
15054
|
repoSpinner.error({ text: "\u{1F4BE} Repo download failed" });
|
|
14904
15055
|
throw new Error(`Can't access ${chalk7.bold(repoUrl)}`);
|
|
14905
15056
|
}
|
|
14906
|
-
const fileWriterStream =
|
|
15057
|
+
const fileWriterStream = fs11.createWriteStream(zipFilePath);
|
|
14907
15058
|
if (!response.body) {
|
|
14908
15059
|
throw new Error("Response body is empty");
|
|
14909
15060
|
}
|
|
14910
|
-
await
|
|
15061
|
+
await pipeline2(response.body, fileWriterStream);
|
|
14911
15062
|
await extract(zipFilePath, { dir: dirname });
|
|
14912
|
-
const repoRoot =
|
|
15063
|
+
const repoRoot = fs11.readdirSync(dirname, { withFileTypes: true }).filter((dirent) => dirent.isDirectory()).map((dirent) => dirent.name)[0];
|
|
14913
15064
|
if (!repoRoot) {
|
|
14914
15065
|
throw new Error("Repo root not found");
|
|
14915
15066
|
}
|
|
14916
15067
|
debug22("repo root %s", repoRoot);
|
|
14917
15068
|
repoSpinner.success({ text: "\u{1F4BE} Repo downloaded successfully" });
|
|
14918
|
-
return
|
|
15069
|
+
return path11.join(dirname, repoRoot);
|
|
14919
15070
|
}
|
|
14920
15071
|
var getReportUrl = ({
|
|
14921
15072
|
organizationId,
|
|
@@ -15023,7 +15174,7 @@ async function getReport(params, { skipPrompts }) {
|
|
|
15023
15174
|
authHeaders: scm.getAuthHeaders(),
|
|
15024
15175
|
downloadUrl
|
|
15025
15176
|
});
|
|
15026
|
-
const reportPath =
|
|
15177
|
+
const reportPath = path11.join(dirname, REPORT_DEFAULT_FILE_NAME);
|
|
15027
15178
|
switch (scanner) {
|
|
15028
15179
|
case "snyk":
|
|
15029
15180
|
await getSnykReport(reportPath, repositoryRoot, { skipPrompts });
|
|
@@ -15068,7 +15219,7 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
15068
15219
|
pullRequest,
|
|
15069
15220
|
polling,
|
|
15070
15221
|
baselineCommit,
|
|
15071
|
-
scanOnly
|
|
15222
|
+
scanOnly: requestedScanOnly
|
|
15072
15223
|
} = params;
|
|
15073
15224
|
debug22("start %s %s", dirname, repo);
|
|
15074
15225
|
const { createSpinner: createSpinner5 } = Spinner2({ ci });
|
|
@@ -15084,11 +15235,20 @@ async function _scan(params, { skipPrompts = false } = {}) {
|
|
|
15084
15235
|
const {
|
|
15085
15236
|
projectId,
|
|
15086
15237
|
organizationId,
|
|
15087
|
-
enableV2Fixes: orgEnableV2Fixes
|
|
15238
|
+
enableV2Fixes: orgEnableV2Fixes,
|
|
15239
|
+
isFixEnabled: orgIsFixEnabled
|
|
15088
15240
|
} = await gqlClient.getLastOrgAndNamedProject({
|
|
15089
15241
|
projectName: mobbProjectName,
|
|
15090
15242
|
userDefinedOrganizationId: userOrganizationId
|
|
15091
15243
|
});
|
|
15244
|
+
const scanOnly = requestedScanOnly || !orgIsFixEnabled;
|
|
15245
|
+
if (scanOnly && !requestedScanOnly) {
|
|
15246
|
+
console.log(
|
|
15247
|
+
chalk7.yellow(
|
|
15248
|
+
"\u2139\uFE0F Your organization is on Mobb Scan (scan-only): issues will be found and reported, but no fixes are generated. Contact support to enable Mobb Fixer."
|
|
15249
|
+
)
|
|
15250
|
+
);
|
|
15251
|
+
}
|
|
15092
15252
|
const {
|
|
15093
15253
|
uploadS3BucketInfo: { repoUploadInfo, reportUploadInfo }
|
|
15094
15254
|
} = await gqlClient.uploadS3BucketInfo();
|
|
@@ -15426,7 +15586,7 @@ async function _zipAndUploadRepo({
|
|
|
15426
15586
|
const zippingSpinner = createSpinner4("\u{1F4E6} Zipping repo").start();
|
|
15427
15587
|
let zipBuffer;
|
|
15428
15588
|
let gitInfo2 = { success: false };
|
|
15429
|
-
if (srcFileStatus.isFile() &&
|
|
15589
|
+
if (srcFileStatus.isFile() && path11.extname(srcPath).toLowerCase() === ".fpr") {
|
|
15430
15590
|
zipBuffer = await repackFpr(srcPath);
|
|
15431
15591
|
} else {
|
|
15432
15592
|
gitInfo2 = await getGitInfo(srcPath);
|
|
@@ -15599,17 +15759,17 @@ async function waitForAnaysisAndReviewPr({
|
|
|
15599
15759
|
}
|
|
15600
15760
|
|
|
15601
15761
|
// src/commands/scan_skill_input.ts
|
|
15602
|
-
import
|
|
15603
|
-
import
|
|
15762
|
+
import fs12 from "fs";
|
|
15763
|
+
import path12 from "path";
|
|
15604
15764
|
import AdmZip2 from "adm-zip";
|
|
15605
15765
|
var LOCAL_SKILL_ZIP_DATA_URL_PREFIX = "data:application/zip;base64,";
|
|
15606
15766
|
var MAX_LOCAL_SKILL_ARCHIVE_BYTES = 2 * 1024 * 1024;
|
|
15607
15767
|
async function resolveSkillScanInput(skillInput) {
|
|
15608
|
-
const resolvedPath =
|
|
15609
|
-
if (!
|
|
15768
|
+
const resolvedPath = path12.resolve(skillInput);
|
|
15769
|
+
if (!fs12.existsSync(resolvedPath)) {
|
|
15610
15770
|
return skillInput;
|
|
15611
15771
|
}
|
|
15612
|
-
const stat5 =
|
|
15772
|
+
const stat5 = fs12.statSync(resolvedPath);
|
|
15613
15773
|
if (!stat5.isDirectory()) {
|
|
15614
15774
|
throw new CliError(
|
|
15615
15775
|
"Local skill input must be a directory containing SKILL.md"
|
|
@@ -15619,18 +15779,18 @@ async function resolveSkillScanInput(skillInput) {
|
|
|
15619
15779
|
}
|
|
15620
15780
|
function packageLocalSkillDirectory(directoryPath) {
|
|
15621
15781
|
const zip = new AdmZip2();
|
|
15622
|
-
const rootPath =
|
|
15782
|
+
const rootPath = path12.resolve(directoryPath);
|
|
15623
15783
|
const filePaths = listDirectoryFiles(rootPath);
|
|
15624
15784
|
let hasSkillMd = false;
|
|
15625
15785
|
for (const relativePath of filePaths) {
|
|
15626
|
-
const fullPath =
|
|
15627
|
-
const normalizedFullPath =
|
|
15628
|
-
if (normalizedFullPath !== rootPath && !normalizedFullPath.startsWith(rootPath +
|
|
15786
|
+
const fullPath = path12.join(rootPath, relativePath);
|
|
15787
|
+
const normalizedFullPath = path12.resolve(fullPath);
|
|
15788
|
+
if (normalizedFullPath !== rootPath && !normalizedFullPath.startsWith(rootPath + path12.sep)) {
|
|
15629
15789
|
continue;
|
|
15630
15790
|
}
|
|
15631
|
-
const fileContent =
|
|
15791
|
+
const fileContent = fs12.readFileSync(normalizedFullPath);
|
|
15632
15792
|
zip.addFile(relativePath, fileContent);
|
|
15633
|
-
if (
|
|
15793
|
+
if (path12.basename(relativePath).toLowerCase() === "skill.md") {
|
|
15634
15794
|
hasSkillMd = true;
|
|
15635
15795
|
}
|
|
15636
15796
|
}
|
|
@@ -15648,17 +15808,17 @@ function packageLocalSkillDirectory(directoryPath) {
|
|
|
15648
15808
|
function listDirectoryFiles(rootPath) {
|
|
15649
15809
|
const files = [];
|
|
15650
15810
|
function walk(relativeDir) {
|
|
15651
|
-
const absoluteDir =
|
|
15652
|
-
const entries =
|
|
15811
|
+
const absoluteDir = path12.join(rootPath, relativeDir);
|
|
15812
|
+
const entries = fs12.readdirSync(absoluteDir, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
|
|
15653
15813
|
for (const entry of entries) {
|
|
15654
|
-
const safeEntryName =
|
|
15814
|
+
const safeEntryName = path12.basename(entry.name).replace(/\0/g, "");
|
|
15655
15815
|
if (!safeEntryName) {
|
|
15656
15816
|
continue;
|
|
15657
15817
|
}
|
|
15658
|
-
const relativePath = relativeDir ?
|
|
15659
|
-
const absolutePath =
|
|
15660
|
-
const normalizedAbsolutePath =
|
|
15661
|
-
if (normalizedAbsolutePath !== rootPath && !normalizedAbsolutePath.startsWith(rootPath +
|
|
15818
|
+
const relativePath = relativeDir ? path12.posix.join(relativeDir, safeEntryName) : safeEntryName;
|
|
15819
|
+
const absolutePath = path12.join(rootPath, relativePath);
|
|
15820
|
+
const normalizedAbsolutePath = path12.resolve(absolutePath);
|
|
15821
|
+
if (normalizedAbsolutePath !== rootPath && !normalizedAbsolutePath.startsWith(rootPath + path12.sep)) {
|
|
15662
15822
|
continue;
|
|
15663
15823
|
}
|
|
15664
15824
|
if (entry.isSymbolicLink()) {
|
|
@@ -15879,7 +16039,7 @@ async function scanSkill(options) {
|
|
|
15879
16039
|
|
|
15880
16040
|
// src/args/validation.ts
|
|
15881
16041
|
import chalk9 from "chalk";
|
|
15882
|
-
import
|
|
16042
|
+
import path13 from "path";
|
|
15883
16043
|
import { z as z28 } from "zod";
|
|
15884
16044
|
function throwRepoUrlErrorMessage({
|
|
15885
16045
|
error,
|
|
@@ -15923,7 +16083,7 @@ function validateRepoUrl(args) {
|
|
|
15923
16083
|
}
|
|
15924
16084
|
var supportExtensions = [".json", ".xml", ".fpr", ".sarif", ".zip"];
|
|
15925
16085
|
function validateReportFileFormat(reportFile) {
|
|
15926
|
-
if (!supportExtensions.includes(
|
|
16086
|
+
if (!supportExtensions.includes(path13.extname(reportFile))) {
|
|
15927
16087
|
throw new CliError(
|
|
15928
16088
|
`
|
|
15929
16089
|
${chalk9.bold(
|
|
@@ -15971,7 +16131,7 @@ function analyzeBuilder(yargs2) {
|
|
|
15971
16131
|
).help();
|
|
15972
16132
|
}
|
|
15973
16133
|
function validateAnalyzeOptions(argv) {
|
|
15974
|
-
if (argv.f && !
|
|
16134
|
+
if (argv.f && !fs13.existsSync(argv.f)) {
|
|
15975
16135
|
throw new CliError(`
|
|
15976
16136
|
Can't access ${chalk10.bold(argv.f)}`);
|
|
15977
16137
|
}
|
|
@@ -16019,8 +16179,8 @@ import { spawn } from "child_process";
|
|
|
16019
16179
|
|
|
16020
16180
|
// src/features/claude_code/daemon.ts
|
|
16021
16181
|
import { readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
16022
|
-
import * as
|
|
16023
|
-
import
|
|
16182
|
+
import * as os9 from "os";
|
|
16183
|
+
import path25 from "path";
|
|
16024
16184
|
import { setTimeout as sleep4 } from "timers/promises";
|
|
16025
16185
|
import Configstore4 from "configstore";
|
|
16026
16186
|
|
|
@@ -16199,7 +16359,7 @@ function loadBreaker(store) {
|
|
|
16199
16359
|
// src/features/analysis/skill_quarantine/runQuarantineCheck.ts
|
|
16200
16360
|
import { stat as stat3 } from "fs/promises";
|
|
16201
16361
|
import { homedir as homedir4 } from "os";
|
|
16202
|
-
import
|
|
16362
|
+
import path20 from "path";
|
|
16203
16363
|
|
|
16204
16364
|
// src/features/analysis/skill_quarantine/constants.ts
|
|
16205
16365
|
var HEARTBEAT_DEBOUNCE_MS = (() => {
|
|
@@ -16214,11 +16374,11 @@ var STUB_MARKER = "\u26D4 QUARANTINED BY TRACY";
|
|
|
16214
16374
|
|
|
16215
16375
|
// src/features/analysis/skill_quarantine/enumerateInstalledSkills.ts
|
|
16216
16376
|
import { homedir as homedir2 } from "os";
|
|
16217
|
-
import
|
|
16377
|
+
import path16 from "path";
|
|
16218
16378
|
|
|
16219
16379
|
// src/features/analysis/context_file_processor.ts
|
|
16220
16380
|
import { createHash } from "crypto";
|
|
16221
|
-
import
|
|
16381
|
+
import path14 from "path";
|
|
16222
16382
|
import AdmZip3 from "adm-zip";
|
|
16223
16383
|
import pLimit5 from "p-limit";
|
|
16224
16384
|
var SANITIZE_CONCURRENCY = 5;
|
|
@@ -16252,7 +16412,7 @@ async function processContextFiles(regularFiles, skillGroups) {
|
|
|
16252
16412
|
);
|
|
16253
16413
|
for (const file of sortedFiles) {
|
|
16254
16414
|
const sanitizedContent = await sanitizeFileContent(file.content);
|
|
16255
|
-
const zipEntryName = group.isFolder ?
|
|
16415
|
+
const zipEntryName = group.isFolder ? path14.relative(group.skillPath, file.path).replace(/\\/g, "/") : path14.basename(file.path);
|
|
16256
16416
|
zip.addFile(zipEntryName, Buffer.from(sanitizedContent, "utf-8"));
|
|
16257
16417
|
const entry = zip.getEntry(zipEntryName);
|
|
16258
16418
|
if (entry) {
|
|
@@ -16271,7 +16431,7 @@ async function processContextFiles(regularFiles, skillGroups) {
|
|
|
16271
16431
|
// src/features/analysis/context_file_scanner.ts
|
|
16272
16432
|
import { lstat, readdir, readFile, realpath, stat } from "fs/promises";
|
|
16273
16433
|
import { homedir } from "os";
|
|
16274
|
-
import
|
|
16434
|
+
import path15 from "path";
|
|
16275
16435
|
import { globby as globby2 } from "globby";
|
|
16276
16436
|
import { parse as parseJsoncLib } from "jsonc-parser";
|
|
16277
16437
|
|
|
@@ -16603,25 +16763,25 @@ function resolveCustomLocationPath(raw, workspaceRoot, home) {
|
|
|
16603
16763
|
return null;
|
|
16604
16764
|
}
|
|
16605
16765
|
if (s.startsWith("~/") || s === "~") {
|
|
16606
|
-
s =
|
|
16766
|
+
s = path15.join(home, s.slice(1));
|
|
16607
16767
|
}
|
|
16608
|
-
if (!
|
|
16609
|
-
s =
|
|
16768
|
+
if (!path15.isAbsolute(s)) {
|
|
16769
|
+
s = path15.resolve(workspaceRoot, s);
|
|
16610
16770
|
}
|
|
16611
|
-
const resolved =
|
|
16771
|
+
const resolved = path15.normalize(s);
|
|
16612
16772
|
if (resolved === "/" || resolved === home) {
|
|
16613
16773
|
return null;
|
|
16614
16774
|
}
|
|
16615
16775
|
for (const sub of SENSITIVE_HOME_SUBDIRS) {
|
|
16616
|
-
const sensitive =
|
|
16617
|
-
if (resolved === sensitive || resolved.startsWith(`${sensitive}${
|
|
16776
|
+
const sensitive = path15.join(home, sub);
|
|
16777
|
+
if (resolved === sensitive || resolved.startsWith(`${sensitive}${path15.sep}`)) {
|
|
16618
16778
|
return null;
|
|
16619
16779
|
}
|
|
16620
16780
|
}
|
|
16621
|
-
const relWorkspace =
|
|
16622
|
-
const relHome =
|
|
16623
|
-
const escapesWorkspace = relWorkspace.startsWith("..") ||
|
|
16624
|
-
const escapesHome = relHome.startsWith("..") ||
|
|
16781
|
+
const relWorkspace = path15.relative(workspaceRoot, resolved);
|
|
16782
|
+
const relHome = path15.relative(home, resolved);
|
|
16783
|
+
const escapesWorkspace = relWorkspace.startsWith("..") || path15.isAbsolute(relWorkspace);
|
|
16784
|
+
const escapesHome = relHome.startsWith("..") || path15.isAbsolute(relHome);
|
|
16625
16785
|
if (escapesWorkspace && escapesHome) {
|
|
16626
16786
|
return null;
|
|
16627
16787
|
}
|
|
@@ -16672,15 +16832,15 @@ async function readJsoncSettings(settingsPath) {
|
|
|
16672
16832
|
putSettingsCache(settingsPath, { mtimeMs, parsed: payload });
|
|
16673
16833
|
return payload;
|
|
16674
16834
|
}
|
|
16675
|
-
function putSettingsCache(
|
|
16676
|
-
if (!settingsCache.has(
|
|
16835
|
+
function putSettingsCache(path38, entry) {
|
|
16836
|
+
if (!settingsCache.has(path38) && settingsCache.size >= MAX_SETTINGS_CACHE_SIZE) {
|
|
16677
16837
|
settingsCache.delete(settingsCache.keys().next().value);
|
|
16678
16838
|
}
|
|
16679
|
-
settingsCache.set(
|
|
16839
|
+
settingsCache.set(path38, entry);
|
|
16680
16840
|
}
|
|
16681
16841
|
async function readCopilotCustomLocations(workspaceRoot) {
|
|
16682
16842
|
const parsed = await readJsoncSettings(
|
|
16683
|
-
|
|
16843
|
+
path15.join(workspaceRoot, ".vscode", "settings.json")
|
|
16684
16844
|
);
|
|
16685
16845
|
if (!parsed) {
|
|
16686
16846
|
return [];
|
|
@@ -16727,7 +16887,7 @@ async function readCopilotCustomLocations(workspaceRoot) {
|
|
|
16727
16887
|
async function readClaudeCodeCustomLocations() {
|
|
16728
16888
|
const home = homedir();
|
|
16729
16889
|
const parsed = await readJsoncSettings(
|
|
16730
|
-
|
|
16890
|
+
path15.join(home, ".claude", "settings.json")
|
|
16731
16891
|
);
|
|
16732
16892
|
if (!parsed) {
|
|
16733
16893
|
return [];
|
|
@@ -16769,7 +16929,7 @@ function groupSkills(files, root, baseDir) {
|
|
|
16769
16929
|
const folderMap = /* @__PURE__ */ new Map();
|
|
16770
16930
|
const standalone = [];
|
|
16771
16931
|
for (const f of skillFiles) {
|
|
16772
|
-
const rel =
|
|
16932
|
+
const rel = path15.relative(baseDir, f.path).replace(/\\/g, "/");
|
|
16773
16933
|
const skillsMarker = "skills/";
|
|
16774
16934
|
const skillsIdx = rel.indexOf(skillsMarker);
|
|
16775
16935
|
if (skillsIdx === -1) {
|
|
@@ -16796,8 +16956,8 @@ function groupSkills(files, root, baseDir) {
|
|
|
16796
16956
|
}
|
|
16797
16957
|
const groups = [];
|
|
16798
16958
|
for (const f of standalone) {
|
|
16799
|
-
const name =
|
|
16800
|
-
const standaloneRel =
|
|
16959
|
+
const name = path15.basename(f.path, path15.extname(f.path));
|
|
16960
|
+
const standaloneRel = path15.relative(baseDir, f.path).replace(/\\/g, "/");
|
|
16801
16961
|
const sessionKey = `skill:${root}:${standaloneRel}`;
|
|
16802
16962
|
groups.push({
|
|
16803
16963
|
name,
|
|
@@ -16811,7 +16971,7 @@ function groupSkills(files, root, baseDir) {
|
|
|
16811
16971
|
}
|
|
16812
16972
|
for (const [folderKey, { folderName, files: folderFiles }] of folderMap) {
|
|
16813
16973
|
const maxMtimeMs = Math.max(...folderFiles.map((f) => f.mtimeMs));
|
|
16814
|
-
const skillPath =
|
|
16974
|
+
const skillPath = path15.join(baseDir, folderKey);
|
|
16815
16975
|
const sessionKey = `skill:${root}:${folderKey}`;
|
|
16816
16976
|
groups.push({
|
|
16817
16977
|
name: folderName,
|
|
@@ -16932,10 +17092,10 @@ async function enumerateGlob(pattern, cwd, category, isDynamic) {
|
|
|
16932
17092
|
} catch {
|
|
16933
17093
|
return [];
|
|
16934
17094
|
}
|
|
16935
|
-
return files.map((
|
|
17095
|
+
return files.map((path38) => ({ path: path38, category }));
|
|
16936
17096
|
}
|
|
16937
17097
|
async function enumerateSkillBundle(baseDir, skillsRoot) {
|
|
16938
|
-
const skillsDir =
|
|
17098
|
+
const skillsDir = path15.resolve(baseDir, skillsRoot);
|
|
16939
17099
|
let manifests;
|
|
16940
17100
|
try {
|
|
16941
17101
|
manifests = await globby2("*/SKILL.md", {
|
|
@@ -16951,7 +17111,7 @@ async function enumerateSkillBundle(baseDir, skillsRoot) {
|
|
|
16951
17111
|
}
|
|
16952
17112
|
const perSkillResults = await Promise.all(
|
|
16953
17113
|
manifests.map(async (manifest) => {
|
|
16954
|
-
const skillDir =
|
|
17114
|
+
const skillDir = path15.dirname(manifest);
|
|
16955
17115
|
try {
|
|
16956
17116
|
return await globby2("**/*", {
|
|
16957
17117
|
cwd: skillDir,
|
|
@@ -17000,11 +17160,11 @@ async function enumerateSymlinkedSkills(skillsDir) {
|
|
|
17000
17160
|
const results = [];
|
|
17001
17161
|
for (const entry of dirEntries) {
|
|
17002
17162
|
if (!entry.isSymbolicLink()) continue;
|
|
17003
|
-
const entryPath =
|
|
17163
|
+
const entryPath = path15.join(skillsDir, entry.name);
|
|
17004
17164
|
try {
|
|
17005
17165
|
const st = await stat(entryPath);
|
|
17006
17166
|
if (st.isDirectory()) {
|
|
17007
|
-
const hasManifest = await stat(
|
|
17167
|
+
const hasManifest = await stat(path15.join(entryPath, "SKILL.md")).then(() => true).catch(() => false);
|
|
17008
17168
|
if (!hasManifest) continue;
|
|
17009
17169
|
const realDir = await realpath(entryPath);
|
|
17010
17170
|
const realFiles = await globby2("**/*", {
|
|
@@ -17017,7 +17177,7 @@ async function enumerateSymlinkedSkills(skillsDir) {
|
|
|
17017
17177
|
}).catch(() => []);
|
|
17018
17178
|
for (const f of realFiles) {
|
|
17019
17179
|
results.push({
|
|
17020
|
-
path:
|
|
17180
|
+
path: path15.join(entryPath, path15.relative(realDir, f)),
|
|
17021
17181
|
category: "skill"
|
|
17022
17182
|
});
|
|
17023
17183
|
}
|
|
@@ -17049,11 +17209,11 @@ function scopeForRoot(root) {
|
|
|
17049
17209
|
return void 0;
|
|
17050
17210
|
}
|
|
17051
17211
|
function deriveIdentifier(filePath, baseDir) {
|
|
17052
|
-
const relative2 =
|
|
17212
|
+
const relative2 = path15.relative(baseDir, filePath);
|
|
17053
17213
|
if (!relative2.startsWith("..")) {
|
|
17054
17214
|
return relative2.replace(/\\/g, "/");
|
|
17055
17215
|
}
|
|
17056
|
-
return
|
|
17216
|
+
return path15.basename(filePath);
|
|
17057
17217
|
}
|
|
17058
17218
|
|
|
17059
17219
|
// src/features/analysis/skill_quarantine/enumerateInstalledSkills.ts
|
|
@@ -17065,8 +17225,8 @@ async function enumerateInstalledSkills(workspaceRoot) {
|
|
|
17065
17225
|
);
|
|
17066
17226
|
const home = homedir2();
|
|
17067
17227
|
const agentGroups = regularFiles.filter((f) => f.category === "agent-config").map((f) => ({
|
|
17068
|
-
name:
|
|
17069
|
-
root: f.path.startsWith(home +
|
|
17228
|
+
name: path16.basename(f.path, path16.extname(f.path)),
|
|
17229
|
+
root: f.path.startsWith(home + path16.sep) ? "home" : "workspace",
|
|
17070
17230
|
skillPath: f.path,
|
|
17071
17231
|
files: [f],
|
|
17072
17232
|
isFolder: false,
|
|
@@ -17150,32 +17310,32 @@ import {
|
|
|
17150
17310
|
unlink,
|
|
17151
17311
|
writeFile
|
|
17152
17312
|
} from "fs/promises";
|
|
17153
|
-
import
|
|
17313
|
+
import path19 from "path";
|
|
17154
17314
|
import AdmZip4 from "adm-zip";
|
|
17155
17315
|
|
|
17156
17316
|
// src/features/analysis/skill_quarantine/paths.ts
|
|
17157
17317
|
import { homedir as homedir3 } from "os";
|
|
17158
|
-
import
|
|
17318
|
+
import path17 from "path";
|
|
17159
17319
|
function getQuarantineRoot() {
|
|
17160
|
-
return
|
|
17320
|
+
return path17.join(homedir3(), ".tracy", "quarantine", "claude", "skills");
|
|
17161
17321
|
}
|
|
17162
17322
|
function getQuarantineZipPath(md5) {
|
|
17163
|
-
return
|
|
17323
|
+
return path17.join(getQuarantineRoot(), `${md5}.zip`);
|
|
17164
17324
|
}
|
|
17165
17325
|
function getTmpZipPath(md5, uuid) {
|
|
17166
|
-
return
|
|
17326
|
+
return path17.join(getQuarantineRoot(), `${md5}_tmp_${uuid}.zip`);
|
|
17167
17327
|
}
|
|
17168
17328
|
var TMP_ZIP_REGEX = /^([0-9a-f]{32})_tmp_[0-9a-f-]+\.zip$/;
|
|
17169
17329
|
var COMMITTED_ZIP_REGEX = /^([0-9a-f]{32})\.zip$/;
|
|
17170
17330
|
|
|
17171
17331
|
// src/features/analysis/skill_quarantine/stubTemplate.ts
|
|
17172
|
-
import
|
|
17332
|
+
import path18 from "path";
|
|
17173
17333
|
import { quote } from "shell-quote";
|
|
17174
17334
|
var LEGACY_SUMMARY_FALLBACK = "not available (scan predates current schema)";
|
|
17175
17335
|
function renderStub(params) {
|
|
17176
17336
|
const folderOrFile = params.isFolder ? "skill folder" : "skill file";
|
|
17177
17337
|
const reason = params.summary ?? LEGACY_SUMMARY_FALLBACK;
|
|
17178
|
-
const extractParent =
|
|
17338
|
+
const extractParent = path18.dirname(params.origPath);
|
|
17179
17339
|
const recoverCommand = `rm -rf ${quote([params.origPath])} && unzip -o ${quote([params.quarantinedZipPath])} -d ${quote([extractParent])}`;
|
|
17180
17340
|
return `# ${STUB_MARKER}
|
|
17181
17341
|
|
|
@@ -17289,7 +17449,7 @@ async function quarantineSkill(params) {
|
|
|
17289
17449
|
}
|
|
17290
17450
|
async function preregisterStubMd5(params) {
|
|
17291
17451
|
const { skillPath, isFolder, origName, log: log2 } = params;
|
|
17292
|
-
const stubFilePath = isFolder ?
|
|
17452
|
+
const stubFilePath = isFolder ? path19.join(skillPath, "SKILL.md") : skillPath;
|
|
17293
17453
|
const stubEntryName = isFolder ? `${origName}/SKILL.md` : origName;
|
|
17294
17454
|
const content = await readFile2(stubFilePath, "utf-8");
|
|
17295
17455
|
const fileStat = await stat2(stubFilePath);
|
|
@@ -17302,7 +17462,7 @@ async function preregisterStubMd5(params) {
|
|
|
17302
17462
|
mtimeMs: fileStat.mtimeMs
|
|
17303
17463
|
};
|
|
17304
17464
|
const stubGroup = {
|
|
17305
|
-
name: isFolder ? origName :
|
|
17465
|
+
name: isFolder ? origName : path19.basename(skillPath, path19.extname(skillPath)),
|
|
17306
17466
|
root: "workspace",
|
|
17307
17467
|
// unused by md5 computation
|
|
17308
17468
|
skillPath,
|
|
@@ -17349,7 +17509,7 @@ async function writeStub(params) {
|
|
|
17349
17509
|
if (isFolder) {
|
|
17350
17510
|
await rm(skillPath, { recursive: true, force: true });
|
|
17351
17511
|
await mkdir(skillPath, { recursive: true });
|
|
17352
|
-
await writeFile(
|
|
17512
|
+
await writeFile(path19.join(skillPath, "SKILL.md"), stubContent, "utf8");
|
|
17353
17513
|
} else {
|
|
17354
17514
|
await writeFile(skillPath, stubContent, "utf8");
|
|
17355
17515
|
}
|
|
@@ -17371,7 +17531,7 @@ async function reconcileAndSweep(log2) {
|
|
|
17371
17531
|
for (const entry of entries) {
|
|
17372
17532
|
const md5 = TMP_ZIP_REGEX.exec(entry)?.[1];
|
|
17373
17533
|
if (md5 === void 0) continue;
|
|
17374
|
-
const full =
|
|
17534
|
+
const full = path19.join(root, entry);
|
|
17375
17535
|
if (committed.has(md5)) {
|
|
17376
17536
|
await unlink(full).catch(
|
|
17377
17537
|
(err) => log2.warn(
|
|
@@ -17431,8 +17591,8 @@ async function addFolderAsync(zip, root, prefix) {
|
|
|
17431
17591
|
async function walk(dir, relPrefix) {
|
|
17432
17592
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
17433
17593
|
for (const e of entries) {
|
|
17434
|
-
const full =
|
|
17435
|
-
const rel =
|
|
17594
|
+
const full = path19.join(dir, e.name);
|
|
17595
|
+
const rel = path19.posix.join(relPrefix, e.name);
|
|
17436
17596
|
if (e.isDirectory()) await walk(full, rel);
|
|
17437
17597
|
else if (e.isFile()) zip.addFile(rel, await readFile2(full));
|
|
17438
17598
|
}
|
|
@@ -17478,8 +17638,8 @@ var SKILL_PARENT_DIRS = [
|
|
|
17478
17638
|
async function getSkillDirsMtimeMs(cwd) {
|
|
17479
17639
|
const home = homedir4();
|
|
17480
17640
|
const dirs = SKILL_PARENT_DIRS.flatMap((d) => [
|
|
17481
|
-
|
|
17482
|
-
|
|
17641
|
+
path20.join(cwd, d),
|
|
17642
|
+
path20.join(home, d)
|
|
17483
17643
|
]);
|
|
17484
17644
|
let max = 0;
|
|
17485
17645
|
for (const dir of dirs) {
|
|
@@ -17592,9 +17752,9 @@ async function runQuarantineCheckIfNeeded(opts) {
|
|
|
17592
17752
|
}
|
|
17593
17753
|
|
|
17594
17754
|
// src/features/claude_code/daemon_pid_file.ts
|
|
17595
|
-
import
|
|
17596
|
-
import
|
|
17597
|
-
import
|
|
17755
|
+
import fs14 from "fs";
|
|
17756
|
+
import os5 from "os";
|
|
17757
|
+
import path21 from "path";
|
|
17598
17758
|
|
|
17599
17759
|
// src/features/claude_code/data_collector_constants.ts
|
|
17600
17760
|
var CC_VERSION_CACHE_KEY = "claudeCode.detectedCCVersion";
|
|
@@ -17620,26 +17780,26 @@ var DAEMON_SHUTDOWN_DRAIN_MS = 6e3;
|
|
|
17620
17780
|
|
|
17621
17781
|
// src/features/claude_code/daemon_pid_file.ts
|
|
17622
17782
|
function getMobbdevDir() {
|
|
17623
|
-
return
|
|
17783
|
+
return path21.join(os5.homedir(), ".mobbdev");
|
|
17624
17784
|
}
|
|
17625
17785
|
function getDaemonCheckScriptPath() {
|
|
17626
|
-
return
|
|
17786
|
+
return path21.join(getMobbdevDir(), "daemon-check.js");
|
|
17627
17787
|
}
|
|
17628
17788
|
var DaemonPidFile = class {
|
|
17629
17789
|
constructor() {
|
|
17630
17790
|
__publicField(this, "data", null);
|
|
17631
17791
|
}
|
|
17632
17792
|
get filePath() {
|
|
17633
|
-
return
|
|
17793
|
+
return path21.join(getMobbdevDir(), "daemon.pid");
|
|
17634
17794
|
}
|
|
17635
17795
|
/** Ensure ~/.mobbdev/ directory exists. */
|
|
17636
17796
|
ensureDir() {
|
|
17637
|
-
|
|
17797
|
+
fs14.mkdirSync(getMobbdevDir(), { recursive: true });
|
|
17638
17798
|
}
|
|
17639
17799
|
/** Read the PID file from disk. Returns the parsed data or null. */
|
|
17640
17800
|
read() {
|
|
17641
17801
|
try {
|
|
17642
|
-
const raw =
|
|
17802
|
+
const raw = fs14.readFileSync(this.filePath, "utf8");
|
|
17643
17803
|
const parsed = JSON.parse(raw);
|
|
17644
17804
|
if (typeof parsed.pid !== "number" || typeof parsed.startedAt !== "number" || typeof parsed.heartbeat !== "number") {
|
|
17645
17805
|
this.data = null;
|
|
@@ -17659,14 +17819,14 @@ var DaemonPidFile = class {
|
|
|
17659
17819
|
heartbeat: Date.now(),
|
|
17660
17820
|
version
|
|
17661
17821
|
};
|
|
17662
|
-
|
|
17822
|
+
fs14.writeFileSync(this.filePath, JSON.stringify(this.data), "utf8");
|
|
17663
17823
|
}
|
|
17664
17824
|
/** Update the heartbeat timestamp of the current PID file. */
|
|
17665
17825
|
updateHeartbeat() {
|
|
17666
17826
|
if (!this.data) this.read();
|
|
17667
17827
|
if (!this.data) return;
|
|
17668
17828
|
this.data.heartbeat = Date.now();
|
|
17669
|
-
|
|
17829
|
+
fs14.writeFileSync(this.filePath, JSON.stringify(this.data), "utf8");
|
|
17670
17830
|
}
|
|
17671
17831
|
/** Check whether the previously read PID data represents a live daemon. */
|
|
17672
17832
|
isAlive() {
|
|
@@ -17683,7 +17843,7 @@ var DaemonPidFile = class {
|
|
|
17683
17843
|
remove() {
|
|
17684
17844
|
this.data = null;
|
|
17685
17845
|
try {
|
|
17686
|
-
|
|
17846
|
+
fs14.unlinkSync(this.filePath);
|
|
17687
17847
|
} catch {
|
|
17688
17848
|
}
|
|
17689
17849
|
}
|
|
@@ -17693,7 +17853,7 @@ var DaemonPidFile = class {
|
|
|
17693
17853
|
import { execFile } from "child_process";
|
|
17694
17854
|
import { createHash as createHash3 } from "crypto";
|
|
17695
17855
|
import { access as access2, open as open4, readdir as readdir3, readFile as readFile3, unlink as unlink2 } from "fs/promises";
|
|
17696
|
-
import
|
|
17856
|
+
import path22 from "path";
|
|
17697
17857
|
import { promisify } from "util";
|
|
17698
17858
|
|
|
17699
17859
|
// src/features/analysis/context_file_uploader.ts
|
|
@@ -18057,8 +18217,8 @@ function createConfigstoreStream(store, opts) {
|
|
|
18057
18217
|
heartbeatBuffer.length = 0;
|
|
18058
18218
|
}
|
|
18059
18219
|
}
|
|
18060
|
-
function setScopePath(
|
|
18061
|
-
scopePath =
|
|
18220
|
+
function setScopePath(path38) {
|
|
18221
|
+
scopePath = path38;
|
|
18062
18222
|
}
|
|
18063
18223
|
return { writable, flush, setScopePath };
|
|
18064
18224
|
}
|
|
@@ -18140,22 +18300,22 @@ function createDdBatch(config2) {
|
|
|
18140
18300
|
|
|
18141
18301
|
// src/utils/shared-logger/hostname.ts
|
|
18142
18302
|
import { createHash as createHash2 } from "crypto";
|
|
18143
|
-
import
|
|
18303
|
+
import os6 from "os";
|
|
18144
18304
|
function hashString(input) {
|
|
18145
18305
|
return createHash2("sha256").update(input).digest("hex").slice(0, 16);
|
|
18146
18306
|
}
|
|
18147
18307
|
function getPlainHostname() {
|
|
18148
18308
|
try {
|
|
18149
|
-
return `${
|
|
18309
|
+
return `${os6.userInfo().username}@${os6.hostname()}`;
|
|
18150
18310
|
} catch {
|
|
18151
|
-
return `unknown@${
|
|
18311
|
+
return `unknown@${os6.hostname()}`;
|
|
18152
18312
|
}
|
|
18153
18313
|
}
|
|
18154
18314
|
function getHashedHostname() {
|
|
18155
18315
|
try {
|
|
18156
|
-
return `${hashString(
|
|
18316
|
+
return `${hashString(os6.userInfo().username)}@${hashString(os6.hostname())}`;
|
|
18157
18317
|
} catch {
|
|
18158
|
-
return `unknown@${hashString(
|
|
18318
|
+
return `unknown@${hashString(os6.hostname())}`;
|
|
18159
18319
|
}
|
|
18160
18320
|
}
|
|
18161
18321
|
|
|
@@ -18282,7 +18442,7 @@ function createLogger(config2) {
|
|
|
18282
18442
|
|
|
18283
18443
|
// src/features/claude_code/hook_logger.ts
|
|
18284
18444
|
var DD_RUM_TOKEN = true ? "pubf59c0182545bfb4c299175119f1abf9b" : "";
|
|
18285
|
-
var CLI_VERSION = true ? "1.4.
|
|
18445
|
+
var CLI_VERSION = true ? "1.4.49" : "unknown";
|
|
18286
18446
|
var NAMESPACE = "mobbdev-claude-code-hook-logs";
|
|
18287
18447
|
var claudeCodeVersion;
|
|
18288
18448
|
function buildDdTags() {
|
|
@@ -18399,12 +18559,12 @@ async function resolveTranscriptPath(transcriptPath, sessionId) {
|
|
|
18399
18559
|
return transcriptPath;
|
|
18400
18560
|
} catch {
|
|
18401
18561
|
}
|
|
18402
|
-
const filename =
|
|
18403
|
-
const dirName =
|
|
18404
|
-
const projectsDir =
|
|
18562
|
+
const filename = path22.basename(transcriptPath);
|
|
18563
|
+
const dirName = path22.basename(path22.dirname(transcriptPath));
|
|
18564
|
+
const projectsDir = path22.dirname(path22.dirname(transcriptPath));
|
|
18405
18565
|
const baseDirName = dirName.replace(/[-.]claude-worktrees-.+$/, "");
|
|
18406
18566
|
if (baseDirName !== dirName) {
|
|
18407
|
-
const candidate =
|
|
18567
|
+
const candidate = path22.join(projectsDir, baseDirName, filename);
|
|
18408
18568
|
try {
|
|
18409
18569
|
await access2(candidate);
|
|
18410
18570
|
hookLog.info(
|
|
@@ -18426,7 +18586,7 @@ async function resolveTranscriptPath(transcriptPath, sessionId) {
|
|
|
18426
18586
|
const dirs = await readdir3(projectsDir);
|
|
18427
18587
|
for (const dir of dirs) {
|
|
18428
18588
|
if (dir === dirName) continue;
|
|
18429
|
-
const candidate =
|
|
18589
|
+
const candidate = path22.join(projectsDir, dir, filename);
|
|
18430
18590
|
try {
|
|
18431
18591
|
await access2(candidate);
|
|
18432
18592
|
hookLog.info(
|
|
@@ -18648,7 +18808,7 @@ async function cleanupStaleSessions(configDir) {
|
|
|
18648
18808
|
);
|
|
18649
18809
|
let deletedCount = 0;
|
|
18650
18810
|
for (const file of sessionFiles) {
|
|
18651
|
-
const filePath =
|
|
18811
|
+
const filePath = path22.join(configDir, file);
|
|
18652
18812
|
try {
|
|
18653
18813
|
const content = JSON.parse(await readFile3(filePath, "utf-8"));
|
|
18654
18814
|
let newest = 0;
|
|
@@ -18945,17 +19105,17 @@ function summarizeUploadErrTypes(errs) {
|
|
|
18945
19105
|
}
|
|
18946
19106
|
|
|
18947
19107
|
// src/features/claude_code/install_hook.ts
|
|
18948
|
-
import
|
|
19108
|
+
import fs15 from "fs";
|
|
18949
19109
|
import fsPromises4 from "fs/promises";
|
|
18950
|
-
import
|
|
18951
|
-
import
|
|
19110
|
+
import os7 from "os";
|
|
19111
|
+
import path23 from "path";
|
|
18952
19112
|
import chalk11 from "chalk";
|
|
18953
19113
|
|
|
18954
19114
|
// src/features/claude_code/daemon-check-shim.tmpl.js
|
|
18955
19115
|
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";
|
|
18956
19116
|
|
|
18957
19117
|
// src/features/claude_code/install_hook.ts
|
|
18958
|
-
var CLAUDE_SETTINGS_PATH =
|
|
19118
|
+
var CLAUDE_SETTINGS_PATH = path23.join(os7.homedir(), ".claude", "settings.json");
|
|
18959
19119
|
var RECOMMENDED_MATCHER = "*";
|
|
18960
19120
|
async function claudeSettingsExists() {
|
|
18961
19121
|
try {
|
|
@@ -18990,8 +19150,8 @@ function getDaemonCheckScript() {
|
|
|
18990
19150
|
);
|
|
18991
19151
|
}
|
|
18992
19152
|
function writeDaemonCheckScript() {
|
|
18993
|
-
|
|
18994
|
-
|
|
19153
|
+
fs15.mkdirSync(getMobbdevDir(), { recursive: true });
|
|
19154
|
+
fs15.writeFileSync(getDaemonCheckScriptPath(), getDaemonCheckScript(), "utf8");
|
|
18995
19155
|
}
|
|
18996
19156
|
function buildHookCommand(envPrefix) {
|
|
18997
19157
|
const nodeBin = process.execPath || "node";
|
|
@@ -19102,17 +19262,17 @@ async function installMobbHooks(options = {}) {
|
|
|
19102
19262
|
|
|
19103
19263
|
// src/features/claude_code/transcript_scanner.ts
|
|
19104
19264
|
import { lstat as lstat2, open as open5, readdir as readdir4, stat as stat4 } from "fs/promises";
|
|
19105
|
-
import
|
|
19106
|
-
import
|
|
19265
|
+
import os8 from "os";
|
|
19266
|
+
import path24 from "path";
|
|
19107
19267
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
19108
19268
|
function getClaudeProjectsDirs() {
|
|
19109
19269
|
const dirs = [];
|
|
19110
19270
|
const configDir = process.env["CLAUDE_CONFIG_DIR"];
|
|
19111
19271
|
if (configDir) {
|
|
19112
|
-
dirs.push(
|
|
19272
|
+
dirs.push(path24.join(configDir, "projects"));
|
|
19113
19273
|
}
|
|
19114
|
-
dirs.push(
|
|
19115
|
-
dirs.push(
|
|
19274
|
+
dirs.push(path24.join(os8.homedir(), ".config", "claude", "projects"));
|
|
19275
|
+
dirs.push(path24.join(os8.homedir(), ".claude", "projects"));
|
|
19116
19276
|
return dirs;
|
|
19117
19277
|
}
|
|
19118
19278
|
var META_MAX_BYTES = 4096;
|
|
@@ -19168,7 +19328,7 @@ async function collectJsonlFiles(files, dir, projectDir, seen, now, results, ove
|
|
|
19168
19328
|
sessionId = basename2;
|
|
19169
19329
|
isSubagent = false;
|
|
19170
19330
|
}
|
|
19171
|
-
const filePath =
|
|
19331
|
+
const filePath = path24.join(dir, file);
|
|
19172
19332
|
if (seen.has(filePath)) continue;
|
|
19173
19333
|
seen.add(filePath);
|
|
19174
19334
|
let fileStat;
|
|
@@ -19202,7 +19362,7 @@ async function scanForTranscripts(projectsDirs = getClaudeProjectsDirs()) {
|
|
|
19202
19362
|
continue;
|
|
19203
19363
|
}
|
|
19204
19364
|
for (const projName of projectDirs) {
|
|
19205
|
-
const projPath =
|
|
19365
|
+
const projPath = path24.join(projectsDir, projName);
|
|
19206
19366
|
let projStat;
|
|
19207
19367
|
try {
|
|
19208
19368
|
projStat = await stat4(projPath);
|
|
@@ -19219,7 +19379,7 @@ async function scanForTranscripts(projectsDirs = getClaudeProjectsDirs()) {
|
|
|
19219
19379
|
await collectJsonlFiles(files, projPath, projPath, seen, now, results);
|
|
19220
19380
|
for (const entry of files) {
|
|
19221
19381
|
if (!UUID_RE.test(entry)) continue;
|
|
19222
|
-
const subagentsDir =
|
|
19382
|
+
const subagentsDir = path24.join(projPath, entry, "subagents");
|
|
19223
19383
|
if (knownAbsentSubagentDirs.has(subagentsDir)) continue;
|
|
19224
19384
|
try {
|
|
19225
19385
|
const s = await stat4(subagentsDir);
|
|
@@ -19325,8 +19485,8 @@ async function startDaemon() {
|
|
|
19325
19485
|
const sessionCwdCache = /* @__PURE__ */ new Map();
|
|
19326
19486
|
let lastContextScanMs = 0;
|
|
19327
19487
|
const machineContext = {
|
|
19328
|
-
cpuCount:
|
|
19329
|
-
totalMemGB: Math.round(
|
|
19488
|
+
cpuCount: os9.cpus().length,
|
|
19489
|
+
totalMemGB: Math.round(os9.totalmem() / 1024 ** 3),
|
|
19330
19490
|
nodeVersion: process.version
|
|
19331
19491
|
};
|
|
19332
19492
|
while (true) {
|
|
@@ -19349,7 +19509,7 @@ async function startDaemon() {
|
|
|
19349
19509
|
for (const transcript of changed) {
|
|
19350
19510
|
const sessionStore = createSessionConfigStore(transcript.sessionId);
|
|
19351
19511
|
if (!cleanupConfigDir) {
|
|
19352
|
-
cleanupConfigDir =
|
|
19512
|
+
cleanupConfigDir = path25.dirname(sessionStore.path);
|
|
19353
19513
|
}
|
|
19354
19514
|
pidFile.updateHeartbeat();
|
|
19355
19515
|
const drainStart = Date.now();
|
|
@@ -19637,6 +19797,12 @@ var claudeCodeDaemonBuilder = (yargs2) => {
|
|
|
19637
19797
|
).strict();
|
|
19638
19798
|
};
|
|
19639
19799
|
var claudeCodeInstallHookHandler = async (argv) => {
|
|
19800
|
+
if (isStandaloneBinary()) {
|
|
19801
|
+
console.error(
|
|
19802
|
+
"Claude Code hooks are not available in the standalone mobbdev binary (they need a Node.js runtime). Install them with the npm package instead: npx mobbdev@latest claude-code-install-hook"
|
|
19803
|
+
);
|
|
19804
|
+
process.exit(1);
|
|
19805
|
+
}
|
|
19640
19806
|
try {
|
|
19641
19807
|
await getAuthenticatedGQLClient({ isSkipPrompts: false });
|
|
19642
19808
|
await installMobbHooks({ saveEnv: argv["save-env"] });
|
|
@@ -19705,8 +19871,8 @@ var WorkspaceService = class {
|
|
|
19705
19871
|
* Sets a known workspace path that was discovered through successful validation
|
|
19706
19872
|
* @param path The validated workspace path to store
|
|
19707
19873
|
*/
|
|
19708
|
-
static setKnownWorkspacePath(
|
|
19709
|
-
this.knownWorkspacePath =
|
|
19874
|
+
static setKnownWorkspacePath(path38) {
|
|
19875
|
+
this.knownWorkspacePath = path38;
|
|
19710
19876
|
}
|
|
19711
19877
|
/**
|
|
19712
19878
|
* Gets the known workspace path that was previously validated
|
|
@@ -19847,7 +20013,7 @@ var logDebug = (message, data) => logger2.log(message, "debug", data);
|
|
|
19847
20013
|
var log = logger2.log.bind(logger2);
|
|
19848
20014
|
|
|
19849
20015
|
// src/mcp/services/McpGQLClient.ts
|
|
19850
|
-
import
|
|
20016
|
+
import os10 from "os";
|
|
19851
20017
|
init_client_generates();
|
|
19852
20018
|
init_configs();
|
|
19853
20019
|
|
|
@@ -20056,6 +20222,7 @@ var partitionInteractiveFixes = (fixes) => {
|
|
|
20056
20222
|
};
|
|
20057
20223
|
|
|
20058
20224
|
// src/mcp/services/McpGQLClient.ts
|
|
20225
|
+
var MVS_SCAN_ONLY_ERROR = "Mobb Vibe Shield is not available for your organization: it is on Mobb Scan (scan-only), which reports issues but does not generate fixes. Contact support to enable Mobb Fixer.";
|
|
20059
20226
|
var McpGQLClient = class extends GQLClient {
|
|
20060
20227
|
constructor(args) {
|
|
20061
20228
|
super({
|
|
@@ -20235,12 +20402,16 @@ var McpGQLClient = class extends GQLClient {
|
|
|
20235
20402
|
throw new Error("User email not found");
|
|
20236
20403
|
}
|
|
20237
20404
|
const orgRes = await this._clientSdk.getLastOrg({ email: userEmail });
|
|
20238
|
-
const
|
|
20405
|
+
const organization = orgRes.user?.[0]?.userOrganizationsAndUserOrganizationRoles?.[0]?.organization;
|
|
20406
|
+
const organizationId = organization?.id;
|
|
20239
20407
|
if (!organizationId) {
|
|
20240
20408
|
throw new Error(
|
|
20241
20409
|
`The user with email:${userEmail} is not associated with any organization`
|
|
20242
20410
|
);
|
|
20243
20411
|
}
|
|
20412
|
+
if (organization.isFixEnabled === false) {
|
|
20413
|
+
throw new Error(MVS_SCAN_ONLY_ERROR);
|
|
20414
|
+
}
|
|
20244
20415
|
const mvsRes = await this._clientSdk.getMvsProject({ organizationId });
|
|
20245
20416
|
const projectId = mvsRes.getMvsProject?.projectId;
|
|
20246
20417
|
if (!projectId) {
|
|
@@ -20353,7 +20524,7 @@ var McpGQLClient = class extends GQLClient {
|
|
|
20353
20524
|
projectId: params.projectId ?? null,
|
|
20354
20525
|
repoUrl: params.repoUrl ?? null,
|
|
20355
20526
|
riskCount: params.riskCount ?? null,
|
|
20356
|
-
computerName:
|
|
20527
|
+
computerName: os10.hostname(),
|
|
20357
20528
|
computerUser: getComputerUser() ?? null,
|
|
20358
20529
|
clientVersion: packageJson.version
|
|
20359
20530
|
});
|
|
@@ -20636,9 +20807,9 @@ async function createAuthenticatedMcpGQLClient({
|
|
|
20636
20807
|
|
|
20637
20808
|
// src/mcp/services/McpUsageService/host.ts
|
|
20638
20809
|
import { execSync as execSync2 } from "child_process";
|
|
20639
|
-
import
|
|
20640
|
-
import
|
|
20641
|
-
import
|
|
20810
|
+
import fs16 from "fs";
|
|
20811
|
+
import os11 from "os";
|
|
20812
|
+
import path26 from "path";
|
|
20642
20813
|
var IDEs = ["cursor", "windsurf", "webstorm", "vscode", "claude"];
|
|
20643
20814
|
var runCommand = (cmd) => {
|
|
20644
20815
|
try {
|
|
@@ -20652,18 +20823,18 @@ var gitInfo = {
|
|
|
20652
20823
|
email: runCommand("git config user.email")
|
|
20653
20824
|
};
|
|
20654
20825
|
var getClaudeWorkspacePaths = () => {
|
|
20655
|
-
const home =
|
|
20656
|
-
const claudeIdePath =
|
|
20826
|
+
const home = os11.homedir();
|
|
20827
|
+
const claudeIdePath = path26.join(home, ".claude", "ide");
|
|
20657
20828
|
const workspacePaths = [];
|
|
20658
|
-
if (!
|
|
20829
|
+
if (!fs16.existsSync(claudeIdePath)) {
|
|
20659
20830
|
return workspacePaths;
|
|
20660
20831
|
}
|
|
20661
20832
|
try {
|
|
20662
|
-
const lockFiles =
|
|
20833
|
+
const lockFiles = fs16.readdirSync(claudeIdePath).filter((file) => file.endsWith(".lock"));
|
|
20663
20834
|
for (const lockFile of lockFiles) {
|
|
20664
|
-
const lockFilePath =
|
|
20835
|
+
const lockFilePath = path26.join(claudeIdePath, lockFile);
|
|
20665
20836
|
try {
|
|
20666
|
-
const lockContent = JSON.parse(
|
|
20837
|
+
const lockContent = JSON.parse(fs16.readFileSync(lockFilePath, "utf8"));
|
|
20667
20838
|
if (lockContent.workspaceFolders && Array.isArray(lockContent.workspaceFolders)) {
|
|
20668
20839
|
workspacePaths.push(...lockContent.workspaceFolders);
|
|
20669
20840
|
}
|
|
@@ -20681,29 +20852,29 @@ var getClaudeWorkspacePaths = () => {
|
|
|
20681
20852
|
return workspacePaths;
|
|
20682
20853
|
};
|
|
20683
20854
|
var getMCPConfigPaths = (hostName) => {
|
|
20684
|
-
const home =
|
|
20855
|
+
const home = os11.homedir();
|
|
20685
20856
|
const currentDir = process.env["WORKSPACE_FOLDER_PATHS"] || process.env["PWD"] || process.cwd();
|
|
20686
20857
|
switch (hostName.toLowerCase()) {
|
|
20687
20858
|
case "cursor":
|
|
20688
20859
|
return [
|
|
20689
|
-
|
|
20860
|
+
path26.join(currentDir, ".cursor", "mcp.json"),
|
|
20690
20861
|
// local first
|
|
20691
|
-
|
|
20862
|
+
path26.join(home, ".cursor", "mcp.json")
|
|
20692
20863
|
];
|
|
20693
20864
|
case "windsurf":
|
|
20694
20865
|
return [
|
|
20695
|
-
|
|
20866
|
+
path26.join(currentDir, ".codeium", "mcp_config.json"),
|
|
20696
20867
|
// local first
|
|
20697
|
-
|
|
20868
|
+
path26.join(home, ".codeium", "windsurf", "mcp_config.json")
|
|
20698
20869
|
];
|
|
20699
20870
|
case "webstorm":
|
|
20700
20871
|
return [];
|
|
20701
20872
|
case "visualstudiocode":
|
|
20702
20873
|
case "vscode":
|
|
20703
20874
|
return [
|
|
20704
|
-
|
|
20875
|
+
path26.join(currentDir, ".vscode", "mcp.json"),
|
|
20705
20876
|
// local first
|
|
20706
|
-
process.platform === "win32" ?
|
|
20877
|
+
process.platform === "win32" ? path26.join(home, "AppData", "Roaming", "Code", "User", "mcp.json") : path26.join(
|
|
20707
20878
|
home,
|
|
20708
20879
|
"Library",
|
|
20709
20880
|
"Application Support",
|
|
@@ -20714,13 +20885,13 @@ var getMCPConfigPaths = (hostName) => {
|
|
|
20714
20885
|
];
|
|
20715
20886
|
case "claude": {
|
|
20716
20887
|
const claudePaths = [
|
|
20717
|
-
|
|
20888
|
+
path26.join(currentDir, ".claude.json"),
|
|
20718
20889
|
// local first
|
|
20719
|
-
|
|
20890
|
+
path26.join(home, ".claude.json")
|
|
20720
20891
|
];
|
|
20721
20892
|
const workspacePaths = getClaudeWorkspacePaths();
|
|
20722
20893
|
for (const workspacePath of workspacePaths) {
|
|
20723
|
-
claudePaths.push(
|
|
20894
|
+
claudePaths.push(path26.join(workspacePath, ".mcp.json"));
|
|
20724
20895
|
}
|
|
20725
20896
|
return claudePaths;
|
|
20726
20897
|
}
|
|
@@ -20729,9 +20900,9 @@ var getMCPConfigPaths = (hostName) => {
|
|
|
20729
20900
|
}
|
|
20730
20901
|
};
|
|
20731
20902
|
var readConfigFile = (filePath) => {
|
|
20732
|
-
if (!
|
|
20903
|
+
if (!fs16.existsSync(filePath)) return null;
|
|
20733
20904
|
try {
|
|
20734
|
-
return JSON.parse(
|
|
20905
|
+
return JSON.parse(fs16.readFileSync(filePath, "utf8"));
|
|
20735
20906
|
} catch (error) {
|
|
20736
20907
|
logWarn(`[UsageService] Failed to read MCP config: ${filePath}`);
|
|
20737
20908
|
return null;
|
|
@@ -20771,7 +20942,7 @@ var readMCPConfig = (hostName) => {
|
|
|
20771
20942
|
};
|
|
20772
20943
|
var getRunningProcesses = () => {
|
|
20773
20944
|
try {
|
|
20774
|
-
return
|
|
20945
|
+
return os11.platform() === "win32" ? execSync2("tasklist", { encoding: "utf8" }) : execSync2("ps aux", { encoding: "utf8" });
|
|
20775
20946
|
} catch {
|
|
20776
20947
|
return "";
|
|
20777
20948
|
}
|
|
@@ -20846,7 +21017,7 @@ var versionCommands = {
|
|
|
20846
21017
|
}
|
|
20847
21018
|
};
|
|
20848
21019
|
var getProcessInfo = (pid) => {
|
|
20849
|
-
const platform2 =
|
|
21020
|
+
const platform2 = os11.platform();
|
|
20850
21021
|
try {
|
|
20851
21022
|
if (platform2 === "linux" || platform2 === "darwin") {
|
|
20852
21023
|
const output = execSync2(`ps -o pid=,ppid=,comm= -p ${pid}`, {
|
|
@@ -20881,10 +21052,10 @@ var getHostInfo = (additionalMcpList) => {
|
|
|
20881
21052
|
const ideConfigPaths = /* @__PURE__ */ new Set();
|
|
20882
21053
|
for (const ide of IDEs) {
|
|
20883
21054
|
const configPaths = getMCPConfigPaths(ide);
|
|
20884
|
-
configPaths.forEach((
|
|
21055
|
+
configPaths.forEach((path38) => ideConfigPaths.add(path38));
|
|
20885
21056
|
}
|
|
20886
21057
|
const uniqueAdditionalPaths = additionalMcpList.filter(
|
|
20887
|
-
(
|
|
21058
|
+
(path38) => !ideConfigPaths.has(path38)
|
|
20888
21059
|
);
|
|
20889
21060
|
for (const ide of IDEs) {
|
|
20890
21061
|
const cfg = readMCPConfig(ide);
|
|
@@ -20965,7 +21136,7 @@ var getHostInfo = (additionalMcpList) => {
|
|
|
20965
21136
|
const config2 = allConfigs[ide] || null;
|
|
20966
21137
|
const ideName = ide.charAt(0).toUpperCase() + ide.slice(1) || "Unknown";
|
|
20967
21138
|
let ideVersion = "Unknown";
|
|
20968
|
-
const platform2 =
|
|
21139
|
+
const platform2 = os11.platform();
|
|
20969
21140
|
const cmds = versionCommands[ideName]?.[platform2] ?? [];
|
|
20970
21141
|
for (const cmd of cmds) {
|
|
20971
21142
|
try {
|
|
@@ -20998,15 +21169,15 @@ var getHostInfo = (additionalMcpList) => {
|
|
|
20998
21169
|
|
|
20999
21170
|
// src/mcp/services/McpUsageService/McpUsageService.ts
|
|
21000
21171
|
import fetch6 from "node-fetch";
|
|
21001
|
-
import
|
|
21172
|
+
import os13 from "os";
|
|
21002
21173
|
import { v4 as uuidv42, v5 as uuidv5 } from "uuid";
|
|
21003
21174
|
init_configs();
|
|
21004
21175
|
|
|
21005
21176
|
// src/mcp/services/McpUsageService/system.ts
|
|
21006
21177
|
init_configs();
|
|
21007
|
-
import
|
|
21008
|
-
import
|
|
21009
|
-
import
|
|
21178
|
+
import fs17 from "fs";
|
|
21179
|
+
import os12 from "os";
|
|
21180
|
+
import path27 from "path";
|
|
21010
21181
|
var MAX_DEPTH = 2;
|
|
21011
21182
|
var patterns = ["mcp", "claude"];
|
|
21012
21183
|
var isFileMatch = (fileName) => {
|
|
@@ -21015,7 +21186,7 @@ var isFileMatch = (fileName) => {
|
|
|
21015
21186
|
};
|
|
21016
21187
|
var safeAccess = async (filePath) => {
|
|
21017
21188
|
try {
|
|
21018
|
-
await
|
|
21189
|
+
await fs17.promises.access(filePath, fs17.constants.R_OK);
|
|
21019
21190
|
return true;
|
|
21020
21191
|
} catch {
|
|
21021
21192
|
return false;
|
|
@@ -21024,9 +21195,9 @@ var safeAccess = async (filePath) => {
|
|
|
21024
21195
|
var searchDir = async (dir, depth = 0) => {
|
|
21025
21196
|
const results = [];
|
|
21026
21197
|
if (depth > MAX_DEPTH) return results;
|
|
21027
|
-
const entries = await
|
|
21198
|
+
const entries = await fs17.promises.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
21028
21199
|
for (const entry of entries) {
|
|
21029
|
-
const fullPath =
|
|
21200
|
+
const fullPath = path27.join(dir, entry.name);
|
|
21030
21201
|
if (entry.isFile() && isFileMatch(entry.name)) {
|
|
21031
21202
|
results.push(fullPath);
|
|
21032
21203
|
} else if (entry.isDirectory()) {
|
|
@@ -21040,17 +21211,17 @@ var searchDir = async (dir, depth = 0) => {
|
|
|
21040
21211
|
};
|
|
21041
21212
|
var findSystemMCPConfigs = async () => {
|
|
21042
21213
|
try {
|
|
21043
|
-
const home =
|
|
21044
|
-
const platform2 =
|
|
21214
|
+
const home = os12.homedir();
|
|
21215
|
+
const platform2 = os12.platform();
|
|
21045
21216
|
const knownDirs = platform2 === "win32" ? [
|
|
21046
|
-
|
|
21047
|
-
|
|
21048
|
-
|
|
21217
|
+
path27.join(home, ".cursor"),
|
|
21218
|
+
path27.join(home, "Documents"),
|
|
21219
|
+
path27.join(home, "Downloads")
|
|
21049
21220
|
] : [
|
|
21050
|
-
|
|
21051
|
-
process.env["XDG_CONFIG_HOME"] ||
|
|
21052
|
-
|
|
21053
|
-
|
|
21221
|
+
path27.join(home, ".cursor"),
|
|
21222
|
+
process.env["XDG_CONFIG_HOME"] || path27.join(home, ".config"),
|
|
21223
|
+
path27.join(home, "Documents"),
|
|
21224
|
+
path27.join(home, "Downloads")
|
|
21054
21225
|
];
|
|
21055
21226
|
const timeoutPromise = new Promise(
|
|
21056
21227
|
(resolve) => setTimeout(() => {
|
|
@@ -21062,7 +21233,7 @@ var findSystemMCPConfigs = async () => {
|
|
|
21062
21233
|
);
|
|
21063
21234
|
const searchPromise = Promise.all(
|
|
21064
21235
|
knownDirs.map(
|
|
21065
|
-
(dir) =>
|
|
21236
|
+
(dir) => fs17.existsSync(dir) ? searchDir(dir) : Promise.resolve([])
|
|
21066
21237
|
)
|
|
21067
21238
|
).then((results) => results.flat());
|
|
21068
21239
|
return await Promise.race([timeoutPromise, searchPromise]);
|
|
@@ -21113,7 +21284,7 @@ var McpUsageService = class {
|
|
|
21113
21284
|
generateHostId() {
|
|
21114
21285
|
const stored = configStore.get(this.configKey);
|
|
21115
21286
|
if (stored?.mcpHostId) return stored.mcpHostId;
|
|
21116
|
-
const interfaces =
|
|
21287
|
+
const interfaces = os13.networkInterfaces();
|
|
21117
21288
|
const macs = [];
|
|
21118
21289
|
for (const iface of Object.values(interfaces)) {
|
|
21119
21290
|
if (!iface) continue;
|
|
@@ -21121,7 +21292,7 @@ var McpUsageService = class {
|
|
|
21121
21292
|
if (net.mac && net.mac !== "00:00:00:00:00:00") macs.push(net.mac);
|
|
21122
21293
|
}
|
|
21123
21294
|
}
|
|
21124
|
-
const macString = macs.length ? macs.sort().join(",") : `${
|
|
21295
|
+
const macString = macs.length ? macs.sort().join(",") : `${os13.hostname()}-${uuidv42()}`;
|
|
21125
21296
|
const hostId = uuidv5(macString, uuidv5.DNS);
|
|
21126
21297
|
logDebug("[UsageService] Generated new host ID", { hostId });
|
|
21127
21298
|
return hostId;
|
|
@@ -21144,7 +21315,7 @@ var McpUsageService = class {
|
|
|
21144
21315
|
mcpHostId,
|
|
21145
21316
|
organizationId,
|
|
21146
21317
|
mcpVersion: packageJson.version,
|
|
21147
|
-
mcpOsName:
|
|
21318
|
+
mcpOsName: os13.platform(),
|
|
21148
21319
|
mcps: JSON.stringify(mcps),
|
|
21149
21320
|
status,
|
|
21150
21321
|
userName: user.name,
|
|
@@ -23465,31 +23636,31 @@ For a complete security audit workflow, use the \`full-security-audit\` prompt.
|
|
|
23465
23636
|
};
|
|
23466
23637
|
|
|
23467
23638
|
// src/mcp/services/McpDetectionService/CursorMcpDetectionService.ts
|
|
23468
|
-
import * as
|
|
23469
|
-
import * as
|
|
23470
|
-
import * as
|
|
23639
|
+
import * as fs20 from "fs";
|
|
23640
|
+
import * as os15 from "os";
|
|
23641
|
+
import * as path29 from "path";
|
|
23471
23642
|
|
|
23472
23643
|
// src/mcp/services/McpDetectionService/BaseMcpDetectionService.ts
|
|
23473
23644
|
init_configs();
|
|
23474
|
-
import * as
|
|
23645
|
+
import * as fs19 from "fs";
|
|
23475
23646
|
import fetch7 from "node-fetch";
|
|
23476
|
-
import * as
|
|
23647
|
+
import * as path28 from "path";
|
|
23477
23648
|
|
|
23478
23649
|
// src/mcp/services/McpDetectionService/McpDetectionServiceUtils.ts
|
|
23479
|
-
import * as
|
|
23480
|
-
import * as
|
|
23650
|
+
import * as fs18 from "fs";
|
|
23651
|
+
import * as os14 from "os";
|
|
23481
23652
|
|
|
23482
23653
|
// src/mcp/services/McpDetectionService/VscodeMcpDetectionService.ts
|
|
23483
|
-
import * as
|
|
23484
|
-
import * as
|
|
23485
|
-
import * as
|
|
23654
|
+
import * as fs21 from "fs";
|
|
23655
|
+
import * as os16 from "os";
|
|
23656
|
+
import * as path30 from "path";
|
|
23486
23657
|
|
|
23487
23658
|
// src/mcp/tools/checkForNewAvailableFixes/CheckForNewAvailableFixesTool.ts
|
|
23488
23659
|
import { z as z37 } from "zod";
|
|
23489
23660
|
|
|
23490
23661
|
// src/mcp/services/PathValidation.ts
|
|
23491
|
-
import
|
|
23492
|
-
import
|
|
23662
|
+
import fs22 from "fs";
|
|
23663
|
+
import path31 from "path";
|
|
23493
23664
|
async function validatePath(inputPath) {
|
|
23494
23665
|
logDebug("Validating MCP path", { inputPath });
|
|
23495
23666
|
if (/^\/[a-zA-Z]:\//.test(inputPath)) {
|
|
@@ -23521,7 +23692,7 @@ async function validatePath(inputPath) {
|
|
|
23521
23692
|
logError(error);
|
|
23522
23693
|
return { isValid: false, error, path: inputPath };
|
|
23523
23694
|
}
|
|
23524
|
-
const normalizedPath =
|
|
23695
|
+
const normalizedPath = path31.normalize(inputPath);
|
|
23525
23696
|
if (normalizedPath.includes("..")) {
|
|
23526
23697
|
const error = `Normalized path contains path traversal patterns: ${inputPath}`;
|
|
23527
23698
|
logError(error);
|
|
@@ -23548,7 +23719,7 @@ async function validatePath(inputPath) {
|
|
|
23548
23719
|
logDebug("Path validation successful", { inputPath });
|
|
23549
23720
|
logDebug("Checking path existence", { inputPath });
|
|
23550
23721
|
try {
|
|
23551
|
-
await
|
|
23722
|
+
await fs22.promises.access(inputPath);
|
|
23552
23723
|
logDebug("Path exists and is accessible", { inputPath });
|
|
23553
23724
|
WorkspaceService.setKnownWorkspacePath(inputPath);
|
|
23554
23725
|
logDebug("Stored validated path in WorkspaceService", { inputPath });
|
|
@@ -24302,10 +24473,10 @@ If you wish to scan files that were recently changed in your git history call th
|
|
|
24302
24473
|
init_FileUtils();
|
|
24303
24474
|
init_GitService();
|
|
24304
24475
|
init_configs();
|
|
24305
|
-
import
|
|
24476
|
+
import fs23 from "fs/promises";
|
|
24306
24477
|
import nodePath from "path";
|
|
24307
24478
|
var getLocalFiles = async ({
|
|
24308
|
-
path:
|
|
24479
|
+
path: path38,
|
|
24309
24480
|
maxFileSize = MCP_MAX_FILE_SIZE,
|
|
24310
24481
|
maxFiles,
|
|
24311
24482
|
isAllFilesScan,
|
|
@@ -24313,17 +24484,17 @@ var getLocalFiles = async ({
|
|
|
24313
24484
|
scanRecentlyChangedFiles
|
|
24314
24485
|
}) => {
|
|
24315
24486
|
logDebug(`[${scanContext}] Starting getLocalFiles`, {
|
|
24316
|
-
path:
|
|
24487
|
+
path: path38,
|
|
24317
24488
|
maxFileSize,
|
|
24318
24489
|
maxFiles,
|
|
24319
24490
|
isAllFilesScan,
|
|
24320
24491
|
scanRecentlyChangedFiles
|
|
24321
24492
|
});
|
|
24322
24493
|
try {
|
|
24323
|
-
const resolvedRepoPath = await
|
|
24494
|
+
const resolvedRepoPath = await fs23.realpath(path38);
|
|
24324
24495
|
logDebug(`[${scanContext}] Resolved repository path`, {
|
|
24325
24496
|
resolvedRepoPath,
|
|
24326
|
-
originalPath:
|
|
24497
|
+
originalPath: path38
|
|
24327
24498
|
});
|
|
24328
24499
|
const gitService = new GitService(resolvedRepoPath, log);
|
|
24329
24500
|
const gitValidation = await gitService.validateRepository();
|
|
@@ -24336,7 +24507,7 @@ var getLocalFiles = async ({
|
|
|
24336
24507
|
if (!gitValidation.isValid || isAllFilesScan) {
|
|
24337
24508
|
try {
|
|
24338
24509
|
files = await FileUtils.getLastChangedFiles({
|
|
24339
|
-
dir:
|
|
24510
|
+
dir: path38,
|
|
24340
24511
|
maxFileSize,
|
|
24341
24512
|
maxFiles,
|
|
24342
24513
|
isAllFilesScan
|
|
@@ -24400,7 +24571,7 @@ var getLocalFiles = async ({
|
|
|
24400
24571
|
absoluteFilePath
|
|
24401
24572
|
);
|
|
24402
24573
|
try {
|
|
24403
|
-
const fileStat = await
|
|
24574
|
+
const fileStat = await fs23.stat(absoluteFilePath);
|
|
24404
24575
|
return {
|
|
24405
24576
|
filename: nodePath.basename(absoluteFilePath),
|
|
24406
24577
|
relativePath,
|
|
@@ -24428,7 +24599,7 @@ var getLocalFiles = async ({
|
|
|
24428
24599
|
logError(`${scanContext}Unexpected error in getLocalFiles`, {
|
|
24429
24600
|
error: error instanceof Error ? error.message : String(error),
|
|
24430
24601
|
stack: error instanceof Error ? error.stack : void 0,
|
|
24431
|
-
path:
|
|
24602
|
+
path: path38
|
|
24432
24603
|
});
|
|
24433
24604
|
throw error;
|
|
24434
24605
|
}
|
|
@@ -24437,8 +24608,8 @@ var getLocalFiles = async ({
|
|
|
24437
24608
|
// src/mcp/services/LocalMobbFolderService.ts
|
|
24438
24609
|
init_GitService();
|
|
24439
24610
|
init_issueTypeCatalog();
|
|
24440
|
-
import
|
|
24441
|
-
import
|
|
24611
|
+
import fs24 from "fs";
|
|
24612
|
+
import path32 from "path";
|
|
24442
24613
|
function extractPathFromPatch(patch) {
|
|
24443
24614
|
const match = patch?.match(/diff --git a\/([^\s]+) b\//);
|
|
24444
24615
|
return match?.[1] ?? null;
|
|
@@ -24520,19 +24691,19 @@ var LocalMobbFolderService = class {
|
|
|
24520
24691
|
"[LocalMobbFolderService] Non-git repository detected, skipping .gitignore operations"
|
|
24521
24692
|
);
|
|
24522
24693
|
}
|
|
24523
|
-
const mobbFolderPath =
|
|
24694
|
+
const mobbFolderPath = path32.join(
|
|
24524
24695
|
this.repoPath,
|
|
24525
24696
|
this.defaultMobbFolderName
|
|
24526
24697
|
);
|
|
24527
|
-
if (!
|
|
24698
|
+
if (!fs24.existsSync(mobbFolderPath)) {
|
|
24528
24699
|
logInfo("[LocalMobbFolderService] Creating .mobb folder", {
|
|
24529
24700
|
mobbFolderPath
|
|
24530
24701
|
});
|
|
24531
|
-
|
|
24702
|
+
fs24.mkdirSync(mobbFolderPath, { recursive: true });
|
|
24532
24703
|
} else {
|
|
24533
24704
|
logDebug("[LocalMobbFolderService] .mobb folder already exists");
|
|
24534
24705
|
}
|
|
24535
|
-
const stats =
|
|
24706
|
+
const stats = fs24.statSync(mobbFolderPath);
|
|
24536
24707
|
if (!stats.isDirectory()) {
|
|
24537
24708
|
throw new Error(`Path exists but is not a directory: ${mobbFolderPath}`);
|
|
24538
24709
|
}
|
|
@@ -24573,13 +24744,13 @@ var LocalMobbFolderService = class {
|
|
|
24573
24744
|
logDebug("[LocalMobbFolderService] Git repository validated successfully");
|
|
24574
24745
|
} else {
|
|
24575
24746
|
try {
|
|
24576
|
-
const stats =
|
|
24747
|
+
const stats = fs24.statSync(this.repoPath);
|
|
24577
24748
|
if (!stats.isDirectory()) {
|
|
24578
24749
|
throw new Error(
|
|
24579
24750
|
`Path exists but is not a directory: ${this.repoPath}`
|
|
24580
24751
|
);
|
|
24581
24752
|
}
|
|
24582
|
-
|
|
24753
|
+
fs24.accessSync(this.repoPath, fs24.constants.R_OK | fs24.constants.W_OK);
|
|
24583
24754
|
logDebug(
|
|
24584
24755
|
"[LocalMobbFolderService] Non-git directory validated successfully"
|
|
24585
24756
|
);
|
|
@@ -24692,8 +24863,8 @@ var LocalMobbFolderService = class {
|
|
|
24692
24863
|
mobbFolderPath,
|
|
24693
24864
|
baseFileName
|
|
24694
24865
|
);
|
|
24695
|
-
const filePath =
|
|
24696
|
-
await
|
|
24866
|
+
const filePath = path32.join(mobbFolderPath, uniqueFileName);
|
|
24867
|
+
await fs24.promises.writeFile(filePath, patch, "utf8");
|
|
24697
24868
|
logInfo("[LocalMobbFolderService] Patch saved successfully", {
|
|
24698
24869
|
filePath,
|
|
24699
24870
|
fileName: uniqueFileName,
|
|
@@ -24750,11 +24921,11 @@ var LocalMobbFolderService = class {
|
|
|
24750
24921
|
* @returns Unique filename that doesn't conflict with existing files
|
|
24751
24922
|
*/
|
|
24752
24923
|
getUniqueFileName(folderPath, baseFileName) {
|
|
24753
|
-
const baseName =
|
|
24754
|
-
const extension =
|
|
24924
|
+
const baseName = path32.parse(baseFileName).name;
|
|
24925
|
+
const extension = path32.parse(baseFileName).ext;
|
|
24755
24926
|
let uniqueFileName = baseFileName;
|
|
24756
24927
|
let index = 1;
|
|
24757
|
-
while (
|
|
24928
|
+
while (fs24.existsSync(path32.join(folderPath, uniqueFileName))) {
|
|
24758
24929
|
uniqueFileName = `${baseName}-${index}${extension}`;
|
|
24759
24930
|
index++;
|
|
24760
24931
|
if (index > 1e3) {
|
|
@@ -24785,18 +24956,18 @@ var LocalMobbFolderService = class {
|
|
|
24785
24956
|
logDebug("[LocalMobbFolderService] Logging patch info", { fixId: fix.id });
|
|
24786
24957
|
try {
|
|
24787
24958
|
const mobbFolderPath = await this.getFolder();
|
|
24788
|
-
const patchInfoPath =
|
|
24959
|
+
const patchInfoPath = path32.join(mobbFolderPath, "patchInfo.md");
|
|
24789
24960
|
const markdownContent = this.generateFixMarkdown(fix, savedPatchFileName);
|
|
24790
24961
|
let existingContent = "";
|
|
24791
|
-
if (
|
|
24792
|
-
existingContent = await
|
|
24962
|
+
if (fs24.existsSync(patchInfoPath)) {
|
|
24963
|
+
existingContent = await fs24.promises.readFile(patchInfoPath, "utf8");
|
|
24793
24964
|
logDebug("[LocalMobbFolderService] Existing patchInfo.md found");
|
|
24794
24965
|
} else {
|
|
24795
24966
|
logDebug("[LocalMobbFolderService] Creating new patchInfo.md file");
|
|
24796
24967
|
}
|
|
24797
24968
|
const separator = existingContent ? "\n\n================================================================================\n\n" : "";
|
|
24798
24969
|
const updatedContent = `${markdownContent}${separator}${existingContent}`;
|
|
24799
|
-
await
|
|
24970
|
+
await fs24.promises.writeFile(patchInfoPath, updatedContent, "utf8");
|
|
24800
24971
|
logInfo("[LocalMobbFolderService] Patch info logged successfully", {
|
|
24801
24972
|
patchInfoPath,
|
|
24802
24973
|
fixId: fix.id,
|
|
@@ -24827,7 +24998,7 @@ var LocalMobbFolderService = class {
|
|
|
24827
24998
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
24828
24999
|
const patch = this.extractPatchFromFix(fix);
|
|
24829
25000
|
const relativePatchedFilePath = patch ? extractPathFromPatch(patch) : null;
|
|
24830
|
-
const patchedFilePath = relativePatchedFilePath ?
|
|
25001
|
+
const patchedFilePath = relativePatchedFilePath ? path32.resolve(this.repoPath, relativePatchedFilePath) : null;
|
|
24831
25002
|
const fixIdentifier = savedPatchFileName ? savedPatchFileName.replace(".patch", "") : fix.id;
|
|
24832
25003
|
let markdown = `# Fix ${fixIdentifier}
|
|
24833
25004
|
|
|
@@ -25169,16 +25340,16 @@ import {
|
|
|
25169
25340
|
unlinkSync,
|
|
25170
25341
|
writeFileSync as writeFileSync3
|
|
25171
25342
|
} from "fs";
|
|
25172
|
-
import
|
|
25343
|
+
import fs25 from "fs/promises";
|
|
25173
25344
|
import parseDiff2 from "parse-diff";
|
|
25174
|
-
import
|
|
25345
|
+
import path33 from "path";
|
|
25175
25346
|
var PatchApplicationService = class {
|
|
25176
25347
|
/**
|
|
25177
25348
|
* Gets the appropriate comment syntax for a file based on its extension
|
|
25178
25349
|
*/
|
|
25179
25350
|
static getCommentSyntax(filePath) {
|
|
25180
|
-
const ext =
|
|
25181
|
-
const basename2 =
|
|
25351
|
+
const ext = path33.extname(filePath).toLowerCase();
|
|
25352
|
+
const basename2 = path33.basename(filePath);
|
|
25182
25353
|
const commentMap = {
|
|
25183
25354
|
// C-style languages (single line comments)
|
|
25184
25355
|
".js": "//",
|
|
@@ -25386,7 +25557,7 @@ var PatchApplicationService = class {
|
|
|
25386
25557
|
}
|
|
25387
25558
|
);
|
|
25388
25559
|
}
|
|
25389
|
-
const dirPath =
|
|
25560
|
+
const dirPath = path33.dirname(normalizedFilePath);
|
|
25390
25561
|
mkdirSync(dirPath, { recursive: true });
|
|
25391
25562
|
writeFileSync3(normalizedFilePath, finalContent, "utf8");
|
|
25392
25563
|
return normalizedFilePath;
|
|
@@ -25395,9 +25566,9 @@ var PatchApplicationService = class {
|
|
|
25395
25566
|
repositoryPath,
|
|
25396
25567
|
targetPath
|
|
25397
25568
|
}) {
|
|
25398
|
-
const repoRoot =
|
|
25399
|
-
const normalizedPath =
|
|
25400
|
-
const repoRootWithSep = repoRoot.endsWith(
|
|
25569
|
+
const repoRoot = path33.resolve(repositoryPath);
|
|
25570
|
+
const normalizedPath = path33.resolve(repoRoot, targetPath);
|
|
25571
|
+
const repoRootWithSep = repoRoot.endsWith(path33.sep) ? repoRoot : `${repoRoot}${path33.sep}`;
|
|
25401
25572
|
if (normalizedPath !== repoRoot && !normalizedPath.startsWith(repoRootWithSep)) {
|
|
25402
25573
|
throw new Error(
|
|
25403
25574
|
`Security violation: target path ${targetPath} resolves outside repository`
|
|
@@ -25406,7 +25577,7 @@ var PatchApplicationService = class {
|
|
|
25406
25577
|
return {
|
|
25407
25578
|
repoRoot,
|
|
25408
25579
|
normalizedPath,
|
|
25409
|
-
relativePath:
|
|
25580
|
+
relativePath: path33.relative(repoRoot, normalizedPath)
|
|
25410
25581
|
};
|
|
25411
25582
|
}
|
|
25412
25583
|
/**
|
|
@@ -25688,9 +25859,9 @@ var PatchApplicationService = class {
|
|
|
25688
25859
|
continue;
|
|
25689
25860
|
}
|
|
25690
25861
|
try {
|
|
25691
|
-
const absolutePath =
|
|
25862
|
+
const absolutePath = path33.resolve(repositoryPath, targetFile);
|
|
25692
25863
|
if (existsSync6(absolutePath)) {
|
|
25693
|
-
const stats = await
|
|
25864
|
+
const stats = await fs25.stat(absolutePath);
|
|
25694
25865
|
const fileModTime = stats.mtime.getTime();
|
|
25695
25866
|
if (fileModTime > scanStartTime) {
|
|
25696
25867
|
logError(
|
|
@@ -25732,7 +25903,7 @@ var PatchApplicationService = class {
|
|
|
25732
25903
|
const appliedFixes = [];
|
|
25733
25904
|
const failedFixes = [];
|
|
25734
25905
|
const skippedFixes = [];
|
|
25735
|
-
const resolvedRepoPath = await
|
|
25906
|
+
const resolvedRepoPath = await fs25.realpath(repositoryPath);
|
|
25736
25907
|
logInfo(
|
|
25737
25908
|
`[${scanContext}] Starting patch application for ${fixes.length} fixes`,
|
|
25738
25909
|
{
|
|
@@ -25921,7 +26092,7 @@ var PatchApplicationService = class {
|
|
|
25921
26092
|
fix,
|
|
25922
26093
|
scanContext
|
|
25923
26094
|
});
|
|
25924
|
-
appliedFiles.push(
|
|
26095
|
+
appliedFiles.push(path33.relative(repositoryPath, actualPath));
|
|
25925
26096
|
logDebug(`[${scanContext}] Created new file: ${relativePath}`);
|
|
25926
26097
|
}
|
|
25927
26098
|
/**
|
|
@@ -25970,7 +26141,7 @@ var PatchApplicationService = class {
|
|
|
25970
26141
|
fix,
|
|
25971
26142
|
scanContext
|
|
25972
26143
|
});
|
|
25973
|
-
appliedFiles.push(
|
|
26144
|
+
appliedFiles.push(path33.relative(repositoryPath, actualPath));
|
|
25974
26145
|
logDebug(`[${scanContext}] Modified file: ${relativePath}`);
|
|
25975
26146
|
}
|
|
25976
26147
|
}
|
|
@@ -26162,13 +26333,13 @@ var PatchApplicationService = class {
|
|
|
26162
26333
|
// src/mcp/services/ScanFiles.ts
|
|
26163
26334
|
init_client_generates();
|
|
26164
26335
|
init_GitService();
|
|
26165
|
-
import
|
|
26336
|
+
import os17 from "os";
|
|
26166
26337
|
init_configs();
|
|
26167
26338
|
|
|
26168
26339
|
// src/mcp/services/FileOperations.ts
|
|
26169
26340
|
init_FileUtils();
|
|
26170
|
-
import
|
|
26171
|
-
import
|
|
26341
|
+
import fs26 from "fs";
|
|
26342
|
+
import path34 from "path";
|
|
26172
26343
|
import AdmZip5 from "adm-zip";
|
|
26173
26344
|
var FileOperations = class {
|
|
26174
26345
|
/**
|
|
@@ -26188,10 +26359,10 @@ var FileOperations = class {
|
|
|
26188
26359
|
let packedFilesCount = 0;
|
|
26189
26360
|
const packedFiles = [];
|
|
26190
26361
|
const excludedFiles = [];
|
|
26191
|
-
const resolvedRepoPath =
|
|
26362
|
+
const resolvedRepoPath = path34.resolve(repositoryPath);
|
|
26192
26363
|
for (const filepath of fileList) {
|
|
26193
|
-
const absoluteFilepath =
|
|
26194
|
-
const resolvedFilePath =
|
|
26364
|
+
const absoluteFilepath = path34.join(repositoryPath, filepath);
|
|
26365
|
+
const resolvedFilePath = path34.resolve(absoluteFilepath);
|
|
26195
26366
|
if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
|
|
26196
26367
|
const reason = "potential path traversal security risk";
|
|
26197
26368
|
logDebug(`[FileOperations] Skipping ${filepath} due to ${reason}`);
|
|
@@ -26238,11 +26409,11 @@ var FileOperations = class {
|
|
|
26238
26409
|
fileList,
|
|
26239
26410
|
repositoryPath
|
|
26240
26411
|
}) {
|
|
26241
|
-
const resolvedRepoPath =
|
|
26412
|
+
const resolvedRepoPath = path34.resolve(repositoryPath);
|
|
26242
26413
|
const validatedPaths = [];
|
|
26243
26414
|
for (const filepath of fileList) {
|
|
26244
|
-
const absoluteFilepath =
|
|
26245
|
-
const resolvedFilePath =
|
|
26415
|
+
const absoluteFilepath = path34.join(repositoryPath, filepath);
|
|
26416
|
+
const resolvedFilePath = path34.resolve(absoluteFilepath);
|
|
26246
26417
|
if (!resolvedFilePath.startsWith(resolvedRepoPath)) {
|
|
26247
26418
|
logDebug(
|
|
26248
26419
|
`[FileOperations] Rejecting ${filepath} - path traversal attempt detected`
|
|
@@ -26250,7 +26421,7 @@ var FileOperations = class {
|
|
|
26250
26421
|
continue;
|
|
26251
26422
|
}
|
|
26252
26423
|
try {
|
|
26253
|
-
await
|
|
26424
|
+
await fs26.promises.access(absoluteFilepath, fs26.constants.R_OK);
|
|
26254
26425
|
validatedPaths.push(filepath);
|
|
26255
26426
|
} catch (error) {
|
|
26256
26427
|
logDebug(
|
|
@@ -26269,8 +26440,8 @@ var FileOperations = class {
|
|
|
26269
26440
|
const fileDataArray = [];
|
|
26270
26441
|
for (const absolutePath of filePaths) {
|
|
26271
26442
|
try {
|
|
26272
|
-
const content = await
|
|
26273
|
-
const relativePath =
|
|
26443
|
+
const content = await fs26.promises.readFile(absolutePath);
|
|
26444
|
+
const relativePath = path34.basename(absolutePath);
|
|
26274
26445
|
fileDataArray.push({
|
|
26275
26446
|
relativePath,
|
|
26276
26447
|
absolutePath,
|
|
@@ -26295,7 +26466,7 @@ var FileOperations = class {
|
|
|
26295
26466
|
relativeFilepath
|
|
26296
26467
|
}) {
|
|
26297
26468
|
try {
|
|
26298
|
-
return await
|
|
26469
|
+
return await fs26.promises.readFile(absoluteFilepath);
|
|
26299
26470
|
} catch (fsError) {
|
|
26300
26471
|
logError(
|
|
26301
26472
|
`[FileOperations] Failed to read ${relativeFilepath} from filesystem: ${fsError}`
|
|
@@ -26439,7 +26610,7 @@ var executeSecurityScan = async ({
|
|
|
26439
26610
|
fileCount,
|
|
26440
26611
|
// MVS device attribution: a developer appears in the MVS Developers grid
|
|
26441
26612
|
// from scanning alone (no fresh login needed).
|
|
26442
|
-
computerName:
|
|
26613
|
+
computerName: os17.hostname(),
|
|
26443
26614
|
computerUser: getComputerUser(),
|
|
26444
26615
|
clientVersion: packageJson.version
|
|
26445
26616
|
};
|
|
@@ -26589,14 +26760,14 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
26589
26760
|
* since the last scan.
|
|
26590
26761
|
*/
|
|
26591
26762
|
async scanForSecurityVulnerabilities({
|
|
26592
|
-
path:
|
|
26763
|
+
path: path38,
|
|
26593
26764
|
isAllDetectionRulesScan,
|
|
26594
26765
|
isAllFilesScan,
|
|
26595
26766
|
scanContext
|
|
26596
26767
|
}) {
|
|
26597
26768
|
this.hasAuthenticationFailed = false;
|
|
26598
26769
|
logDebug(`[${scanContext}] Scanning for new security vulnerabilities`, {
|
|
26599
|
-
path:
|
|
26770
|
+
path: path38
|
|
26600
26771
|
});
|
|
26601
26772
|
if (!this.gqlClient) {
|
|
26602
26773
|
logInfo(`[${scanContext}] No GQL client found, skipping scan`);
|
|
@@ -26612,11 +26783,11 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
26612
26783
|
}
|
|
26613
26784
|
logDebug(
|
|
26614
26785
|
`[${scanContext}] Connected to the API, assembling list of files to scan`,
|
|
26615
|
-
{ path:
|
|
26786
|
+
{ path: path38 }
|
|
26616
26787
|
);
|
|
26617
26788
|
const isBackgroundScan = scanContext === ScanContext.BACKGROUND_INITIAL || scanContext === ScanContext.BACKGROUND_PERIODIC;
|
|
26618
26789
|
const files = await getLocalFiles({
|
|
26619
|
-
path:
|
|
26790
|
+
path: path38,
|
|
26620
26791
|
isAllFilesScan,
|
|
26621
26792
|
scanContext,
|
|
26622
26793
|
scanRecentlyChangedFiles: !isBackgroundScan
|
|
@@ -26642,13 +26813,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
26642
26813
|
});
|
|
26643
26814
|
const { fixReportId, projectId } = await scanFiles({
|
|
26644
26815
|
fileList: filesToScan.map((file) => file.relativePath),
|
|
26645
|
-
repositoryPath:
|
|
26816
|
+
repositoryPath: path38,
|
|
26646
26817
|
gqlClient: this.gqlClient,
|
|
26647
26818
|
isAllDetectionRulesScan,
|
|
26648
26819
|
scanContext
|
|
26649
26820
|
});
|
|
26650
26821
|
logInfo(
|
|
26651
|
-
`[${scanContext}] Security scan completed for ${
|
|
26822
|
+
`[${scanContext}] Security scan completed for ${path38} reportId: ${fixReportId} projectId: ${projectId}`
|
|
26652
26823
|
);
|
|
26653
26824
|
if (isAllFilesScan) {
|
|
26654
26825
|
return;
|
|
@@ -26952,13 +27123,13 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
26952
27123
|
});
|
|
26953
27124
|
return scannedFiles.some((file) => file.relativePath === fixFile);
|
|
26954
27125
|
}
|
|
26955
|
-
async getFreshFixes({ path:
|
|
27126
|
+
async getFreshFixes({ path: path38 }) {
|
|
26956
27127
|
const scanContext = ScanContext.USER_REQUEST;
|
|
26957
|
-
logDebug(`[${scanContext}] Getting fresh fixes`, { path:
|
|
26958
|
-
if (this.path !==
|
|
26959
|
-
this.path =
|
|
27128
|
+
logDebug(`[${scanContext}] Getting fresh fixes`, { path: path38 });
|
|
27129
|
+
if (this.path !== path38) {
|
|
27130
|
+
this.path = path38;
|
|
26960
27131
|
this.reset();
|
|
26961
|
-
logInfo(`[${scanContext}] Reset service state for new path`, { path:
|
|
27132
|
+
logInfo(`[${scanContext}] Reset service state for new path`, { path: path38 });
|
|
26962
27133
|
}
|
|
26963
27134
|
try {
|
|
26964
27135
|
const loginContext = createMcpLoginContext("check_new_fixes");
|
|
@@ -26977,7 +27148,7 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
26977
27148
|
}
|
|
26978
27149
|
throw error;
|
|
26979
27150
|
}
|
|
26980
|
-
this.triggerScan({ path:
|
|
27151
|
+
this.triggerScan({ path: path38, gqlClient: this.gqlClient });
|
|
26981
27152
|
let isMvsAutoFixEnabled = null;
|
|
26982
27153
|
try {
|
|
26983
27154
|
isMvsAutoFixEnabled = await this.gqlClient.getMvsAutoFixSettings();
|
|
@@ -27011,33 +27182,33 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
27011
27182
|
return noFreshFixesPrompt;
|
|
27012
27183
|
}
|
|
27013
27184
|
triggerScan({
|
|
27014
|
-
path:
|
|
27185
|
+
path: path38,
|
|
27015
27186
|
gqlClient
|
|
27016
27187
|
}) {
|
|
27017
|
-
if (this.path !==
|
|
27018
|
-
this.path =
|
|
27188
|
+
if (this.path !== path38) {
|
|
27189
|
+
this.path = path38;
|
|
27019
27190
|
this.reset();
|
|
27020
|
-
logInfo(`Reset service state for new path in triggerScan`, { path:
|
|
27191
|
+
logInfo(`Reset service state for new path in triggerScan`, { path: path38 });
|
|
27021
27192
|
}
|
|
27022
27193
|
this.gqlClient = gqlClient;
|
|
27023
27194
|
if (!this.intervalId) {
|
|
27024
|
-
this.startPeriodicScanning(
|
|
27025
|
-
this.executeInitialScan(
|
|
27026
|
-
void this.executeInitialFullScan(
|
|
27195
|
+
this.startPeriodicScanning(path38);
|
|
27196
|
+
this.executeInitialScan(path38);
|
|
27197
|
+
void this.executeInitialFullScan(path38);
|
|
27027
27198
|
}
|
|
27028
27199
|
}
|
|
27029
|
-
startPeriodicScanning(
|
|
27200
|
+
startPeriodicScanning(path38) {
|
|
27030
27201
|
const scanContext = ScanContext.BACKGROUND_PERIODIC;
|
|
27031
27202
|
logDebug(
|
|
27032
27203
|
`[${scanContext}] Starting periodic scan for new security vulnerabilities`,
|
|
27033
27204
|
{
|
|
27034
|
-
path:
|
|
27205
|
+
path: path38
|
|
27035
27206
|
}
|
|
27036
27207
|
);
|
|
27037
27208
|
this.intervalId = setInterval(() => {
|
|
27038
|
-
logDebug(`[${scanContext}] Triggering periodic security scan`, { path:
|
|
27209
|
+
logDebug(`[${scanContext}] Triggering periodic security scan`, { path: path38 });
|
|
27039
27210
|
this.scanForSecurityVulnerabilities({
|
|
27040
|
-
path:
|
|
27211
|
+
path: path38,
|
|
27041
27212
|
scanContext
|
|
27042
27213
|
}).catch((error) => {
|
|
27043
27214
|
logError(`[${scanContext}] Error during periodic security scan`, {
|
|
@@ -27046,45 +27217,45 @@ var _CheckForNewAvailableFixesService = class _CheckForNewAvailableFixesService
|
|
|
27046
27217
|
});
|
|
27047
27218
|
}, MCP_PERIODIC_CHECK_INTERVAL);
|
|
27048
27219
|
}
|
|
27049
|
-
async executeInitialFullScan(
|
|
27220
|
+
async executeInitialFullScan(path38) {
|
|
27050
27221
|
const scanContext = ScanContext.FULL_SCAN;
|
|
27051
|
-
logDebug(`[${scanContext}] Triggering initial full security scan`, { path:
|
|
27222
|
+
logDebug(`[${scanContext}] Triggering initial full security scan`, { path: path38 });
|
|
27052
27223
|
logDebug(`[${scanContext}] Full scan paths scanned`, {
|
|
27053
27224
|
fullScanPathsScanned: this.fullScanPathsScanned
|
|
27054
27225
|
});
|
|
27055
|
-
if (this.fullScanPathsScanned.includes(
|
|
27226
|
+
if (this.fullScanPathsScanned.includes(path38)) {
|
|
27056
27227
|
logDebug(`[${scanContext}] Full scan already executed for this path`, {
|
|
27057
|
-
path:
|
|
27228
|
+
path: path38
|
|
27058
27229
|
});
|
|
27059
27230
|
return;
|
|
27060
27231
|
}
|
|
27061
27232
|
configStore.set("fullScanPathsScanned", [
|
|
27062
27233
|
...this.fullScanPathsScanned,
|
|
27063
|
-
|
|
27234
|
+
path38
|
|
27064
27235
|
]);
|
|
27065
27236
|
try {
|
|
27066
27237
|
await this.scanForSecurityVulnerabilities({
|
|
27067
|
-
path:
|
|
27238
|
+
path: path38,
|
|
27068
27239
|
isAllFilesScan: true,
|
|
27069
27240
|
isAllDetectionRulesScan: true,
|
|
27070
27241
|
scanContext: ScanContext.FULL_SCAN
|
|
27071
27242
|
});
|
|
27072
|
-
if (!this.fullScanPathsScanned.includes(
|
|
27073
|
-
this.fullScanPathsScanned.push(
|
|
27243
|
+
if (!this.fullScanPathsScanned.includes(path38)) {
|
|
27244
|
+
this.fullScanPathsScanned.push(path38);
|
|
27074
27245
|
configStore.set("fullScanPathsScanned", this.fullScanPathsScanned);
|
|
27075
27246
|
}
|
|
27076
|
-
logInfo(`[${scanContext}] Full scan completed`, { path:
|
|
27247
|
+
logInfo(`[${scanContext}] Full scan completed`, { path: path38 });
|
|
27077
27248
|
} catch (error) {
|
|
27078
27249
|
logError(`[${scanContext}] Error during initial full security scan`, {
|
|
27079
27250
|
error
|
|
27080
27251
|
});
|
|
27081
27252
|
}
|
|
27082
27253
|
}
|
|
27083
|
-
executeInitialScan(
|
|
27254
|
+
executeInitialScan(path38) {
|
|
27084
27255
|
const scanContext = ScanContext.BACKGROUND_INITIAL;
|
|
27085
|
-
logDebug(`[${scanContext}] Triggering initial security scan`, { path:
|
|
27256
|
+
logDebug(`[${scanContext}] Triggering initial security scan`, { path: path38 });
|
|
27086
27257
|
this.scanForSecurityVulnerabilities({
|
|
27087
|
-
path:
|
|
27258
|
+
path: path38,
|
|
27088
27259
|
scanContext: ScanContext.BACKGROUND_INITIAL
|
|
27089
27260
|
}).catch((error) => {
|
|
27090
27261
|
logError(`[${scanContext}] Error during initial security scan`, { error });
|
|
@@ -27185,9 +27356,9 @@ Example payload:
|
|
|
27185
27356
|
`Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
|
|
27186
27357
|
);
|
|
27187
27358
|
}
|
|
27188
|
-
const
|
|
27359
|
+
const path38 = pathValidationResult.path;
|
|
27189
27360
|
const resultText = await this.newFixesService.getFreshFixes({
|
|
27190
|
-
path:
|
|
27361
|
+
path: path38
|
|
27191
27362
|
});
|
|
27192
27363
|
logInfo("CheckForNewAvailableFixesTool execution completed", {
|
|
27193
27364
|
resultText
|
|
@@ -27368,8 +27539,8 @@ Call this tool instead of ${MCP_TOOL_SCAN_AND_FIX_VULNERABILITIES} when you only
|
|
|
27368
27539
|
`Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
|
|
27369
27540
|
);
|
|
27370
27541
|
}
|
|
27371
|
-
const
|
|
27372
|
-
const gitService = new GitService(
|
|
27542
|
+
const path38 = pathValidationResult.path;
|
|
27543
|
+
const gitService = new GitService(path38, log);
|
|
27373
27544
|
const gitValidation = await gitService.validateRepository();
|
|
27374
27545
|
if (!gitValidation.isValid) {
|
|
27375
27546
|
throw new Error(`Invalid git repository: ${gitValidation.error}`);
|
|
@@ -27406,7 +27577,7 @@ Call this tool instead of ${MCP_TOOL_SCAN_AND_FIX_VULNERABILITIES} when you only
|
|
|
27406
27577
|
}
|
|
27407
27578
|
const fixResult = await this.availableFixesService.checkForAvailableFixes({
|
|
27408
27579
|
repoUrl: originUrl,
|
|
27409
|
-
repositoryPath:
|
|
27580
|
+
repositoryPath: path38,
|
|
27410
27581
|
limit: args.limit,
|
|
27411
27582
|
offset: args.offset,
|
|
27412
27583
|
fileFilter: actualFileFilter
|
|
@@ -27991,7 +28162,7 @@ Example payload (Mode B \u2014 abstain from every interactive fix, no rescan):
|
|
|
27991
28162
|
`Invalid path: potential security risk detected in path: ${pathValidationResult.error}`
|
|
27992
28163
|
);
|
|
27993
28164
|
}
|
|
27994
|
-
const
|
|
28165
|
+
const path38 = pathValidationResult.path;
|
|
27995
28166
|
if (args.interactiveAnswers !== void 0) {
|
|
27996
28167
|
if (args.interactiveAnswers.length === 0) {
|
|
27997
28168
|
return this.createSuccessResponse(
|
|
@@ -28001,7 +28172,7 @@ Example payload (Mode B \u2014 abstain from every interactive fix, no rescan):
|
|
|
28001
28172
|
try {
|
|
28002
28173
|
const result = await this.vulnerabilityFixService.applyInteractiveAnswers({
|
|
28003
28174
|
interactiveAnswers: args.interactiveAnswers,
|
|
28004
|
-
repositoryPath:
|
|
28175
|
+
repositoryPath: path38
|
|
28005
28176
|
});
|
|
28006
28177
|
return this.createSuccessResponse(result);
|
|
28007
28178
|
} catch (error) {
|
|
@@ -28013,7 +28184,7 @@ Example payload (Mode B \u2014 abstain from every interactive fix, no rescan):
|
|
|
28013
28184
|
}
|
|
28014
28185
|
}
|
|
28015
28186
|
const files = await getLocalFiles({
|
|
28016
|
-
path:
|
|
28187
|
+
path: path38,
|
|
28017
28188
|
maxFileSize: MCP_MAX_FILE_SIZE,
|
|
28018
28189
|
maxFiles: args.maxFiles,
|
|
28019
28190
|
scanContext: ScanContext.USER_REQUEST,
|
|
@@ -28033,7 +28204,7 @@ Example payload (Mode B \u2014 abstain from every interactive fix, no rescan):
|
|
|
28033
28204
|
try {
|
|
28034
28205
|
const fixResult = await this.vulnerabilityFixService.processVulnerabilities({
|
|
28035
28206
|
fileList: files.map((file) => file.relativePath),
|
|
28036
|
-
repositoryPath:
|
|
28207
|
+
repositoryPath: path38,
|
|
28037
28208
|
offset: args.offset,
|
|
28038
28209
|
limit: args.limit,
|
|
28039
28210
|
isRescan: args.rescan || !!args.maxFiles
|
|
@@ -28137,7 +28308,7 @@ var mcpHandler = async (_args) => {
|
|
|
28137
28308
|
};
|
|
28138
28309
|
|
|
28139
28310
|
// src/args/commands/review.ts
|
|
28140
|
-
import
|
|
28311
|
+
import fs27 from "fs";
|
|
28141
28312
|
import chalk12 from "chalk";
|
|
28142
28313
|
function reviewBuilder(yargs2) {
|
|
28143
28314
|
return yargs2.option("f", {
|
|
@@ -28174,7 +28345,7 @@ function reviewBuilder(yargs2) {
|
|
|
28174
28345
|
).help();
|
|
28175
28346
|
}
|
|
28176
28347
|
function validateReviewOptions(argv) {
|
|
28177
|
-
if (!
|
|
28348
|
+
if (!fs27.existsSync(argv.f)) {
|
|
28178
28349
|
throw new CliError(`
|
|
28179
28350
|
Can't access ${chalk12.bold(argv.f)}`);
|
|
28180
28351
|
}
|
|
@@ -28334,10 +28505,10 @@ init_client_generates();
|
|
|
28334
28505
|
init_urlParser2();
|
|
28335
28506
|
|
|
28336
28507
|
// src/features/codeium_intellij/codeium_language_server_grpc_client.ts
|
|
28337
|
-
import
|
|
28508
|
+
import path35 from "path";
|
|
28338
28509
|
import * as grpc from "@grpc/grpc-js";
|
|
28339
28510
|
import * as protoLoader from "@grpc/proto-loader";
|
|
28340
|
-
var PROTO_PATH =
|
|
28511
|
+
var PROTO_PATH = path35.join(
|
|
28341
28512
|
getModuleRootDir(),
|
|
28342
28513
|
"src/features/codeium_intellij/proto/exa/language_server_pb/language_server.proto"
|
|
28343
28514
|
);
|
|
@@ -28349,7 +28520,7 @@ function loadProto() {
|
|
|
28349
28520
|
defaults: true,
|
|
28350
28521
|
oneofs: true,
|
|
28351
28522
|
includeDirs: [
|
|
28352
|
-
|
|
28523
|
+
path35.join(getModuleRootDir(), "src/features/codeium_intellij/proto")
|
|
28353
28524
|
]
|
|
28354
28525
|
});
|
|
28355
28526
|
return grpc.loadPackageDefinition(
|
|
@@ -28403,30 +28574,30 @@ async function getGrpcClient(port, csrf) {
|
|
|
28403
28574
|
}
|
|
28404
28575
|
|
|
28405
28576
|
// src/features/codeium_intellij/parse_intellij_logs.ts
|
|
28406
|
-
import
|
|
28407
|
-
import
|
|
28408
|
-
import
|
|
28577
|
+
import fs28 from "fs";
|
|
28578
|
+
import os18 from "os";
|
|
28579
|
+
import path36 from "path";
|
|
28409
28580
|
function getLogsDir() {
|
|
28410
28581
|
if (process.platform === "darwin") {
|
|
28411
|
-
return
|
|
28582
|
+
return path36.join(os18.homedir(), "Library/Logs/JetBrains");
|
|
28412
28583
|
} else if (process.platform === "win32") {
|
|
28413
|
-
return
|
|
28414
|
-
process.env["LOCALAPPDATA"] ||
|
|
28584
|
+
return path36.join(
|
|
28585
|
+
process.env["LOCALAPPDATA"] || path36.join(os18.homedir(), "AppData/Local"),
|
|
28415
28586
|
"JetBrains"
|
|
28416
28587
|
);
|
|
28417
28588
|
} else {
|
|
28418
|
-
return
|
|
28589
|
+
return path36.join(os18.homedir(), ".cache/JetBrains");
|
|
28419
28590
|
}
|
|
28420
28591
|
}
|
|
28421
28592
|
function parseIdeLogDir(ideLogDir) {
|
|
28422
|
-
const logFiles =
|
|
28593
|
+
const logFiles = fs28.readdirSync(ideLogDir).filter((f) => /^idea(\.\d+)?\.log$/.test(f)).map((f) => ({
|
|
28423
28594
|
name: f,
|
|
28424
|
-
mtime:
|
|
28595
|
+
mtime: fs28.statSync(path36.join(ideLogDir, f)).mtimeMs
|
|
28425
28596
|
})).sort((a, b) => a.mtime - b.mtime).map((f) => f.name);
|
|
28426
28597
|
let latestCsrf = null;
|
|
28427
28598
|
let latestPort = null;
|
|
28428
28599
|
for (const logFile of logFiles) {
|
|
28429
|
-
const lines =
|
|
28600
|
+
const lines = fs28.readFileSync(path36.join(ideLogDir, logFile), "utf-8").split("\n");
|
|
28430
28601
|
for (const line of lines) {
|
|
28431
28602
|
if (!line.includes(
|
|
28432
28603
|
"com.codeium.intellij.language_server.LanguageServerProcessHandler"
|
|
@@ -28452,13 +28623,13 @@ function parseIdeLogDir(ideLogDir) {
|
|
|
28452
28623
|
function findRunningCodeiumLanguageServers() {
|
|
28453
28624
|
const results = [];
|
|
28454
28625
|
const logsDir = getLogsDir();
|
|
28455
|
-
if (!
|
|
28456
|
-
for (const ide of
|
|
28457
|
-
let ideLogDir =
|
|
28626
|
+
if (!fs28.existsSync(logsDir)) return results;
|
|
28627
|
+
for (const ide of fs28.readdirSync(logsDir)) {
|
|
28628
|
+
let ideLogDir = path36.join(logsDir, ide);
|
|
28458
28629
|
if (process.platform !== "darwin") {
|
|
28459
|
-
ideLogDir =
|
|
28630
|
+
ideLogDir = path36.join(ideLogDir, "log");
|
|
28460
28631
|
}
|
|
28461
|
-
if (!
|
|
28632
|
+
if (!fs28.existsSync(ideLogDir) || !fs28.statSync(ideLogDir).isDirectory()) {
|
|
28462
28633
|
continue;
|
|
28463
28634
|
}
|
|
28464
28635
|
const result = parseIdeLogDir(ideLogDir);
|
|
@@ -28638,11 +28809,11 @@ function processChatStepCodeAction(step) {
|
|
|
28638
28809
|
|
|
28639
28810
|
// src/features/codeium_intellij/install_hook.ts
|
|
28640
28811
|
import fsPromises5 from "fs/promises";
|
|
28641
|
-
import
|
|
28642
|
-
import
|
|
28812
|
+
import os19 from "os";
|
|
28813
|
+
import path37 from "path";
|
|
28643
28814
|
import chalk14 from "chalk";
|
|
28644
28815
|
function getCodeiumHooksPath() {
|
|
28645
|
-
return
|
|
28816
|
+
return path37.join(os19.homedir(), ".codeium", "hooks.json");
|
|
28646
28817
|
}
|
|
28647
28818
|
async function readCodeiumHooks() {
|
|
28648
28819
|
const hooksPath = getCodeiumHooksPath();
|
|
@@ -28655,7 +28826,7 @@ async function readCodeiumHooks() {
|
|
|
28655
28826
|
}
|
|
28656
28827
|
async function writeCodeiumHooks(config2) {
|
|
28657
28828
|
const hooksPath = getCodeiumHooksPath();
|
|
28658
|
-
const dir =
|
|
28829
|
+
const dir = path37.dirname(hooksPath);
|
|
28659
28830
|
await fsPromises5.mkdir(dir, { recursive: true });
|
|
28660
28831
|
await fsPromises5.writeFile(
|
|
28661
28832
|
hooksPath,
|