mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-03-11 00:47:52 +00:00
106 lines
3.3 KiB
JavaScript
106 lines
3.3 KiB
JavaScript
const http = require('http');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const TEXT_EXTS = new Set([
|
|
'.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs', '.vue', '.svelte', '.astro',
|
|
'.css', '.scss', '.sass', '.less',
|
|
'.html', '.htm', '.xml', '.svg',
|
|
'.json', '.yaml', '.yml', '.toml', '.ini',
|
|
'.md', '.mdx', '.txt', '.rst',
|
|
'.py', '.rb', '.go', '.rs', '.java', '.c', '.cpp', '.h', '.hpp', '.cs',
|
|
'.sh', '.bash', '.zsh', '.fish',
|
|
'.sql', '.graphql', '.gql',
|
|
]);
|
|
|
|
const SKIP_DIRS = new Set([
|
|
'node_modules', '.git', 'dist', 'build', '.next', '.nuxt',
|
|
'coverage', '.cache', '__pycache__', '.venv', 'venv',
|
|
'target', 'vendor', '.turbo', 'out', '.output', 'tmp',
|
|
]);
|
|
|
|
function scan(dir, max = 5000) {
|
|
const files = [];
|
|
(function walk(d, depth) {
|
|
if (depth > 6 || files.length >= max) return;
|
|
let entries;
|
|
try { entries = fs.readdirSync(d, { withFileTypes: true }); } catch { return; }
|
|
for (const e of entries) {
|
|
if (files.length >= max) break;
|
|
if (e.name.startsWith('.') && e.name !== '.env') continue;
|
|
const full = path.join(d, e.name);
|
|
if (e.isDirectory()) {
|
|
if (!SKIP_DIRS.has(e.name)) walk(full, depth + 1);
|
|
} else if (e.isFile()) {
|
|
try {
|
|
const stat = fs.statSync(full);
|
|
files.push({
|
|
full,
|
|
rel: path.relative(dir, full),
|
|
ext: path.extname(e.name).toLowerCase() || '(none)',
|
|
size: stat.size,
|
|
mtime: stat.mtimeMs,
|
|
});
|
|
} catch { /* skip unreadable */ }
|
|
}
|
|
}
|
|
})(dir, 0);
|
|
return files;
|
|
}
|
|
|
|
function countLines(full, size) {
|
|
if (size > 256 * 1024) return 0; // skip large files
|
|
try { return (fs.readFileSync(full, 'utf-8').match(/\n/g) || []).length + 1; }
|
|
catch { return 0; }
|
|
}
|
|
|
|
function getStats(projectPath) {
|
|
if (!projectPath || !path.isAbsolute(projectPath)) throw new Error('Invalid path');
|
|
if (!fs.existsSync(projectPath)) throw new Error('Path does not exist');
|
|
|
|
const files = scan(projectPath);
|
|
const byExt = {};
|
|
let totalLines = 0;
|
|
let totalSize = 0;
|
|
|
|
for (const f of files) {
|
|
byExt[f.ext] = (byExt[f.ext] || 0) + 1;
|
|
totalSize += f.size;
|
|
if (TEXT_EXTS.has(f.ext)) totalLines += countLines(f.full, f.size);
|
|
}
|
|
|
|
return {
|
|
totalFiles: files.length,
|
|
totalLines,
|
|
totalSize,
|
|
byExtension: Object.entries(byExt).sort((a, b) => b[1] - a[1]).slice(0, 12),
|
|
largest: [...files].sort((a, b) => b.size - a.size).slice(0, 6).map(f => ({ name: f.rel, size: f.size })),
|
|
recent: [...files].sort((a, b) => b.mtime - a.mtime).slice(0, 6).map(f => ({ name: f.rel, mtime: f.mtime })),
|
|
};
|
|
}
|
|
|
|
const server = http.createServer((req, res) => {
|
|
res.setHeader('Content-Type', 'application/json');
|
|
|
|
if (req.method === 'GET' && req.url.startsWith('/stats')) {
|
|
try {
|
|
const { searchParams } = new URL(req.url, 'http://localhost');
|
|
const stats = getStats(searchParams.get('path'));
|
|
res.end(JSON.stringify(stats));
|
|
} catch (err) {
|
|
res.writeHead(400);
|
|
res.end(JSON.stringify({ error: err.message }));
|
|
}
|
|
return;
|
|
}
|
|
|
|
res.writeHead(404);
|
|
res.end(JSON.stringify({ error: 'Not found' }));
|
|
});
|
|
|
|
server.listen(0, '127.0.0.1', () => {
|
|
const { port } = server.address();
|
|
// Signal readiness to the host — this JSON line is required
|
|
console.log(JSON.stringify({ ready: true, port }));
|
|
});
|