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