tina4-nodejs 3.13.100 → 3.13.101
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/CLAUDE.md +3 -3
- package/README.md +16 -0
- package/package.json +1 -1
- package/packages/cli/dist/bin.js +455 -652
- package/packages/cli/src/bin.ts +0 -6
- package/packages/core/dist/index.js +444 -510
- package/packages/core/src/aiClient.ts +288 -0
- package/packages/core/src/devAdmin.ts +1 -2
- package/packages/core/src/index.ts +2 -0
- package/packages/core/src/metrics.ts +79 -631
- package/packages/orm/dist/index.js +445 -517
- package/types/core/src/aiClient.d.ts +66 -0
- package/types/core/src/index.d.ts +2 -0
- package/types/core/src/metrics.d.ts +0 -35
- package/packages/cli/src/commands/metrics.ts +0 -160
- package/types/cli/src/commands/metrics.d.ts +0 -6
package/packages/cli/dist/bin.js
CHANGED
|
@@ -19567,14 +19567,14 @@ async function discoverRoutes(routesDir) {
|
|
|
19567
19567
|
const currentMtime = statSync7(filePath).mtimeMs;
|
|
19568
19568
|
if (_seenFiles.has(filePath) && _seenMtimes.get(filePath) === currentMtime) continue;
|
|
19569
19569
|
const method = name.toUpperCase();
|
|
19570
|
-
const
|
|
19571
|
-
const pattern = filePathToPattern(
|
|
19570
|
+
const relativePath2 = relative(routesDir, filePath);
|
|
19571
|
+
const pattern = filePathToPattern(relativePath2);
|
|
19572
19572
|
try {
|
|
19573
19573
|
const moduleUrl = `file://${filePath}?t=${currentMtime}`;
|
|
19574
19574
|
const mod = await import(moduleUrl);
|
|
19575
19575
|
const handler = mod.default ?? mod.handler;
|
|
19576
19576
|
if (typeof handler !== "function") {
|
|
19577
|
-
console.warn(` Warning: ${
|
|
19577
|
+
console.warn(` Warning: ${relativePath2} does not export a handler function, skipping`);
|
|
19578
19578
|
continue;
|
|
19579
19579
|
}
|
|
19580
19580
|
const meta = mod.meta;
|
|
@@ -19586,7 +19586,7 @@ async function discoverRoutes(routesDir) {
|
|
|
19586
19586
|
_seenMtimes.set(filePath, currentMtime);
|
|
19587
19587
|
registeredFromThisScan++;
|
|
19588
19588
|
} catch (err) {
|
|
19589
|
-
console.error(` Error loading route ${
|
|
19589
|
+
console.error(` Error loading route ${relativePath2}:`, err);
|
|
19590
19590
|
recordBrokenImport(filePath, err);
|
|
19591
19591
|
}
|
|
19592
19592
|
}
|
|
@@ -19620,8 +19620,8 @@ function recordBrokenImport(filePath, error) {
|
|
|
19620
19620
|
} catch {
|
|
19621
19621
|
}
|
|
19622
19622
|
}
|
|
19623
|
-
function filePathToPattern(
|
|
19624
|
-
const parts =
|
|
19623
|
+
function filePathToPattern(relativePath2) {
|
|
19624
|
+
const parts = relativePath2.replace(/\\/g, "/").split("/").slice(0, -1);
|
|
19625
19625
|
const urlParts = parts.map((part) => {
|
|
19626
19626
|
if (part.startsWith("[...") && part.endsWith("]")) {
|
|
19627
19627
|
const name = part.slice(4, -1);
|
|
@@ -22878,500 +22878,127 @@ import * as fs3 from "node:fs";
|
|
|
22878
22878
|
import * as path2 from "node:path";
|
|
22879
22879
|
import { spawnSync } from "node:child_process";
|
|
22880
22880
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
22881
|
-
function
|
|
22882
|
-
|
|
22883
|
-
|
|
22884
|
-
|
|
22885
|
-
|
|
22886
|
-
|
|
22887
|
-
if (entry.isDirectory()) {
|
|
22888
|
-
if (!exclude.includes(entry.name)) {
|
|
22889
|
-
results.push(...walkFiles(fullPath, extensions, exclude));
|
|
22890
|
-
}
|
|
22891
|
-
} else if (entry.isFile()) {
|
|
22892
|
-
const ext = path2.extname(entry.name);
|
|
22893
|
-
if (extensions.includes(ext) && !entry.name.endsWith(".d.ts")) {
|
|
22894
|
-
results.push(fullPath);
|
|
22895
|
-
}
|
|
22896
|
-
}
|
|
22897
|
-
}
|
|
22898
|
-
return results;
|
|
22899
|
-
}
|
|
22900
|
-
function readFileSafe(filePath) {
|
|
22901
|
-
try {
|
|
22902
|
-
return fs3.readFileSync(filePath, "utf-8");
|
|
22903
|
-
} catch {
|
|
22904
|
-
return null;
|
|
22905
|
-
}
|
|
22906
|
-
}
|
|
22907
|
-
function relativePath(filePath, root = ".") {
|
|
22908
|
-
return path2.relative(root, filePath);
|
|
22909
|
-
}
|
|
22910
|
-
function countLines(source) {
|
|
22911
|
-
const lines = source.split("\n");
|
|
22912
|
-
let loc = 0;
|
|
22913
|
-
let blank = 0;
|
|
22914
|
-
let comment = 0;
|
|
22915
|
-
let inBlockComment = false;
|
|
22916
|
-
for (const line of lines) {
|
|
22917
|
-
const stripped = line.trim();
|
|
22918
|
-
if (!stripped) {
|
|
22919
|
-
blank++;
|
|
22920
|
-
continue;
|
|
22921
|
-
}
|
|
22922
|
-
if (inBlockComment) {
|
|
22923
|
-
comment++;
|
|
22924
|
-
if (stripped.includes("*/")) {
|
|
22925
|
-
inBlockComment = false;
|
|
22926
|
-
}
|
|
22927
|
-
continue;
|
|
22928
|
-
}
|
|
22929
|
-
if (stripped.startsWith("/*")) {
|
|
22930
|
-
comment++;
|
|
22931
|
-
if (!stripped.includes("*/") || stripped.endsWith("/*")) {
|
|
22932
|
-
inBlockComment = true;
|
|
22933
|
-
}
|
|
22934
|
-
continue;
|
|
22935
|
-
}
|
|
22936
|
-
if (stripped.startsWith("//")) {
|
|
22937
|
-
comment++;
|
|
22938
|
-
continue;
|
|
22939
|
-
}
|
|
22940
|
-
loc++;
|
|
22941
|
-
}
|
|
22942
|
-
return { loc, blank, comment };
|
|
22943
|
-
}
|
|
22944
|
-
function stripLiterals(source) {
|
|
22945
|
-
const out = [];
|
|
22946
|
-
const n = source.length;
|
|
22947
|
-
let i = 0;
|
|
22948
|
-
let prevSignificant = "";
|
|
22949
|
-
let prevWord = "";
|
|
22950
|
-
const regexKeywords = /* @__PURE__ */ new Set([
|
|
22951
|
-
"return",
|
|
22952
|
-
"typeof",
|
|
22953
|
-
"instanceof",
|
|
22954
|
-
"in",
|
|
22955
|
-
"of",
|
|
22956
|
-
"new",
|
|
22957
|
-
"delete",
|
|
22958
|
-
"void",
|
|
22959
|
-
"throw",
|
|
22960
|
-
"case",
|
|
22961
|
-
"do",
|
|
22962
|
-
"else",
|
|
22963
|
-
"yield",
|
|
22964
|
-
"await"
|
|
22965
|
-
]);
|
|
22966
|
-
function prevEndsExpression() {
|
|
22967
|
-
if (prevSignificant === "") return false;
|
|
22968
|
-
if (/[A-Za-z0-9_$]/.test(prevSignificant)) {
|
|
22969
|
-
return !regexKeywords.has(prevWord);
|
|
22970
|
-
}
|
|
22971
|
-
if (prevSignificant === ")" || prevSignificant === "]") return true;
|
|
22972
|
-
if (prevSignificant === ".") return true;
|
|
22973
|
-
return false;
|
|
22974
|
-
}
|
|
22975
|
-
while (i < n) {
|
|
22976
|
-
const ch = source[i];
|
|
22977
|
-
const next = i + 1 < n ? source[i + 1] : "";
|
|
22978
|
-
if (ch === "/" && next === "/") {
|
|
22979
|
-
out.push("//");
|
|
22980
|
-
i += 2;
|
|
22981
|
-
while (i < n && source[i] !== "\n") {
|
|
22982
|
-
out.push(" ");
|
|
22983
|
-
i++;
|
|
22984
|
-
}
|
|
22985
|
-
continue;
|
|
22986
|
-
}
|
|
22987
|
-
if (ch === "/" && next === "*") {
|
|
22988
|
-
out.push("/*");
|
|
22989
|
-
i += 2;
|
|
22990
|
-
while (i < n && !(source[i] === "*" && source[i + 1] === "/")) {
|
|
22991
|
-
out.push(source[i] === "\n" ? "\n" : " ");
|
|
22992
|
-
i++;
|
|
22993
|
-
}
|
|
22994
|
-
if (i < n) {
|
|
22995
|
-
out.push("*/");
|
|
22996
|
-
i += 2;
|
|
22997
|
-
}
|
|
22998
|
-
continue;
|
|
22999
|
-
}
|
|
23000
|
-
if (ch === '"' || ch === "'") {
|
|
23001
|
-
const quote = ch;
|
|
23002
|
-
out.push(quote);
|
|
23003
|
-
i++;
|
|
23004
|
-
while (i < n && source[i] !== quote) {
|
|
23005
|
-
if (source[i] === "\\" && i + 1 < n) {
|
|
23006
|
-
out.push(" ");
|
|
23007
|
-
i += 2;
|
|
23008
|
-
continue;
|
|
23009
|
-
}
|
|
23010
|
-
if (source[i] === "\n") {
|
|
23011
|
-
out.push("\n");
|
|
23012
|
-
i++;
|
|
23013
|
-
break;
|
|
23014
|
-
}
|
|
23015
|
-
out.push(" ");
|
|
23016
|
-
i++;
|
|
23017
|
-
}
|
|
23018
|
-
if (i < n && source[i] === quote) {
|
|
23019
|
-
out.push(quote);
|
|
23020
|
-
i++;
|
|
23021
|
-
}
|
|
23022
|
-
prevSignificant = quote;
|
|
23023
|
-
prevWord = "";
|
|
23024
|
-
continue;
|
|
23025
|
-
}
|
|
23026
|
-
if (ch === "`") {
|
|
23027
|
-
out.push("`");
|
|
23028
|
-
i++;
|
|
23029
|
-
while (i < n && source[i] !== "`") {
|
|
23030
|
-
if (source[i] === "\\" && i + 1 < n) {
|
|
23031
|
-
out.push(source[i + 1] === "\n" ? " \n" : " ");
|
|
23032
|
-
i += 2;
|
|
23033
|
-
continue;
|
|
23034
|
-
}
|
|
23035
|
-
if (source[i] === "$" && source[i + 1] === "{") {
|
|
23036
|
-
out.push("${");
|
|
23037
|
-
i += 2;
|
|
23038
|
-
let depth = 1;
|
|
23039
|
-
const exprStart = i;
|
|
23040
|
-
while (i < n && depth > 0) {
|
|
23041
|
-
if (source[i] === "{") depth++;
|
|
23042
|
-
else if (source[i] === "}") depth--;
|
|
23043
|
-
if (depth === 0) break;
|
|
23044
|
-
i++;
|
|
23045
|
-
}
|
|
23046
|
-
out.push(stripLiterals(source.slice(exprStart, i)));
|
|
23047
|
-
if (i < n && source[i] === "}") {
|
|
23048
|
-
out.push("}");
|
|
23049
|
-
i++;
|
|
23050
|
-
}
|
|
23051
|
-
continue;
|
|
23052
|
-
}
|
|
23053
|
-
out.push(source[i] === "\n" ? "\n" : " ");
|
|
23054
|
-
i++;
|
|
23055
|
-
}
|
|
23056
|
-
if (i < n && source[i] === "`") {
|
|
23057
|
-
out.push("`");
|
|
23058
|
-
i++;
|
|
23059
|
-
}
|
|
23060
|
-
prevSignificant = "`";
|
|
23061
|
-
prevWord = "";
|
|
23062
|
-
continue;
|
|
23063
|
-
}
|
|
23064
|
-
if (ch === "/" && !prevEndsExpression()) {
|
|
23065
|
-
let j = i + 1;
|
|
23066
|
-
let ok = false;
|
|
23067
|
-
let inClass = false;
|
|
23068
|
-
while (j < n) {
|
|
23069
|
-
const c = source[j];
|
|
23070
|
-
if (c === "\\") {
|
|
23071
|
-
j += 2;
|
|
23072
|
-
continue;
|
|
23073
|
-
}
|
|
23074
|
-
if (c === "\n") break;
|
|
23075
|
-
if (c === "[") inClass = true;
|
|
23076
|
-
else if (c === "]") inClass = false;
|
|
23077
|
-
else if (c === "/" && !inClass) {
|
|
23078
|
-
ok = true;
|
|
23079
|
-
break;
|
|
23080
|
-
}
|
|
23081
|
-
j++;
|
|
23082
|
-
}
|
|
23083
|
-
if (ok) {
|
|
23084
|
-
out.push("/");
|
|
23085
|
-
for (let k = i + 1; k < j; k++) out.push(" ");
|
|
23086
|
-
out.push("/");
|
|
23087
|
-
i = j + 1;
|
|
23088
|
-
while (i < n && /[a-z]/i.test(source[i])) {
|
|
23089
|
-
out.push(source[i]);
|
|
23090
|
-
i++;
|
|
23091
|
-
}
|
|
23092
|
-
prevSignificant = "/";
|
|
23093
|
-
prevWord = "";
|
|
23094
|
-
continue;
|
|
23095
|
-
}
|
|
23096
|
-
}
|
|
23097
|
-
out.push(ch);
|
|
23098
|
-
if (!/\s/.test(ch)) {
|
|
23099
|
-
prevSignificant = ch;
|
|
23100
|
-
if (/[A-Za-z0-9_$]/.test(ch)) {
|
|
23101
|
-
prevWord = /[A-Za-z0-9_$]/.test(source[i - 1] ?? "") ? prevWord + ch : ch;
|
|
23102
|
-
} else {
|
|
23103
|
-
prevWord = "";
|
|
23104
|
-
}
|
|
23105
|
-
}
|
|
23106
|
-
i++;
|
|
22881
|
+
function containsTypeScript(directory) {
|
|
22882
|
+
if (!fs3.existsSync(directory) || !fs3.statSync(directory).isDirectory()) return false;
|
|
22883
|
+
for (const entry of fs3.readdirSync(directory, { withFileTypes: true })) {
|
|
22884
|
+
if (["node_modules", ".git", "dist", "build"].includes(entry.name)) continue;
|
|
22885
|
+
const target = path2.join(directory, entry.name);
|
|
22886
|
+
if (entry.isDirectory() ? containsTypeScript(target) : /\.[cm]?[jt]sx?$/.test(entry.name)) return true;
|
|
23107
22887
|
}
|
|
23108
|
-
return
|
|
23109
|
-
}
|
|
23110
|
-
function countClassesQuick(source) {
|
|
23111
|
-
const matches = source.match(
|
|
23112
|
-
/(?:^|\n)\s*(?:export\s+)?(?:abstract\s+)?class\s+\w+/g
|
|
23113
|
-
);
|
|
23114
|
-
return matches ? matches.length : 0;
|
|
23115
|
-
}
|
|
23116
|
-
function countFunctionsQuick(source) {
|
|
23117
|
-
const clean = stripLiterals(source);
|
|
23118
|
-
let count = 0;
|
|
23119
|
-
const funcDecls = clean.match(
|
|
23120
|
-
/(?:^|\n)\s*(?:export\s+)?(?:async\s+)?function\s+\w+\s*\(/g
|
|
23121
|
-
);
|
|
23122
|
-
if (funcDecls) count += funcDecls.length;
|
|
23123
|
-
const methods = clean.match(
|
|
23124
|
-
/(?:^|\n)\s*(?:public\s+|private\s+|protected\s+)?(?:static\s+)?(?:async\s+)?(?:get\s+|set\s+)?\w+\s*\([^)]*\)\s*(?::\s*\S+)?\s*\{/g
|
|
23125
|
-
);
|
|
23126
|
-
if (methods) count += methods.length;
|
|
23127
|
-
const arrows = clean.match(
|
|
23128
|
-
/(?:^|\n)\s*(?:export\s+)?(?:const|let|var)\s+\w+\s*=\s*(?:async\s+)?\(/g
|
|
23129
|
-
);
|
|
23130
|
-
if (arrows) count += arrows.length;
|
|
23131
|
-
return count;
|
|
23132
|
-
}
|
|
23133
|
-
function resolveRoot(root = "src") {
|
|
23134
|
-
const rootPath = path2.resolve(root);
|
|
23135
|
-
if (fs3.existsSync(rootPath) && walkFiles(rootPath, [".ts", ".js"]).length > 0) {
|
|
23136
|
-
_lastScanRoot = rootPath;
|
|
23137
|
-
return root;
|
|
23138
|
-
}
|
|
23139
|
-
const fwDir = path2.resolve(path2.dirname(new URL(import.meta.url).pathname));
|
|
23140
|
-
_lastScanRoot = fwDir;
|
|
23141
|
-
return fwDir;
|
|
23142
|
-
}
|
|
23143
|
-
function quickMetrics(root = "src") {
|
|
23144
|
-
root = resolveRoot(root);
|
|
23145
|
-
const rootPath = path2.resolve(root);
|
|
23146
|
-
if (!fs3.existsSync(rootPath)) {
|
|
23147
|
-
return { error: `Directory not found: ${root}` };
|
|
23148
|
-
}
|
|
23149
|
-
const tsFiles = walkFiles(rootPath, [".ts", ".js"]);
|
|
23150
|
-
const twigFiles = walkFiles(rootPath, [".twig", ".html"]);
|
|
23151
|
-
const migrationsDir = path2.resolve("migrations");
|
|
23152
|
-
const migrationFiles = [
|
|
23153
|
-
...walkFiles(migrationsDir, [".sql"]),
|
|
23154
|
-
...walkFiles(migrationsDir, [".ts"])
|
|
23155
|
-
];
|
|
23156
|
-
const scssFiles = walkFiles(rootPath, [".scss", ".css"]);
|
|
23157
|
-
let totalLoc = 0;
|
|
23158
|
-
let totalBlank = 0;
|
|
23159
|
-
let totalComment = 0;
|
|
23160
|
-
let totalClasses = 0;
|
|
23161
|
-
let totalFunctions = 0;
|
|
23162
|
-
const fileDetails = [];
|
|
23163
|
-
for (const f of tsFiles) {
|
|
23164
|
-
const source = readFileSafe(f);
|
|
23165
|
-
if (source === null) continue;
|
|
23166
|
-
const counts = countLines(source);
|
|
23167
|
-
const classes = countClassesQuick(source);
|
|
23168
|
-
const functions = countFunctionsQuick(source);
|
|
23169
|
-
totalLoc += counts.loc;
|
|
23170
|
-
totalBlank += counts.blank;
|
|
23171
|
-
totalComment += counts.comment;
|
|
23172
|
-
totalClasses += classes;
|
|
23173
|
-
totalFunctions += functions;
|
|
23174
|
-
fileDetails.push({
|
|
23175
|
-
path: relativePath(f, rootPath),
|
|
23176
|
-
loc: counts.loc,
|
|
23177
|
-
blank: counts.blank,
|
|
23178
|
-
comment: counts.comment,
|
|
23179
|
-
classes,
|
|
23180
|
-
functions
|
|
23181
|
-
});
|
|
23182
|
-
}
|
|
23183
|
-
fileDetails.sort((a, b) => b.loc - a.loc);
|
|
23184
|
-
let routeCount = 0;
|
|
23185
|
-
let ormCount = 0;
|
|
23186
|
-
for (const f of tsFiles) {
|
|
23187
|
-
const source = readFileSafe(f);
|
|
23188
|
-
if (source === null) continue;
|
|
23189
|
-
const routes = source.match(
|
|
23190
|
-
/(?:router\s*\.\s*(?:get|post|put|delete|patch|any)\s*\(|@(?:get|post|put|delete|patch)\s*\()/g
|
|
23191
|
-
);
|
|
23192
|
-
if (routes) routeCount += routes.length;
|
|
23193
|
-
const orms = source.match(
|
|
23194
|
-
/class\s+\w+\s+extends\s+(?:ORM|Model)\b/g
|
|
23195
|
-
);
|
|
23196
|
-
if (orms) ormCount += orms.length;
|
|
23197
|
-
}
|
|
23198
|
-
const breakdown = {
|
|
23199
|
-
typescript: tsFiles.filter((f) => f.endsWith(".ts")).length,
|
|
23200
|
-
javascript: tsFiles.filter((f) => f.endsWith(".js")).length,
|
|
23201
|
-
templates: twigFiles.length,
|
|
23202
|
-
migrations: migrationFiles.length,
|
|
23203
|
-
stylesheets: scssFiles.length
|
|
23204
|
-
};
|
|
23205
|
-
return {
|
|
23206
|
-
file_count: tsFiles.length,
|
|
23207
|
-
total_loc: totalLoc,
|
|
23208
|
-
total_blank: totalBlank,
|
|
23209
|
-
total_comment: totalComment,
|
|
23210
|
-
lloc: totalLoc,
|
|
23211
|
-
classes: totalClasses,
|
|
23212
|
-
functions: totalFunctions,
|
|
23213
|
-
route_count: routeCount,
|
|
23214
|
-
orm_count: ormCount,
|
|
23215
|
-
template_count: twigFiles.length,
|
|
23216
|
-
migration_count: migrationFiles.length,
|
|
23217
|
-
avg_file_size: tsFiles.length > 0 ? Math.round(totalLoc / tsFiles.length * 10) / 10 : 0,
|
|
23218
|
-
largest_files: fileDetails.slice(0, 10),
|
|
23219
|
-
breakdown
|
|
23220
|
-
};
|
|
22888
|
+
return false;
|
|
23221
22889
|
}
|
|
23222
|
-
function
|
|
23223
|
-
const resolved =
|
|
23224
|
-
const
|
|
23225
|
-
|
|
23226
|
-
|
|
23227
|
-
return [resolved, scanningFramework ? "framework" : "project"];
|
|
22890
|
+
function resolveTarget(root = "src") {
|
|
22891
|
+
const resolved = containsTypeScript(root) ? path2.resolve(root) : path2.dirname(fileURLToPath2(import.meta.url));
|
|
22892
|
+
const mode = containsTypeScript(root) ? "project" : "framework";
|
|
22893
|
+
lastScanRoot = resolved;
|
|
22894
|
+
return [resolved, mode];
|
|
23228
22895
|
}
|
|
23229
22896
|
function enginePath() {
|
|
23230
|
-
const names = process.platform === "win32" ? ["tina4.exe", "tina4
|
|
23231
|
-
for (const
|
|
23232
|
-
if (!dir) continue;
|
|
22897
|
+
const names = process.platform === "win32" ? ["tina4.exe", "tina4"] : ["tina4"];
|
|
22898
|
+
for (const directory of (process.env.PATH || "").split(path2.delimiter)) {
|
|
23233
22899
|
for (const name of names) {
|
|
23234
|
-
const candidate = path2.join(
|
|
22900
|
+
const candidate = path2.join(directory, name);
|
|
23235
22901
|
try {
|
|
23236
|
-
if (!fs3.statSync(candidate).isFile()) continue;
|
|
23237
22902
|
fs3.accessSync(candidate, fs3.constants.X_OK);
|
|
22903
|
+
if (!fs3.statSync(candidate).isFile()) continue;
|
|
22904
|
+
const descriptor = fs3.openSync(candidate, "r");
|
|
22905
|
+
const header = Buffer.alloc(2);
|
|
22906
|
+
fs3.readSync(descriptor, header, 0, 2, 0);
|
|
22907
|
+
fs3.closeSync(descriptor);
|
|
22908
|
+
if (header.toString("latin1") !== "#!") return candidate;
|
|
23238
22909
|
} catch {
|
|
23239
22910
|
continue;
|
|
23240
22911
|
}
|
|
23241
|
-
try {
|
|
23242
|
-
const fd = fs3.openSync(candidate, "r");
|
|
23243
|
-
const buf = Buffer.alloc(2);
|
|
23244
|
-
fs3.readSync(fd, buf, 0, 2, 0);
|
|
23245
|
-
fs3.closeSync(fd);
|
|
23246
|
-
if (buf.toString("latin1") === "#!") continue;
|
|
23247
|
-
} catch {
|
|
23248
|
-
}
|
|
23249
|
-
return candidate;
|
|
23250
22912
|
}
|
|
23251
22913
|
}
|
|
23252
22914
|
return null;
|
|
23253
22915
|
}
|
|
23254
22916
|
function runEngine(target) {
|
|
23255
22917
|
const binary = enginePath();
|
|
23256
|
-
if (binary
|
|
23257
|
-
|
|
23258
|
-
}
|
|
23259
|
-
const proc = spawnSync(binary, ["metrics", "--path", target, "--json"], {
|
|
22918
|
+
if (!binary) throw new MetricsEngineError(`tina4 not found on PATH - ${INSTALL_HINT}`);
|
|
22919
|
+
const processResult = spawnSync(binary, ["metrics", "--path", target, "--json"], {
|
|
23260
22920
|
encoding: "utf8",
|
|
23261
|
-
timeout:
|
|
22921
|
+
timeout: 6e4,
|
|
23262
22922
|
maxBuffer: 64 * 1024 * 1024
|
|
23263
22923
|
});
|
|
23264
|
-
if (
|
|
23265
|
-
|
|
23266
|
-
if (err.code === "ETIMEDOUT") {
|
|
23267
|
-
throw new MetricsEngineError(`tina4 metrics timed out after ${TIMEOUT_MS / 1e3}s on ${target}`);
|
|
23268
|
-
}
|
|
23269
|
-
throw new MetricsEngineError(`could not run ${binary}: ${err.message}`);
|
|
23270
|
-
}
|
|
23271
|
-
if (proc.status !== 0) {
|
|
23272
|
-
const detail = (proc.stderr || proc.stdout || "").trim().split("\n")[0];
|
|
23273
|
-
throw new MetricsEngineError(
|
|
23274
|
-
`tina4 metrics failed on ${target}: ${detail || `exit code ${proc.status}`}`
|
|
23275
|
-
);
|
|
22924
|
+
if (processResult.error) {
|
|
22925
|
+
throw new MetricsEngineError(`could not run ${binary}: ${processResult.error.message}`);
|
|
23276
22926
|
}
|
|
23277
|
-
if (
|
|
23278
|
-
|
|
22927
|
+
if (processResult.status !== 0) {
|
|
22928
|
+
const detail = (processResult.stderr || processResult.stdout || "").trim().split("\n")[0];
|
|
22929
|
+
throw new MetricsEngineError(`tina4 metrics failed on ${target}: ${detail || processResult.status}`);
|
|
23279
22930
|
}
|
|
23280
|
-
let payload;
|
|
23281
22931
|
try {
|
|
23282
|
-
payload = JSON.parse(
|
|
23283
|
-
|
|
23284
|
-
|
|
23285
|
-
|
|
23286
|
-
|
|
23287
|
-
|
|
22932
|
+
const payload = JSON.parse(processResult.stdout);
|
|
22933
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
22934
|
+
throw new Error("non-object payload");
|
|
22935
|
+
}
|
|
22936
|
+
return payload;
|
|
22937
|
+
} catch (error) {
|
|
22938
|
+
throw new MetricsEngineError(`tina4 metrics returned unreadable JSON: ${error.message}`);
|
|
23288
22939
|
}
|
|
23289
|
-
return payload;
|
|
23290
22940
|
}
|
|
23291
|
-
function
|
|
23292
|
-
|
|
23293
|
-
|
|
23294
|
-
if (!ok) {
|
|
23295
|
-
throw new MetricsEngineError(
|
|
23296
|
-
`engine payload has no usable '${key}' - the installed tina4 CLI predates a field the dashboard renders. Update it: ${INSTALL_HINT}`
|
|
23297
|
-
);
|
|
22941
|
+
function requireArray(payload, key) {
|
|
22942
|
+
if (!Array.isArray(payload[key])) {
|
|
22943
|
+
throw new MetricsEngineError(`engine payload has no usable '${key}' - ${INSTALL_HINT}`);
|
|
23298
22944
|
}
|
|
23299
|
-
return
|
|
22945
|
+
return payload[key];
|
|
23300
22946
|
}
|
|
23301
22947
|
function fullAnalysis(root = "src") {
|
|
23302
|
-
const [resolved, scanMode] =
|
|
22948
|
+
const [resolved, scanMode] = resolveTarget(root);
|
|
23303
22949
|
const payload = runEngine(resolved);
|
|
23304
|
-
const summary =
|
|
23305
|
-
|
|
23306
|
-
|
|
23307
|
-
|
|
23308
|
-
|
|
23309
|
-
|
|
23310
|
-
|
|
23311
|
-
|
|
22950
|
+
const summary = payload.summary;
|
|
22951
|
+
if (!summary || typeof summary !== "object" || Array.isArray(summary)) {
|
|
22952
|
+
throw new MetricsEngineError(`engine payload has no usable 'summary' - ${INSTALL_HINT}`);
|
|
22953
|
+
}
|
|
22954
|
+
const fileMetrics = requireArray(payload, "file_metrics");
|
|
22955
|
+
const functions = requireArray(payload, "most_complex_functions");
|
|
22956
|
+
const missingSummary = SUMMARY_KEYS.filter((key) => !(key in summary));
|
|
22957
|
+
if (missingSummary.length) throw new MetricsEngineError(`engine summary is missing ${missingSummary.join(", ")}`);
|
|
22958
|
+
const missingFile = fileMetrics.length ? FILE_KEYS.filter((key) => !(key in fileMetrics[0])) : [];
|
|
22959
|
+
if (missingFile.length) throw new MetricsEngineError(`engine file_metrics is missing ${missingFile.join(", ")}`);
|
|
22960
|
+
const missingFunction = functions.length ? FUNCTION_KEYS.filter((key) => !(key in functions[0])) : [];
|
|
22961
|
+
if (missingFunction.length) {
|
|
22962
|
+
throw new MetricsEngineError(`engine function metrics are missing ${missingFunction.join(", ")}`);
|
|
23312
22963
|
}
|
|
23313
|
-
|
|
23314
|
-
|
|
23315
|
-
|
|
23316
|
-
|
|
23317
|
-
|
|
23318
|
-
|
|
23319
|
-
|
|
23320
|
-
|
|
23321
|
-
|
|
23322
|
-
for (const key of SUMMARY_KEYS) result[key] = summary[key];
|
|
23323
|
-
result.file_metrics = fileMetrics;
|
|
23324
|
-
result.most_complex_functions = functions.slice(0, 15);
|
|
23325
|
-
result.dependency_graph = payload.dependency_graph || {};
|
|
23326
|
-
result.scan_mode = scanMode;
|
|
23327
|
-
result.scan_root = path2.resolve(resolved);
|
|
23328
|
-
result.engine = "tina4-cli";
|
|
23329
|
-
return result;
|
|
23330
|
-
}
|
|
23331
|
-
function offenders(root = "src", top = 20) {
|
|
23332
|
-
const [resolved, scanMode] = resolveScanTarget(root);
|
|
23333
|
-
const payload = runEngine(resolved);
|
|
23334
|
-
const found = requireKey(payload, "offenders", true);
|
|
23335
|
-
const summary = { ...requireKey(payload, "summary", false) };
|
|
23336
|
-
summary.scan_mode = scanMode;
|
|
23337
|
-
summary.scan_root = path2.resolve(resolved);
|
|
23338
|
-
summary.engine = "tina4-cli";
|
|
23339
|
-
if (summary.total_offenders === void 0) summary.total_offenders = found.length;
|
|
23340
|
-
return { offenders: found.slice(0, top), summary };
|
|
22964
|
+
return {
|
|
22965
|
+
...Object.fromEntries(SUMMARY_KEYS.map((key) => [key, summary[key]])),
|
|
22966
|
+
file_metrics: fileMetrics,
|
|
22967
|
+
most_complex_functions: functions.slice(0, 15),
|
|
22968
|
+
dependency_graph: payload.dependency_graph || {},
|
|
22969
|
+
scan_mode: scanMode,
|
|
22970
|
+
scan_root: resolved,
|
|
22971
|
+
engine: "tina4-cli"
|
|
22972
|
+
};
|
|
23341
22973
|
}
|
|
23342
22974
|
function fileDetail(filePath) {
|
|
23343
22975
|
if (!filePath) throw new MetricsEngineError("fileDetail needs a path");
|
|
23344
22976
|
let target = filePath;
|
|
23345
|
-
if (!fs3.existsSync(target) &&
|
|
23346
|
-
const candidate = path2.join(_lastScanRoot, filePath);
|
|
23347
|
-
if (fs3.existsSync(candidate)) target = candidate;
|
|
23348
|
-
}
|
|
22977
|
+
if (!fs3.existsSync(target) && lastScanRoot) target = path2.join(lastScanRoot, filePath);
|
|
23349
22978
|
if (!fs3.existsSync(target)) throw new MetricsEngineError(`no such file: ${filePath}`);
|
|
23350
22979
|
if (fs3.statSync(target).isDirectory()) throw new MetricsEngineError(`not a file: ${filePath}`);
|
|
23351
22980
|
const payload = runEngine(target);
|
|
23352
|
-
const
|
|
23353
|
-
if (!
|
|
23354
|
-
|
|
23355
|
-
|
|
23356
|
-
|
|
22981
|
+
const files = requireArray(payload, "file_metrics");
|
|
22982
|
+
if (!files.length) throw new MetricsEngineError(`engine reported no metrics for ${filePath}`);
|
|
22983
|
+
return {
|
|
22984
|
+
...files[0],
|
|
22985
|
+
function_count: files[0].functions || 0,
|
|
22986
|
+
functions: requireArray(payload, "most_complex_functions"),
|
|
22987
|
+
engine: "tina4-cli"
|
|
22988
|
+
};
|
|
23357
22989
|
}
|
|
23358
|
-
var
|
|
22990
|
+
var lastScanRoot, MetricsEngineError, INSTALL_HINT, SUMMARY_KEYS, FILE_KEYS, FUNCTION_KEYS;
|
|
23359
22991
|
var init_metrics = __esm({
|
|
23360
22992
|
"../core/src/metrics.ts"() {
|
|
23361
22993
|
"use strict";
|
|
23362
|
-
|
|
22994
|
+
lastScanRoot = "";
|
|
23363
22995
|
MetricsEngineError = class extends Error {
|
|
23364
22996
|
constructor(message) {
|
|
23365
22997
|
super(message);
|
|
23366
22998
|
this.name = "MetricsEngineError";
|
|
23367
22999
|
}
|
|
23368
23000
|
};
|
|
23369
|
-
|
|
23370
|
-
INSTALL_HINT = [
|
|
23371
|
-
"the tina4 CLI provides the metrics engine (ADR-0002). Install it with",
|
|
23372
|
-
" curl -fsSL https://tina4.com/install.sh | sh",
|
|
23373
|
-
"or see https://tina4.com/cli"
|
|
23374
|
-
].join("\n");
|
|
23001
|
+
INSTALL_HINT = "update the native tina4 CLI: https://tina4.com/cli";
|
|
23375
23002
|
SUMMARY_KEYS = ["files_analyzed", "total_functions", "avg_complexity", "avg_maintainability"];
|
|
23376
23003
|
FILE_KEYS = ["path", "loc", "avg_complexity", "maintainability", "has_tests"];
|
|
23377
23004
|
FUNCTION_KEYS = ["name", "file", "line", "complexity", "loc"];
|
|
@@ -23379,7 +23006,7 @@ var init_metrics = __esm({
|
|
|
23379
23006
|
});
|
|
23380
23007
|
|
|
23381
23008
|
// ../core/src/feedback.ts
|
|
23382
|
-
import { readFileSync as
|
|
23009
|
+
import { readFileSync as readFileSync12, existsSync as existsSync14 } from "node:fs";
|
|
23383
23010
|
import { dirname as dirname8, join as join19, resolve as resolve10 } from "node:path";
|
|
23384
23011
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
23385
23012
|
function feedbackEnabled() {
|
|
@@ -23520,7 +23147,7 @@ var init_feedback = __esm({
|
|
|
23520
23147
|
handleFeedbackWidgetJs = (_req, res) => {
|
|
23521
23148
|
let body;
|
|
23522
23149
|
if (existsSync14(WIDGET_BUNDLE_PATH)) {
|
|
23523
|
-
body =
|
|
23150
|
+
body = readFileSync12(WIDGET_BUNDLE_PATH);
|
|
23524
23151
|
} else {
|
|
23525
23152
|
body = "console.warn('tina4-feedback-widget bundle not built yet');";
|
|
23526
23153
|
}
|
|
@@ -23535,7 +23162,7 @@ var init_feedback = __esm({
|
|
|
23535
23162
|
});
|
|
23536
23163
|
|
|
23537
23164
|
// ../core/src/version.ts
|
|
23538
|
-
import { existsSync as existsSync15, readFileSync as
|
|
23165
|
+
import { existsSync as existsSync15, readFileSync as readFileSync13 } from "node:fs";
|
|
23539
23166
|
import { dirname as dirname9, join as join20 } from "node:path";
|
|
23540
23167
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
23541
23168
|
function resolveFrameworkVersion() {
|
|
@@ -23544,7 +23171,7 @@ function resolveFrameworkVersion() {
|
|
|
23544
23171
|
const pkgPath = join20(dir, "package.json");
|
|
23545
23172
|
if (existsSync15(pkgPath)) {
|
|
23546
23173
|
try {
|
|
23547
|
-
const pkg = JSON.parse(
|
|
23174
|
+
const pkg = JSON.parse(readFileSync13(pkgPath, "utf-8"));
|
|
23548
23175
|
if (typeof pkg.version === "string" && pkg.version) return pkg.version;
|
|
23549
23176
|
} catch {
|
|
23550
23177
|
}
|
|
@@ -26332,8 +25959,8 @@ __export(context_exports, {
|
|
|
26332
25959
|
fts5Supported: () => fts5Supported
|
|
26333
25960
|
});
|
|
26334
25961
|
import { DatabaseSync as DatabaseSync4 } from "node:sqlite";
|
|
26335
|
-
import { existsSync as existsSync17, mkdirSync as mkdirSync14, readFileSync as
|
|
26336
|
-
import { basename as basename5, dirname as dirname11, extname as
|
|
25962
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync14, readFileSync as readFileSync15, readdirSync as readdirSync10, realpathSync as realpathSync5 } from "node:fs";
|
|
25963
|
+
import { basename as basename5, dirname as dirname11, extname as extname5, isAbsolute as isAbsolute6, join as join22, relative as relative3, resolve as resolve12 } from "node:path";
|
|
26337
25964
|
function fts5Supported() {
|
|
26338
25965
|
try {
|
|
26339
25966
|
const conn = new DatabaseSync4(":memory:");
|
|
@@ -26468,7 +26095,7 @@ var init_context = __esm({
|
|
|
26468
26095
|
}
|
|
26469
26096
|
// ── indexing ───────────────────────────────────────────────
|
|
26470
26097
|
static chunksFor(label, text) {
|
|
26471
|
-
const ext =
|
|
26098
|
+
const ext = extname5(label).toLowerCase();
|
|
26472
26099
|
const special = SPECIAL_FILES.has(basename5(label).toLowerCase());
|
|
26473
26100
|
if (CODE_EXTS.has(ext) || CONFIG_EXTS.has(ext) || special) {
|
|
26474
26101
|
return chunkCode(text, label);
|
|
@@ -26486,7 +26113,7 @@ var init_context = __esm({
|
|
|
26486
26113
|
const stored = label != null ? String(label) : String(file);
|
|
26487
26114
|
let text;
|
|
26488
26115
|
try {
|
|
26489
|
-
text =
|
|
26116
|
+
text = readFileSync15(file, "utf-8");
|
|
26490
26117
|
} catch {
|
|
26491
26118
|
return 0;
|
|
26492
26119
|
}
|
|
@@ -26507,7 +26134,7 @@ var init_context = __esm({
|
|
|
26507
26134
|
static eligible(filename) {
|
|
26508
26135
|
const fn = filename.toLowerCase();
|
|
26509
26136
|
if (fn.endsWith(".min.js")) return false;
|
|
26510
|
-
const ext =
|
|
26137
|
+
const ext = extname5(fn);
|
|
26511
26138
|
return CODE_EXTS.has(ext) || DOC_EXTS.has(ext) || CONFIG_EXTS.has(ext) || SPECIAL_FILES.has(fn);
|
|
26512
26139
|
}
|
|
26513
26140
|
/**
|
|
@@ -26536,7 +26163,7 @@ var init_context = __esm({
|
|
|
26536
26163
|
for (const fn of files) {
|
|
26537
26164
|
if (!_Context.eligible(fn)) continue;
|
|
26538
26165
|
const full = join22(dir, fn);
|
|
26539
|
-
const rel =
|
|
26166
|
+
const rel = relative3(rootAbs, full);
|
|
26540
26167
|
total += this.indexPath(full, rel);
|
|
26541
26168
|
}
|
|
26542
26169
|
for (const d of subdirs) walk2(join22(dir, d));
|
|
@@ -26557,7 +26184,7 @@ var init_context = __esm({
|
|
|
26557
26184
|
const raw = String(changedPath);
|
|
26558
26185
|
const abs = isAbsolute6(raw) ? raw : join22(process.cwd(), raw);
|
|
26559
26186
|
const resolved = realResolve(resolve12(abs));
|
|
26560
|
-
const rel =
|
|
26187
|
+
const rel = relative3(this.root, resolved);
|
|
26561
26188
|
if (rel === "" || rel.startsWith("..") || isAbsolute6(rel)) {
|
|
26562
26189
|
return -1;
|
|
26563
26190
|
}
|
|
@@ -28479,7 +28106,7 @@ var init_job = __esm({
|
|
|
28479
28106
|
});
|
|
28480
28107
|
|
|
28481
28108
|
// ../core/src/queueBackends/liteBackend.ts
|
|
28482
|
-
import { mkdirSync as mkdirSync15, readdirSync as readdirSync11, readFileSync as
|
|
28109
|
+
import { mkdirSync as mkdirSync15, readdirSync as readdirSync11, readFileSync as readFileSync16, writeFileSync as writeFileSync11, unlinkSync as unlinkSync7, existsSync as existsSync18 } from "node:fs";
|
|
28483
28110
|
import { join as join23 } from "node:path";
|
|
28484
28111
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
28485
28112
|
var LiteBackend;
|
|
@@ -28579,7 +28206,7 @@ var init_liteBackend = __esm({
|
|
|
28579
28206
|
const filePath = join23(dir, filename);
|
|
28580
28207
|
let job;
|
|
28581
28208
|
try {
|
|
28582
|
-
job = JSON.parse(
|
|
28209
|
+
job = JSON.parse(readFileSync16(filePath, "utf-8"));
|
|
28583
28210
|
} catch {
|
|
28584
28211
|
continue;
|
|
28585
28212
|
}
|
|
@@ -28643,7 +28270,7 @@ var init_liteBackend = __esm({
|
|
|
28643
28270
|
const filePath = join23(reservedDir, filename);
|
|
28644
28271
|
let record;
|
|
28645
28272
|
try {
|
|
28646
|
-
record = JSON.parse(
|
|
28273
|
+
record = JSON.parse(readFileSync16(filePath, "utf-8"));
|
|
28647
28274
|
} catch {
|
|
28648
28275
|
continue;
|
|
28649
28276
|
}
|
|
@@ -28756,7 +28383,7 @@ var init_liteBackend = __esm({
|
|
|
28756
28383
|
let count = 0;
|
|
28757
28384
|
for (const file of files) {
|
|
28758
28385
|
try {
|
|
28759
|
-
const job = JSON.parse(
|
|
28386
|
+
const job = JSON.parse(readFileSync16(join23(scanDir, file), "utf-8"));
|
|
28760
28387
|
if (job.status === status2) count++;
|
|
28761
28388
|
} catch {
|
|
28762
28389
|
}
|
|
@@ -28813,7 +28440,7 @@ var init_liteBackend = __esm({
|
|
|
28813
28440
|
const files = readdirSync11(dir).filter((f) => f.endsWith(".queue-data")).sort();
|
|
28814
28441
|
for (const file of files) {
|
|
28815
28442
|
try {
|
|
28816
|
-
const job = JSON.parse(
|
|
28443
|
+
const job = JSON.parse(readFileSync16(join23(dir, file), "utf-8"));
|
|
28817
28444
|
const attempts = job.attempts || 0;
|
|
28818
28445
|
if (attempts > 0 && attempts < maxRetries) {
|
|
28819
28446
|
results.push(job);
|
|
@@ -28839,7 +28466,7 @@ var init_liteBackend = __esm({
|
|
|
28839
28466
|
const failedDir = join23(this.basePath, q, "failed");
|
|
28840
28467
|
const filePath = join23(failedDir, `${jobId}.queue-data`);
|
|
28841
28468
|
if (existsSync18(filePath)) {
|
|
28842
|
-
const job = JSON.parse(
|
|
28469
|
+
const job = JSON.parse(readFileSync16(filePath, "utf-8"));
|
|
28843
28470
|
job.status = "pending";
|
|
28844
28471
|
job.attempts = (job.attempts || 0) + 1;
|
|
28845
28472
|
job.error = void 0;
|
|
@@ -28863,7 +28490,7 @@ var init_liteBackend = __esm({
|
|
|
28863
28490
|
const files = readdirSync11(failedDir).filter((f) => f.endsWith(".queue-data")).sort();
|
|
28864
28491
|
for (const file of files) {
|
|
28865
28492
|
try {
|
|
28866
|
-
const job = JSON.parse(
|
|
28493
|
+
const job = JSON.parse(readFileSync16(join23(failedDir, file), "utf-8"));
|
|
28867
28494
|
if ((job.attempts || 0) >= maxRetries) {
|
|
28868
28495
|
job.status = "dead";
|
|
28869
28496
|
results.push(job);
|
|
@@ -28897,7 +28524,7 @@ var init_liteBackend = __esm({
|
|
|
28897
28524
|
const files = readdirSync11(dir).filter((f) => f.endsWith(".queue-data"));
|
|
28898
28525
|
for (const file of files) {
|
|
28899
28526
|
try {
|
|
28900
|
-
const job = JSON.parse(
|
|
28527
|
+
const job = JSON.parse(readFileSync16(join23(dir, file), "utf-8"));
|
|
28901
28528
|
if (job.status === status2) {
|
|
28902
28529
|
unlinkSync7(join23(dir, file));
|
|
28903
28530
|
count++;
|
|
@@ -28924,7 +28551,7 @@ var init_liteBackend = __esm({
|
|
|
28924
28551
|
for (const file of files) {
|
|
28925
28552
|
try {
|
|
28926
28553
|
const filePath = join23(failedDir, file);
|
|
28927
|
-
const job = JSON.parse(
|
|
28554
|
+
const job = JSON.parse(readFileSync16(filePath, "utf-8"));
|
|
28928
28555
|
if ((job.attempts || 0) >= maxRetries) {
|
|
28929
28556
|
continue;
|
|
28930
28557
|
}
|
|
@@ -28956,7 +28583,7 @@ var init_liteBackend = __esm({
|
|
|
28956
28583
|
const filePath = join23(dir, file);
|
|
28957
28584
|
let job;
|
|
28958
28585
|
try {
|
|
28959
|
-
job = JSON.parse(
|
|
28586
|
+
job = JSON.parse(readFileSync16(filePath, "utf-8"));
|
|
28960
28587
|
} catch {
|
|
28961
28588
|
continue;
|
|
28962
28589
|
}
|
|
@@ -30419,7 +30046,7 @@ function detectVersion(projectRoot3) {
|
|
|
30419
30046
|
}
|
|
30420
30047
|
return "0.0.0";
|
|
30421
30048
|
}
|
|
30422
|
-
function
|
|
30049
|
+
function relativePath(absPath, projectRoot3, frameworkRoots) {
|
|
30423
30050
|
const norm = path6.resolve(absPath);
|
|
30424
30051
|
for (const fw of frameworkRoots) {
|
|
30425
30052
|
const parent = path6.dirname(fw);
|
|
@@ -30884,7 +30511,7 @@ function buildEntriesForFile(absPath, source, fwRoots, projectRoot3, version, ou
|
|
|
30884
30511
|
} catch {
|
|
30885
30512
|
return;
|
|
30886
30513
|
}
|
|
30887
|
-
const rel =
|
|
30514
|
+
const rel = relativePath(absPath, projectRoot3, fwRoots);
|
|
30888
30515
|
for (const cls of parsed.classes) {
|
|
30889
30516
|
if (!cls.exported && source === "framework") {
|
|
30890
30517
|
continue;
|
|
@@ -31520,8 +31147,8 @@ ${end}
|
|
|
31520
31147
|
|
|
31521
31148
|
// ../core/src/devAdmin.ts
|
|
31522
31149
|
import { cpus as osCpus } from "node:os";
|
|
31523
|
-
import { readFileSync as
|
|
31524
|
-
import { join as join27, dirname as dirname13, resolve as resolve16, relative as
|
|
31150
|
+
import { readFileSync as readFileSync20, writeFileSync as writeFileSync15, existsSync as existsSync22, readdirSync as readdirSync15, mkdirSync as mkdirSync18, copyFileSync as copyFileSync2, statSync as statSync16 } from "node:fs";
|
|
31151
|
+
import { join as join27, dirname as dirname13, resolve as resolve16, relative as relative7 } from "node:path";
|
|
31525
31152
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
31526
31153
|
import { timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
31527
31154
|
function escapeHtml(value) {
|
|
@@ -31640,7 +31267,7 @@ function readQueueDir(dir, topic, status2) {
|
|
|
31640
31267
|
for (const filename of readdirSync15(dir).sort()) {
|
|
31641
31268
|
if (!filename.endsWith(".queue-data")) continue;
|
|
31642
31269
|
try {
|
|
31643
|
-
jobs.push(mapQueueJob(JSON.parse(
|
|
31270
|
+
jobs.push(mapQueueJob(JSON.parse(readFileSync20(join27(dir, filename), "utf-8")), topic, status2));
|
|
31644
31271
|
} catch {
|
|
31645
31272
|
}
|
|
31646
31273
|
}
|
|
@@ -31759,7 +31386,7 @@ function resolveDevEnvVar(key) {
|
|
|
31759
31386
|
if (live !== void 0 && live !== "") return live;
|
|
31760
31387
|
const envPath = join27(process.cwd(), ".env");
|
|
31761
31388
|
if (!existsSync22(envPath)) return "";
|
|
31762
|
-
for (const line of
|
|
31389
|
+
for (const line of readFileSync20(envPath, "utf-8").split("\n")) {
|
|
31763
31390
|
const t = line.trim();
|
|
31764
31391
|
if (!t || t.startsWith("#") || !t.includes("=")) continue;
|
|
31765
31392
|
const eq = t.indexOf("=");
|
|
@@ -31769,7 +31396,7 @@ function resolveDevEnvVar(key) {
|
|
|
31769
31396
|
}
|
|
31770
31397
|
function upsertDevEnvVar(key, value) {
|
|
31771
31398
|
const envPath = join27(process.cwd(), ".env");
|
|
31772
|
-
const lines = existsSync22(envPath) ?
|
|
31399
|
+
const lines = existsSync22(envPath) ? readFileSync20(envPath, "utf-8").split("\n") : [];
|
|
31773
31400
|
let found = false;
|
|
31774
31401
|
const out = [];
|
|
31775
31402
|
for (const line of lines) {
|
|
@@ -31802,7 +31429,7 @@ function parseEnvFile() {
|
|
|
31802
31429
|
const envPath = join27(process.cwd(), ".env");
|
|
31803
31430
|
const result = {};
|
|
31804
31431
|
if (!existsSync22(envPath)) return result;
|
|
31805
|
-
const lines =
|
|
31432
|
+
const lines = readFileSync20(envPath, "utf-8").split("\n");
|
|
31806
31433
|
for (const line of lines) {
|
|
31807
31434
|
const trimmed = line.trim();
|
|
31808
31435
|
if (!trimmed || trimmed.startsWith("#") || !trimmed.includes("=")) continue;
|
|
@@ -31842,7 +31469,7 @@ function handleGalleryDeploy(router) {
|
|
|
31842
31469
|
const copied = [];
|
|
31843
31470
|
const allFiles = walkDirRecursive(gallerySrc);
|
|
31844
31471
|
for (const srcFile of allFiles) {
|
|
31845
|
-
const rel =
|
|
31472
|
+
const rel = relative7(gallerySrc, srcFile);
|
|
31846
31473
|
const dest = join27(projectSrc, rel);
|
|
31847
31474
|
mkdirSync18(dirname13(dest), { recursive: true });
|
|
31848
31475
|
copyFileSync2(srcFile, dest);
|
|
@@ -32504,9 +32131,6 @@ var init_devAdmin = __esm({
|
|
|
32504
32131
|
{ method: "GET", pattern: "/__dev/api/gallery", handler: handleGalleryList },
|
|
32505
32132
|
{ method: "POST", pattern: "/__dev/api/gallery/deploy", handler: handleGalleryDeploy(router) },
|
|
32506
32133
|
// Metrics
|
|
32507
|
-
{ method: "GET", pattern: "/__dev/api/metrics", handler: (_req, res) => {
|
|
32508
|
-
res.json(quickMetrics());
|
|
32509
|
-
} },
|
|
32510
32134
|
// No fallback (ADR-0002): a missing or stale CLI is a 503 naming the
|
|
32511
32135
|
// install command, never zeros that read as a healthy codebase.
|
|
32512
32136
|
{ method: "GET", pattern: "/__dev/api/metrics/full", handler: (_req, res) => {
|
|
@@ -33315,7 +32939,7 @@ var init_devAdmin = __esm({
|
|
|
33315
32939
|
}
|
|
33316
32940
|
try {
|
|
33317
32941
|
const envPath = join27(process.cwd(), ".env");
|
|
33318
|
-
const lines = existsSync22(envPath) ?
|
|
32942
|
+
const lines = existsSync22(envPath) ? readFileSync20(envPath, "utf-8").split("\n") : [];
|
|
33319
32943
|
const keysFound = { TINA4_DATABASE_URL: false, TINA4_DATABASE_USERNAME: false, TINA4_DATABASE_PASSWORD: false };
|
|
33320
32944
|
const newLines = [];
|
|
33321
32945
|
for (const line of lines) {
|
|
@@ -33361,12 +32985,12 @@ var init_devAdmin = __esm({
|
|
|
33361
32985
|
const metaFile = join27(entryPath, "meta.json");
|
|
33362
32986
|
if (statSync16(entryPath).isDirectory() && existsSync22(metaFile)) {
|
|
33363
32987
|
try {
|
|
33364
|
-
const meta = JSON.parse(
|
|
32988
|
+
const meta = JSON.parse(readFileSync20(metaFile, "utf-8"));
|
|
33365
32989
|
meta.id = entry;
|
|
33366
32990
|
const srcDir = join27(entryPath, "src");
|
|
33367
32991
|
if (existsSync22(srcDir)) {
|
|
33368
32992
|
const allFiles = walkDirRecursive(srcDir);
|
|
33369
|
-
meta.files = allFiles.map((f) =>
|
|
32993
|
+
meta.files = allFiles.map((f) => relative7(srcDir, f));
|
|
33370
32994
|
}
|
|
33371
32995
|
const projectSrc = resolve16(process.cwd(), "src");
|
|
33372
32996
|
if (existsSync22(srcDir) && meta.files) {
|
|
@@ -33484,7 +33108,7 @@ var init_devAdmin = __esm({
|
|
|
33484
33108
|
for (const name of readdirSync15(target).sort()) {
|
|
33485
33109
|
if (devFilesHidden(name)) continue;
|
|
33486
33110
|
const full = join27(target, name);
|
|
33487
|
-
const entryRel =
|
|
33111
|
+
const entryRel = relative7(root, full).replace(/\\/g, "/");
|
|
33488
33112
|
if (isSecretPath(entryRel)) continue;
|
|
33489
33113
|
let isDir = false;
|
|
33490
33114
|
let size = null;
|
|
@@ -33529,7 +33153,7 @@ var init_devAdmin = __esm({
|
|
|
33529
33153
|
size
|
|
33530
33154
|
});
|
|
33531
33155
|
}
|
|
33532
|
-
res.json({ path:
|
|
33156
|
+
res.json({ path: relative7(root, target).replace(/\\/g, "/") || ".", branch, entries });
|
|
33533
33157
|
};
|
|
33534
33158
|
DEV_ADMIN_LANG_MAP = {
|
|
33535
33159
|
".py": "python",
|
|
@@ -33581,8 +33205,8 @@ var init_devAdmin = __esm({
|
|
|
33581
33205
|
return;
|
|
33582
33206
|
}
|
|
33583
33207
|
try {
|
|
33584
|
-
const content =
|
|
33585
|
-
const path8 =
|
|
33208
|
+
const content = readFileSync20(target, "utf-8");
|
|
33209
|
+
const path8 = relative7(root, target);
|
|
33586
33210
|
res.json({ path: path8, content, language: devAdminLanguage(path8), bytes: Buffer.byteLength(content, "utf-8") });
|
|
33587
33211
|
} catch (e) {
|
|
33588
33212
|
res.json({ error: e.message }, 500);
|
|
@@ -33604,10 +33228,10 @@ var init_devAdmin = __esm({
|
|
|
33604
33228
|
writeFileSync15(target, content, "utf-8");
|
|
33605
33229
|
try {
|
|
33606
33230
|
const { Plan: Plan2 } = await Promise.resolve().then(() => (init_plan(), plan_exports));
|
|
33607
|
-
Plan2.recordAction(existed ? "patched" : "created",
|
|
33231
|
+
Plan2.recordAction(existed ? "patched" : "created", relative7(root, target));
|
|
33608
33232
|
} catch {
|
|
33609
33233
|
}
|
|
33610
|
-
res.json({ ok: true, path:
|
|
33234
|
+
res.json({ ok: true, path: relative7(root, target), bytes: Buffer.byteLength(content, "utf-8") });
|
|
33611
33235
|
} catch (e) {
|
|
33612
33236
|
res.json({ error: e.message }, 500);
|
|
33613
33237
|
}
|
|
@@ -33627,7 +33251,7 @@ var init_devAdmin = __esm({
|
|
|
33627
33251
|
return;
|
|
33628
33252
|
}
|
|
33629
33253
|
try {
|
|
33630
|
-
const buf =
|
|
33254
|
+
const buf = readFileSync20(target);
|
|
33631
33255
|
const ext = target.slice(target.lastIndexOf(".") + 1).toLowerCase();
|
|
33632
33256
|
const mime = {
|
|
33633
33257
|
js: "application/javascript",
|
|
@@ -33669,7 +33293,7 @@ var init_devAdmin = __esm({
|
|
|
33669
33293
|
const { renameSync: renameSync3 } = await import("node:fs");
|
|
33670
33294
|
mkdirSync18(dirname13(dst), { recursive: true });
|
|
33671
33295
|
renameSync3(src, dst);
|
|
33672
|
-
res.json({ ok: true, from:
|
|
33296
|
+
res.json({ ok: true, from: relative7(root, src), to: relative7(root, dst) });
|
|
33673
33297
|
} catch (e) {
|
|
33674
33298
|
res.json({ error: e.message }, 500);
|
|
33675
33299
|
}
|
|
@@ -33690,7 +33314,7 @@ var init_devAdmin = __esm({
|
|
|
33690
33314
|
try {
|
|
33691
33315
|
const { rmSync } = await import("node:fs");
|
|
33692
33316
|
rmSync(target, { recursive: true, force: true });
|
|
33693
|
-
res.json({ ok: true, deleted:
|
|
33317
|
+
res.json({ ok: true, deleted: relative7(root, target) });
|
|
33694
33318
|
} catch (e) {
|
|
33695
33319
|
res.json({ error: e.message }, 500);
|
|
33696
33320
|
}
|
|
@@ -34024,7 +33648,7 @@ var init_devAdmin = __esm({
|
|
|
34024
33648
|
});
|
|
34025
33649
|
};
|
|
34026
33650
|
handleDevAdminJs = async (_req, res) => {
|
|
34027
|
-
const { readFileSync:
|
|
33651
|
+
const { readFileSync: readFileSync29, existsSync: existsSync37 } = await import("node:fs");
|
|
34028
33652
|
const { dirname: dirname17, join: join40, resolve: resolve30 } = await import("node:path");
|
|
34029
33653
|
const { fileURLToPath: fileURLToPath10 } = await import("node:url");
|
|
34030
33654
|
const dir = dirname17(fileURLToPath10(import.meta.url));
|
|
@@ -34040,7 +33664,7 @@ var init_devAdmin = __esm({
|
|
|
34040
33664
|
for (const jsPath of candidates) {
|
|
34041
33665
|
if (existsSync37(jsPath)) {
|
|
34042
33666
|
try {
|
|
34043
|
-
const content =
|
|
33667
|
+
const content = readFileSync29(jsPath, "utf-8");
|
|
34044
33668
|
res.raw.writeHead(200, { "Content-Type": "application/javascript; charset=utf-8", "Cache-Control": "no-cache" });
|
|
34045
33669
|
res.raw.end(content);
|
|
34046
33670
|
return;
|
|
@@ -34055,7 +33679,7 @@ var init_devAdmin = __esm({
|
|
|
34055
33679
|
});
|
|
34056
33680
|
|
|
34057
33681
|
// ../core/src/i18n.ts
|
|
34058
|
-
import { readFileSync as
|
|
33682
|
+
import { readFileSync as readFileSync21, readdirSync as readdirSync16, existsSync as existsSync23 } from "node:fs";
|
|
34059
33683
|
import { join as join28, resolve as resolve17 } from "node:path";
|
|
34060
33684
|
var I18n;
|
|
34061
33685
|
var init_i18n = __esm({
|
|
@@ -34152,7 +33776,7 @@ var init_i18n = __esm({
|
|
|
34152
33776
|
const filePath = join28(this._localeDir, `${locale}.json`);
|
|
34153
33777
|
if (existsSync23(filePath)) {
|
|
34154
33778
|
try {
|
|
34155
|
-
const raw =
|
|
33779
|
+
const raw = readFileSync21(filePath, "utf-8");
|
|
34156
33780
|
const data = JSON.parse(raw);
|
|
34157
33781
|
this._translations.set(locale, _I18n._flatten(data));
|
|
34158
33782
|
return;
|
|
@@ -34165,7 +33789,7 @@ var init_i18n = __esm({
|
|
|
34165
33789
|
const yamlPath = join28(this._localeDir, `${locale}${ext}`);
|
|
34166
33790
|
if (existsSync23(yamlPath)) {
|
|
34167
33791
|
try {
|
|
34168
|
-
const raw =
|
|
33792
|
+
const raw = readFileSync21(yamlPath, "utf-8");
|
|
34169
33793
|
const data = _I18n._parseSimpleYaml(raw);
|
|
34170
33794
|
this._translations.set(locale, _I18n._flatten(data));
|
|
34171
33795
|
return;
|
|
@@ -35028,8 +34652,8 @@ var init_docsAutoDiscovery = __esm({
|
|
|
35028
34652
|
// ../core/src/server.ts
|
|
35029
34653
|
import { createServer as createServer2 } from "node:http";
|
|
35030
34654
|
import { randomBytes as randomBytes7 } from "node:crypto";
|
|
35031
|
-
import { resolve as resolve19, dirname as dirname14, join as join30, relative as
|
|
35032
|
-
import { existsSync as existsSync25, readdirSync as readdirSync17, readFileSync as
|
|
34655
|
+
import { resolve as resolve19, dirname as dirname14, join as join30, relative as relative8 } from "node:path";
|
|
34656
|
+
import { existsSync as existsSync25, readdirSync as readdirSync17, readFileSync as readFileSync23, statSync as statSync17 } from "node:fs";
|
|
35033
34657
|
import { isatty } from "node:tty";
|
|
35034
34658
|
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
35035
34659
|
import { execFileSync as execFileSync3, exec } from "node:child_process";
|
|
@@ -35253,7 +34877,7 @@ function getGalleryDeployedState() {
|
|
|
35253
34877
|
if (existsSync25(srcDir)) {
|
|
35254
34878
|
const files = walkGalleryFiles(srcDir);
|
|
35255
34879
|
const projectSrc = resolve19(process.cwd(), "src");
|
|
35256
|
-
state[entry] = files.every((f) => existsSync25(join30(projectSrc,
|
|
34880
|
+
state[entry] = files.every((f) => existsSync25(join30(projectSrc, relative8(srcDir, f))));
|
|
35257
34881
|
} else {
|
|
35258
34882
|
state[entry] = false;
|
|
35259
34883
|
}
|
|
@@ -35690,7 +35314,7 @@ function serveTemplateFallback(ctx) {
|
|
|
35690
35314
|
if ((ctx.req.method ?? "GET") !== "GET") return false;
|
|
35691
35315
|
const tplFile = resolveTemplate(ctx.pathname, ctx.templatesDir);
|
|
35692
35316
|
if (!tplFile) return false;
|
|
35693
|
-
const html = ctx.frondEngine ? ctx.frondEngine.render(tplFile, {}) :
|
|
35317
|
+
const html = ctx.frondEngine ? ctx.frondEngine.render(tplFile, {}) : readFileSync23(resolve19(ctx.templatesDir, tplFile), "utf-8");
|
|
35694
35318
|
ctx.res.raw.writeHead(200, void 0, { "Content-Type": "text/html; charset=utf-8" });
|
|
35695
35319
|
ctx.res.raw.end(html);
|
|
35696
35320
|
return true;
|
|
@@ -36517,7 +36141,7 @@ var init_mqttMessage = __esm({
|
|
|
36517
36141
|
import net2 from "node:net";
|
|
36518
36142
|
import tls from "node:tls";
|
|
36519
36143
|
import { randomBytes as randomBytes8 } from "node:crypto";
|
|
36520
|
-
import { existsSync as existsSync26, readFileSync as
|
|
36144
|
+
import { existsSync as existsSync26, readFileSync as readFileSync24 } from "node:fs";
|
|
36521
36145
|
var MqttError, MqttTimeoutError, CONNECT, CONNACK, PUBLISH, PUBACK, SUBSCRIBE, SUBACK, PINGREQ, PINGRESP, DISCONNECT, PROTOCOL_LEVEL, DEFAULT_PORT, DEFAULT_TLS_PORT, DEFAULT_URL, DEFAULT_KEEPALIVE, SUBSCRIPTION_REFUSED, MAX_REMAINING_LENGTH, QOS2_REFUSED_MESSAGE, CONNACK_RETURN_CODES, Mqtt;
|
|
36522
36146
|
var init_mqtt = __esm({
|
|
36523
36147
|
"../core/src/mqtt.ts"() {
|
|
@@ -36982,7 +36606,7 @@ var init_mqtt = __esm({
|
|
|
36982
36606
|
servername: this.host,
|
|
36983
36607
|
rejectUnauthorized: this.tlsVerify
|
|
36984
36608
|
};
|
|
36985
|
-
if (this.tlsVerify && this.caFile) opts.ca =
|
|
36609
|
+
if (this.tlsVerify && this.caFile) opts.ca = readFileSync24(this.caFile);
|
|
36986
36610
|
sock = tls.connect(opts, () => settle(() => resolve30(sock)));
|
|
36987
36611
|
} else {
|
|
36988
36612
|
sock = net2.createConnection({ host: this.host, port: this.port }, () => settle(() => resolve30(sock)));
|
|
@@ -37203,7 +36827,7 @@ var init_mqtt = __esm({
|
|
|
37203
36827
|
|
|
37204
36828
|
// ../core/src/service.ts
|
|
37205
36829
|
import { readdirSync as readdirSync18, statSync as statSync18, watchFile, unwatchFile } from "node:fs";
|
|
37206
|
-
import { join as join31, extname as
|
|
36830
|
+
import { join as join31, extname as extname7 } from "node:path";
|
|
37207
36831
|
import { pathToFileURL } from "node:url";
|
|
37208
36832
|
function matchCronField(field, value) {
|
|
37209
36833
|
if (field === "*") return true;
|
|
@@ -37374,7 +36998,7 @@ var init_service = __esm({
|
|
|
37374
36998
|
return discovered;
|
|
37375
36999
|
}
|
|
37376
37000
|
for (const entry of entries) {
|
|
37377
|
-
const ext =
|
|
37001
|
+
const ext = extname7(entry);
|
|
37378
37002
|
if (ext !== ".ts" && ext !== ".js") continue;
|
|
37379
37003
|
const fullPath = join31(dir, entry);
|
|
37380
37004
|
const stat = statSync18(fullPath);
|
|
@@ -37490,7 +37114,7 @@ var init_service = __esm({
|
|
|
37490
37114
|
return;
|
|
37491
37115
|
}
|
|
37492
37116
|
for (const entry of entries) {
|
|
37493
|
-
const ext =
|
|
37117
|
+
const ext = extname7(entry);
|
|
37494
37118
|
if (ext !== ".ts" && ext !== ".js") continue;
|
|
37495
37119
|
const fullPath = join31(dir, entry);
|
|
37496
37120
|
if (watchedFiles.has(fullPath)) continue;
|
|
@@ -38193,7 +37817,7 @@ var init_api = __esm({
|
|
|
38193
37817
|
// ../core/src/messenger.ts
|
|
38194
37818
|
import net3 from "node:net";
|
|
38195
37819
|
import tls2 from "node:tls";
|
|
38196
|
-
import { readFileSync as
|
|
37820
|
+
import { readFileSync as readFileSync25 } from "node:fs";
|
|
38197
37821
|
import { basename as basename7 } from "node:path";
|
|
38198
37822
|
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
38199
37823
|
function tlsRejectUnauthorized() {
|
|
@@ -38290,7 +37914,7 @@ function buildMimeMessage(options) {
|
|
|
38290
37914
|
}
|
|
38291
37915
|
for (const filePath of options.attachments) {
|
|
38292
37916
|
const fileName = basename7(filePath);
|
|
38293
|
-
const fileData =
|
|
37917
|
+
const fileData = readFileSync25(filePath);
|
|
38294
37918
|
const base64Data = fileData.toString("base64");
|
|
38295
37919
|
lines.push("");
|
|
38296
37920
|
lines.push(`--${boundary}`);
|
|
@@ -39771,9 +39395,9 @@ __export(ai_exports, {
|
|
|
39771
39395
|
skillBlock: () => skillBlock,
|
|
39772
39396
|
writeOrMerge: () => writeOrMerge
|
|
39773
39397
|
});
|
|
39774
|
-
import { existsSync as existsSync27, mkdirSync as mkdirSync21, writeFileSync as writeFileSync18, readFileSync as
|
|
39398
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync21, writeFileSync as writeFileSync18, readFileSync as readFileSync26 } from "node:fs";
|
|
39775
39399
|
import { homedir } from "node:os";
|
|
39776
|
-
import { join as join32, resolve as resolve20, relative as
|
|
39400
|
+
import { join as join32, resolve as resolve20, relative as relative9, dirname as dirname15 } from "node:path";
|
|
39777
39401
|
import { fileURLToPath as fileURLToPath8 } from "node:url";
|
|
39778
39402
|
import { execSync as execSync2, execFileSync as execFileSync4 } from "node:child_process";
|
|
39779
39403
|
import { createInterface } from "node:readline";
|
|
@@ -39781,7 +39405,7 @@ function readVersion() {
|
|
|
39781
39405
|
try {
|
|
39782
39406
|
const thisDir = dirname15(fileURLToPath8(import.meta.url));
|
|
39783
39407
|
const rootPkg = resolve20(thisDir, "..", "..", "..", "package.json");
|
|
39784
|
-
const pkg = JSON.parse(
|
|
39408
|
+
const pkg = JSON.parse(readFileSync26(rootPkg, "utf-8"));
|
|
39785
39409
|
return pkg.version ?? "0.0.0";
|
|
39786
39410
|
} catch {
|
|
39787
39411
|
return "0.0.0";
|
|
@@ -40004,7 +39628,7 @@ function writeOrMerge(contextPath, contextFile, frameworkGuide) {
|
|
|
40004
39628
|
writeFileSync18(contextPath, frameworkGuide.replace(/\s+$/, "") + "\n\n" + block + "\n", "utf-8");
|
|
40005
39629
|
return "Installed";
|
|
40006
39630
|
}
|
|
40007
|
-
const existing =
|
|
39631
|
+
const existing = readFileSync26(contextPath, "utf-8");
|
|
40008
39632
|
if (hasMarkers(existing, start2, end)) {
|
|
40009
39633
|
writeFileSync18(contextPath, replaceMarkerBlock(existing, block, start2, end), "utf-8");
|
|
40010
39634
|
return "Refreshed skill block in";
|
|
@@ -40028,7 +39652,7 @@ function installForTool(root, tool, context) {
|
|
|
40028
39652
|
const parentDir = dirname15(contextPath);
|
|
40029
39653
|
mkdirSync21(parentDir, { recursive: true });
|
|
40030
39654
|
const action = writeOrMerge(contextPath, tool.contextFile, context);
|
|
40031
|
-
const rel =
|
|
39655
|
+
const rel = relative9(root, contextPath);
|
|
40032
39656
|
created.push(rel);
|
|
40033
39657
|
console.log(` ${GREEN2}\u2713${RESET2} ${action} ${rel}`);
|
|
40034
39658
|
if (tool.name === "claude-code") {
|
|
@@ -40406,7 +40030,7 @@ function generateClaudeCodeContext() {
|
|
|
40406
40030
|
const repoRoot = resolve20(thisDir, "..", "..", "..");
|
|
40407
40031
|
const claudeMdPath = join32(repoRoot, "CLAUDE.md");
|
|
40408
40032
|
if (existsSync27(claudeMdPath)) {
|
|
40409
|
-
return
|
|
40033
|
+
return readFileSync26(claudeMdPath, "utf-8");
|
|
40410
40034
|
}
|
|
40411
40035
|
} catch {
|
|
40412
40036
|
}
|
|
@@ -40600,6 +40224,292 @@ export default class User {
|
|
|
40600
40224
|
}
|
|
40601
40225
|
});
|
|
40602
40226
|
|
|
40227
|
+
// ../core/src/aiClient.ts
|
|
40228
|
+
import http2 from "node:http";
|
|
40229
|
+
import https2 from "node:https";
|
|
40230
|
+
var AiError, AiConfigError, AiTimeoutError, AiParseError, AiHTTPError, Ai;
|
|
40231
|
+
var init_aiClient = __esm({
|
|
40232
|
+
"../core/src/aiClient.ts"() {
|
|
40233
|
+
"use strict";
|
|
40234
|
+
AiError = class extends Error {
|
|
40235
|
+
};
|
|
40236
|
+
AiConfigError = class extends AiError {
|
|
40237
|
+
};
|
|
40238
|
+
AiTimeoutError = class extends AiError {
|
|
40239
|
+
};
|
|
40240
|
+
AiParseError = class extends AiError {
|
|
40241
|
+
};
|
|
40242
|
+
AiHTTPError = class extends AiError {
|
|
40243
|
+
constructor(message, status2 = null) {
|
|
40244
|
+
super(message);
|
|
40245
|
+
this.status = status2;
|
|
40246
|
+
}
|
|
40247
|
+
};
|
|
40248
|
+
Ai = class {
|
|
40249
|
+
static chat(messages, options = {}) {
|
|
40250
|
+
this.validateMessages(messages);
|
|
40251
|
+
const config = this.config("chat", options);
|
|
40252
|
+
const body = this.chatBody(config, messages, options);
|
|
40253
|
+
const headers = this.headers(config);
|
|
40254
|
+
return options.stream ? this.streamRequest(config, headers, body) : this.chatResponse(config, headers, body);
|
|
40255
|
+
}
|
|
40256
|
+
static async complete(prompt, options = {}) {
|
|
40257
|
+
if (typeof prompt !== "string") throw new AiConfigError("AI prompt must be a string");
|
|
40258
|
+
return (await this.chat([{ role: "user", content: prompt }], { ...options, stream: false })).text;
|
|
40259
|
+
}
|
|
40260
|
+
static async embed(textOrTexts, options = {}) {
|
|
40261
|
+
const single = typeof textOrTexts === "string";
|
|
40262
|
+
if (!single && (!Array.isArray(textOrTexts) || textOrTexts.length === 0 || !textOrTexts.every((item) => typeof item === "string"))) {
|
|
40263
|
+
throw new AiConfigError("AI embedding input must be a string or a non-empty list of strings");
|
|
40264
|
+
}
|
|
40265
|
+
const config = this.config("embed", options);
|
|
40266
|
+
if (config.provider === "anthropic") throw new AiConfigError("Anthropic does not provide the embedding endpoint in this contract");
|
|
40267
|
+
const raw = await this.requestJson(config, this.headers(config), { model: config.model, input: textOrTexts });
|
|
40268
|
+
try {
|
|
40269
|
+
const data = raw.data.sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
|
|
40270
|
+
const vectors = data.map((item) => item.embedding);
|
|
40271
|
+
const expected = single ? 1 : textOrTexts.length;
|
|
40272
|
+
if (vectors.length !== expected || !vectors.every((vector) => Array.isArray(vector) && vector.length > 0 && vector.every((value) => typeof value === "number" && Number.isFinite(value)))) throw new Error();
|
|
40273
|
+
return single ? vectors[0] : vectors;
|
|
40274
|
+
} catch {
|
|
40275
|
+
throw new AiParseError("AI provider returned a malformed embedding response");
|
|
40276
|
+
}
|
|
40277
|
+
}
|
|
40278
|
+
static validateMessages(messages) {
|
|
40279
|
+
if (!Array.isArray(messages) || messages.length === 0 || !messages.every((message) => message && ["system", "user", "assistant"].includes(message.role) && typeof message.content === "string")) {
|
|
40280
|
+
throw new AiConfigError("AI messages must contain supported roles and string content");
|
|
40281
|
+
}
|
|
40282
|
+
}
|
|
40283
|
+
static number(name, fallback, minimum) {
|
|
40284
|
+
const value = process.env[name] === void 0 ? fallback : Number(process.env[name]);
|
|
40285
|
+
if (!Number.isFinite(value) || value < minimum) throw new AiConfigError(`${name} must be numeric and at least ${minimum}`);
|
|
40286
|
+
return value;
|
|
40287
|
+
}
|
|
40288
|
+
static config(capability, options) {
|
|
40289
|
+
const provider = (options.provider ?? process.env.TINA4_AI_PROVIDER ?? "local").trim().toLowerCase();
|
|
40290
|
+
if (provider !== "local" && provider !== "openai" && provider !== "anthropic") throw new AiConfigError("TINA4_AI_PROVIDER must be local, openai, or anthropic");
|
|
40291
|
+
const key = process.env.TINA4_AI_KEY || null;
|
|
40292
|
+
if ((provider === "openai" || provider === "anthropic") && !key) throw new AiConfigError(`TINA4_AI_KEY is required for the ${provider} provider`);
|
|
40293
|
+
const defaults = {
|
|
40294
|
+
local: ["http://localhost:11437", "llama3.2"],
|
|
40295
|
+
openai: ["https://api.openai.com/v1", "gpt-4o-mini"],
|
|
40296
|
+
anthropic: ["https://api.anthropic.com/v1", "claude-3-5-haiku-latest"]
|
|
40297
|
+
};
|
|
40298
|
+
const rawUrl = capability === "embed" && process.env.TINA4_EMBED_URL ? process.env.TINA4_EMBED_URL : process.env.TINA4_AI_URL ?? defaults[provider][0];
|
|
40299
|
+
const model = (options.model ?? process.env.TINA4_AI_MODEL ?? defaults[provider][1]).trim();
|
|
40300
|
+
if (!model) throw new AiConfigError("AI model must be a non-empty string");
|
|
40301
|
+
const totalTimeout = options.timeout === void 0 ? this.number("TINA4_AI_TIMEOUT", 60, 1e-3) : Number(options.timeout);
|
|
40302
|
+
if (!Number.isFinite(totalTimeout) || totalTimeout <= 0) throw new AiConfigError("AI timeout must be greater than zero");
|
|
40303
|
+
return { provider, url: this.endpoint(rawUrl, capability, provider), model, key, totalTimeout, connectTimeout: this.number("TINA4_AI_CONNECT_TIMEOUT", 10, 1e-3), maxRetries: Math.trunc(this.number("TINA4_AI_MAX_RETRIES", 2, 0)) };
|
|
40304
|
+
}
|
|
40305
|
+
static endpoint(value, capability, provider) {
|
|
40306
|
+
let url;
|
|
40307
|
+
try {
|
|
40308
|
+
url = new URL(value);
|
|
40309
|
+
} catch {
|
|
40310
|
+
throw new AiConfigError("AI URL must be an http or https URL");
|
|
40311
|
+
}
|
|
40312
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") throw new AiConfigError("AI URL must be an http or https URL");
|
|
40313
|
+
const path8 = url.pathname.replace(/\/+$/, "");
|
|
40314
|
+
if (path8 === "" || path8 === "/v1" || path8 === "/api") {
|
|
40315
|
+
const suffix = provider === "anthropic" ? "/messages" : capability === "embed" ? "/embeddings" : "/chat/completions";
|
|
40316
|
+
url.pathname = (path8 || "/v1") + suffix;
|
|
40317
|
+
}
|
|
40318
|
+
return url.toString();
|
|
40319
|
+
}
|
|
40320
|
+
static headers(config) {
|
|
40321
|
+
const headers = { "content-type": "application/json", accept: "application/json" };
|
|
40322
|
+
if (config.provider === "openai") headers.authorization = `Bearer ${config.key}`;
|
|
40323
|
+
if (config.provider === "anthropic") {
|
|
40324
|
+
headers["x-api-key"] = config.key;
|
|
40325
|
+
headers["anthropic-version"] = "2023-06-01";
|
|
40326
|
+
}
|
|
40327
|
+
return headers;
|
|
40328
|
+
}
|
|
40329
|
+
static chatBody(config, messages, options) {
|
|
40330
|
+
const body = { model: config.model, messages, stream: options.stream ?? false };
|
|
40331
|
+
if (options.temperature !== void 0) body.temperature = options.temperature;
|
|
40332
|
+
if (options.maxTokens !== void 0) body.max_tokens = options.maxTokens;
|
|
40333
|
+
if (config.provider === "anthropic") {
|
|
40334
|
+
const system = messages.filter((message) => message.role === "system").map((message) => message.content);
|
|
40335
|
+
body.messages = messages.filter((message) => message.role !== "system");
|
|
40336
|
+
body.max_tokens = options.maxTokens ?? 1024;
|
|
40337
|
+
if (system.length) body.system = system.join("\n\n");
|
|
40338
|
+
}
|
|
40339
|
+
return body;
|
|
40340
|
+
}
|
|
40341
|
+
static open(config, deadline, headers, body) {
|
|
40342
|
+
const remainingMs = deadline - performance.now();
|
|
40343
|
+
if (remainingMs <= 0) return Promise.reject(new AiTimeoutError("AI total request timeout expired"));
|
|
40344
|
+
const url = new URL(config.url);
|
|
40345
|
+
const payload = JSON.stringify(body);
|
|
40346
|
+
const controller = new AbortController();
|
|
40347
|
+
const totalTimer = setTimeout(() => controller.abort(new AiTimeoutError("AI total request timeout expired")), remainingMs);
|
|
40348
|
+
return new Promise((resolve30, reject) => {
|
|
40349
|
+
const client = url.protocol === "https:" ? https2 : http2;
|
|
40350
|
+
const request = client.request(url, { method: "POST", headers: { ...headers, "content-length": Buffer.byteLength(payload) }, signal: controller.signal }, (response) => {
|
|
40351
|
+
clearTimeout(connectTimer);
|
|
40352
|
+
resolve30({ response, cleanup: () => {
|
|
40353
|
+
clearTimeout(totalTimer);
|
|
40354
|
+
clearTimeout(connectTimer);
|
|
40355
|
+
} });
|
|
40356
|
+
});
|
|
40357
|
+
const connectTimer = setTimeout(() => request.destroy(new AiTimeoutError("AI connection timeout expired")), Math.min(config.connectTimeout * 1e3, remainingMs));
|
|
40358
|
+
request.on("socket", (socket) => {
|
|
40359
|
+
if (!socket.connecting) clearTimeout(connectTimer);
|
|
40360
|
+
socket.once(url.protocol === "https:" ? "secureConnect" : "connect", () => clearTimeout(connectTimer));
|
|
40361
|
+
});
|
|
40362
|
+
request.once("error", (error) => {
|
|
40363
|
+
clearTimeout(totalTimer);
|
|
40364
|
+
clearTimeout(connectTimer);
|
|
40365
|
+
if (error instanceof AiError) reject(error);
|
|
40366
|
+
else if (controller.signal.aborted) reject(new AiTimeoutError("AI total request timeout expired"));
|
|
40367
|
+
else reject(new AiHTTPError(`AI transport failed (${error.name})`));
|
|
40368
|
+
});
|
|
40369
|
+
request.end(payload);
|
|
40370
|
+
});
|
|
40371
|
+
}
|
|
40372
|
+
static async readBody(response) {
|
|
40373
|
+
const chunks = [];
|
|
40374
|
+
for await (const chunk of response) chunks.push(Buffer.from(chunk));
|
|
40375
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
40376
|
+
}
|
|
40377
|
+
static retryDelay(headers, deadline) {
|
|
40378
|
+
const value = Array.isArray(headers["retry-after"]) ? headers["retry-after"][0] : headers["retry-after"];
|
|
40379
|
+
const requested = value !== void 0 && Number.isFinite(Number(value)) ? Math.max(0, Number(value) * 1e3) : 100;
|
|
40380
|
+
const delay = Math.min(requested, Math.max(0, deadline - performance.now()));
|
|
40381
|
+
return new Promise((resolve30) => setTimeout(resolve30, delay));
|
|
40382
|
+
}
|
|
40383
|
+
static async requestJson(config, headers, body) {
|
|
40384
|
+
const deadline = performance.now() + config.totalTimeout * 1e3;
|
|
40385
|
+
for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
|
|
40386
|
+
let opened = null;
|
|
40387
|
+
try {
|
|
40388
|
+
opened = await this.open(config, deadline, headers, body);
|
|
40389
|
+
const status2 = opened.response.statusCode ?? 0;
|
|
40390
|
+
const responseHeaders = opened.response.headers;
|
|
40391
|
+
const raw = await this.readBody(opened.response);
|
|
40392
|
+
opened.cleanup();
|
|
40393
|
+
opened = null;
|
|
40394
|
+
if (status2 < 200 || status2 >= 300) {
|
|
40395
|
+
if ((status2 === 429 || status2 >= 500) && attempt < config.maxRetries) {
|
|
40396
|
+
await this.retryDelay(responseHeaders, deadline);
|
|
40397
|
+
continue;
|
|
40398
|
+
}
|
|
40399
|
+
throw new AiHTTPError(`AI provider returned HTTP ${status2}`, status2);
|
|
40400
|
+
}
|
|
40401
|
+
let parsed;
|
|
40402
|
+
try {
|
|
40403
|
+
parsed = JSON.parse(raw);
|
|
40404
|
+
} catch {
|
|
40405
|
+
throw new AiParseError("AI provider returned malformed JSON");
|
|
40406
|
+
}
|
|
40407
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new AiParseError("AI provider returned a non-object JSON response");
|
|
40408
|
+
return parsed;
|
|
40409
|
+
} catch (error) {
|
|
40410
|
+
opened?.cleanup();
|
|
40411
|
+
if (error instanceof AiParseError || error instanceof AiHTTPError && error.status !== null) throw error;
|
|
40412
|
+
if (attempt >= config.maxRetries) throw error;
|
|
40413
|
+
}
|
|
40414
|
+
}
|
|
40415
|
+
throw new AiHTTPError("AI request failed");
|
|
40416
|
+
}
|
|
40417
|
+
static normalizeChat(provider, raw) {
|
|
40418
|
+
try {
|
|
40419
|
+
if (provider === "anthropic") {
|
|
40420
|
+
const content = raw.content;
|
|
40421
|
+
const parts = content.filter((item) => (item.type ?? "text") === "text").map((item) => item.text);
|
|
40422
|
+
if (!parts.length || !parts.every((part) => typeof part === "string")) throw new Error();
|
|
40423
|
+
const usage2 = raw.usage ?? {};
|
|
40424
|
+
const promptTokens = Number(usage2.input_tokens ?? 0);
|
|
40425
|
+
const completionTokens = Number(usage2.output_tokens ?? 0);
|
|
40426
|
+
return { text: parts.join(""), model: String(raw.model ?? ""), usage: { promptTokens, completionTokens, totalTokens: promptTokens + completionTokens }, finishReason: raw.stop_reason == null ? null : String(raw.stop_reason), raw };
|
|
40427
|
+
}
|
|
40428
|
+
const choice = raw.choices[0];
|
|
40429
|
+
const text = choice.message.content;
|
|
40430
|
+
if (typeof text !== "string") throw new Error();
|
|
40431
|
+
const usage = raw.usage ?? {};
|
|
40432
|
+
return { text, model: String(raw.model ?? ""), usage: { promptTokens: Number(usage.prompt_tokens ?? 0), completionTokens: Number(usage.completion_tokens ?? 0), totalTokens: Number(usage.total_tokens ?? 0) }, finishReason: choice.finish_reason == null ? null : String(choice.finish_reason), raw };
|
|
40433
|
+
} catch {
|
|
40434
|
+
throw new AiParseError("AI provider returned a malformed chat response");
|
|
40435
|
+
}
|
|
40436
|
+
}
|
|
40437
|
+
static async chatResponse(config, headers, body) {
|
|
40438
|
+
return this.normalizeChat(config.provider, await this.requestJson(config, headers, body));
|
|
40439
|
+
}
|
|
40440
|
+
static streamDelta(provider, data) {
|
|
40441
|
+
if (data === "[DONE]") return { completed: true };
|
|
40442
|
+
let event;
|
|
40443
|
+
try {
|
|
40444
|
+
event = JSON.parse(data);
|
|
40445
|
+
} catch {
|
|
40446
|
+
throw new AiParseError("AI provider returned malformed stream data");
|
|
40447
|
+
}
|
|
40448
|
+
const text = provider === "anthropic" ? event.type === "content_block_delta" ? event.delta?.text : void 0 : event.choices?.[0]?.delta?.content;
|
|
40449
|
+
if (text !== void 0 && text !== null && typeof text !== "string") throw new AiParseError("AI provider returned malformed stream data");
|
|
40450
|
+
return { completed: false, text };
|
|
40451
|
+
}
|
|
40452
|
+
static async *streamData(response) {
|
|
40453
|
+
let buffer = "";
|
|
40454
|
+
for await (const chunk of response) {
|
|
40455
|
+
buffer += Buffer.from(chunk).toString("utf8");
|
|
40456
|
+
let newline;
|
|
40457
|
+
while ((newline = buffer.indexOf("\n")) >= 0) {
|
|
40458
|
+
const line = buffer.slice(0, newline).trim();
|
|
40459
|
+
buffer = buffer.slice(newline + 1);
|
|
40460
|
+
if (line.startsWith("data:")) yield line.slice(5).trim();
|
|
40461
|
+
}
|
|
40462
|
+
}
|
|
40463
|
+
}
|
|
40464
|
+
static streamError(error) {
|
|
40465
|
+
if (error instanceof AiError) return error;
|
|
40466
|
+
if (error instanceof Error && error.name === "AbortError") return new AiTimeoutError("AI total request timeout expired");
|
|
40467
|
+
return new AiHTTPError(`AI transport failed (${error instanceof Error ? error.name : "Error"})`);
|
|
40468
|
+
}
|
|
40469
|
+
static async *streamRequest(config, headers, body) {
|
|
40470
|
+
const deadline = performance.now() + config.totalTimeout * 1e3;
|
|
40471
|
+
let yielded = false;
|
|
40472
|
+
for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
|
|
40473
|
+
let opened = null;
|
|
40474
|
+
try {
|
|
40475
|
+
opened = await this.open(config, deadline, { ...headers, accept: "text/event-stream" }, body);
|
|
40476
|
+
const status2 = opened.response.statusCode ?? 0;
|
|
40477
|
+
if (status2 < 200 || status2 >= 300) {
|
|
40478
|
+
await this.readBody(opened.response);
|
|
40479
|
+
if ((status2 === 429 || status2 >= 500) && attempt < config.maxRetries) {
|
|
40480
|
+
await this.retryDelay(opened.response.headers, deadline);
|
|
40481
|
+
opened.cleanup();
|
|
40482
|
+
opened = null;
|
|
40483
|
+
continue;
|
|
40484
|
+
}
|
|
40485
|
+
throw new AiHTTPError(`AI provider returned HTTP ${status2}`, status2);
|
|
40486
|
+
}
|
|
40487
|
+
let completed = false;
|
|
40488
|
+
for await (const data of this.streamData(opened.response)) {
|
|
40489
|
+
const delta = this.streamDelta(config.provider, data);
|
|
40490
|
+
if (delta.completed) {
|
|
40491
|
+
completed = true;
|
|
40492
|
+
break;
|
|
40493
|
+
}
|
|
40494
|
+
if (delta.text === void 0) continue;
|
|
40495
|
+
yielded = true;
|
|
40496
|
+
yield delta.text;
|
|
40497
|
+
}
|
|
40498
|
+
opened.cleanup();
|
|
40499
|
+
opened = null;
|
|
40500
|
+
if (completed) return;
|
|
40501
|
+
throw new AiParseError("AI provider stream ended before [DONE]");
|
|
40502
|
+
} catch (error) {
|
|
40503
|
+
opened?.cleanup();
|
|
40504
|
+
const failure = this.streamError(error);
|
|
40505
|
+
if (failure instanceof AiParseError || failure instanceof AiHTTPError && failure.status !== null || yielded || attempt >= config.maxRetries) throw failure;
|
|
40506
|
+
}
|
|
40507
|
+
}
|
|
40508
|
+
}
|
|
40509
|
+
};
|
|
40510
|
+
}
|
|
40511
|
+
});
|
|
40512
|
+
|
|
40603
40513
|
// ../core/src/queueBackends/rabbitmqBackend.ts
|
|
40604
40514
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
40605
40515
|
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
@@ -42303,6 +42213,12 @@ __export(src_exports3, {
|
|
|
42303
42213
|
APPLICATION_JSON: () => APPLICATION_JSON,
|
|
42304
42214
|
APPLICATION_OCTET: () => APPLICATION_OCTET,
|
|
42305
42215
|
APPLICATION_XML: () => APPLICATION_XML,
|
|
42216
|
+
Ai: () => Ai,
|
|
42217
|
+
AiConfigError: () => AiConfigError,
|
|
42218
|
+
AiError: () => AiError,
|
|
42219
|
+
AiHTTPError: () => AiHTTPError,
|
|
42220
|
+
AiParseError: () => AiParseError,
|
|
42221
|
+
AiTimeoutError: () => AiTimeoutError,
|
|
42306
42222
|
Api: () => Api,
|
|
42307
42223
|
Auth: () => Auth,
|
|
42308
42224
|
CANONICAL_SESSION_BACKENDS: () => CANONICAL_SESSION_BACKENDS,
|
|
@@ -42630,6 +42546,7 @@ var init_src3 = __esm({
|
|
|
42630
42546
|
init_htmlElement();
|
|
42631
42547
|
init_errorOverlay();
|
|
42632
42548
|
init_ai();
|
|
42549
|
+
init_aiClient();
|
|
42633
42550
|
init_liteBackend();
|
|
42634
42551
|
init_rabbitmqBackend();
|
|
42635
42552
|
init_kafkaBackend();
|
|
@@ -43143,7 +43060,7 @@ async function listRoutes() {
|
|
|
43143
43060
|
}
|
|
43144
43061
|
|
|
43145
43062
|
// src/commands/test.ts
|
|
43146
|
-
import { existsSync as existsSync32, readdirSync as readdirSync20, readFileSync as
|
|
43063
|
+
import { existsSync as existsSync32, readdirSync as readdirSync20, readFileSync as readFileSync27, statSync as statSync19 } from "node:fs";
|
|
43147
43064
|
import { resolve as resolve27, join as join34 } from "node:path";
|
|
43148
43065
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
43149
43066
|
import { execSync as execSync3 } from "node:child_process";
|
|
@@ -43180,7 +43097,7 @@ async function runInlineTests(cwd) {
|
|
|
43180
43097
|
for (const file of walkSource(srcDir)) {
|
|
43181
43098
|
let text;
|
|
43182
43099
|
try {
|
|
43183
|
-
text =
|
|
43100
|
+
text = readFileSync27(file, "utf-8");
|
|
43184
43101
|
} catch {
|
|
43185
43102
|
continue;
|
|
43186
43103
|
}
|
|
@@ -43244,8 +43161,8 @@ async function runTests(testPath) {
|
|
|
43244
43161
|
console.log(` Found ${testFiles.length} test file(s)
|
|
43245
43162
|
`);
|
|
43246
43163
|
for (const file of testFiles) {
|
|
43247
|
-
const
|
|
43248
|
-
console.log(` Running: ${
|
|
43164
|
+
const relative10 = file.replace(cwd + "/", "");
|
|
43165
|
+
console.log(` Running: ${relative10}`);
|
|
43249
43166
|
try {
|
|
43250
43167
|
execSync3(`npx tsx "${file}"`, { cwd, stdio: "inherit" });
|
|
43251
43168
|
} catch {
|
|
@@ -44894,8 +44811,8 @@ async function runSeeds(seedPath) {
|
|
|
44894
44811
|
`);
|
|
44895
44812
|
let failed = false;
|
|
44896
44813
|
for (const file of seedFiles) {
|
|
44897
|
-
const
|
|
44898
|
-
console.log(` Seeding: ${
|
|
44814
|
+
const relative10 = file.replace(cwd + "/", "");
|
|
44815
|
+
console.log(` Seeding: ${relative10}`);
|
|
44899
44816
|
try {
|
|
44900
44817
|
execSync4(`npx tsx "${file}"`, { cwd, stdio: "inherit" });
|
|
44901
44818
|
} catch {
|
|
@@ -44909,121 +44826,14 @@ async function runSeeds(seedPath) {
|
|
|
44909
44826
|
console.log("\n All seeds completed.");
|
|
44910
44827
|
}
|
|
44911
44828
|
|
|
44912
|
-
// src/commands/metrics.ts
|
|
44913
|
-
init_metrics();
|
|
44914
|
-
function parseFlags(args) {
|
|
44915
|
-
const flags = { top: 20, json: false, path: "src", failOn: null };
|
|
44916
|
-
for (let i = 0; i < args.length; i++) {
|
|
44917
|
-
const a = args[i];
|
|
44918
|
-
switch (a) {
|
|
44919
|
-
case "--json":
|
|
44920
|
-
flags.json = true;
|
|
44921
|
-
break;
|
|
44922
|
-
case "--top": {
|
|
44923
|
-
const v = args[++i];
|
|
44924
|
-
if (v === void 0 || !/^\d+$/.test(v)) {
|
|
44925
|
-
return { error: `--top expects a number (got '${v ?? ""}')` };
|
|
44926
|
-
}
|
|
44927
|
-
flags.top = parseInt(v, 10);
|
|
44928
|
-
break;
|
|
44929
|
-
}
|
|
44930
|
-
case "--path": {
|
|
44931
|
-
const v = args[++i];
|
|
44932
|
-
if (v === void 0) return { error: "--path expects a directory" };
|
|
44933
|
-
flags.path = v;
|
|
44934
|
-
break;
|
|
44935
|
-
}
|
|
44936
|
-
case "--fail-on": {
|
|
44937
|
-
const v = args[++i];
|
|
44938
|
-
if (v !== "warn" && v !== "error") {
|
|
44939
|
-
return { error: `invalid --fail-on '${v ?? ""}' (use warn or error)` };
|
|
44940
|
-
}
|
|
44941
|
-
flags.failOn = v;
|
|
44942
|
-
break;
|
|
44943
|
-
}
|
|
44944
|
-
default:
|
|
44945
|
-
return { error: `unknown option '${a}'` };
|
|
44946
|
-
}
|
|
44947
|
-
}
|
|
44948
|
-
return flags;
|
|
44949
|
-
}
|
|
44950
|
-
function runMetrics(args = []) {
|
|
44951
|
-
const parsed = parseFlags(args);
|
|
44952
|
-
if ("error" in parsed) {
|
|
44953
|
-
console.log(` ${parsed.error}`);
|
|
44954
|
-
return 2;
|
|
44955
|
-
}
|
|
44956
|
-
const { top, json, path: path8, failOn } = parsed;
|
|
44957
|
-
let result;
|
|
44958
|
-
try {
|
|
44959
|
-
result = offenders(path8, Number.MAX_SAFE_INTEGER);
|
|
44960
|
-
} catch (e) {
|
|
44961
|
-
if (e instanceof MetricsEngineError) {
|
|
44962
|
-
console.error(` metrics error: ${e.message}`);
|
|
44963
|
-
return 2;
|
|
44964
|
-
}
|
|
44965
|
-
throw e;
|
|
44966
|
-
}
|
|
44967
|
-
const summary = result.summary;
|
|
44968
|
-
const allOffenders = result.offenders;
|
|
44969
|
-
const found = allOffenders.slice(0, top);
|
|
44970
|
-
const severities = new Set(allOffenders.map((o) => o.severity));
|
|
44971
|
-
let exitCode = 0;
|
|
44972
|
-
if (failOn === "warn" && (severities.has("warn") || severities.has("error"))) {
|
|
44973
|
-
exitCode = 1;
|
|
44974
|
-
} else if (failOn === "error" && severities.has("error")) {
|
|
44975
|
-
exitCode = 1;
|
|
44976
|
-
}
|
|
44977
|
-
if (json) {
|
|
44978
|
-
console.log(JSON.stringify({ summary, offenders: found }, null, 2));
|
|
44979
|
-
return exitCode;
|
|
44980
|
-
}
|
|
44981
|
-
const useColor = Boolean(process.stdout.isTTY);
|
|
44982
|
-
const c = (text, code) => useColor ? `\x1B[${code}m${text}\x1B[0m` : text;
|
|
44983
|
-
const sevColor = { error: "31", warn: "33", info: "2" };
|
|
44984
|
-
console.log("");
|
|
44985
|
-
console.log(` Tina4 Metrics \u2014 ${summary.scan_mode} scan (${summary.scan_root})`);
|
|
44986
|
-
console.log(
|
|
44987
|
-
` files: ${summary.files_analyzed} functions: ${summary.total_functions} avg complexity: ${summary.avg_complexity} avg maintainability: ${summary.avg_maintainability}`
|
|
44988
|
-
);
|
|
44989
|
-
console.log(
|
|
44990
|
-
` offenders: ${summary.total_offenders} total` + (found.length ? ` (showing top ${found.length})` : "")
|
|
44991
|
-
);
|
|
44992
|
-
console.log("");
|
|
44993
|
-
if (found.length === 0) {
|
|
44994
|
-
console.log(" " + c("\u2713 no offenders \u2014 clean", "32"));
|
|
44995
|
-
console.log("");
|
|
44996
|
-
return exitCode;
|
|
44997
|
-
}
|
|
44998
|
-
const locs = found.map((o) => `${o.file}:${o.line}`);
|
|
44999
|
-
const locW = Math.max("FILE:LINE".length, ...locs.map((s) => s.length));
|
|
45000
|
-
const kindW = Math.max("KIND".length, ...found.map((o) => o.kind.length));
|
|
45001
|
-
const pad = (s, w) => s.padEnd(w);
|
|
45002
|
-
const header = ` ${pad("#", 3)} ${pad("SEVERITY", 8)} ${pad("KIND", kindW)} ${pad(
|
|
45003
|
-
"FILE:LINE",
|
|
45004
|
-
locW
|
|
45005
|
-
)} DETAIL`;
|
|
45006
|
-
console.log(c(header, "1"));
|
|
45007
|
-
console.log(" " + "-".repeat(header.length - 2));
|
|
45008
|
-
found.forEach((o, idx) => {
|
|
45009
|
-
const i = idx + 1;
|
|
45010
|
-
const sevCell = c(pad(o.severity, 8), sevColor[o.severity]);
|
|
45011
|
-
console.log(
|
|
45012
|
-
` ${String(i).padStart(3)} ${sevCell} ${pad(o.kind, kindW)} ${pad(locs[idx], locW)} ${o.detail}`
|
|
45013
|
-
);
|
|
45014
|
-
});
|
|
45015
|
-
console.log("");
|
|
45016
|
-
return exitCode;
|
|
45017
|
-
}
|
|
45018
|
-
|
|
45019
44829
|
// src/commands/queue.ts
|
|
45020
44830
|
init_dotenv();
|
|
45021
44831
|
init_queue();
|
|
45022
44832
|
import { readdirSync as readdirSync22, statSync as statSync20 } from "node:fs";
|
|
45023
|
-
import { extname as
|
|
44833
|
+
import { extname as extname8, join as join37 } from "node:path";
|
|
45024
44834
|
import { pathToFileURL as pathToFileURL3 } from "node:url";
|
|
45025
44835
|
var BOOLEAN_FLAGS = /* @__PURE__ */ new Set(["once", "json"]);
|
|
45026
|
-
function
|
|
44836
|
+
function parseFlags(args) {
|
|
45027
44837
|
const flags = {};
|
|
45028
44838
|
const positional = [];
|
|
45029
44839
|
let i = 0;
|
|
@@ -45058,7 +44868,7 @@ async function resolveQueueHandler(servicesDir, topic) {
|
|
|
45058
44868
|
}
|
|
45059
44869
|
for (const entry of entries.sort()) {
|
|
45060
44870
|
if (entry.startsWith("_")) continue;
|
|
45061
|
-
const ext =
|
|
44871
|
+
const ext = extname8(entry);
|
|
45062
44872
|
if (ext !== ".ts" && ext !== ".js") continue;
|
|
45063
44873
|
const fullPath = join37(servicesDir, entry);
|
|
45064
44874
|
try {
|
|
@@ -45079,7 +44889,7 @@ function firstJob(yielded) {
|
|
|
45079
44889
|
}
|
|
45080
44890
|
async function queueWork(args) {
|
|
45081
44891
|
loadEnv();
|
|
45082
|
-
const { flags, positional } =
|
|
44892
|
+
const { flags, positional } = parseFlags(args);
|
|
45083
44893
|
const topic = positional[0] ?? "default";
|
|
45084
44894
|
const once2 = Boolean(flags.once);
|
|
45085
44895
|
let pollSeconds;
|
|
@@ -45131,7 +44941,7 @@ async function queueWork(args) {
|
|
|
45131
44941
|
}
|
|
45132
44942
|
async function queueStats(args) {
|
|
45133
44943
|
loadEnv();
|
|
45134
|
-
const { flags, positional } =
|
|
44944
|
+
const { flags, positional } = parseFlags(args);
|
|
45135
44945
|
const topic = positional[0] ?? "default";
|
|
45136
44946
|
const queue = new Queue({ topic });
|
|
45137
44947
|
const stats = {
|
|
@@ -45162,7 +44972,7 @@ async function queueStats(args) {
|
|
|
45162
44972
|
}
|
|
45163
44973
|
async function queueRetry(args) {
|
|
45164
44974
|
loadEnv();
|
|
45165
|
-
const { positional } =
|
|
44975
|
+
const { positional } = parseFlags(args);
|
|
45166
44976
|
const topic = positional[0] ?? "default";
|
|
45167
44977
|
const queue = new Queue({ topic });
|
|
45168
44978
|
const dead = queue.deadLetters(0);
|
|
@@ -45178,7 +44988,7 @@ async function queueRetry(args) {
|
|
|
45178
44988
|
}
|
|
45179
44989
|
async function queueClear(args) {
|
|
45180
44990
|
loadEnv();
|
|
45181
|
-
const { positional } =
|
|
44991
|
+
const { positional } = parseFlags(args);
|
|
45182
44992
|
const status2 = positional[0] ?? "completed";
|
|
45183
44993
|
const topic = positional[1] ?? "default";
|
|
45184
44994
|
const queue = new Queue({ topic });
|
|
@@ -45215,7 +45025,7 @@ async function queueCommand(args = []) {
|
|
|
45215
45025
|
import { accessSync as accessSync2, constants as constants2, existsSync as existsSync35, statSync as statSync21 } from "node:fs";
|
|
45216
45026
|
import { basename as basename8, delimiter as delimiter2, join as join38 } from "node:path";
|
|
45217
45027
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
45218
|
-
function
|
|
45028
|
+
function parseFlags2(args) {
|
|
45219
45029
|
const flags = {};
|
|
45220
45030
|
let i = 0;
|
|
45221
45031
|
while (i < args.length) {
|
|
@@ -45253,7 +45063,7 @@ function whichDocker() {
|
|
|
45253
45063
|
return null;
|
|
45254
45064
|
}
|
|
45255
45065
|
function buildImage(args) {
|
|
45256
|
-
const flags =
|
|
45066
|
+
const flags = parseFlags2(args);
|
|
45257
45067
|
let tag = typeof flags.tag === "string" ? flags.tag : "";
|
|
45258
45068
|
if (!tag) {
|
|
45259
45069
|
const dirName = basename8(process.cwd()).toLowerCase();
|
|
@@ -45288,7 +45098,7 @@ function buildImage(args) {
|
|
|
45288
45098
|
|
|
45289
45099
|
// src/bin.ts
|
|
45290
45100
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
45291
|
-
import { existsSync as existsSync36, readFileSync as
|
|
45101
|
+
import { existsSync as existsSync36, readFileSync as readFileSync28, statSync as statSync22 } from "node:fs";
|
|
45292
45102
|
import { delimiter as delimiter3, dirname as dirname16, join as join39 } from "node:path";
|
|
45293
45103
|
import { fileURLToPath as fileURLToPath9, pathToFileURL as pathToFileURL4 } from "node:url";
|
|
45294
45104
|
function readCliVersion() {
|
|
@@ -45297,7 +45107,7 @@ function readCliVersion() {
|
|
|
45297
45107
|
const pkgPath = join39(dir, "package.json");
|
|
45298
45108
|
if (existsSync36(pkgPath)) {
|
|
45299
45109
|
try {
|
|
45300
|
-
const pkg = JSON.parse(
|
|
45110
|
+
const pkg = JSON.parse(readFileSync28(pkgPath, "utf-8"));
|
|
45301
45111
|
if (typeof pkg.version === "string" && pkg.version) return pkg.version;
|
|
45302
45112
|
} catch {
|
|
45303
45113
|
}
|
|
@@ -45533,13 +45343,6 @@ var COMMANDS = {
|
|
|
45533
45343
|
usage: "[file]",
|
|
45534
45344
|
summary: "Run database seed files from src/seeds/"
|
|
45535
45345
|
},
|
|
45536
|
-
metrics: {
|
|
45537
|
-
handler: (a) => {
|
|
45538
|
-
process.exit(runMetrics(a));
|
|
45539
|
-
},
|
|
45540
|
-
usage: "[--top N] [--json] [--fail-on warn|error] [--path DIR]",
|
|
45541
|
-
summary: "Rank top code-quality offenders"
|
|
45542
|
-
},
|
|
45543
45346
|
console: {
|
|
45544
45347
|
handler: async () => {
|
|
45545
45348
|
await openConsole();
|