app
Adom Desktop
UnreviewedLaptop bridge: screenshots, file transfer, notifications, KiCad + Fusion 360 control, real-Chrome (pup) automation. One install gives Claude the main + pup + kicad + fusion skills.
#!/bin/bash
# Hydrogen Desktop Bootstrap Script
# HD downloads this from the wiki and runs it via:
# docker exec -u adom hydrogen-workspace bash /tmp/hd-bootstrap.sh
#
# Each step prints a structured line that HD's frontend parses:
# STEP|<status>|<command>|<message>
# status: run|done|skip|fail
# HD renders these as a terminal-style log in the Bootstrap panel.
#
# Order matters — install everything FIRST, reload ONCE, then activate.
set -euo pipefail
CODESERVER="/usr/lib/code-server/bin/code-server"
HD_HOST="host.docker.internal"
step() {
local status="$1" cmd="$2" msg="$3"
echo "STEP|${status}|${cmd}|${msg}"
}
# ── 1. adom-vscode binary (needed before extension install) ──
if ! command -v adom-vscode &>/dev/null; then
step run "curl -fsSL .../adom-vscode -o /usr/local/bin/adom-vscode" "downloading binary..."
curl -fsSL "https://wiki-ufypy5dpx93o.adom.cloud/static/apps/adom-vscode/adom-vscode" -o /tmp/adom-vscode-dl
chmod +x /tmp/adom-vscode-dl
sudo mv /tmp/adom-vscode-dl /usr/local/bin/adom-vscode 2>/dev/null || mv /tmp/adom-vscode-dl /usr/local/bin/adom-vscode
step done "curl -fsSL .../adom-vscode -o /usr/local/bin/adom-vscode" "binary installed"
else
step skip "which adom-vscode" "binary already installed"
fi
# ── 2. adom-vscode VS Code extension + skill ──
step run "adom-vscode install" "installing extension + skill..."
/usr/local/bin/adom-vscode install 2>/dev/null || true
step done "adom-vscode install" "extension + skill deployed"
# ── 3. adom-vscode extension in code-server (the install command may use wrong path) ──
if ! $CODESERVER --list-extensions 2>/dev/null | grep -q "adom.adom-vscode"; then
step run "$CODESERVER --install-extension /tmp/adom-vscode-0.1.0.vsix" "registering extension in code-server..."
$CODESERVER --install-extension /tmp/adom-vscode-0.1.0.vsix 2>/dev/null || true
step done "$CODESERVER --install-extension /tmp/adom-vscode-0.1.0.vsix" "registered"
else
step skip "$CODESERVER --list-extensions | grep adom" "already registered"
fi
# ── 4. Claude Code Extension ──
if $CODESERVER --list-extensions 2>/dev/null | grep -q "anthropic.claude-code"; then
step skip "$CODESERVER --list-extensions | grep claude-code" "already installed"
else
step run "$CODESERVER --install-extension anthropic.claude-code" "installing Claude Code..."
$CODESERVER --install-extension anthropic.claude-code 2>/dev/null || true
step done "$CODESERVER --install-extension anthropic.claude-code" "installed"
fi
# ── 5. Claude Code CLI ──
if command -v claude &>/dev/null; then
step skip "which claude" "CLI already installed"
else
step run "curl -fsSL https://claude.ai/install.sh | bash" "installing Claude CLI..."
curl -fsSL https://claude.ai/install.sh -o /tmp/claude-install.sh 2>/dev/null
bash /tmp/claude-install.sh 2>/dev/null || true
step done "curl -fsSL https://claude.ai/install.sh | bash" "CLI installed"
fi
# ── 6. Gallia Platform ──
if [ -f "$HOME/.claude/skills/adom/SKILL.md" ]; then
step skip "test -f ~/.claude/skills/adom/SKILL.md" "gallia already installed"
else
step run "curl -fsSL .../gallia-bundle.tar.gz | tar xz" "downloading gallia..."
curl -fsSL "https://wiki-ufypy5dpx93o.adom.cloud/static/apps/gallia-bundle/gallia-bundle.tar.gz" -o /tmp/gallia-bundle.tar.gz
tar xzf /tmp/gallia-bundle.tar.gz -C "$HOME/"
step done "curl -fsSL .../gallia-bundle.tar.gz | tar xz" "extracted"
step run "cd gallia && npm install && node install.mjs" "deploying skills..."
cd "$HOME/gallia" && npm install --no-audit --no-fund 2>&1 | tail -3
if node "$HOME/gallia/install.mjs" --project "$HOME/project" 2>&1 | tail -5; then
step done "cd gallia && npm install && node install.mjs" "skills deployed"
else
step fail "cd gallia && npm install && node install.mjs" "install.mjs failed (non-fatal)"
fi
fi
# ── 6b. Set ADOM_CARBON_URL to route adom-cli workspace commands to HD ──
CARBON_LINE="export ADOM_CARBON_URL=http://host.docker.internal:9000"
HYDROGEN_LINE="export ADOM_HYDROGEN_URL=http://host.docker.internal:9000"
if grep -q "ADOM_CARBON_URL" "$HOME/.bashrc" 2>/dev/null; then
step skip "grep ADOM_CARBON_URL ~/.bashrc" "already set"
else
step run "echo export ADOM_CARBON/HYDROGEN_URL >> ~/.bashrc" "routing adom-cli to HD..."
echo "$CARBON_LINE" >> "$HOME/.bashrc"
echo "$HYDROGEN_LINE" >> "$HOME/.bashrc"
step done "echo export ADOM_CARBON/HYDROGEN_URL >> ~/.bashrc" "adom-cli routed to HD"
fi
export ADOM_CARBON_URL=http://host.docker.internal:9000
export ADOM_HYDROGEN_URL=http://host.docker.internal:9000
# ── 6c. Inject API key from HD (host has the session token) ──
if [ -f /var/run/adom/api-key ] && [ -s /var/run/adom/api-key ]; then
step skip "test -f /var/run/adom/api-key" "API key already present"
else
step run "inject api-key from HD" "fetching session token from host..."
TOKEN=$(curl -sf http://host.docker.internal:9001/auth-token 2>/dev/null || echo "")
if [ -n "$TOKEN" ]; then
sudo mkdir -p /var/run/adom
echo -n "$TOKEN" | sudo tee /var/run/adom/api-key > /dev/null
sudo chmod 644 /var/run/adom/api-key
step done "inject api-key from HD" "API key injected"
else
step fail "inject api-key from HD" "no token available from HD"
fi
fi
# ── 7. VS Code Settings ──
step run "configure VS Code settings" "dark mode, Claude Code, trust..."
node -e "
const fs = require('fs');
const p = '$HOME/.local/share/code-server/User/settings.json';
let s = {}; try { s = JSON.parse(fs.readFileSync(p, 'utf8')) } catch(e) {}
Object.assign(s, {
'security.workspace.trust.enabled': false,
'workbench.startupEditor': 'none',
'workbench.activityBar.visible': true,
'workbench.colorTheme': 'Default Dark Modern',
'workbench.statusBar.visible': false,
'claudeCode.allowDangerouslySkipPermissions': true,
'claudeCode.initialPermissionMode': 'bypassPermissions',
'claudeCode.preferredLocation': 'panel',
'github.copilot.chat.enabled': false,
'github.copilot.enable': {'*': false},
'security.trustedDomains': ['*'],
'workbench.trustedDomains.promptInTrustedWorkspace': false,
'remote.portsAttributes': {'*': {'onAutoForward': 'silent'}}
});
fs.mkdirSync(require('path').dirname(p), {recursive: true});
fs.writeFileSync(p, JSON.stringify(s, null, 2));
console.log('configured');
"
step done "configure VS Code settings" "settings applied"
# ── 7b. Patch workbench.html BEFORE reload so it loads with the page ──
step run "patch workbench.html" "making external links propagate to parent browser..."
WORKBENCH="/usr/lib/code-server/lib/vscode/out/vs/code/browser/workbench/workbench.html"
MARKER="__hdLinkPropagation"
if ! grep -q "$MARKER" "$WORKBENCH" 2>/dev/null; then
sudo tee -a "$WORKBENCH" > /dev/null 2>&1 << 'LINKPATCH'
<script>
// Hydrogen Desktop: suppress "open external website?" dialog
// All URL interception is handled by WebView2's on_new_window + on_navigation
// in the Rust layer. This patch ONLY sets trusted domains in IndexedDB so
// code-server doesn't show its own confirmation dialog.
(function() {
if (window.__hdLinkPropagation) return;
window.__hdLinkPropagation = true;
try {
var req = indexedDB.open('vscode-web-state-db-global', 1);
req.onupgradeneeded = function(e) {
var db = e.target.result;
if (!db.objectStoreNames.contains('ItemTable')) {
db.createObjectStore('ItemTable');
}
};
req.onsuccess = function(e) {
var db = e.target.result;
try {
var tx = db.transaction('ItemTable', 'readwrite');
var store = tx.objectStore('ItemTable');
store.put(JSON.stringify(['*']), 'http.linkProtectionTrustedDomains');
tx.oncomplete = function() { console.log('[hd] Trusted domains set to * in IndexedDB'); db.close(); };
} catch(err) { db.close(); }
};
} catch(e) { console.error('[hd] Failed to set trusted domains:', e); }
console.log('[hd] Trusted domains patch loaded');
})();
</script>
LINKPATCH
step done "patch workbench.html" "trusted domains set"
else
step skip "patch workbench.html" "already patched"
fi
# ── 8. Reload VS Code (ONCE — after all extensions + settings are in place) ──
step run "adom-vscode reload" "reloading VS Code to activate extensions..."
# First trigger HD iframe reload so the webview picks up changes
curl -sf -X POST "http://host.docker.internal:9001/reload-vscode" 2>/dev/null || true
sleep 3
# Then reload the code-server window so extension host restarts
/usr/local/bin/adom-vscode reload 2>/dev/null || true
step done "adom-vscode reload" "VS Code reloaded"
# ── 9. Wait for adom-vscode extension to activate ──
step run "curl -sf http://127.0.0.1:8821/health" "waiting for adom-vscode (up to 60s)..."
VSCODE_READY=false
for i in $(seq 1 20); do
if curl -sf http://127.0.0.1:8821/health >/dev/null 2>&1; then
VSCODE_READY=true
break
fi
# Retry reload at 30s
if [ "$i" = "10" ]; then
/usr/local/bin/adom-vscode reload 2>/dev/null || true
fi
sleep 3
done
if [ "$VSCODE_READY" = true ]; then
step done "curl -sf http://127.0.0.1:8821/health" "extension active on :8821"
# ── 10. Clean VS Code layout (hide chrome, open Claude Code) ──
step run "configure VS Code layout" "hiding sidebars, panel, status bar..."
/usr/local/bin/adom-vscode sidebar hide 2>/dev/null || true
/usr/local/bin/adom-vscode panel hide 2>/dev/null || true
curl -sf -X POST http://127.0.0.1:8821/command \
-H "Content-Type: application/json" \
-d '{"command":"workbench.action.closeAuxiliaryBar"}' || true
step done "configure VS Code layout" "clean layout ready"
# ── 10b. Open Claude Code with countdown + auto-submit ──
step run "open Claude Code" "launching Claude Code panel..."
curl -sf -X POST http://127.0.0.1:8821/command \
-H "Content-Type: application/json" \
-d '{"command":"claude-vscode.editor.open"}' || true
sleep 1
curl -sf -X POST http://127.0.0.1:8821/command \
-H "Content-Type: application/json" \
-d '{"command":"claude-vscode.focus"}' || true
step done "open Claude Code" "Claude Code panel open"
step run "say hello to Claude" "3..."
sleep 1
step run "say hello to Claude" "2..."
sleep 1
step run "say hello to Claude" "1..."
sleep 1
SEED_MSG="hey, i'm here and ready to go. do you know who i am? and what can you do for me?"
curl -sf -X POST http://127.0.0.1:8821/command \
-H "Content-Type: application/json" \
-d "{\"command\":\"claude-vscode.editor.open\",\"args\":[null,\"$SEED_MSG\"]}" || true
step done "say hello to Claude" "message sent"
else
step fail "curl -sf http://127.0.0.1:8821/health" "timed out after 60s"
fi
# ── 11. Start relay server ──
if curl -sf http://127.0.0.1:8766/health >/dev/null 2>&1; then
step skip "curl -sf http://127.0.0.1:8766/health" "relay already running"
else
step run "nohup adom-desktop serve &" "starting relay..."
nohup adom-desktop serve > /tmp/adom-desktop-relay.log 2>&1 & disown
sleep 2
if curl -sf http://127.0.0.1:8766/health >/dev/null 2>&1; then
step done "nohup adom-desktop serve &" "relay on :8765/:8766"
else
step fail "nohup adom-desktop serve &" "relay failed to start"
fi
fi
echo "STEP|done|bootstrap|complete"