cto-ai-cli 4.0.0 → 5.0.0
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/DOCS.md +201 -2
- package/README.md +216 -312
- package/dist/action/index.js +271 -156
- package/dist/api/dashboard.js +271 -156
- package/dist/api/dashboard.js.map +1 -1
- package/dist/api/server.js +276 -155
- package/dist/api/server.js.map +1 -1
- package/dist/cli/gateway.js +298 -183
- package/dist/cli/score.js +1396 -241
- package/dist/cli/v2/index.js +290 -175
- package/dist/cli/v2/index.js.map +1 -1
- package/dist/engine/index.d.ts +121 -1
- package/dist/engine/index.js +1035 -212
- package/dist/engine/index.js.map +1 -1
- package/dist/fsevents-X6WP4TKM.node +0 -0
- package/dist/gateway/index.js +298 -183
- package/dist/gateway/index.js.map +1 -1
- package/dist/interact/index.js +263 -148
- package/dist/interact/index.js.map +1 -1
- package/dist/mcp/v2.js +287 -172
- package/dist/mcp/v2.js.map +1 -1
- package/package.json +8 -22
package/dist/api/server.js
CHANGED
|
@@ -1005,10 +1005,7 @@ function deduplicateFindings(findings) {
|
|
|
1005
1005
|
}
|
|
1006
1006
|
|
|
1007
1007
|
// src/engine/pruner.ts
|
|
1008
|
-
import { Project as Project2, SyntaxKind as SyntaxKind2 } from "ts-morph";
|
|
1009
1008
|
import { readFile as readFile4 } from "fs/promises";
|
|
1010
|
-
import { existsSync as existsSync3 } from "fs";
|
|
1011
|
-
import { join as join5 } from "path";
|
|
1012
1009
|
var TS_EXTENSIONS2 = /* @__PURE__ */ new Set(["ts", "tsx", "js", "jsx", "mts", "mjs"]);
|
|
1013
1010
|
async function pruneFile(file, level) {
|
|
1014
1011
|
if (level === "excluded") {
|
|
@@ -1031,23 +1028,7 @@ async function pruneTypeScript(file, level) {
|
|
|
1031
1028
|
} catch {
|
|
1032
1029
|
return emptyResult(file, level);
|
|
1033
1030
|
}
|
|
1034
|
-
|
|
1035
|
-
try {
|
|
1036
|
-
const tsConfigPath = findTsConfig(file.path);
|
|
1037
|
-
project = new Project2({
|
|
1038
|
-
tsConfigFilePath: tsConfigPath,
|
|
1039
|
-
skipAddingFilesFromTsConfig: true,
|
|
1040
|
-
compilerOptions: tsConfigPath ? void 0 : { allowJs: true, esModuleInterop: true }
|
|
1041
|
-
});
|
|
1042
|
-
project.createSourceFile(file.path, content, { overwrite: true });
|
|
1043
|
-
} catch {
|
|
1044
|
-
return pruneGenericFromContent(file, content, level);
|
|
1045
|
-
}
|
|
1046
|
-
const sourceFile = project.getSourceFiles()[0];
|
|
1047
|
-
if (!sourceFile) {
|
|
1048
|
-
return pruneGenericFromContent(file, content, level);
|
|
1049
|
-
}
|
|
1050
|
-
const prunedContent = level === "signatures" ? extractSignaturesAST(sourceFile) : extractSkeletonAST(sourceFile);
|
|
1031
|
+
const prunedContent = level === "signatures" ? extractSignaturesRegex(content) : extractSkeletonRegex(content);
|
|
1051
1032
|
const prunedTokens = countTokensChars4(Buffer.byteLength(prunedContent, "utf-8"));
|
|
1052
1033
|
const savingsPercent = file.tokens > 0 ? (file.tokens - prunedTokens) / file.tokens * 100 : 0;
|
|
1053
1034
|
return {
|
|
@@ -1059,131 +1040,281 @@ async function pruneTypeScript(file, level) {
|
|
|
1059
1040
|
savingsPercent: Math.max(0, savingsPercent)
|
|
1060
1041
|
};
|
|
1061
1042
|
}
|
|
1062
|
-
function
|
|
1043
|
+
function extractSignaturesRegex(content) {
|
|
1044
|
+
const lines = content.split("\n");
|
|
1063
1045
|
const parts = [];
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1046
|
+
let i = 0;
|
|
1047
|
+
while (i < lines.length) {
|
|
1048
|
+
const line = lines[i];
|
|
1049
|
+
const trimmed = line.trim();
|
|
1050
|
+
if (trimmed === "") {
|
|
1051
|
+
i++;
|
|
1052
|
+
continue;
|
|
1053
|
+
}
|
|
1054
|
+
if (trimmed.startsWith("/**")) {
|
|
1055
|
+
const docLines = [];
|
|
1056
|
+
while (i < lines.length) {
|
|
1057
|
+
docLines.push(lines[i]);
|
|
1058
|
+
if (lines[i].includes("*/")) {
|
|
1059
|
+
i++;
|
|
1060
|
+
break;
|
|
1061
|
+
}
|
|
1062
|
+
i++;
|
|
1063
|
+
}
|
|
1064
|
+
parts.push(docLines.join("\n"));
|
|
1065
|
+
continue;
|
|
1066
|
+
}
|
|
1067
|
+
if (trimmed.startsWith("//")) {
|
|
1068
|
+
parts.push(line);
|
|
1069
|
+
i++;
|
|
1070
|
+
continue;
|
|
1071
|
+
}
|
|
1072
|
+
if (/^\s*(import|export)\s/.test(line) && (trimmed.includes(" from ") || trimmed.startsWith("import "))) {
|
|
1073
|
+
const block = collectBracedLine(lines, i);
|
|
1074
|
+
parts.push(block.text);
|
|
1075
|
+
i = block.nextIndex;
|
|
1076
|
+
continue;
|
|
1077
|
+
}
|
|
1078
|
+
if (/^\s*export\s*(\{|\*)/.test(trimmed)) {
|
|
1079
|
+
const block = collectBracedLine(lines, i);
|
|
1080
|
+
parts.push(block.text);
|
|
1081
|
+
i = block.nextIndex;
|
|
1082
|
+
continue;
|
|
1083
|
+
}
|
|
1084
|
+
if (/^\s*(export\s+)?type\s+\w/.test(trimmed) && !trimmed.startsWith("typeof")) {
|
|
1085
|
+
const block = collectBalanced(lines, i);
|
|
1086
|
+
parts.push(block.text);
|
|
1087
|
+
i = block.nextIndex;
|
|
1088
|
+
continue;
|
|
1089
|
+
}
|
|
1090
|
+
if (/^\s*(export\s+)?interface\s+\w/.test(trimmed)) {
|
|
1091
|
+
const block = collectBalanced(lines, i);
|
|
1092
|
+
parts.push(block.text);
|
|
1093
|
+
i = block.nextIndex;
|
|
1094
|
+
continue;
|
|
1095
|
+
}
|
|
1096
|
+
if (/^\s*(export\s+)?(const\s+)?enum\s+\w/.test(trimmed)) {
|
|
1097
|
+
const block = collectBalanced(lines, i);
|
|
1098
|
+
parts.push(block.text);
|
|
1099
|
+
i = block.nextIndex;
|
|
1100
|
+
continue;
|
|
1101
|
+
}
|
|
1102
|
+
const fnMatch = trimmed.match(/^(export\s+)?(async\s+)?function\s+(\w+)/);
|
|
1103
|
+
if (fnMatch) {
|
|
1104
|
+
const sig = extractFnSignature(lines, i);
|
|
1105
|
+
parts.push(`${sig} { /* ... */ }`);
|
|
1106
|
+
i = skipBlock(lines, i);
|
|
1107
|
+
continue;
|
|
1108
|
+
}
|
|
1109
|
+
const arrowMatch = trimmed.match(/^(export\s+)?(const|let|var)\s+(\w+)/);
|
|
1110
|
+
if (arrowMatch && looksLikeFunctionDecl(lines, i)) {
|
|
1111
|
+
const prefix = trimmed.match(/^((?:export\s+)?(?:const|let|var)\s+\w+[^=]*=)/)?.[1];
|
|
1112
|
+
if (prefix) {
|
|
1113
|
+
parts.push(`${prefix} /* ... */;`);
|
|
1107
1114
|
}
|
|
1115
|
+
i = skipBlock(lines, i);
|
|
1116
|
+
continue;
|
|
1108
1117
|
}
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
for (const prop of cls.getProperties()) {
|
|
1123
|
-
parts.push(` ${prop.getText()}`);
|
|
1124
|
-
}
|
|
1125
|
-
const ctor = cls.getConstructors()[0];
|
|
1126
|
-
if (ctor) {
|
|
1127
|
-
const ctorParams = ctor.getParameters().map((p) => p.getText()).join(", ");
|
|
1128
|
-
parts.push(` constructor(${ctorParams}) { /* ... */ }`);
|
|
1129
|
-
}
|
|
1130
|
-
for (const method of cls.getMethods()) {
|
|
1131
|
-
const isStatic = method.isStatic();
|
|
1132
|
-
const isAsync = method.isAsync();
|
|
1133
|
-
const methodName = method.getName();
|
|
1134
|
-
const methodParams = method.getParameters().map((p) => p.getText()).join(", ");
|
|
1135
|
-
const returnType = method.getReturnTypeNode()?.getText();
|
|
1136
|
-
const returnStr = returnType ? `: ${returnType}` : "";
|
|
1137
|
-
const staticStr = isStatic ? "static " : "";
|
|
1138
|
-
const asyncStr = isAsync ? "async " : "";
|
|
1139
|
-
parts.push(` ${staticStr}${asyncStr}${methodName}(${methodParams})${returnStr} { /* ... */ }`);
|
|
1140
|
-
}
|
|
1141
|
-
parts.push("}");
|
|
1142
|
-
}
|
|
1143
|
-
for (const exp of sf.getExportDeclarations()) {
|
|
1144
|
-
parts.push(exp.getText());
|
|
1145
|
-
}
|
|
1146
|
-
for (const exp of sf.getExportAssignments()) {
|
|
1147
|
-
parts.push(exp.getText());
|
|
1118
|
+
if (arrowMatch) {
|
|
1119
|
+
const block = collectStatement(lines, i);
|
|
1120
|
+
parts.push(block.text);
|
|
1121
|
+
i = block.nextIndex;
|
|
1122
|
+
continue;
|
|
1123
|
+
}
|
|
1124
|
+
if (/^\s*(export\s+)?(abstract\s+)?class\s+\w/.test(trimmed)) {
|
|
1125
|
+
const classOutline = extractClassOutline(lines, i);
|
|
1126
|
+
parts.push(classOutline.text);
|
|
1127
|
+
i = classOutline.nextIndex;
|
|
1128
|
+
continue;
|
|
1129
|
+
}
|
|
1130
|
+
i++;
|
|
1148
1131
|
}
|
|
1149
1132
|
return parts.join("\n");
|
|
1150
1133
|
}
|
|
1151
|
-
function
|
|
1134
|
+
function extractSkeletonRegex(content) {
|
|
1135
|
+
const lines = content.split("\n");
|
|
1152
1136
|
const parts = [];
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1137
|
+
let i = 0;
|
|
1138
|
+
while (i < lines.length) {
|
|
1139
|
+
const trimmed = lines[i].trim();
|
|
1140
|
+
if (/^import\s/.test(trimmed)) {
|
|
1141
|
+
const block = collectBracedLine(lines, i);
|
|
1142
|
+
parts.push(block.text);
|
|
1143
|
+
i = block.nextIndex;
|
|
1144
|
+
continue;
|
|
1145
|
+
}
|
|
1146
|
+
if (/^export\s+(type|interface)\s+\w/.test(trimmed)) {
|
|
1147
|
+
const block = collectBalanced(lines, i);
|
|
1148
|
+
parts.push(block.text);
|
|
1149
|
+
i = block.nextIndex;
|
|
1150
|
+
continue;
|
|
1151
|
+
}
|
|
1152
|
+
if (/^export\s+(const\s+)?enum\s+\w/.test(trimmed)) {
|
|
1153
|
+
const block = collectBalanced(lines, i);
|
|
1154
|
+
parts.push(block.text);
|
|
1155
|
+
i = block.nextIndex;
|
|
1156
|
+
continue;
|
|
1157
|
+
}
|
|
1158
|
+
if (/^export\s+(async\s+)?function\s+\w/.test(trimmed)) {
|
|
1159
|
+
const sig = extractFnSignature(lines, i);
|
|
1160
|
+
parts.push(`${sig};`);
|
|
1161
|
+
i = skipBlock(lines, i);
|
|
1162
|
+
continue;
|
|
1163
|
+
}
|
|
1164
|
+
if (/^export\s+(abstract\s+)?class\s+/.test(trimmed)) {
|
|
1165
|
+
const nameMatch = trimmed.match(/class\s+(\w+)/);
|
|
1166
|
+
const name = nameMatch?.[1] ?? "Unknown";
|
|
1167
|
+
const end = skipBlock(lines, i);
|
|
1168
|
+
const methods = [];
|
|
1169
|
+
for (let j = i + 1; j < end; j++) {
|
|
1170
|
+
const mt = lines[j].trim();
|
|
1171
|
+
const mm = mt.match(/^(?:static\s+)?(?:async\s+)?(\w+)\s*\(/);
|
|
1172
|
+
if (mm && mm[1] !== "constructor") methods.push(mm[1]);
|
|
1173
|
+
}
|
|
1174
|
+
parts.push(`export class ${name} { /* methods: ${methods.join(", ")} */ }`);
|
|
1175
|
+
i = end;
|
|
1176
|
+
continue;
|
|
1177
|
+
}
|
|
1178
|
+
if (/^export\s*(\{|\*)/.test(trimmed)) {
|
|
1179
|
+
const block = collectBracedLine(lines, i);
|
|
1180
|
+
parts.push(block.text);
|
|
1181
|
+
i = block.nextIndex;
|
|
1182
|
+
continue;
|
|
1183
|
+
}
|
|
1184
|
+
i++;
|
|
1184
1185
|
}
|
|
1185
1186
|
return parts.join("\n");
|
|
1186
1187
|
}
|
|
1188
|
+
function collectBracedLine(lines, start) {
|
|
1189
|
+
let text = lines[start];
|
|
1190
|
+
let i = start + 1;
|
|
1191
|
+
while (i < lines.length && !text.includes(";") && !text.trimEnd().endsWith("'") && !text.trimEnd().endsWith('"')) {
|
|
1192
|
+
text += "\n" + lines[i];
|
|
1193
|
+
i++;
|
|
1194
|
+
}
|
|
1195
|
+
return { text, nextIndex: i };
|
|
1196
|
+
}
|
|
1197
|
+
function collectBalanced(lines, start) {
|
|
1198
|
+
let depth = 0;
|
|
1199
|
+
let text = "";
|
|
1200
|
+
let i = start;
|
|
1201
|
+
let started = false;
|
|
1202
|
+
while (i < lines.length) {
|
|
1203
|
+
const line = lines[i];
|
|
1204
|
+
text += (text ? "\n" : "") + line;
|
|
1205
|
+
for (const ch of line) {
|
|
1206
|
+
if (ch === "{" || ch === "(") {
|
|
1207
|
+
depth++;
|
|
1208
|
+
started = true;
|
|
1209
|
+
}
|
|
1210
|
+
if (ch === "}" || ch === ")") depth--;
|
|
1211
|
+
}
|
|
1212
|
+
i++;
|
|
1213
|
+
if (started && depth <= 0) break;
|
|
1214
|
+
if (!started && line.includes(";")) break;
|
|
1215
|
+
}
|
|
1216
|
+
return { text, nextIndex: i };
|
|
1217
|
+
}
|
|
1218
|
+
function collectStatement(lines, start) {
|
|
1219
|
+
let text = lines[start];
|
|
1220
|
+
let i = start + 1;
|
|
1221
|
+
if (text.includes(";")) return { text, nextIndex: i };
|
|
1222
|
+
let depth = 0;
|
|
1223
|
+
for (const ch of text) {
|
|
1224
|
+
if (ch === "{" || ch === "(" || ch === "[") depth++;
|
|
1225
|
+
if (ch === "}" || ch === ")" || ch === "]") depth--;
|
|
1226
|
+
}
|
|
1227
|
+
while (i < lines.length && depth > 0) {
|
|
1228
|
+
text += "\n" + lines[i];
|
|
1229
|
+
for (const ch of lines[i]) {
|
|
1230
|
+
if (ch === "{" || ch === "(" || ch === "[") depth++;
|
|
1231
|
+
if (ch === "}" || ch === ")" || ch === "]") depth--;
|
|
1232
|
+
}
|
|
1233
|
+
i++;
|
|
1234
|
+
}
|
|
1235
|
+
return { text, nextIndex: i };
|
|
1236
|
+
}
|
|
1237
|
+
function extractFnSignature(lines, start) {
|
|
1238
|
+
let sig = "";
|
|
1239
|
+
let i = start;
|
|
1240
|
+
while (i < lines.length) {
|
|
1241
|
+
const line = lines[i].trim();
|
|
1242
|
+
sig += (sig ? " " : "") + line;
|
|
1243
|
+
if (line.includes("{")) {
|
|
1244
|
+
sig = sig.replace(/\s*\{[^]*$/, "").trim();
|
|
1245
|
+
break;
|
|
1246
|
+
}
|
|
1247
|
+
i++;
|
|
1248
|
+
}
|
|
1249
|
+
return sig;
|
|
1250
|
+
}
|
|
1251
|
+
function skipBlock(lines, start) {
|
|
1252
|
+
let depth = 0;
|
|
1253
|
+
let i = start;
|
|
1254
|
+
let foundBrace = false;
|
|
1255
|
+
while (i < lines.length) {
|
|
1256
|
+
for (const ch of lines[i]) {
|
|
1257
|
+
if (ch === "{") {
|
|
1258
|
+
depth++;
|
|
1259
|
+
foundBrace = true;
|
|
1260
|
+
}
|
|
1261
|
+
if (ch === "}") depth--;
|
|
1262
|
+
}
|
|
1263
|
+
i++;
|
|
1264
|
+
if (foundBrace && depth <= 0) break;
|
|
1265
|
+
if (!foundBrace && lines[i - 1].includes(";")) break;
|
|
1266
|
+
}
|
|
1267
|
+
return i;
|
|
1268
|
+
}
|
|
1269
|
+
function looksLikeFunctionDecl(lines, start) {
|
|
1270
|
+
const chunk = lines.slice(start, Math.min(start + 5, lines.length)).join(" ");
|
|
1271
|
+
return /=>/.test(chunk) || /=\s*function/.test(chunk);
|
|
1272
|
+
}
|
|
1273
|
+
function extractClassOutline(lines, start) {
|
|
1274
|
+
const header = lines[start].trim();
|
|
1275
|
+
let headerText = header;
|
|
1276
|
+
let i = start + 1;
|
|
1277
|
+
if (!header.includes("{")) {
|
|
1278
|
+
while (i < lines.length) {
|
|
1279
|
+
headerText += " " + lines[i].trim();
|
|
1280
|
+
if (lines[i].includes("{")) {
|
|
1281
|
+
i++;
|
|
1282
|
+
break;
|
|
1283
|
+
}
|
|
1284
|
+
i++;
|
|
1285
|
+
}
|
|
1286
|
+
} else {
|
|
1287
|
+
i = start + 1;
|
|
1288
|
+
}
|
|
1289
|
+
const bodyParts = [headerText.replace(/\{[^]*$/, "{").trim()];
|
|
1290
|
+
let depth = 1;
|
|
1291
|
+
while (i < lines.length && depth > 0) {
|
|
1292
|
+
const line = lines[i];
|
|
1293
|
+
const trimmed = line.trim();
|
|
1294
|
+
for (const ch of line) {
|
|
1295
|
+
if (ch === "{") depth++;
|
|
1296
|
+
if (ch === "}") depth--;
|
|
1297
|
+
}
|
|
1298
|
+
if (depth <= 0) {
|
|
1299
|
+
i++;
|
|
1300
|
+
break;
|
|
1301
|
+
}
|
|
1302
|
+
if (depth === 1) {
|
|
1303
|
+
if (/^(private|protected|public|readonly|static|#)/.test(trimmed) && !trimmed.includes("(")) {
|
|
1304
|
+
bodyParts.push(` ${trimmed}`);
|
|
1305
|
+
} else if (/^constructor\s*\(/.test(trimmed)) {
|
|
1306
|
+
const sig = extractFnSignature(lines, i);
|
|
1307
|
+
bodyParts.push(` ${sig} { /* ... */ }`);
|
|
1308
|
+
} else if (/^(?:static\s+)?(?:async\s+)?(?:get\s+|set\s+)?\w+\s*[(<]/.test(trimmed) && !trimmed.startsWith("//")) {
|
|
1309
|
+
const sig = extractFnSignature(lines, i);
|
|
1310
|
+
bodyParts.push(` ${sig} { /* ... */ }`);
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
i++;
|
|
1314
|
+
}
|
|
1315
|
+
bodyParts.push("}");
|
|
1316
|
+
return { text: bodyParts.join("\n"), nextIndex: i };
|
|
1317
|
+
}
|
|
1187
1318
|
async function pruneGeneric(file, level) {
|
|
1188
1319
|
let content;
|
|
1189
1320
|
try {
|
|
@@ -1244,22 +1375,6 @@ function emptyResult(file, level) {
|
|
|
1244
1375
|
savingsPercent: 100
|
|
1245
1376
|
};
|
|
1246
1377
|
}
|
|
1247
|
-
function addJSDoc(node, parts) {
|
|
1248
|
-
if (!node.getJsDocs) return;
|
|
1249
|
-
const docs = node.getJsDocs();
|
|
1250
|
-
if (docs.length > 0) {
|
|
1251
|
-
parts.push(docs[0].getText());
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
|
-
function findTsConfig(filePath) {
|
|
1255
|
-
let dir = filePath;
|
|
1256
|
-
for (let i = 0; i < 10; i++) {
|
|
1257
|
-
dir = join5(dir, "..");
|
|
1258
|
-
const candidate = join5(dir, "tsconfig.json");
|
|
1259
|
-
if (existsSync3(candidate)) return candidate;
|
|
1260
|
-
}
|
|
1261
|
-
return void 0;
|
|
1262
|
-
}
|
|
1263
1378
|
|
|
1264
1379
|
// src/engine/graph-utils.ts
|
|
1265
1380
|
function buildAdjacencyList(edges) {
|
|
@@ -2922,18 +3037,18 @@ function formatCost(cost) {
|
|
|
2922
3037
|
// src/govern/audit.ts
|
|
2923
3038
|
import { randomUUID, createHash as createHash5 } from "crypto";
|
|
2924
3039
|
import { readdir as readdir3, chmod } from "fs/promises";
|
|
2925
|
-
import { join as
|
|
3040
|
+
import { join as join5 } from "path";
|
|
2926
3041
|
import { userInfo } from "os";
|
|
2927
3042
|
import { homedir } from "os";
|
|
2928
3043
|
var CTO_DIR = ".cto-ai";
|
|
2929
3044
|
var AUDIT_DIR = "audit";
|
|
2930
3045
|
var MAX_ENTRIES_PER_FILE = 500;
|
|
2931
3046
|
function getAuditDir() {
|
|
2932
|
-
return
|
|
3047
|
+
return join5(homedir(), CTO_DIR, AUDIT_DIR);
|
|
2933
3048
|
}
|
|
2934
3049
|
function getCurrentAuditFile() {
|
|
2935
3050
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0].replace(/-/g, "");
|
|
2936
|
-
return
|
|
3051
|
+
return join5(getAuditDir(), `audit_${date}.json`);
|
|
2937
3052
|
}
|
|
2938
3053
|
function computeIntegrityHash(entry) {
|
|
2939
3054
|
const payload = JSON.stringify({
|
|
@@ -2961,7 +3076,7 @@ async function readJSON(filePath) {
|
|
|
2961
3076
|
}
|
|
2962
3077
|
async function writeJSON(filePath, data) {
|
|
2963
3078
|
const { writeFile: writeFile2 } = await import("fs/promises");
|
|
2964
|
-
await ensureDir(
|
|
3079
|
+
await ensureDir(join5(filePath, ".."));
|
|
2965
3080
|
await writeFile2(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
2966
3081
|
}
|
|
2967
3082
|
async function logAudit(action, projectPath, details = {}) {
|
|
@@ -3134,6 +3249,12 @@ function checkRateLimit(ip) {
|
|
|
3134
3249
|
entry.count++;
|
|
3135
3250
|
return true;
|
|
3136
3251
|
}
|
|
3252
|
+
setInterval(() => {
|
|
3253
|
+
const now = Date.now();
|
|
3254
|
+
for (const [ip, entry] of requestCounts) {
|
|
3255
|
+
if (now > entry.reset) requestCounts.delete(ip);
|
|
3256
|
+
}
|
|
3257
|
+
}, 5 * 6e4).unref();
|
|
3137
3258
|
function getIP(req) {
|
|
3138
3259
|
return req.headers["x-forwarded-for"]?.split(",")[0]?.trim() ?? req.socket.remoteAddress ?? "unknown";
|
|
3139
3260
|
}
|