Files
gemini-cli/scripts/gce-startup.sh
T
Adam Weidman 7bc0515c7c feat: GCE forever agent with Cloud Run bridge support
- Add gce-startup.sh: self-contained VM startup (install, build, configure, run)
- Add start-forever.js: dual-process launcher for local development
- Add npm scripts: start:forever, start:bridge
- Bind A2A listener to 0.0.0.0 (was 127.0.0.1) for remote bridge access
- Pre-configure all headless settings to bypass interactive dialogs
- Use .env file + script pseudo-TTY for systemd service
2026-03-03 23:45:17 -05:00

175 lines
4.8 KiB
Bash

#!/usr/bin/env bash
# GCE startup script — runs on VM boot.
# Installs Node.js, clones, builds, starts forever agent + bridge.
# Pass gemini-api-key via instance metadata.
set -euo pipefail
LOG="/var/log/forever-agent-startup.log"
exec > >(tee -a "$LOG") 2>&1
echo "=== Forever Agent startup $(date) ==="
# Read API key from instance metadata
GEMINI_API_KEY=$(curl -sf -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/attributes/gemini-api-key" || echo "")
CHAT_PROJECT_NUMBER=$(curl -sf -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/attributes/chat-project-number" || echo "")
if [ -z "$GEMINI_API_KEY" ]; then
echo "ERROR: gemini-api-key not set in instance metadata"
exit 1
fi
echo "API key loaded (${#GEMINI_API_KEY} chars)"
# Install Node.js + screen (first boot only)
if ! command -v node &>/dev/null; then
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt-get install -y nodejs git screen
fi
echo "Node $(node --version)"
# Clone/update repo
REPO_DIR="/opt/forever-agent"
if [ ! -d "$REPO_DIR" ]; then
GIT_TERMINAL_PROMPT=0 git clone -b afw/forever-gchat \
https://github.com/google-gemini/gemini-cli.git "$REPO_DIR"
else
cd "$REPO_DIR" && GIT_TERMINAL_PROMPT=0 git pull --ff-only || true
fi
# npm install && npm run build
cd "$REPO_DIR"
npm install 2>&1 | tail -5
npm run build 2>&1 | tail -10
# Pre-create workspace and config to skip interactive prompts
WORK_DIR="/opt/forever-workspace"
mkdir -p "$WORK_DIR/.gemini"
mkdir -p /root/.gemini
# Pre-trust the workspace folder (skips "Do you trust this folder?" dialog)
# --yolo doesn't bypass folder trust, so we must pre-configure it
cat > /root/.gemini/trustedFolders.json << 'TRUSTEOF'
{
"/opt/forever-workspace": "TRUST_FOLDER"
}
TRUSTEOF
# Disable folder trust + skip session retention dialog
# Write to both user-level AND workspace-level to be safe
for SETTINGS_DIR in /root/.gemini "$WORK_DIR/.gemini"; do
mkdir -p "$SETTINGS_DIR"
cat > "$SETTINGS_DIR/settings.json" << 'SETTINGSEOF'
{
"security": {
"folderTrust": {
"enabled": false
},
"auth": {
"selectedType": "gemini-api-key",
"useExternal": true
}
},
"general": {
"sessionRetention": {
"enabled": true,
"maxAge": "30d",
"warningAcknowledged": true
}
}
}
SETTINGSEOF
done
cat > "$WORK_DIR/.gemini/GEMINI.md" << 'GEMINIEOF'
---
sisyphus:
enabled: true
idleTimeout: 30
prompt: "continue with the next task"
---
# Mission
You are a forever-running autonomous agent accessible via Google Chat.
Process incoming tasks, answer questions, and proactively work on improvements.
GEMINIEOF
# Create systemd service for the chat bridge
cat > /etc/systemd/system/chat-bridge.service << EOF
[Unit]
Description=Google Chat Bridge
After=network.target
[Service]
Type=simple
Environment=GOOGLE_API_KEY=${GEMINI_API_KEY}
Environment=CHAT_PROJECT_NUMBER=${CHAT_PROJECT_NUMBER}
Environment=A2A_PORT=3100
Environment=BRIDGE_PORT=8081
Environment=A2A_URL=http://127.0.0.1:3100
Environment=GIT_TERMINAL_PROMPT=0
WorkingDirectory=${REPO_DIR}
ExecStart=/usr/bin/node ${REPO_DIR}/packages/a2a-server/dist/src/chat-bridge/bridge.js
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
# Write env vars to a file — sourced by the wrapper script
cat > /etc/forever-agent.env << ENVEOF
export HOME=/root
export GEMINI_CLI_HOME=/root
export GOOGLE_API_KEY='${GEMINI_API_KEY}'
export GEMINI_API_KEY='${GEMINI_API_KEY}'
export A2A_PORT=3100
export GIT_TERMINAL_PROMPT=0
ENVEOF
chmod 600 /etc/forever-agent.env
# Create a wrapper script that sources env and uses 'script' for pseudo-TTY
cat > /usr/local/bin/start-forever-agent.sh << 'WRAPPEREOF'
#!/usr/bin/env bash
set -a
source /etc/forever-agent.env
set +a
# script provides a pseudo-TTY for Ink; output goes to /dev/null (TUI noise)
exec /usr/bin/script -qfc "node /opt/forever-agent/packages/cli/dist/index.js --forever --a2a-port 3100 --yolo" /dev/null
WRAPPEREOF
chmod +x /usr/local/bin/start-forever-agent.sh
# Create systemd service for the forever agent
cat > /etc/systemd/system/forever-agent.service << EOF
[Unit]
Description=Gemini CLI Forever Agent
After=network.target chat-bridge.service
[Service]
Type=simple
Environment=GOOGLE_API_KEY=${GEMINI_API_KEY}
Environment=A2A_PORT=3100
Environment=GIT_TERMINAL_PROMPT=0
Environment=HOME=/root
Environment=GEMINI_CLI_HOME=/root
WorkingDirectory=${WORK_DIR}
ExecStart=/usr/local/bin/start-forever-agent.sh
Restart=on-failure
RestartSec=10
StandardOutput=null
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
# Start both services
systemctl daemon-reload
systemctl enable chat-bridge forever-agent
systemctl start chat-bridge
sleep 2
systemctl start forever-agent
echo "=== Chat bridge started on port 8081 ==="
echo "=== Forever agent started in screen session ==="
echo "=== Startup complete $(date) ==="