diff --git a/.gemini/skills/offload/scripts/fleet.ts b/.gemini/skills/offload/scripts/fleet.ts index 8fe5c8eabd..004d421d53 100644 --- a/.gemini/skills/offload/scripts/fleet.ts +++ b/.gemini/skills/offload/scripts/fleet.ts @@ -44,29 +44,23 @@ async function provisionWorker() { return; } - console.log(`šŸš€ Provisioning modern container worker (COS + Cloud-Init): ${name}...`); + console.log(`šŸš€ Provisioning modern container worker (COS + Startup Script): ${name}...`); // Get local public key for native SSH access const pubKeyPath = path.join(os.homedir(), '.ssh/google_compute_engine.pub'); const pubKey = fs.existsSync(pubKeyPath) ? fs.readFileSync(pubKeyPath, 'utf8').trim() : ''; const sshKeyMetadata = pubKey ? `${USER}:${pubKey}` : ''; - // Modern Cloud-Init (user-data) configuration for COS - const cloudConfig = `#cloud-config -runcmd: - - | - # Expand the root partition to use the full 200GB for high performance - /usr/bin/growpart /dev/sda 1 - /usr/sbin/resize2fs /dev/sda1 - - docker run -d --name maintainer-worker --restart always \\ + // Direct Startup Script for COS (Native Docker launch) + const startupScript = `#!/bin/bash + # Pull and Run the maintainer container + docker pull ${imageUri} + docker run -d --name maintainer-worker --restart always \\ -v /home/node/dev:/home/node/dev:rw \\ -v /home/node/.gemini:/home/node/.gemini:rw \\ -v /home/node/.offload:/home/node/.offload:rw \\ ${imageUri} /bin/bash -c "while true; do sleep 1000; done" -`; - - const tempPath = path.join(process.env.TMPDIR || '/tmp', `cloud-init-${name}.yaml`); - fs.writeFileSync(tempPath, cloudConfig); + `; const result = spawnSync('gcloud', [ 'compute', 'instances', 'create', name, @@ -77,16 +71,13 @@ runcmd: '--image-project', 'cos-cloud', '--boot-disk-size', '200GB', '--boot-disk-type', 'pd-balanced', - '--metadata-from-file', `user-data=${tempPath}`, - '--metadata', `enable-oslogin=TRUE${sshKeyMetadata ? `,ssh-keys=${sshKeyMetadata}` : ''}`, + '--metadata', `startup-script=${startupScript},enable-oslogin=TRUE${sshKeyMetadata ? `,ssh-keys=${sshKeyMetadata}` : ''}`, '--labels', `owner=${USER.replace(/[^a-z0-9_-]/g, '_')},type=offload-worker`, '--tags', `gcli-offload-${USER}`, '--network-interface', 'network-tier=PREMIUM,no-address', '--scopes', 'https://www.googleapis.com/auth/cloud-platform' ], { stdio: 'inherit' }); - fs.unlinkSync(tempPath); - if (result.status === 0) { console.log(`\nāœ… Worker ${name} is being provisioned.`); console.log(`šŸ‘‰ Container 'maintainer-worker' will start natively via Cloud-Init.`); diff --git a/.gemini/skills/offload/scripts/setup.ts b/.gemini/skills/offload/scripts/setup.ts index 60a892774e..311e6a5d8a 100644 --- a/.gemini/skills/offload/scripts/setup.ts +++ b/.gemini/skills/offload/scripts/setup.ts @@ -139,15 +139,23 @@ Host ${sshAlias} if (fs.existsSync(lp)) { spawnSync(`${rsyncBase} ${lp} ${remoteHost}:~/.gemini/google_accounts.json`, { shell: true }); } - } + // 4. Scoped Token Onboarding if (await confirm('Generate a scoped, secure token for the autonomous agent? (Recommended)')) { - const magicLink = `https://github.com/settings/tokens/beta/new?description=Offload-${env.USER}&repositories[]=${encodeURIComponent(upstreamRepo)}&repositories[]=${encodeURIComponent(userFork)}&permissions[contents]=write&permissions[pull_requests]=write&permissions[metadata]=read`; - + // Correct URL for Fine-Grained PAT (Beta) + const baseUrl = 'https://github.com/settings/personal-access-tokens/new'; + const name = `Offload-${env.USER}`; + const magicLink = `${baseUrl}?name=${encodeURIComponent(name)}&description=Gemini+CLI+Offload+Worker&contents=write&pull_requests=write&metadata=read`; + console.log('\nšŸ” SECURITY: Create a token using the link below:'); - // Print the link as a single, clean line for maximum clickability - console.log(`\n\x1b[1;34m${magicLink}\x1b[0m\n`); - - const scopedToken = await prompt('Paste Scoped Token', ''); + console.log('\n' + magicLink + '\n'); + console.log('šŸ‘‰ INSTRUCTIONS:'); + console.log('1. Click the link above.'); + console.log('2. Under "Repository access", select "Only select repositories".'); + console.log(`3. Select "${upstreamRepo}" and "${userFork}".`); + console.log('4. Click "Generate token" at the bottom.'); + + const scopedToken = await prompt('\nPaste Scoped Token', ''); + if (scopedToken) { spawnSync(`ssh ${remoteHost} "mkdir -p ~/.offload && echo ${scopedToken} > ~/.offload/.gh_token && chmod 600 ~/.offload/.gh_token"`, { shell: true }); }