Spaces:
Paused
Paused
File size: 4,658 Bytes
529090e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | import { exec } from 'child_process';
import { promisify } from 'util';
import * as fs from 'fs/promises';
import * as path from 'path';
import { logger } from '../../utils/logger.js';
import { eventBus } from '../../mcp/EventBus.js';
const execAsync = promisify(exec);
export interface DevToolsStatus {
lastScan: string | null;
repoCount: number;
brainPath: string;
isScanning: boolean;
lastError: string | null;
}
export class DevToolsService {
private static instance: DevToolsService;
private isScanning = false;
private devToolsPath: string;
private brainPath: string | null = null;
private constructor() {
// Assuming DevTools-Monorepo is parallel to WidgeTDC root
// WidgeTDC/apps/backend -> ../../../DevTools-Monorepo
this.devToolsPath = path.resolve(process.cwd(), '../../../DevTools-Monorepo');
this.findBrainPath();
}
public static getInstance(): DevToolsService {
if (!DevToolsService.instance) {
DevToolsService.instance = new DevToolsService();
}
return DevToolsService.instance;
}
private async findBrainPath() {
// Logic mirrors PowerShell script to find active brain
const potentialPaths = [
'G:\\My Drive\\WidgeTDC-Config',
path.join(process.env.USERPROFILE || '', 'Google Drive', 'WidgeTDC-Config'),
path.join(process.env.OneDrive || '', 'WidgeTDC-Config'),
path.join(process.env.HOME || process.env.USERPROFILE || '', '.widget-tdc')
];
for (const p of potentialPaths) {
try {
await fs.access(p);
this.brainPath = path.join(p, 'brain.json');
break;
} catch {
continue;
}
}
}
public async getStatus(): Promise<DevToolsStatus> {
let repoCount = 0;
let lastScan = null;
if (this.brainPath) {
try {
const data = await fs.readFile(this.brainPath, 'utf-8');
const brain = JSON.parse(data);
if (brain.templates) {
repoCount = Object.keys(brain.templates).length;
// Find most recent scan time
const dates = Object.values(brain.templates)
.map((t: any) => t.last_scanned ? new Date(t.last_scanned).getTime() : 0);
if (dates.length > 0) {
lastScan = new Date(Math.max(...dates)).toISOString();
}
}
} catch (error) {
logger.error('Failed to read brain.json', error);
}
}
return {
lastScan,
repoCount,
brainPath: this.brainPath || 'Not Found',
isScanning: this.isScanning,
lastError: null
};
}
public async runScan(): Promise<void> {
if (this.isScanning) throw new Error('Scan already in progress');
this.isScanning = true;
eventBus.emit('devtools:scan:started', {});
try {
const script = path.join(this.devToolsPath, 'widget-agent.ps1');
const command = `pwsh -NoProfile -ExecutionPolicy Bypass -File "${script}" scan-github`;
logger.info(`Running DevTools scan: ${command}`);
const { stdout, stderr } = await execAsync(command);
logger.info('DevTools scan completed', { stdout });
if (stderr) logger.warn('DevTools scan stderr', { stderr });
eventBus.emit('devtools:scan:completed', { output: stdout });
} catch (error) {
logger.error('DevTools scan failed', error);
eventBus.emit('devtools:scan:failed', { error: String(error) });
throw error;
} finally {
this.isScanning = false;
}
}
public async validateRepo(repoPath: string): Promise<string> {
const script = path.join(this.devToolsPath, 'widget-agent.ps1');
// We need to run validate in the context of the target repo
const command = `pwsh -NoProfile -ExecutionPolicy Bypass -File "${script}" validate`;
try {
const { stdout } = await execAsync(command, { cwd: repoPath });
return stdout;
} catch (error: any) {
// PowerShell script returns exit code 1 on validation failure, which throws here
if (error.stdout) return error.stdout; // Return the validation output even if it failed
throw error;
}
}
}
export const devToolsService = DevToolsService.getInstance();
|