Files
gemini-cli/.gemini/skills/offload/scripts/fleet.ts

140 lines
4.0 KiB
TypeScript

/**
* Offload Fleet Manager
*
* Manages dynamic GCP workers for offloading tasks.
*/
import { spawnSync } from 'child_process';
import path from 'path';
import fs from 'fs';
import { fileURLToPath } from 'url';
import { ProviderFactory } from './providers/ProviderFactory.ts';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const REPO_ROOT = path.resolve(__dirname, '../../../..');
const USER = process.env.USER || 'mattkorwel';
const INSTANCE_PREFIX = `gcli-offload-${USER}`;
const DEFAULT_ZONE = 'us-west1-a';
function getProjectId(): string {
const settingsPath = path.join(REPO_ROOT, '.gemini/offload/settings.json');
if (fs.existsSync(settingsPath)) {
try {
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
return settings.deepReview?.projectId;
} catch (e) {}
}
return process.env.GOOGLE_CLOUD_PROJECT || '';
}
async function listWorkers() {
const projectId = getProjectId();
if (!projectId) {
console.error('❌ Project ID not found. Run "npm run offload:setup" first.');
return;
}
console.log(`🔍 Listing Offload Workers for ${USER} in ${projectId}...`);
spawnSync('gcloud', [
'compute', 'instances', 'list',
'--project', projectId,
'--filter', `name~^${INSTANCE_PREFIX}`,
'--format', 'table(name,zone,status,networkInterfaces[0].networkIP:label=INTERNAL_IP,creationTimestamp)'
], { stdio: 'inherit' });
}
async function provisionWorker() {
const projectId = getProjectId();
if (!projectId) {
console.error('❌ Project ID not found. Run "npm run offload:setup" first.');
return;
}
const provider = ProviderFactory.getProvider({
projectId: projectId,
zone: DEFAULT_ZONE,
instanceName: INSTANCE_PREFIX
});
const status = await provider.getStatus();
if (status.status !== 'UNKNOWN' && status.status !== 'ERROR') {
console.log(`✅ Worker ${INSTANCE_PREFIX} already exists and is ${status.status}.`);
return;
}
await provider.provision();
}
async function stopWorker() {
const projectId = getProjectId();
const provider = ProviderFactory.getProvider({
projectId: projectId,
zone: DEFAULT_ZONE,
instanceName: INSTANCE_PREFIX
});
console.log(`🛑 Stopping offload worker: ${INSTANCE_PREFIX}...`);
await provider.stop();
}
async function remoteStatus() {
const settingsPath = path.join(REPO_ROOT, '.gemini/offload/settings.json');
if (!fs.existsSync(settingsPath)) {
console.error('❌ Settings not found. Run "npm run offload:setup" first.');
return;
}
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
const config = settings.deepReview;
const provider = ProviderFactory.getProvider({
projectId: config?.projectId || getProjectId(),
zone: config?.zone || DEFAULT_ZONE,
instanceName: INSTANCE_PREFIX
});
console.log(`📡 Fetching remote status from ${INSTANCE_PREFIX}...`);
await provider.exec('tsx .offload/scripts/status.ts');
}
async function rebuildWorker() {
const projectId = getProjectId();
console.log(`🔥 Rebuilding worker ${INSTANCE_PREFIX}...`);
const knownHostsPath = path.join(REPO_ROOT, '.gemini/offload_known_hosts');
if (fs.existsSync(knownHostsPath)) {
console.log(` - Clearing isolated known_hosts...`);
fs.unlinkSync(knownHostsPath);
}
spawnSync('gcloud', ['compute', 'instances', 'delete', INSTANCE_PREFIX, '--project', projectId, '--zone', DEFAULT_ZONE, '--quiet'], { stdio: 'inherit' });
await provisionWorker();
}
async function main() {
const action = process.argv[2] || 'list';
switch (action) {
case 'list':
await listWorkers();
break;
case 'provision':
await provisionWorker();
break;
case 'rebuild':
await rebuildWorker();
break;
case 'stop':
await stopWorker();
break;
case 'status':
await remoteStatus();
break;
default:
console.error(`❌ Unknown fleet action: ${action}`);
process.exit(1);
}
}
main().catch(console.error);