app
Gallia Bundle
UnreviewedAdom platform bundle for Hydrogen Desktop. Built by adom-inc/gallia-hd-bundle from upstream adom-inc/gallia — strips legacy-container cleanup, drops bootstrap.sh, ships 117 skills + cleaned-down insta
#!/usr/bin/env bash
# ──────────────────────────────────────────────────────────────
# Adom Container Bootstrap
#
# Sets up a fresh Adom container with Claude Code + Gallia.
#
# Full install (all workspaces, ~233MB node_modules):
# bash <(curl -fsSL <gallia-host>/bootstrap.sh)
#
# Service container (only server + named workspace, ~12MB node_modules):
# bash <(curl -fsSL <gallia-host>/bootstrap.sh) --service jlcpcb
# bash <(curl -fsSL <gallia-host>/bootstrap.sh) --service digikey
# (Mouser moved out of gallia — installed separately as adom-mouser CLI.)
# (For phone remote control, install adom-rc separately — see github.com/adom-inc/adom-rc.)
#
# Serve from any existing gallia container:
# cd ~/gallia/scripts && python3 -m http.server 9999 --bind 0.0.0.0 &
# ──────────────────────────────────────────────────────────────
set -euo pipefail
# ── Parse arguments ──────────────────────────────────────────
# --service <name> Only install the server + named workspace (e.g. jlcpcb, digikey)
# Saves ~200MB by skipping unrelated workspaces (viewer, etc.)
SERVICE_NAME=""
while [[ $# -gt 0 ]]; do
case "$1" in
--service)
SERVICE_NAME="$2"
shift 2
;;
--no-squad)
# Legacy flag — squad has been removed from gallia. Accept silently for back-compat.
shift
;;
*)
shift
;;
esac
done
BOLD='\033[1m'
DIM='\033[2m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
CYAN='\033[0;36m'
NC='\033[0m'
header() { echo -e "\n${BOLD}${CYAN}══ $1 ══${NC}\n"; }
ok() { echo -e " ${GREEN}✓${NC} $1"; }
info() { echo -e " ${DIM}$1${NC}"; }
warn() { echo -e " ${YELLOW}⚠${NC} $1"; }
fail() { echo -e " ${RED}✗${NC} $1"; exit 1; }
ask() { echo -en " ${BOLD}$1${NC} "; }
SETTINGS_FILE="$HOME/.local/share/code-server/User/settings.json"
if [ -n "$SERVICE_NAME" ]; then
echo -e "\n${BOLD}${CYAN}Adom Bootstrap — service mode: ${SERVICE_NAME}${NC}"
else
echo -e "\n${BOLD}${CYAN}Adom Bootstrap — full install${NC}"
fi
# ── Phase 1: Prerequisites ──────────────────────────────────
header "Phase 1 · Prerequisites"
# 1a. Claude Code VS Code extension
if ls "$HOME/.local/share/code-server/extensions/anthropic.claude-code-"* &>/dev/null; then
ok "Claude Code extension already installed"
else
echo " Installing Claude Code extension..."
/usr/lib/code-server/bin/code-server --install-extension anthropic.claude-code 2>/dev/null \
&& ok "Claude Code extension installed" \
|| warn "Auto-install failed — install 'Claude Code' from the Extensions panel in VS Code"
fi
# 1b. Disable Copilot chat sidebar + configure Claude Code defaults
mkdir -p "$(dirname "$SETTINGS_FILE")"
if [ -f "$SETTINGS_FILE" ]; then
python3 -c "
import json, sys
with open('$SETTINGS_FILE') as f:
s = json.load(f)
changed = False
# Settings that MUST be set (overwrite even if already present)
forced = {
'github.copilot.chat.enabled': False,
'github.copilot.enable': {'*': False},
'chat.agent.enabled': False,
'chat.agentsControl.enabled': False,
'chat.unifiedAgentsBar.enabled': False,
'workbench.secondarySideBar.defaultVisibility': 'hidden',
'workbench.navigationControl.enabled': False,
'claudeCode.allowDangerouslySkipPermissions': True,
'claudeCode.initialPermissionMode': 'bypassPermissions',
# Hide the Python interpreter status-bar item (and its discovery spinner).
# In Adom, Claude resolves Python interpreters/envs itself; the persistent
# spinner in the user's peripheral vision is just visual noise.
'python.interpreter.infoVisibility': 'never',
'github.gitAuthentication': False,
'git.enableSmartCommit': False,
'git.autofetch': False,
}
for k, v in forced.items():
if s.get(k) != v:
s[k] = v
changed = True
# Settings that are defaults (only set if missing)
defaults = {
'workbench.secondarySideBar.visible': False,
'workbench.colorTheme': 'Default Dark Modern',
'claudeCode.preferredLocation': 'panel',
'claudeCode.selectedModel': 'opus',
}
for k, v in defaults.items():
if k not in s:
s[k] = v
changed = True
if changed:
with open('$SETTINGS_FILE', 'w') as f:
json.dump(s, f, indent=4)
print(' \033[0;32m✓\033[0m VS Code settings updated (Copilot disabled, Claude Code configured)')
else:
print(' \033[0;32m✓\033[0m VS Code settings already configured')
"
else
cat > "$SETTINGS_FILE" << 'SETTINGS'
{
"workbench.colorTheme": "Default Dark Modern",
"workbench.secondarySideBar.visible": false,
"workbench.secondarySideBar.defaultVisibility": "hidden",
"workbench.navigationControl.enabled": false,
"github.copilot.chat.enabled": false,
"github.copilot.enable": { "*": false },
"chat.agent.enabled": false,
"chat.agentsControl.enabled": false,
"chat.unifiedAgentsBar.enabled": false,
"claudeCode.preferredLocation": "panel",
"claudeCode.selectedModel": "opus",
"claudeCode.allowDangerouslySkipPermissions": true,
"claudeCode.initialPermissionMode": "bypassPermissions",
"python.interpreter.infoVisibility": "never",
"github.gitAuthentication": false,
"git.enableSmartCommit": false,
"git.autofetch": false
}
SETTINGS
ok "Created VS Code settings (Copilot disabled, Claude Code configured)"
fi
# 1c. Claude Code CLI
if command -v claude &>/dev/null; then
ok "Claude Code CLI installed ($(claude --version 2>/dev/null | head -1))"
else
echo " Installing Claude Code CLI..."
curl -fsSL https://claude.ai/install.sh | bash 2>/dev/null
export PATH="$HOME/.local/bin:$HOME/.claude/bin:$PATH"
if command -v claude &>/dev/null; then
ok "Claude Code CLI installed"
else
warn "Claude Code CLI install failed"
fi
fi
# Ensure ~/.local/bin is in PATH for future shells (needed for claude CLI)
if ! grep -q '/.local/bin' "$HOME/.bashrc" 2>/dev/null; then
echo 'export PATH="$HOME/.local/bin:$HOME/.claude/bin:$PATH"' >> "$HOME/.bashrc"
fi
if ! grep -q '/.local/bin' "$HOME/.profile" 2>/dev/null; then
echo 'export PATH="$HOME/.local/bin:$HOME/.claude/bin:$PATH"' >> "$HOME/.profile"
fi
# 1d. GitHub CLI
if command -v gh &>/dev/null; then
ok "GitHub CLI installed ($(gh --version 2>/dev/null | head -1 | sed 's/gh version /v/'))"
else
echo " Installing GitHub CLI..."
(type -p wget &>/dev/null || sudo apt install wget -y) \
&& sudo mkdir -p -m 755 /etc/apt/keyrings \
&& wget -qO- https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
&& sudo apt update -qq && sudo apt install gh -y -qq
ok "GitHub CLI installed"
fi
# ── Phase 2: Authentication ─────────────────────────────────
header "Phase 2 · Authentication"
# 2a. GitHub (optional — wiki fallback available)
GALLIA_FROM_WIKI=false
if gh auth status &>/dev/null 2>&1; then
ok "GitHub already authenticated ($(gh auth status 2>&1 | grep 'Logged in' | sed 's/.*as //' | head -1))"
if gh repo view adom-inc/gallia --json name &>/dev/null 2>&1; then
ok "Access to adom-inc/gallia confirmed"
else
warn "No GitHub access to adom-inc — will install gallia from Adom Wiki."
GALLIA_FROM_WIKI=true
fi
else
echo ""
ask "Do you have a GitHub account with adom-inc access? [y/N]"
read -r gh_answer
if [[ "$gh_answer" =~ ^[Yy] ]]; then
echo ""
echo " A browser tab will open — sign in and authorize the CLI."
echo ""
gh auth login -p https -h github.com -w
echo ""
ok "GitHub authenticated"
if gh repo view adom-inc/gallia --json name &>/dev/null 2>&1; then
ok "Access to adom-inc/gallia confirmed"
else
warn "No GitHub access to adom-inc — will install gallia from Adom Wiki."
GALLIA_FROM_WIKI=true
fi
else
info "Skipping GitHub — will install everything from Adom Wiki."
GALLIA_FROM_WIKI=true
fi
fi
# 2b. Claude Code
if [ -f "$HOME/.claude/.credentials.json" ] && python3 -c "
import json
with open('$HOME/.claude/.credentials.json') as f:
c = json.load(f)
assert 'claudeAiOauth' in c and c['claudeAiOauth'].get('accessToken')
" 2>/dev/null; then
ok "Claude Code already authenticated"
else
echo ""
echo " Claude Code authentication required."
echo ""
# Method 1: Use our PKCE auth script (fast, no extra install)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)"
AUTH_SCRIPT="${SCRIPT_DIR}/claude-auth.mjs"
# If gallia is already cloned, use the script from there
if [ ! -f "$AUTH_SCRIPT" ] && [ -f "$HOME/gallia/scripts/claude-auth.mjs" ]; then
AUTH_SCRIPT="$HOME/gallia/scripts/claude-auth.mjs"
fi
# If script not available yet (first run before clone), download it
if [ ! -f "$AUTH_SCRIPT" ]; then
AUTH_SCRIPT="/tmp/claude-auth.mjs"
echo " Downloading auth helper..."
if gh api repos/adom-inc/gallia/contents/scripts/claude-auth.mjs --jq '.content' 2>/dev/null | base64 -d > "$AUTH_SCRIPT" 2>/dev/null; then
true
elif curl -fsSL "https://raw.githubusercontent.com/adom-inc/gallia/main/scripts/claude-auth.mjs" -o "$AUTH_SCRIPT" 2>/dev/null; then
true
else
curl -fsSL "https://wiki-ufypy5dpx93o.adom.cloud/static/apps/gallia-bundle/claude-auth.mjs" -o "$AUTH_SCRIPT" 2>/dev/null || true
fi
fi
if [ -f "$AUTH_SCRIPT" ]; then
node "$AUTH_SCRIPT" < /dev/tty
fi
# Method 2: Fall back to Claude Code CLI if script didn't succeed
if [ ! -f "$HOME/.claude/.credentials.json" ] || ! python3 -c "
import json
with open('$HOME/.claude/.credentials.json') as f:
c = json.load(f)
assert 'claudeAiOauth' in c and c['claudeAiOauth'].get('accessToken')
" 2>/dev/null; then
warn "Auth script didn't succeed. Falling back to Claude Code CLI..."
if command -v claude &>/dev/null; then
echo " Select option 1 (Claude account), sign in, then exit with /exit or Ctrl+C."
echo ""
claude < /dev/tty
else
warn "Claude Code CLI not available. Authenticate later via the Claude Code panel in VS Code."
fi
fi
# Verify
if [ -f "$HOME/.claude/.credentials.json" ]; then
ok "Claude Code credentials found"
else
warn "Auth incomplete — authenticate via the Claude Code panel after VS Code reload"
fi
fi
# ── Phase 3: Install Adom ─────────────────────────────────
header "Phase 3 · Installing Adom"
if [ -d "$HOME/gallia" ]; then
echo " ~/gallia exists — pulling latest..."
if [ -d "$HOME/gallia/.git" ]; then
cd ~/gallia && git pull
else
echo " (wiki-installed, no git history — re-downloading)"
GALLIA_FROM_WIKI=true
fi
fi
if [ ! -d "$HOME/gallia" ] || [ "$GALLIA_FROM_WIKI" = "true" ]; then
if [ "$GALLIA_FROM_WIKI" = "true" ]; then
echo " Downloading gallia from Adom Wiki..."
WIKI_URL="https://wiki-ufypy5dpx93o.adom.cloud/static/apps"
curl -fsSL "$WIKI_URL/gallia-bundle/gallia.tar.gz" -o /tmp/gallia.tar.gz
rm -rf "$HOME/gallia"
mkdir -p "$HOME/gallia"
tar xzf /tmp/gallia.tar.gz -C "$HOME/gallia"
rm /tmp/gallia.tar.gz
ok "Installed gallia from wiki"
else
echo " Cloning adom-inc/gallia..."
gh repo clone adom-inc/gallia ~/gallia
fi
fi
cd ~/gallia
if [ -n "$SERVICE_NAME" ]; then
echo " Installing Node dependencies (server + viewer + $SERVICE_NAME only)..."
npm install --workspace=server --workspace=viewer --workspace="$SERVICE_NAME" --no-audit --no-fund 2>&1 | tail -1
ok "Installed server + viewer + $SERVICE_NAME workspaces (skipped 3D bundles, etc.)"
else
echo " Installing Node dependencies (all workspaces)..."
npm install --no-audit --no-fund 2>&1 | tail -1
fi
echo " Running Adom installer..."
node install.mjs --project ~/project
# Fix project directory permissions (created by root during container init)
# Without this, drag-drop and file creation in VS Code fails for user adom
for d in ~/project ~/project/plugin-content ~/project/project-content ~/project/user-content; do
if [ -d "$d" ] && [ "$(stat -c '%U' "$d" 2>/dev/null)" != "adom" ]; then
sudo chown -R adom:adom "$d" 2>/dev/null && true
fi
done
ok "Project directory permissions fixed"
# ── Phase 3b: Start Service (if --service) ────────────────
if [ -n "$SERVICE_NAME" ]; then
START_SCRIPT="$HOME/gallia/services/$SERVICE_NAME/start-${SERVICE_NAME}.sh"
if [ -f "$START_SCRIPT" ]; then
header "Phase 3b · Starting $SERVICE_NAME service"
bash "$START_SCRIPT"
sleep 2
# Auto-detect port from server.js (looks for PORT = ... || '8775' pattern)
SERVICE_PORT=""
SERVICE_SERVER="$HOME/gallia/services/$SERVICE_NAME/server.js"
if [ -f "$SERVICE_SERVER" ]; then
SERVICE_PORT="$(grep -oP "'\K\d{4,5}(?=')" "$SERVICE_SERVER" | head -1)"
fi
if [ -n "$SERVICE_PORT" ] && curl -sf --max-time 2 "http://127.0.0.1:${SERVICE_PORT}/health" > /dev/null 2>&1; then
ok "$SERVICE_NAME service running on port $SERVICE_PORT"
elif [ -n "$SERVICE_PORT" ]; then
warn "$SERVICE_NAME service may not be running — check ~/gallia/services/$SERVICE_NAME/server.log"
fi
else
info "No start script found at $START_SCRIPT — start the service manually"
fi
fi
# Phase 4 (Claude Squad) was removed. RC-from-phone now lives in adom-rc:
# https://github.com/adom-inc/adom-rc
# ── Done ─────────────────────────────────────────────────────
header "Bootstrap Complete"
# Get viewer URL
VIEWER_URL=""
if [ -n "${VSCODE_PROXY_URI:-}" ]; then
VIEWER_URL="$(echo "$VSCODE_PROXY_URI" | sed 's/{{port}}/8770/')"
fi
echo -e " ${BOLD}Refresh your browser tab${NC} to reload VS Code and connect everything."
echo ""
echo " Then open ${BOLD}Claude Code${NC} by clicking the orange Claude logo"
echo " on top/right of any document panel and start building!"
echo ""
if [ -n "$VIEWER_URL" ]; then
echo " Adom Viewer URL (for the Adom app Web View panel):"
echo " ${VIEWER_URL}"
echo ""
fi
echo " Install the desktop app for KiCad/Fusion 360 control, shell access,"
echo " screenshots, USB device management, and more:"
echo " https://github.com/adom-inc/gallia/releases"
echo ""
echo -e " To use the claude CLI, open a new terminal and type ${BOLD}claude${NC}."
echo ""
echo -e " ${GREEN}${BOLD}Welcome to Adom!${NC}"
echo ""