mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-06-10 19:37:17 -07:00
7bc0515c7c
- 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
175 lines
4.8 KiB
Bash
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) ==="
|