 85bf1341f3
			
		
	
	85bf1341f3
	
	
	
		
			
			Frontend Enhancements: - Complete React TypeScript frontend with modern UI components - Distributed workflows management interface with real-time updates - Socket.IO integration for live agent status monitoring - Agent management dashboard with cluster visualization - Project management interface with metrics and task tracking - Responsive design with proper error handling and loading states Backend Infrastructure: - Distributed coordinator for multi-agent workflow orchestration - Cluster management API with comprehensive agent operations - Enhanced database models for agents and projects - Project service for filesystem-based project discovery - Performance monitoring and metrics collection - Comprehensive API documentation and error handling Documentation: - Complete distributed development guide (README_DISTRIBUTED.md) - Comprehensive development report with architecture insights - System configuration templates and deployment guides The platform now provides a complete web interface for managing the distributed AI cluster with real-time monitoring, workflow orchestration, and agent coordination capabilities. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			121 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			121 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| const {promisify} = require('util');
 | |
| const fs = require('fs');
 | |
| const path = require('path');
 | |
| const fastGlob = require('fast-glob');
 | |
| const gitIgnore = require('ignore');
 | |
| const slash = require('slash');
 | |
| 
 | |
| const DEFAULT_IGNORE = [
 | |
| 	'**/node_modules/**',
 | |
| 	'**/flow-typed/**',
 | |
| 	'**/coverage/**',
 | |
| 	'**/.git'
 | |
| ];
 | |
| 
 | |
| const readFileP = promisify(fs.readFile);
 | |
| 
 | |
| const mapGitIgnorePatternTo = base => ignore => {
 | |
| 	if (ignore.startsWith('!')) {
 | |
| 		return '!' + path.posix.join(base, ignore.slice(1));
 | |
| 	}
 | |
| 
 | |
| 	return path.posix.join(base, ignore);
 | |
| };
 | |
| 
 | |
| const parseGitIgnore = (content, options) => {
 | |
| 	const base = slash(path.relative(options.cwd, path.dirname(options.fileName)));
 | |
| 
 | |
| 	return content
 | |
| 		.split(/\r?\n/)
 | |
| 		.filter(Boolean)
 | |
| 		.filter(line => !line.startsWith('#'))
 | |
| 		.map(mapGitIgnorePatternTo(base));
 | |
| };
 | |
| 
 | |
| const reduceIgnore = files => {
 | |
| 	const ignores = gitIgnore();
 | |
| 	for (const file of files) {
 | |
| 		ignores.add(parseGitIgnore(file.content, {
 | |
| 			cwd: file.cwd,
 | |
| 			fileName: file.filePath
 | |
| 		}));
 | |
| 	}
 | |
| 
 | |
| 	return ignores;
 | |
| };
 | |
| 
 | |
| const ensureAbsolutePathForCwd = (cwd, p) => {
 | |
| 	cwd = slash(cwd);
 | |
| 	if (path.isAbsolute(p)) {
 | |
| 		if (slash(p).startsWith(cwd)) {
 | |
| 			return p;
 | |
| 		}
 | |
| 
 | |
| 		throw new Error(`Path ${p} is not in cwd ${cwd}`);
 | |
| 	}
 | |
| 
 | |
| 	return path.join(cwd, p);
 | |
| };
 | |
| 
 | |
| const getIsIgnoredPredecate = (ignores, cwd) => {
 | |
| 	return p => ignores.ignores(slash(path.relative(cwd, ensureAbsolutePathForCwd(cwd, p.path || p))));
 | |
| };
 | |
| 
 | |
| const getFile = async (file, cwd) => {
 | |
| 	const filePath = path.join(cwd, file);
 | |
| 	const content = await readFileP(filePath, 'utf8');
 | |
| 
 | |
| 	return {
 | |
| 		cwd,
 | |
| 		filePath,
 | |
| 		content
 | |
| 	};
 | |
| };
 | |
| 
 | |
| const getFileSync = (file, cwd) => {
 | |
| 	const filePath = path.join(cwd, file);
 | |
| 	const content = fs.readFileSync(filePath, 'utf8');
 | |
| 
 | |
| 	return {
 | |
| 		cwd,
 | |
| 		filePath,
 | |
| 		content
 | |
| 	};
 | |
| };
 | |
| 
 | |
| const normalizeOptions = ({
 | |
| 	ignore = [],
 | |
| 	cwd = slash(process.cwd())
 | |
| } = {}) => {
 | |
| 	return {ignore, cwd};
 | |
| };
 | |
| 
 | |
| module.exports = async options => {
 | |
| 	options = normalizeOptions(options);
 | |
| 
 | |
| 	const paths = await fastGlob('**/.gitignore', {
 | |
| 		ignore: DEFAULT_IGNORE.concat(options.ignore),
 | |
| 		cwd: options.cwd
 | |
| 	});
 | |
| 
 | |
| 	const files = await Promise.all(paths.map(file => getFile(file, options.cwd)));
 | |
| 	const ignores = reduceIgnore(files);
 | |
| 
 | |
| 	return getIsIgnoredPredecate(ignores, options.cwd);
 | |
| };
 | |
| 
 | |
| module.exports.sync = options => {
 | |
| 	options = normalizeOptions(options);
 | |
| 
 | |
| 	const paths = fastGlob.sync('**/.gitignore', {
 | |
| 		ignore: DEFAULT_IGNORE.concat(options.ignore),
 | |
| 		cwd: options.cwd
 | |
| 	});
 | |
| 
 | |
| 	const files = paths.map(file => getFileSync(file, options.cwd));
 | |
| 	const ignores = reduceIgnore(files);
 | |
| 
 | |
| 	return getIsIgnoredPredecate(ignores, options.cwd);
 | |
| };
 |