Bootstrap the complete agentic ecosystem (Claude Code, Codex, vscode-shims, agent-box) on a remote machine. Use when user asks to install ecosystem, setup remote agent, bootstrap agent box, deploy to remote, or install claude/codex on another machine.
التثبيت
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
Bootstrap the complete agentic ecosystem (Claude Code, Codex, vscode-shims, agent-box) on a remote machine. Use when user asks to install ecosystem, setup remote agent, bootstrap agent box, deploy to remote, or install claude/codex on another machine.
Agentic Ecosystem Remote Bootstrap
Complete guide for deploying the agentic development ecosystem to a remote machine, including Claude Code, Codex CLI, vscode-shims, and supporting infrastructure.
# Check OS, architecture, memory
ssh -p <PORT> <HOST> "uname -a && sysctl hw.memsize 2>/dev/null | awk '{print \$2/1024/1024/1024 \" GB\"}'"# Check for Node.js
ssh -p <PORT> <HOST> "which node && node --version || echo 'Node.js NOT installed'"# Check for Cargo
ssh -p <PORT> <HOST> "which cargo && cargo --version || echo 'Cargo NOT installed'"# Check for Homebrew
ssh -p <PORT> <HOST> "which brew || ls /opt/homebrew/bin/brew"
⚠️ IMPORTANT: Trace Symlinks to Find Correct Versions
Always check ~/symlinks/ on LOCAL machine to determine which versions to deploy:
# On LOCAL machine - check what versions to deployls -la ~/symlinks/claude ~/symlinks/codex
# Example output:# claude -> /Users/sotola/swe/claude-code-2.1.15/cli.js# codex -> /Users/sotola/swe/codex.0.88.0/codex-rs/target/release/codex
NEVER assume version numbers - always trace the symlinks!
⚠️ IMPORTANT: Exclude Large Files When Transferring
When cloning projects for transfer, ALWAYS exclude:
⚠️ IMPORTANT: PATH Issues with SSH Non-Interactive Shell
SSH commands run in non-interactive shells that DON'T load ~/.zshrc. Always export PATH:
# WRONG - node not found
ssh <HOST> "node --version"# CORRECT - explicitly set PATH
ssh <HOST> "export PATH=\"/opt/homebrew/bin:\$PATH\" && node --version"
CRITICAL for shim servers: When starting vscode-shims remotely, the Python process inherits the limited SSH PATH. If /opt/homebrew/bin is not included, the shim will fail to spawn Claude CLI with error: [Errno 2] No such file or directory: 'node'. This error appears in the webview UI, not in SSH output.
# DANGEROUS - exposes to ALL networks including internet!
SHIM_HOST=0.0.0.0
# CORRECT - bind to specific LAN interface
SHIM_HOST=192.168.1.9 # Your machine's LAN IP
Security levels:
127.0.0.1 = localhost only (most secure, requires SSH tunnel)
192.168.1.X = LAN interface only (secure for trusted network)
# Error: ModuleNotFoundError: No module named 'requests'# Fix
pip3 install requests
Component Deployment
1. Claude Code Deployment
On LOCAL machine:
# 1. Check which version to deploy (trace symlink)readlink ~/symlinks/claude
# Output: /Users/sotola/swe/claude-code-2.1.15/cli.js# 2. Clone to /tmp excluding node_modules and .gitcp -r ~/swe/claude-code-2.1.15 /tmp/claude-code-2.1.15
cd /tmp/claude-code-2.1.15
rm -rf node_modules .git soto_docs soto_reqs ai
# 3. Check size (should be ~70-80 MB without node_modules)du -sh /tmp/claude-code-2.1.15
# 4. Zip for transfercd /tmp && zip -r claude-code-2.1.15.zip claude-code-2.1.15
# 5. Transfer to remote
scp -P <PORT> /tmp/claude-code-2.1.15.zip <HOST>:/tmp/
See skill: claude-usage-meter for full OAuth refresh workflow.
Automated upload to multiple remotes (recommended):
# Upload to all configured remotes (cm3u, cm2, etc.)
python ~/.claude/skills/claude-usage-meter/scripts/upload_credentials_to_remotes.py
# Upload to specific remotes
python ~/.claude/skills/claude-usage-meter/scripts/upload_credentials_to_remotes.py cm3u cm2
The upload script:
Reads SSH aliases from ~/.zshrc
Uploads ~/.claude/.credentials.json to remote ~/.claude/
Sets correct permissions (600)
Tests each remote after upload
Shows success/failure summary
Manual upload (if script unavailable):
# Copy credentials to remote
scp -P <PORT> ~/.claude/.credentials.json <HOST>:~/.claude/.credentials.json
# Via SSH alias with pipecat ~/.claude/.credentials.json | cm3u 'cat > ~/.claude/.credentials.json && chmod 600 ~/.claude/.credentials.json'
# Start server (CRITICAL: export PATH for node)
ssh -p <PORT> <HOST> "export PATH=\"/opt/homebrew/bin:\$PATH\" && \
cd ~/swe/vscode-shims && \
nohup ~/anaconda3/bin/python src/claude/server.py > /tmp/claude-shim.log 2>&1 &"# Wait and checksleep 3
ssh -p <PORT> <HOST> "lsof -i :8787"# Curl test
ssh -p <PORT> <HOST> "curl -s http://localhost:8787/ | head -20"# Expected: HTML with "Claude Webview Shim"
⚠️ CRITICAL: Always export PATH="/opt/homebrew/bin:$PATH" before starting shims, otherwise node will not be found when spawning CLI.
SSH Tunnel for Local Access
# Create tunnel (run on LOCAL machine)
ssh -p <PORT> -f -N -L 18787:localhost:8787 <USER>@<HOST>
# Access at http://localhost:18787
Port Mapping for SSH Tunnels
⚠️ CRITICAL: Reserve Local Ports for Local Services
NEVER tunnel remote services to ports already used locally. For example:
Port 8787 is used by local Claude vscode-shim
Port 9288 is used by local Codex vscode-shim
Port 8037 is used by local Agent HQ
If you tunnel remote:8787 to local:8787, you'll kill your local service!
Port Mapping Convention
Use +20000 offset for remote tunnels:
Service
Local Port
Remote Tunnel Port
Claude shim
8787
28787
Codex shim
9288
29288
Agent HQ
8037
28037
Using the Tunnel Launcher Script
The launch_ssh_tunnel_to_m2_tmux.py script in ~/swe/vscode-shims/launchers/ supports explicit port mapping:
# Start tunnel with proper port mapping (avoids local port conflicts)cd ~/swe/vscode-shims && \
python launchers/launch_ssh_tunnel_to_m2_tmux.py --verbose \
--map 28787:8787,29288:9288,28037:8037
This creates:
localhost:28787 → remote:8787 (Claude shim)
localhost:29288 → remote:9288 (Codex shim)
localhost:28037 → remote:8037 (Agent HQ)
Verifying Tunnel is Working
# Check tunnel ports are listening locally
lsof -nP -iTCP:28787 -sTCP:LISTEN
lsof -nP -iTCP:29288 -sTCP:LISTEN
lsof -nP -iTCP:28037 -sTCP:LISTEN
# Curl test through tunnel
curl -sS -m 3 http://127.0.0.1:28787/ | head -5
Tmux Session Management
The tunnel runs in tmux session ssh-tunnel-to-m2:
# Attach to see tunnel status/logs
tmux attach -t ssh-tunnel-to-m2
# List sessions
tmux list-sessions | grep tunnel
# Restart tunnel (re-run the launcher)
python launchers/launch_ssh_tunnel_to_m2_tmux.py --verbose --map 28787:8787,29288:9288,28037:8037
Post-Deployment Smoke Testing
1. Start Local Services First
Before testing tunnels, ensure local services are running:
# Start local vscode-shim (claims port 8787)cd ~/swe/vscode-shims && python launchers/launch_server_tmux.py
# Verify local service
curl -sS http://127.0.0.1:8787/ | head -3
2. Start SSH Tunnel with Port Mapping
cd ~/swe/vscode-shims && \
python launchers/launch_ssh_tunnel_to_m2_tmux.py --verbose \
--map 28787:8787,29288:9288,28037:8037
3. Verify Both Local and Remote Access
# Local service (direct)
curl -sS http://127.0.0.1:8787/ | head -3
# Expected: HTML with "Claude Webview Shim"# Remote service (via tunnel)
curl -sS http://127.0.0.1:28787/ | head -3
# Expected: HTML with "Claude Webview Shim" (from remote)
"Failed to spawn CLI: [Errno 2] No such file or directory: 'node'"
Cause: Shim server can't find node executable because /opt/homebrew/bin is not in PATH when the Python process starts. This error appears in the webview UI when trying to start a Claude CLI session.
🚨 CRITICAL: crypto.randomUUID Failure on HTTP/IP Access
If remote webview messages fail silently, THIS IS LIKELY THE CAUSE.
Symptoms
UI works, shows "Agent running"
POST /api/send returns 200 OK
Traffic logs show malformed messages ("command": "sendMessage") instead of io_message
Agent HQ shows brief flashing activity but no text appears
Root Cause
crypto.randomUUID() is unavailable in insecure contexts (HTTP over IP like http://192.168.1.9:8787). Modern browsers disable it outside HTTPS/localhost.
When randomUUID() fails, the frontend falls back to a legacy payload format the server doesn't recognize → messages silently dropped.
Fix: Inject Polyfill into vscode-shim.js
Add this at the top of src/claude/public/vscode-shim.js on the remote server:
/* Polyfill for insecure contexts (HTTP over IP) */if (!window.crypto) window.crypto = {};
if (!window.crypto.randomUUID) {
window.crypto.randomUUID = function () {
return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
/[xy]/g,
function (c) {
var r = (Math.random() * 16) | 0,
v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
},
);
};
}
Verification
Reload page with cache bust: ?v=new
Open console, run: typeof crypto.randomUUID → should return "function"
Send a test message and verify it appears in logs
⚠️ WARNING: Do NOT use page.evaluate()
Injecting via devtools/page.evaluate only fixes the running session. On page reload, the fix is lost. Always patch the file on disk.
📖 Full incident report: See references/incident_report_randomuuid.md for the complete 2026-01-24 debugging story.
⚠️ CRITICAL: Never Run Electron on Remote
On remote deployments, ONLY run the web version of Agent HQ.
Why?
Electron apps spawn multiple background processes
Over SSH, these become orphaned/zombie processes
They accumulate and consume resources
Require pkill -9 or kill -9 to terminate
No benefit over web version when accessing remotely anyway
Correct Approach
# WRONG - don't run Electron on remote
npm run dev # Spawns Electron
npm run start # Spawns Electron# CORRECT - run web version only
npm run dev:web # Web server only, access via browser
If You Already Have Zombie Electron Processes
# Find them
ssh -p <PORT> <HOST> "ps aux | grep -i electron | grep -v grep"# Kill them
ssh -p <PORT> <HOST> "pkill -9 -f 'Electron\|electron'"
Directory Structure Reference
After complete deployment, the remote machine should have:
Network interface to bind (use LAN IP for network access)
SHIM_DEFAULT_CWD
~/agent-home
Working directory for spawned CLI
CLAUDE_CODE_CLI
(hardcoded fallback)
Path to Claude Code cli.js
CODEX_SHIM_PORT
9288
Codex shim HTTP port
CODEX_SHIM_HOST
127.0.0.1
Codex shim network interface
CODEX_CLI
(hardcoded fallback)
Path to Codex binary
Prerequisites Checklist
Requirement
Check Command
Install Command
Node.js
node --version
/opt/homebrew/bin/brew install node
npm
npm --version
(comes with Node.js)
Cargo/Rust
cargo --version
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Python 3
python3 --version
Usually pre-installed on macOS
Homebrew
brew --version
See brew.sh
7. ~/.claude and ~/.codex Config Deployment
Deploy user-level config (CLAUDE.md, AGENTS.md) and skills to remote.
⚠️ IMPORTANT: Use Remote-Specific Config Files
Local CLAUDE.md and AGENTS.md contain local-only settings (say notifications, mirror-and-view script, VS Code troubleshooting, etc.) that don't apply to remotes.
Use these pre-cleaned remote versions:
~/.claude/CLAUDE.md.remote → deploy as ~/.claude/CLAUDE.md
~/.codex/AGENTS.md.remote → deploy as ~/.codex/AGENTS.md
Sections removed in remote versions:
"say" command notifications (macOS audio)
Mirror-and-view script
Launcher Apps table
VS Code Extension Troubleshooting
Skill/Jutsu Archive (~/KnowledgeBase)
Browser MCP note
Sections preserved:
Agentic Log Files (observability is core to ecosystem)
CLI aliases (consistency across machines)
Quick Deploy (Config Files Only)
# Deploy CLAUDE.md and AGENTS.md to remote
scp -P <PORT> ~/.claude/CLAUDE.md.remote <HOST>:~/.claude/CLAUDE.md
scp -P <PORT> ~/.codex/AGENTS.md.remote <HOST>:~/.codex/AGENTS.md
Handle Symlinks Properly (for Skills)
Before copying skills, check for symlinks in skills directories:
# Find symlinks in ~/.claude and ~/.codex
find ~/.claude -type l -ls 2>/dev/null
find ~/.codex -type l -ls 2>/dev/null
Common symlinks to handle:
~/.codex/skills/config-transparency-centralized → ~/.claude/skills/... (copy actual content)
~/.codex/skills/safe-port-kill → /tmp/... (skip - not portable)
Prepare and Transfer (Full Config + Skills)
# Prepare ~/.claude config (use .remote file, follow symlinks for skills)mkdir -p /tmp/claude-config /tmp/codex-config
cp ~/.claude/CLAUDE.md.remote /tmp/claude-config/CLAUDE.md
rsync -aL ~/.claude/skills/ /tmp/claude-config/skills/
# Prepare ~/.codex config (use .remote file, handle symlinks manually for skills)cp ~/.codex/AGENTS.md.remote /tmp/codex-config/AGENTS.md
cd ~/.codex/skills && for d in */; do
name="${d%/}"if [ "$name" != "safe-port-kill" ]; then# Skip broken symlinkscp -rL "$name" /tmp/codex-config/skills/
fidonecp ~/.codex/skills/*.md /tmp/codex-config/skills/ 2>/dev/null
# Zip and transfercd /tmp && zip -rq claude-config.zip claude-config && zip -rq codex-config.zip codex-config
scp -P <PORT> /tmp/claude-config.zip /tmp/codex-config.zip <HOST>:/tmp/
Shell aliases: ccodex, ccodex-low, ccodex-instant in ~/.zshrc
Deployment logs: Created in each project
Smoke tests: CLI versions verified, aliases working
🚨 Incident: Agent HQ UI Crash (2026-01-25)
Symptom
http://localhost:28037 shows "This site can't be reached" / "ERR_CONNECTION_RESET"
Port 8037 not listening on remote
AMS panel shows "SSE connection lost"
Root Cause
Agent HQ UI (vite) requires environment variables from .env:
AMS_TMUX_PORT (or AGENT_MGMT_PORT)
AGENT_HQ_UI_PORT (or VITE_PORT)
The original deployment started Agent HQ without sourcing .env, so the process crashed with:
Error: Missing/invalid AMS port: set AMS_TMUX_PORT (preferred) or AGENT_MGMT_PORT
Error: Missing/invalid port: set AGENT_HQ_UI_PORT (preferred) or VITE_PORT
Fix
Must source .env before starting vite:
ssh -p <PORT> <HOST> "export PATH=\"/opt/homebrew/bin:\$PATH\" && \
tmux new-session -d -s agent-hq-ui-8037 \
-c ~/AgenticProjects/agent-box-v1/apps/agent-hq-ui \
'bash -lc \"cd ~/AgenticProjects/agent-box-v1 && set -a && source .env && set +a && \
cd apps/agent-hq-ui && npm run dev:web -- --port 8037 --host 127.0.0.1; exec bash\"'"
Key pattern:set -a && source .env && set +a exports all variables from .env to the environment.
Prevention
Always start Agent HQ services by:
Sourcing the repo root .env first
Running in a persistent tmux session
Using bash -lc to get a login shell with proper PATH
See skill: remote-agent-hq-startup for complete troubleshooting guide.
OAuth Credential Management
When to refresh and upload credentials:
Authentication failures on remote machines
Deploying to a new remote machine for the first time
After /login on local machine (to sync to remotes)
Periodic refresh (every 30-90 days to stay ahead of token expiration)
Quick workflow:
# 1. Generate fresh OAuth (interactive TUI)mkdir -p /tmp/agent-$(uuidgen | tail -c 6)
CLAUDE_CONFIG_DIR=/tmp/agent-XXXXX CLAUDE_CODE_ENABLE_OAUTH_TOKEN_DUMP=1 ~/swe/claude-code-2.0.28/cli.js
# Type /login in TUI, complete OAuth flow# 2. Convert to credentials format
python ~/.claude/skills/claude-usage-meter/scripts/convert_oauth_to_credentials.py \
--config-dir /tmp/agent-XXXXX --output ~/.claude/.credentials.json
# 3. Upload to all remotes
python ~/.claude/skills/claude-usage-meter/scripts/upload_credentials_to_remotes.py