seaworthycode 1.2.16 → 1.2.18
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/index.js +58 -15
- package/dist/index.js.map +1 -1
- package/package.json +11 -11
- package/wasm/tree-sitter-sql.wasm +0 -0
package/dist/index.js
CHANGED
|
@@ -407,10 +407,11 @@ async function crawl(options) {
|
|
|
407
407
|
const { isBinary, isGenerated } = await peekFileHead(fullPath);
|
|
408
408
|
if (isBinary || isGenerated) continue;
|
|
409
409
|
if (await shouldSkipMinifiedJs(fullPath, entry.name)) continue;
|
|
410
|
+
let cachedContent;
|
|
410
411
|
files.push({
|
|
411
412
|
path: fullPath,
|
|
412
413
|
relativePath: relPath,
|
|
413
|
-
content: () => fs.readFile(fullPath, "utf-8"),
|
|
414
|
+
content: () => cachedContent ??= fs.readFile(fullPath, "utf-8"),
|
|
414
415
|
language: inferLanguage(entry.name),
|
|
415
416
|
size: stat2.size
|
|
416
417
|
});
|
|
@@ -428,10 +429,11 @@ async function crawl(options) {
|
|
|
428
429
|
const { isBinary, isGenerated } = await peekFileHead(fullPath);
|
|
429
430
|
if (isBinary || isGenerated) continue;
|
|
430
431
|
if (await shouldSkipMinifiedJs(fullPath, entry.name)) continue;
|
|
432
|
+
let cachedContent;
|
|
431
433
|
files.push({
|
|
432
434
|
path: fullPath,
|
|
433
435
|
relativePath: relPath,
|
|
434
|
-
content: () => fs.readFile(fullPath, "utf-8"),
|
|
436
|
+
content: () => cachedContent ??= fs.readFile(fullPath, "utf-8"),
|
|
435
437
|
language: inferLanguage(entry.name),
|
|
436
438
|
size: stat2.size
|
|
437
439
|
});
|
|
@@ -5985,6 +5987,14 @@ function getMaxFilesPerLlmCheck() {
|
|
|
5985
5987
|
}
|
|
5986
5988
|
return 150;
|
|
5987
5989
|
}
|
|
5990
|
+
function getMechanicalMaxFiles() {
|
|
5991
|
+
const env2 = process.env.SEAWORTHY_MECHANICAL_MAX_FILES;
|
|
5992
|
+
if (env2 !== void 0) {
|
|
5993
|
+
const n = parseInt(env2, 10);
|
|
5994
|
+
if (!isNaN(n) && n > 0) return n;
|
|
5995
|
+
}
|
|
5996
|
+
return 2e3;
|
|
5997
|
+
}
|
|
5988
5998
|
function withLLMTimeout(promise, ms) {
|
|
5989
5999
|
return new Promise((resolve22, reject) => {
|
|
5990
6000
|
const timerId = setTimeout(() => {
|
|
@@ -6028,14 +6038,27 @@ function downgradeSeverity(severity) {
|
|
|
6028
6038
|
if (idx < 0 || idx >= SEVERITY_ORDER3.length - 1) return severity;
|
|
6029
6039
|
return SEVERITY_ORDER3[idx + 1];
|
|
6030
6040
|
}
|
|
6031
|
-
function
|
|
6041
|
+
function estimateTokens(text22) {
|
|
6042
|
+
return Math.ceil(text22.length / 4);
|
|
6043
|
+
}
|
|
6044
|
+
async function filterFilesByBudget(files, budget) {
|
|
6032
6045
|
if (budget === void 0 || budget === Infinity) {
|
|
6033
6046
|
return files;
|
|
6034
6047
|
}
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6048
|
+
const result = [];
|
|
6049
|
+
for (const file of files) {
|
|
6050
|
+
let tokens2;
|
|
6051
|
+
if (file.size !== void 0) {
|
|
6052
|
+
tokens2 = Math.ceil(file.size / 4);
|
|
6053
|
+
} else {
|
|
6054
|
+
const content = await file.content();
|
|
6055
|
+
tokens2 = estimateTokens(content);
|
|
6056
|
+
}
|
|
6057
|
+
if (tokens2 <= budget) {
|
|
6058
|
+
result.push(file);
|
|
6059
|
+
}
|
|
6060
|
+
}
|
|
6061
|
+
return result;
|
|
6039
6062
|
}
|
|
6040
6063
|
async function buildCacheKey(ctx, template, files) {
|
|
6041
6064
|
if (!ctx.llm) return void 0;
|
|
@@ -6086,7 +6109,9 @@ function capProjectScopedFindings(findings) {
|
|
|
6086
6109
|
}
|
|
6087
6110
|
async function runProjectScopedMechanical(eligibleFiles, template) {
|
|
6088
6111
|
if (eligibleFiles.length === 0) return [];
|
|
6089
|
-
const
|
|
6112
|
+
const maxFiles = getMechanicalMaxFiles();
|
|
6113
|
+
const scanFiles = eligibleFiles.length > maxFiles ? eligibleFiles.slice(0, maxFiles) : eligibleFiles;
|
|
6114
|
+
const contents = await Promise.all(scanFiles.map((f) => f.content()));
|
|
6090
6115
|
const combined = contents.join("\n");
|
|
6091
6116
|
let shouldFlag = false;
|
|
6092
6117
|
const pattern = template.fallbackPatterns[0];
|
|
@@ -6113,13 +6138,23 @@ async function runProjectAbsenceChecks(eligibleFiles, template) {
|
|
|
6113
6138
|
if (absencePatterns.length === 0 || eligibleFiles.length === 0) {
|
|
6114
6139
|
return [];
|
|
6115
6140
|
}
|
|
6116
|
-
const
|
|
6141
|
+
const maxFiles = getMechanicalMaxFiles();
|
|
6142
|
+
const scanFiles = eligibleFiles.length > maxFiles ? eligibleFiles.slice(0, maxFiles) : eligibleFiles;
|
|
6117
6143
|
const anchorFile = pickProjectAnchorFile(eligibleFiles);
|
|
6144
|
+
const foundIndices = /* @__PURE__ */ new Set();
|
|
6145
|
+
for (const file of scanFiles) {
|
|
6146
|
+
if (foundIndices.size === absencePatterns.length) break;
|
|
6147
|
+
const content = await file.content();
|
|
6148
|
+
for (let i = 0; i < absencePatterns.length; i++) {
|
|
6149
|
+
if (!foundIndices.has(i) && absencePatterns[i].regex.test(content)) {
|
|
6150
|
+
foundIndices.add(i);
|
|
6151
|
+
}
|
|
6152
|
+
}
|
|
6153
|
+
}
|
|
6118
6154
|
const results = [];
|
|
6119
|
-
for (
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
results.push(makeMechanicalFinding(template, pattern, anchorFile, 1));
|
|
6155
|
+
for (let i = 0; i < absencePatterns.length; i++) {
|
|
6156
|
+
if (!foundIndices.has(i)) {
|
|
6157
|
+
results.push(makeMechanicalFinding(template, absencePatterns[i], anchorFile, 1));
|
|
6123
6158
|
}
|
|
6124
6159
|
}
|
|
6125
6160
|
return results;
|
|
@@ -6131,8 +6166,16 @@ async function runMechanicalCheck(ctx, template) {
|
|
|
6131
6166
|
}
|
|
6132
6167
|
const perFilePatterns = template.fallbackPatterns.filter((p) => !p.absenceBased);
|
|
6133
6168
|
const projectResults = await runProjectAbsenceChecks(eligibleFiles, template);
|
|
6169
|
+
const maxMechanicalFiles = getMechanicalMaxFiles();
|
|
6170
|
+
let cappedFiles = eligibleFiles;
|
|
6171
|
+
if (cappedFiles.length > maxMechanicalFiles) {
|
|
6172
|
+
console.error(
|
|
6173
|
+
`[seaworthy] ${template.checkId}: ${cappedFiles.length} eligible files \u2014 capping mechanical scan to ${maxMechanicalFiles}`
|
|
6174
|
+
);
|
|
6175
|
+
cappedFiles = cappedFiles.slice(0, maxMechanicalFiles);
|
|
6176
|
+
}
|
|
6134
6177
|
const fileResults = perFilePatterns.length === 0 ? [] : await runWithConcurrencyCollect(
|
|
6135
|
-
|
|
6178
|
+
cappedFiles,
|
|
6136
6179
|
async (file) => {
|
|
6137
6180
|
const content = await file.content();
|
|
6138
6181
|
const lines = content.split("\n");
|
|
@@ -6212,7 +6255,7 @@ async function runLLMCheck(ctx, template) {
|
|
|
6212
6255
|
return [];
|
|
6213
6256
|
}
|
|
6214
6257
|
const proEligibleFiles = ctx.files.filter((f) => isProCheckEligible(f, template));
|
|
6215
|
-
let eligibleFiles = filterFilesByBudget(proEligibleFiles, template.tokenBudget);
|
|
6258
|
+
let eligibleFiles = await filterFilesByBudget(proEligibleFiles, template.tokenBudget);
|
|
6216
6259
|
const maxFiles = getMaxFilesPerLlmCheck();
|
|
6217
6260
|
if (eligibleFiles.length > maxFiles) {
|
|
6218
6261
|
console.error(
|