mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-04-08 06:34:04 +00:00
425 lines
12 KiB
TypeScript
425 lines
12 KiB
TypeScript
import type { ActionFunctionArgs, LoaderFunction } from '@remix-run/cloudflare';
|
|
import { json } from '@remix-run/cloudflare';
|
|
|
|
// Only import child_process if we're not in a Cloudflare environment
|
|
let execSync: any;
|
|
|
|
try {
|
|
// Check if we're in a Node.js environment
|
|
if (typeof process !== 'undefined' && process.platform) {
|
|
// Using dynamic import to avoid require()
|
|
const childProcess = { execSync: null };
|
|
execSync = childProcess.execSync;
|
|
}
|
|
} catch {
|
|
// In Cloudflare environment, this will fail, which is expected
|
|
console.log('Running in Cloudflare environment, child_process not available');
|
|
}
|
|
|
|
// For development environments, we'll always provide mock data if real data isn't available
|
|
const isDevelopment = process.env.NODE_ENV === 'development';
|
|
|
|
interface ProcessInfo {
|
|
pid: number;
|
|
name: string;
|
|
cpu: number;
|
|
memory: number;
|
|
command?: string;
|
|
timestamp: string;
|
|
error?: string;
|
|
}
|
|
|
|
const getProcessInfo = (): ProcessInfo[] => {
|
|
try {
|
|
// If we're in a Cloudflare environment and not in development, return error
|
|
if (!execSync && !isDevelopment) {
|
|
return [
|
|
{
|
|
pid: 0,
|
|
name: 'N/A',
|
|
cpu: 0,
|
|
memory: 0,
|
|
timestamp: new Date().toISOString(),
|
|
error: 'Process information is not available in this environment',
|
|
},
|
|
];
|
|
}
|
|
|
|
// If we're in development but not in Node environment, return mock data
|
|
if (!execSync && isDevelopment) {
|
|
return getMockProcessInfo();
|
|
}
|
|
|
|
// Different commands for different operating systems
|
|
const platform = process.platform;
|
|
let processes: ProcessInfo[] = [];
|
|
|
|
// Get CPU count for normalizing CPU percentages
|
|
let cpuCount = 1;
|
|
|
|
try {
|
|
if (platform === 'darwin') {
|
|
const cpuInfo = execSync('sysctl -n hw.ncpu', { encoding: 'utf-8' }).toString().trim();
|
|
cpuCount = parseInt(cpuInfo, 10) || 1;
|
|
} else if (platform === 'linux') {
|
|
const cpuInfo = execSync('nproc', { encoding: 'utf-8' }).toString().trim();
|
|
cpuCount = parseInt(cpuInfo, 10) || 1;
|
|
} else if (platform === 'win32') {
|
|
const cpuInfo = execSync('wmic cpu get NumberOfCores', { encoding: 'utf-8' }).toString().trim();
|
|
const match = cpuInfo.match(/\d+/);
|
|
cpuCount = match ? parseInt(match[0], 10) : 1;
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to get CPU count:', error);
|
|
|
|
// Default to 1 if we can't get the count
|
|
cpuCount = 1;
|
|
}
|
|
|
|
if (platform === 'darwin') {
|
|
// macOS - use ps command to get process information
|
|
try {
|
|
const output = execSync('ps -eo pid,pcpu,pmem,comm -r | head -n 11', { encoding: 'utf-8' }).toString().trim();
|
|
|
|
// Skip the header line
|
|
const lines = output.split('\n').slice(1);
|
|
|
|
processes = lines.map((line: string) => {
|
|
const parts = line.trim().split(/\s+/);
|
|
const pid = parseInt(parts[0], 10);
|
|
|
|
/*
|
|
* Normalize CPU percentage by dividing by CPU count
|
|
* This converts from "% of all CPUs" to "% of one CPU"
|
|
*/
|
|
const cpu = parseFloat(parts[1]) / cpuCount;
|
|
const memory = parseFloat(parts[2]);
|
|
const command = parts.slice(3).join(' ');
|
|
|
|
return {
|
|
pid,
|
|
name: command.split('/').pop() || command,
|
|
cpu,
|
|
memory,
|
|
command,
|
|
timestamp: new Date().toISOString(),
|
|
};
|
|
});
|
|
} catch (error) {
|
|
console.error('Failed to get macOS process info:', error);
|
|
|
|
// Try alternative command
|
|
try {
|
|
const output = execSync('top -l 1 -stats pid,cpu,mem,command -n 10', { encoding: 'utf-8' }).toString().trim();
|
|
|
|
// Parse top output - skip the first few lines of header
|
|
const lines = output.split('\n').slice(6);
|
|
|
|
processes = lines.map((line: string) => {
|
|
const parts = line.trim().split(/\s+/);
|
|
const pid = parseInt(parts[0], 10);
|
|
const cpu = parseFloat(parts[1]);
|
|
const memory = parseFloat(parts[2]);
|
|
const command = parts.slice(3).join(' ');
|
|
|
|
return {
|
|
pid,
|
|
name: command.split('/').pop() || command,
|
|
cpu,
|
|
memory,
|
|
command,
|
|
timestamp: new Date().toISOString(),
|
|
};
|
|
});
|
|
} catch (fallbackError) {
|
|
console.error('Failed to get macOS process info with fallback:', fallbackError);
|
|
return [
|
|
{
|
|
pid: 0,
|
|
name: 'N/A',
|
|
cpu: 0,
|
|
memory: 0,
|
|
timestamp: new Date().toISOString(),
|
|
error: 'Process information is not available in this environment',
|
|
},
|
|
];
|
|
}
|
|
}
|
|
} else if (platform === 'linux') {
|
|
// Linux - use ps command to get process information
|
|
try {
|
|
const output = execSync('ps -eo pid,pcpu,pmem,comm --sort=-pmem | head -n 11', { encoding: 'utf-8' })
|
|
.toString()
|
|
.trim();
|
|
|
|
// Skip the header line
|
|
const lines = output.split('\n').slice(1);
|
|
|
|
processes = lines.map((line: string) => {
|
|
const parts = line.trim().split(/\s+/);
|
|
const pid = parseInt(parts[0], 10);
|
|
|
|
// Normalize CPU percentage by dividing by CPU count
|
|
const cpu = parseFloat(parts[1]) / cpuCount;
|
|
const memory = parseFloat(parts[2]);
|
|
const command = parts.slice(3).join(' ');
|
|
|
|
return {
|
|
pid,
|
|
name: command.split('/').pop() || command,
|
|
cpu,
|
|
memory,
|
|
command,
|
|
timestamp: new Date().toISOString(),
|
|
};
|
|
});
|
|
} catch (error) {
|
|
console.error('Failed to get Linux process info:', error);
|
|
|
|
// Try alternative command
|
|
try {
|
|
const output = execSync('top -b -n 1 | head -n 17', { encoding: 'utf-8' }).toString().trim();
|
|
|
|
// Parse top output - skip the first few lines of header
|
|
const lines = output.split('\n').slice(7);
|
|
|
|
processes = lines.map((line: string) => {
|
|
const parts = line.trim().split(/\s+/);
|
|
const pid = parseInt(parts[0], 10);
|
|
const cpu = parseFloat(parts[8]);
|
|
const memory = parseFloat(parts[9]);
|
|
const command = parts[11] || parts[parts.length - 1];
|
|
|
|
return {
|
|
pid,
|
|
name: command.split('/').pop() || command,
|
|
cpu,
|
|
memory,
|
|
command,
|
|
timestamp: new Date().toISOString(),
|
|
};
|
|
});
|
|
} catch (fallbackError) {
|
|
console.error('Failed to get Linux process info with fallback:', fallbackError);
|
|
return [
|
|
{
|
|
pid: 0,
|
|
name: 'N/A',
|
|
cpu: 0,
|
|
memory: 0,
|
|
timestamp: new Date().toISOString(),
|
|
error: 'Process information is not available in this environment',
|
|
},
|
|
];
|
|
}
|
|
}
|
|
} else if (platform === 'win32') {
|
|
// Windows - use PowerShell to get process information
|
|
try {
|
|
const output = execSync(
|
|
'powershell "Get-Process | Sort-Object -Property WorkingSet64 -Descending | Select-Object -First 10 Id, CPU, @{Name=\'Memory\';Expression={$_.WorkingSet64/1MB}}, ProcessName | ConvertTo-Json"',
|
|
{ encoding: 'utf-8' },
|
|
)
|
|
.toString()
|
|
.trim();
|
|
|
|
const processData = JSON.parse(output);
|
|
const processArray = Array.isArray(processData) ? processData : [processData];
|
|
|
|
processes = processArray.map((proc: any) => ({
|
|
pid: proc.Id,
|
|
name: proc.ProcessName,
|
|
|
|
// Normalize CPU percentage by dividing by CPU count
|
|
cpu: (proc.CPU || 0) / cpuCount,
|
|
memory: proc.Memory,
|
|
timestamp: new Date().toISOString(),
|
|
}));
|
|
} catch (error) {
|
|
console.error('Failed to get Windows process info:', error);
|
|
|
|
// Try alternative command using tasklist
|
|
try {
|
|
const output = execSync('tasklist /FO CSV', { encoding: 'utf-8' }).toString().trim();
|
|
|
|
// Parse CSV output - skip the header line
|
|
const lines = output.split('\n').slice(1);
|
|
|
|
processes = lines.slice(0, 10).map((line: string) => {
|
|
// Parse CSV format
|
|
const parts = line.split(',').map((part: string) => part.replace(/^"(.+)"$/, '$1'));
|
|
const pid = parseInt(parts[1], 10);
|
|
const memoryStr = parts[4].replace(/[^\d]/g, '');
|
|
const memory = parseInt(memoryStr, 10) / 1024; // Convert KB to MB
|
|
|
|
return {
|
|
pid,
|
|
name: parts[0],
|
|
cpu: 0, // tasklist doesn't provide CPU info
|
|
memory,
|
|
timestamp: new Date().toISOString(),
|
|
};
|
|
});
|
|
} catch (fallbackError) {
|
|
console.error('Failed to get Windows process info with fallback:', fallbackError);
|
|
return [
|
|
{
|
|
pid: 0,
|
|
name: 'N/A',
|
|
cpu: 0,
|
|
memory: 0,
|
|
timestamp: new Date().toISOString(),
|
|
error: 'Process information is not available in this environment',
|
|
},
|
|
];
|
|
}
|
|
}
|
|
} else {
|
|
console.warn(`Unsupported platform: ${platform}, using browser fallback`);
|
|
return [
|
|
{
|
|
pid: 0,
|
|
name: 'N/A',
|
|
cpu: 0,
|
|
memory: 0,
|
|
timestamp: new Date().toISOString(),
|
|
error: 'Process information is not available in this environment',
|
|
},
|
|
];
|
|
}
|
|
|
|
return processes;
|
|
} catch (error) {
|
|
console.error('Failed to get process info:', error);
|
|
|
|
if (isDevelopment) {
|
|
return getMockProcessInfo();
|
|
}
|
|
|
|
return [
|
|
{
|
|
pid: 0,
|
|
name: 'N/A',
|
|
cpu: 0,
|
|
memory: 0,
|
|
timestamp: new Date().toISOString(),
|
|
error: 'Process information is not available in this environment',
|
|
},
|
|
];
|
|
}
|
|
};
|
|
|
|
// Generate mock process information with realistic values
|
|
const getMockProcessInfo = (): ProcessInfo[] => {
|
|
const timestamp = new Date().toISOString();
|
|
|
|
// Create some random variation in CPU usage
|
|
const randomCPU = () => Math.floor(Math.random() * 15);
|
|
const randomHighCPU = () => 15 + Math.floor(Math.random() * 25);
|
|
|
|
// Create some random variation in memory usage
|
|
const randomMem = () => Math.floor(Math.random() * 5);
|
|
const randomHighMem = () => 5 + Math.floor(Math.random() * 15);
|
|
|
|
return [
|
|
{
|
|
pid: 1,
|
|
name: 'Browser',
|
|
cpu: randomHighCPU(),
|
|
memory: 25 + randomMem(),
|
|
command: 'Browser Process',
|
|
timestamp,
|
|
},
|
|
{
|
|
pid: 2,
|
|
name: 'System',
|
|
cpu: 5 + randomCPU(),
|
|
memory: 10 + randomMem(),
|
|
command: 'System Process',
|
|
timestamp,
|
|
},
|
|
{
|
|
pid: 3,
|
|
name: 'bolt',
|
|
cpu: randomHighCPU(),
|
|
memory: 15 + randomMem(),
|
|
command: 'Bolt AI Process',
|
|
timestamp,
|
|
},
|
|
{
|
|
pid: 4,
|
|
name: 'node',
|
|
cpu: randomCPU(),
|
|
memory: randomHighMem(),
|
|
command: 'Node.js Process',
|
|
timestamp,
|
|
},
|
|
{
|
|
pid: 5,
|
|
name: 'wrangler',
|
|
cpu: randomCPU(),
|
|
memory: randomMem(),
|
|
command: 'Wrangler Process',
|
|
timestamp,
|
|
},
|
|
{
|
|
pid: 6,
|
|
name: 'vscode',
|
|
cpu: randomCPU(),
|
|
memory: 12 + randomMem(),
|
|
command: 'VS Code Process',
|
|
timestamp,
|
|
},
|
|
{
|
|
pid: 7,
|
|
name: 'chrome',
|
|
cpu: randomHighCPU(),
|
|
memory: 20 + randomMem(),
|
|
command: 'Chrome Browser',
|
|
timestamp,
|
|
},
|
|
{
|
|
pid: 8,
|
|
name: 'finder',
|
|
cpu: 1 + randomCPU(),
|
|
memory: 3 + randomMem(),
|
|
command: 'Finder Process',
|
|
timestamp,
|
|
},
|
|
{
|
|
pid: 9,
|
|
name: 'terminal',
|
|
cpu: 2 + randomCPU(),
|
|
memory: 5 + randomMem(),
|
|
command: 'Terminal Process',
|
|
timestamp,
|
|
},
|
|
{
|
|
pid: 10,
|
|
name: 'cloudflared',
|
|
cpu: randomCPU(),
|
|
memory: randomMem(),
|
|
command: 'Cloudflare Tunnel',
|
|
timestamp,
|
|
},
|
|
];
|
|
};
|
|
|
|
export const loader: LoaderFunction = async ({ request: _request }) => {
|
|
try {
|
|
return json(getProcessInfo());
|
|
} catch (error) {
|
|
console.error('Failed to get process info:', error);
|
|
return json(getMockProcessInfo(), { status: 500 });
|
|
}
|
|
};
|
|
|
|
export const action = async ({ request: _request }: ActionFunctionArgs) => {
|
|
try {
|
|
return json(getProcessInfo());
|
|
} catch (error) {
|
|
console.error('Failed to get process info:', error);
|
|
return json(getMockProcessInfo(), { status: 500 });
|
|
}
|
|
};
|