From 0ee5d0823ea997c6a52b7468e3420187985b2d74 Mon Sep 17 00:00:00 2001 From: mkorwel Date: Mon, 16 Mar 2026 15:42:23 -0700 Subject: [PATCH] feat(offload): make system project-agnostic and update docs --- .gemini/skills/offload/scripts/fleet.ts | 38 ++++++++++++++++++++----- .gemini/skills/offload/scripts/setup.ts | 9 +++++- MAINTAINER_ONBOARDING.md | 5 ++-- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/.gemini/skills/offload/scripts/fleet.ts b/.gemini/skills/offload/scripts/fleet.ts index daa11d1581..6b9df3b871 100644 --- a/.gemini/skills/offload/scripts/fleet.ts +++ b/.gemini/skills/offload/scripts/fleet.ts @@ -12,25 +12,47 @@ import { ProviderFactory } from './providers/ProviderFactory.ts'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const REPO_ROOT = path.resolve(__dirname, '../../../..'); -const PROJECT_ID = 'gemini-cli-team-quota'; 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/settings.json'); + if (fs.existsSync(settingsPath)) { + try { + const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); + return settings.maintainer?.deepReview?.projectId; + } catch (e) {} + } + return process.env.GOOGLE_CLOUD_PROJECT || ''; +} + async function listWorkers() { - console.log(`šŸ” Listing Offload Workers for ${USER} in ${PROJECT_ID}...`); + 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', PROJECT_ID, + '--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: PROJECT_ID, + projectId: projectId, zone: DEFAULT_ZONE, instanceName: INSTANCE_PREFIX }); @@ -45,8 +67,9 @@ async function provisionWorker() { } async function stopWorker() { + const projectId = getProjectId(); const provider = ProviderFactory.getProvider({ - projectId: PROJECT_ID, + projectId: projectId, zone: DEFAULT_ZONE, instanceName: INSTANCE_PREFIX }); @@ -65,7 +88,7 @@ async function remoteStatus() { const config = settings.maintainer?.deepReview; const provider = ProviderFactory.getProvider({ - projectId: config?.projectId || PROJECT_ID, + projectId: config?.projectId || getProjectId(), zone: config?.zone || DEFAULT_ZONE, instanceName: INSTANCE_PREFIX }); @@ -75,6 +98,7 @@ async function remoteStatus() { } async function rebuildWorker() { + const projectId = getProjectId(); console.log(`šŸ”„ Rebuilding worker ${INSTANCE_PREFIX}...`); const knownHostsPath = path.join(REPO_ROOT, '.gemini/offload_known_hosts'); @@ -83,7 +107,7 @@ async function rebuildWorker() { fs.unlinkSync(knownHostsPath); } - spawnSync('gcloud', ['compute', 'instances', 'delete', INSTANCE_PREFIX, '--project', PROJECT_ID, '--zone', DEFAULT_ZONE, '--quiet'], { stdio: 'inherit' }); + spawnSync('gcloud', ['compute', 'instances', 'delete', INSTANCE_PREFIX, '--project', projectId, '--zone', DEFAULT_ZONE, '--quiet'], { stdio: 'inherit' }); await provisionWorker(); } diff --git a/.gemini/skills/offload/scripts/setup.ts b/.gemini/skills/offload/scripts/setup.ts index e77631d960..9bfcc5ae64 100644 --- a/.gemini/skills/offload/scripts/setup.ts +++ b/.gemini/skills/offload/scripts/setup.ts @@ -36,7 +36,14 @@ async function confirm(question: string): Promise { export async function runSetup(env: NodeJS.ProcessEnv = process.env) { console.log('\n🌟 Initializing Dedicated Offload Worker...'); - const projectId = await prompt('GCP Project ID', 'gemini-cli-team-quota'); + const defaultProject = env.GOOGLE_CLOUD_PROJECT || ''; + const projectId = await prompt('GCP Project ID', defaultProject); + + if (!projectId) { + console.error('āŒ Project ID is required. Set GOOGLE_CLOUD_PROJECT or enter it manually.'); + return 1; + } + const zone = await prompt('Compute Zone', 'us-west1-a'); const targetVM = `gcli-offload-${env.USER || 'mattkorwel'}`; diff --git a/MAINTAINER_ONBOARDING.md b/MAINTAINER_ONBOARDING.md index 344de8b8ce..cd16082b40 100644 --- a/MAINTAINER_ONBOARDING.md +++ b/MAINTAINER_ONBOARDING.md @@ -6,11 +6,10 @@ preflight) to a dedicated GCP worker. ## Prerequisites -1. **Google Cloud Access**: Ensure you have access to the - `gemini-cli-team-quota` project. +1. **Google Cloud Access**: You will need a Google Cloud Project with billing enabled. You can use a shared team project or, **ideally, your own personal GCP project** for maximum isolation. 2. **GCloud CLI**: Authenticated locally (`gcloud auth login`). 3. **GitHub CLI**: Authenticated locally (`gh auth login`). -4. **IAP Permissions**: Ensure you have the `IAP-secured Tunnel User` role on the project. +4. **IAP Permissions**: Ensure you have the `IAP-secured Tunnel User` role on your chosen project. 5. **Corporate Identity**: Run `gcert` (or your internal equivalent) recently to ensure SSH certificates are valid. ## Architecture: Hybrid VM + Container šŸ—ļø