garc-gws-agent-runtime/lib/bootstrap.sh
林 駿甫 (Shunsuke Hayashi) a69b9d9160 feat: initial release — GARC v0.1.0
Permission-first AI agent runtime for Google Workspace.
Ports the LARC/OpenClaw governance model (disclosure chain,
execution gates, queue/ingress) to Gmail, Calendar, Drive,
Sheets, Tasks, and People APIs with Claude Code as the
execution engine.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 08:59:12 +09:00

272 lines
7.9 KiB
Bash

#!/usr/bin/env bash
# GARC bootstrap.sh — Disclosure chain loading from Google Drive
# Mirrors LARC's bootstrap.sh but uses Google Drive instead of Lark Drive
GARC_DISCLOSURE_FILES=("SOUL.md" "USER.md" "MEMORY.md" "RULES.md" "HEARTBEAT.md")
garc_init() {
echo "GARC v0.1.0 — Initializing workspace..."
local config_dir="${HOME}/.garc"
mkdir -p "${config_dir}/cache/workspace"
# Check for config file
if [[ ! -f "${config_dir}/config.env" ]]; then
echo "Config not found. Creating from example..."
cp "${GARC_DIR}/config/config.env.example" "${config_dir}/config.env"
echo "✅ Created ${config_dir}/config.env"
echo ""
echo "Next steps:"
echo " 1. Edit ~/.garc/config.env with your Google Drive folder ID and Sheets ID"
echo " 2. Download OAuth2 credentials.json from Google Cloud Console"
echo " 3. Save to ~/.garc/credentials.json"
echo " 4. Run: garc auth login --profile backoffice_agent"
echo " 5. Run: garc bootstrap --agent main"
return 0
fi
# Validate required config
source "${config_dir}/config.env"
local missing=()
[[ -z "${GARC_DRIVE_FOLDER_ID:-}" ]] && missing+=("GARC_DRIVE_FOLDER_ID")
[[ -z "${GARC_SHEETS_ID:-}" ]] && missing+=("GARC_SHEETS_ID")
if [[ ${#missing[@]} -gt 0 ]]; then
echo "⚠️ Missing required config values in ~/.garc/config.env:"
for key in "${missing[@]}"; do
echo " - ${key}"
done
return 1
fi
echo "✅ Config OK"
echo " Drive folder: ${GARC_DRIVE_FOLDER_ID}"
echo " Sheets ID: ${GARC_SHEETS_ID}"
# Verify auth token
if [[ -f "${GARC_TOKEN_FILE:-${config_dir}/token.json}" ]]; then
echo "✅ Auth token present"
else
echo "⚠️ No auth token. Run: garc auth login --profile backoffice_agent"
fi
echo ""
echo "Workspace initialized. Run 'garc bootstrap --agent main' to load context."
}
garc_bootstrap() {
local agent_id="${GARC_DEFAULT_AGENT:-main}"
while [[ $# -gt 0 ]]; do
case "$1" in
--agent) agent_id="$2"; shift 2 ;;
*) shift ;;
esac
done
echo "Bootstrapping agent '${agent_id}' from Google Drive..."
local workspace_dir="${GARC_CACHE_DIR}/workspace/${agent_id}"
mkdir -p "${workspace_dir}/memory"
local drive_folder="${GARC_DRIVE_FOLDER_ID:-}"
if [[ -z "${drive_folder}" ]]; then
echo "Error: GARC_DRIVE_FOLDER_ID not set in ~/.garc/config.env" >&2
return 1
fi
# Download disclosure chain files from Google Drive
echo "Downloading disclosure chain from Drive folder: ${drive_folder}"
for filename in "${GARC_DISCLOSURE_FILES[@]}"; do
local local_path="${workspace_dir}/${filename}"
echo -n " ${filename}... "
if python3 "${GARC_DIR}/scripts/garc-drive-helper.py" download \
--folder-id "${drive_folder}" \
--filename "${filename}" \
--output "${local_path}" 2>/dev/null; then
echo "✅"
else
echo "⚠️ (not found, creating placeholder)"
_garc_create_placeholder "${local_path}" "${filename}" "${agent_id}"
fi
done
# Download today's daily memory if present
local today
today=$(date +%Y-%m-%d)
local daily_memory="${workspace_dir}/memory/${today}.md"
echo -n " memory/${today}.md... "
if python3 "${GARC_DIR}/scripts/garc-drive-helper.py" download \
--folder-id "${drive_folder}" \
--filename "memory/${today}.md" \
--output "${daily_memory}" 2>/dev/null; then
echo "✅"
else
echo "(none)"
fi
# Build consolidated AGENT_CONTEXT.md
_garc_build_context "${workspace_dir}" "${agent_id}"
echo ""
echo "✅ Bootstrap complete."
echo " Context: ${workspace_dir}/AGENT_CONTEXT.md"
echo ""
cat "${workspace_dir}/AGENT_CONTEXT.md" | head -20
echo " [... $(wc -l < "${workspace_dir}/AGENT_CONTEXT.md") lines total]"
}
_garc_create_placeholder() {
local filepath="$1"
local filename="$2"
local agent_id="$3"
case "${filename}" in
SOUL.md)
cat > "${filepath}" <<EOF
# SOUL — Agent Identity
agent_id: ${agent_id}
platform: Google Workspace
runtime: GARC v0.1.0
## Core Principles
- Permission-first: always check scopes before acting
- Transparency: explain actions before executing
- Gate compliance: respect none/preview/approval tiers
## Created
$(date -u +"%Y-%m-%dT%H:%M:%SZ") (placeholder — upload your SOUL.md to Google Drive)
EOF
;;
USER.md)
cat > "${filepath}" <<EOF
# USER — User Profile
## Identity
name: (set in Google Drive)
email: ${GARC_GMAIL_DEFAULT_TO:-not set}
## Preferences
language: Japanese / English
timezone: Asia/Tokyo
## Created
$(date -u +"%Y-%m-%dT%H:%M:%SZ") (placeholder — upload your USER.md to Google Drive)
EOF
;;
MEMORY.md)
cat > "${filepath}" <<EOF
# MEMORY — Long-term Memory Index
Last sync: $(date -u +"%Y-%m-%dT%H:%M:%SZ")
Backend: Google Sheets (${GARC_SHEETS_ID:-not configured})
## Recent entries
(pull from Sheets with: garc memory pull)
EOF
;;
RULES.md)
cat > "${filepath}" <<EOF
# RULES — Operating Rules
1. Always run 'garc auth suggest' before new task categories
2. Respect execution gate policies (none/preview/approval)
3. Confirm with user before preview-gated operations
4. Wait for approval before approval-gated operations
5. Log all actions to heartbeat table
EOF
;;
HEARTBEAT.md)
cat > "${filepath}" <<EOF
# HEARTBEAT — System State
agent_id: ${agent_id}
last_bootstrap: $(date -u +"%Y-%m-%dT%H:%M:%SZ")
status: initialized
platform: Google Workspace
sheets_id: ${GARC_SHEETS_ID:-not configured}
EOF
;;
esac
}
_garc_build_context() {
local workspace_dir="$1"
local agent_id="$2"
local context_file="${workspace_dir}/AGENT_CONTEXT.md"
cat > "${context_file}" <<EOF
# AGENT_CONTEXT — ${agent_id}
# Built: $(date -u +"%Y-%m-%dT%H:%M:%SZ")
# Source: Google Drive folder ${GARC_DRIVE_FOLDER_ID:-unknown}
# ════════════════════════════════════════════════════════
EOF
for filename in "${GARC_DISCLOSURE_FILES[@]}"; do
local filepath="${workspace_dir}/${filename}"
if [[ -f "${filepath}" ]]; then
echo "## ${filename}" >> "${context_file}"
echo "" >> "${context_file}"
cat "${filepath}" >> "${context_file}"
echo "" >> "${context_file}"
echo "---" >> "${context_file}"
echo "" >> "${context_file}"
fi
done
# Append daily memory if present
local today
today=$(date +%Y-%m-%d)
local daily_memory="${workspace_dir}/memory/${today}.md"
if [[ -f "${daily_memory}" ]]; then
echo "## Daily Memory (${today})" >> "${context_file}"
echo "" >> "${context_file}"
cat "${daily_memory}" >> "${context_file}"
echo "" >> "${context_file}"
fi
}
garc_status() {
echo "GARC Status"
echo "==========="
local config_file="${HOME}/.garc/config.env"
if [[ -f "${config_file}" ]]; then
source "${config_file}"
echo "Config: ✅ ${config_file}"
echo " Drive folder: ${GARC_DRIVE_FOLDER_ID:-❌ not set}"
echo " Sheets ID: ${GARC_SHEETS_ID:-❌ not set}"
echo " Gmail to: ${GARC_GMAIL_DEFAULT_TO:-❌ not set}"
echo " Calendar: ${GARC_CALENDAR_ID:-primary}"
else
echo "Config: ❌ not found (run: garc init)"
return 1
fi
local token_file="${GARC_TOKEN_FILE:-${HOME}/.garc/token.json}"
if [[ -f "${token_file}" ]]; then
echo "Auth token: ✅ ${token_file}"
else
echo "Auth token: ❌ not found (run: garc auth login)"
fi
local creds_file="${GARC_CREDENTIALS_FILE:-${HOME}/.garc/credentials.json}"
if [[ -f "${creds_file}" ]]; then
echo "Credentials: ✅ ${creds_file}"
else
echo "Credentials: ❌ not found (download from Google Cloud Console)"
fi
local agent_id="${GARC_DEFAULT_AGENT:-main}"
local workspace_dir="${GARC_CACHE_DIR:-${HOME}/.garc/cache}/workspace/${agent_id}"
if [[ -f "${workspace_dir}/AGENT_CONTEXT.md" ]]; then
echo "Context: ✅ ${workspace_dir}/AGENT_CONTEXT.md"
else
echo "Context: ❌ not bootstrapped (run: garc bootstrap --agent ${agent_id})"
fi
}