mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-24 21:10:43 -07:00
feat(workspaces): implement unified scripts/workspaces.ts entry point and simplify commands
This commit is contained in:
@@ -1,51 +1,75 @@
|
||||
# Gemini Workspaces: Maintainer Onboarding
|
||||
|
||||
Gemini Workspaces allow you to offload heavy tasks (PR reviews, agentic fixes, full builds) to a high-performance GCP worker. It uses a **Unified Data Disk** architecture to ensure your work persists even if the VM is deleted or recreated.
|
||||
Gemini Workspaces allow you to offload heavy tasks (PR reviews, agentic fixes,
|
||||
full builds) to a high-performance GCP worker. It uses a **Unified Data Disk**
|
||||
architecture to ensure your work persists even if the VM is deleted or
|
||||
recreated.
|
||||
|
||||
## 1. Local Prerequisites
|
||||
|
||||
Before starting, ensure you have:
|
||||
* **GCloud CLI**: Authenticated (`gcloud auth login`).
|
||||
* **GitHub CLI**: Authenticated (`gh auth login`).
|
||||
* **Project Access**: A GCP Project ID where you have `Editor` or `Compute Admin` roles.
|
||||
|
||||
- **GCloud CLI**: Authenticated (`gcloud auth login`).
|
||||
- **GitHub CLI**: Authenticated (`gh auth login`).
|
||||
- **Project Access**: A GCP Project ID where you have `Editor` or
|
||||
`Compute Admin` roles.
|
||||
|
||||
## 2. Initialization
|
||||
Run the setup script using `npx tsx` to ensure all dependencies are available:
|
||||
|
||||
Run the setup script using the unified workspaces entry point:
|
||||
|
||||
```bash
|
||||
npx tsx .gemini/skills/workspaces/scripts/setup.ts
|
||||
npx tsx scripts/workspaces.ts setup
|
||||
```
|
||||
|
||||
**What happens during setup:**
|
||||
1. **Auth Discovery**: It will detect your `GEMINI_API_KEY` (from `~/.env`) and `GH_TOKEN`.
|
||||
|
||||
1. **Auth Discovery**: It will detect your `GEMINI_API_KEY` (from `~/.env`) and
|
||||
`GH_TOKEN`.
|
||||
2. **Project Choice**: You will be prompted for your GCP Project and Zone.
|
||||
3. **Infrastructure Check**: It verifies if your worker (`gcli-workspace-<user>`) exists.
|
||||
4. **SSH Magic**: It generates a local `.gemini/workspaces/ssh_config` for seamless access.
|
||||
3. **Infrastructure Check**: It verifies if your worker
|
||||
(`gcli-workspace-<user>`) exists.
|
||||
4. **SSH Magic**: It generates a local `.gemini/workspaces/ssh_config` for
|
||||
seamless access.
|
||||
|
||||
## 3. Provisioning
|
||||
|
||||
If the setup informs you that the worker was not found, provision it:
|
||||
|
||||
```bash
|
||||
npx tsx .gemini/skills/workspaces/scripts/fleet.ts provision
|
||||
npx tsx scripts/workspaces.ts fleet provision
|
||||
```
|
||||
*This creates a VM with a 10GB Boot Disk and a 200GB Data Disk. Initialization takes ~1 minute.*
|
||||
|
||||
_This creates a VM with a 10GB Boot Disk and a 200GB Data Disk. Initialization
|
||||
takes ~1 minute._
|
||||
|
||||
## 4. Finalizing Remote Setup
|
||||
|
||||
Run the setup script one last time to clone the repo and sync credentials:
|
||||
|
||||
```bash
|
||||
npx tsx .gemini/skills/workspaces/scripts/setup.ts
|
||||
npx tsx scripts/workspaces.ts setup
|
||||
```
|
||||
*When you see "ALL SYSTEMS GO!", your workspace is ready.*
|
||||
|
||||
_When you see "ALL SYSTEMS GO!", your workspace is ready._
|
||||
|
||||
## 5. Daily Usage
|
||||
Once initialized, you can launch tasks directly through `npm`:
|
||||
|
||||
* **Review a PR**: `npm run workspace <PR_NUMBER> review`
|
||||
* **Fix a PR**: `npm run workspace <PR_NUMBER> fix`
|
||||
* **Check Status**: `npx tsx .gemini/skills/workspaces/scripts/status.ts`
|
||||
* **Stop Worker**: `npx tsx .gemini/skills/workspaces/scripts/fleet.ts stop` (Recommended when finished to save cost).
|
||||
Once initialized, you can launch tasks directly through `npm` or the entry
|
||||
point:
|
||||
|
||||
- **Review a PR**: `npm run workspace <PR_NUMBER> review`
|
||||
- **Launch a Shell**: `npm run workspace:shell <ID>`
|
||||
- **Check Status**: `npm run workspace:status`
|
||||
- **Cleanup All**: `npm run workspace:clean-all`
|
||||
- **Kill Task**: `npm run workspace:kill <PR> <action>`
|
||||
- **Stop Worker**: `npx tsx scripts/workspaces.ts fleet stop` (Recommended when
|
||||
finished to save cost).
|
||||
|
||||
## Troubleshooting
|
||||
* **Permission Denied (Docker)**: The orchestrator handles this by using `sudo docker` internally.
|
||||
* **Dubious Ownership**: The system automatically adds `/mnt/disks/data/main` to Git's safe directory list.
|
||||
* **Missing tsx**: Always prefer `npx tsx` when running scripts manually.
|
||||
|
||||
- **Permission Denied (Docker)**: The orchestrator handles this by using
|
||||
`sudo docker` internally.
|
||||
- **Dubious Ownership**: The system automatically adds `/mnt/disks/data/main` to
|
||||
Git's safe directory list.
|
||||
- **Missing tsx**: Always prefer `npx tsx` when running scripts manually.
|
||||
|
||||
20
package.json
20
package.json
@@ -64,16 +64,16 @@
|
||||
"telemetry": "node scripts/telemetry.js",
|
||||
"check:lockfile": "node scripts/check-lockfile.js",
|
||||
"clean": "node scripts/clean.js",
|
||||
"workspace": "tsx .gemini/skills/workspaces/scripts/orchestrator.ts",
|
||||
"workspace:setup": "tsx .gemini/skills/workspaces/scripts/setup.ts",
|
||||
"workspace:shell": "tsx .gemini/skills/workspaces/scripts/orchestrator.ts shell",
|
||||
"workspace:check": "tsx .gemini/skills/workspaces/scripts/check.ts",
|
||||
"workspace:clean-all": "tsx .gemini/skills/workspaces/scripts/clean.ts",
|
||||
"workspace:kill": "tsx .gemini/skills/workspaces/scripts/clean.ts",
|
||||
"workspace:fleet": "tsx .gemini/skills/workspaces/scripts/fleet.ts",
|
||||
"workspace:status": "tsx .gemini/skills/workspaces/scripts/status.ts",
|
||||
"workspace:attach": "tsx .gemini/skills/workspaces/scripts/attach.ts",
|
||||
"workspace:logs": "tsx .gemini/skills/workspaces/scripts/logs.ts",
|
||||
"workspace": "tsx ./scripts/workspaces.ts",
|
||||
"workspace:setup": "tsx ./scripts/workspaces.ts setup",
|
||||
"workspace:shell": "tsx ./scripts/workspaces.ts shell",
|
||||
"workspace:check": "tsx ./scripts/workspaces.ts check",
|
||||
"workspace:clean-all": "tsx ./scripts/workspaces.ts clean-all",
|
||||
"workspace:kill": "tsx ./scripts/workspaces.ts kill",
|
||||
"workspace:fleet": "tsx ./scripts/workspaces.ts fleet",
|
||||
"workspace:status": "tsx ./scripts/workspaces.ts status",
|
||||
"workspace:attach": "tsx ./scripts/workspaces.ts attach",
|
||||
"workspace:logs": "tsx ./scripts/workspaces.ts logs",
|
||||
"pre-commit": "node scripts/pre-commit.js"
|
||||
},
|
||||
"overrides": {
|
||||
|
||||
79
scripts/workspaces.ts
Executable file
79
scripts/workspaces.ts
Executable file
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env npx tsx
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const REPO_ROOT = path.resolve(__dirname, '..');
|
||||
|
||||
const commands: Record<string, string> = {
|
||||
setup: '.gemini/skills/workspaces/scripts/setup.ts',
|
||||
shell: '.gemini/skills/workspaces/scripts/orchestrator.ts shell',
|
||||
check: '.gemini/skills/workspaces/scripts/check.ts',
|
||||
'clean-all': '.gemini/skills/workspaces/scripts/clean.ts',
|
||||
kill: '.gemini/skills/workspaces/scripts/clean.ts',
|
||||
fleet: '.gemini/skills/workspaces/scripts/fleet.ts',
|
||||
status: '.gemini/skills/workspaces/scripts/status.ts',
|
||||
attach: '.gemini/skills/workspaces/scripts/attach.ts',
|
||||
logs: '.gemini/skills/workspaces/scripts/logs.ts',
|
||||
};
|
||||
|
||||
function printUsage() {
|
||||
console.log('Gemini Workspaces Management CLI');
|
||||
console.log(
|
||||
'\nUsage: scripts/workspaces.ts <command> [args] [--open foreground|tab|window]',
|
||||
);
|
||||
console.log('\nCommands:');
|
||||
console.log(
|
||||
' setup Initialize or reconfigure your remote worker',
|
||||
);
|
||||
console.log(' <pr-number> [action] Launch a PR task (review, fix, ready)');
|
||||
console.log(' shell [id] Open an ad-hoc interactive session');
|
||||
console.log(' status See worker and session overview');
|
||||
console.log(' check <pr-number> Deep-dive into PR logs');
|
||||
console.log(' kill <pr-number> <act> Surgical removal of a task');
|
||||
console.log(' clean-all Full remote cleanup');
|
||||
console.log(' fleet <action> Manage VM life cycle (stop, provision)');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const args = process.argv.slice(2);
|
||||
const cmd = args[0];
|
||||
|
||||
if (!cmd || cmd === '--help' || cmd === '-h') {
|
||||
printUsage();
|
||||
}
|
||||
|
||||
let scriptPath = commands[cmd];
|
||||
let finalArgs = args.slice(1);
|
||||
|
||||
// Default: If it's a number, it's a PR orchestrator task
|
||||
if (!scriptPath && /^\d+$/.test(cmd)) {
|
||||
scriptPath = '.gemini/skills/workspaces/scripts/orchestrator.ts';
|
||||
finalArgs = args; // Pass the PR number as the first arg
|
||||
}
|
||||
|
||||
if (!scriptPath) {
|
||||
console.error(`❌ Unknown command: ${cmd}`);
|
||||
printUsage();
|
||||
}
|
||||
|
||||
const [realScript, ...internalArgs] = scriptPath.split(' ');
|
||||
const fullScriptPath = path.join(REPO_ROOT, realScript);
|
||||
|
||||
const result = spawnSync(
|
||||
'npx',
|
||||
['tsx', fullScriptPath, ...internalArgs, ...finalArgs],
|
||||
{ stdio: 'inherit' },
|
||||
);
|
||||
|
||||
process.exit(result.status ?? 0);
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
Reference in New Issue
Block a user