mirror of
https://github.com/getcompanion-ai/companion-skill.git
synced 2026-04-15 07:04:43 +00:00
Initial companion-offload skill for Claude Code
This commit is contained in:
commit
ec7919c4ba
4 changed files with 352 additions and 0 deletions
114
companion-offload/scripts/offload.sh
Executable file
114
companion-offload/scripts/offload.sh
Executable file
|
|
@ -0,0 +1,114 @@
|
|||
#!/usr/bin/env bash
|
||||
# Companion Offload Script
|
||||
# Syncs local Claude Code environment to a Companion sandbox and starts a Claude session there.
|
||||
#
|
||||
# Usage: ./offload.sh [task_prompt]
|
||||
# Example: ./offload.sh "finish implementing the API endpoint"
|
||||
#
|
||||
# Prerequisites:
|
||||
# - companion CLI installed and logged in (companion login)
|
||||
# - A running sandbox (companion ls / companion create <handle>)
|
||||
# - rsync available locally
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
TASK_PROMPT="${1:-}"
|
||||
LOCAL_DIR="$(pwd)"
|
||||
|
||||
# --- Check companion CLI ---
|
||||
if ! command -v companion >/dev/null 2>&1; then
|
||||
echo "[offload] ERROR: companion CLI not found. Install with: npm install -g @getcompanion/cli"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[offload] Checking authentication..."
|
||||
companion whoami || { echo "[offload] ERROR: Not logged in. Run 'companion login' first."; exit 1; }
|
||||
|
||||
# --- Get sandbox info ---
|
||||
echo "[offload] Checking sandbox status..."
|
||||
SANDBOX_OUTPUT=$(companion ls 2>&1) || { echo "[offload] ERROR: No sandbox found. Create one with: companion create <handle>"; exit 1; }
|
||||
echo "$SANDBOX_OUTPUT"
|
||||
|
||||
# Extract handle from companion ls output
|
||||
HANDLE=$(echo "$SANDBOX_OUTPUT" | grep -i "handle:" | head -1 | awk '{print $NF}' | tr -d '[:space:]')
|
||||
if [ -z "$HANDLE" ]; then
|
||||
echo "[offload] ERROR: Could not determine sandbox handle from 'companion ls' output."
|
||||
exit 1
|
||||
fi
|
||||
echo "[offload] Sandbox handle: $HANDLE"
|
||||
|
||||
# --- Inject SSH key ---
|
||||
echo "[offload] Setting up SSH key..."
|
||||
companion ssh --inject-only 2>&1 || { echo "[offload] ERROR: Failed to inject SSH key."; exit 1; }
|
||||
|
||||
# --- Build SSH connection parameters ---
|
||||
KEY_PATH="$HOME/.companion/ssh/id_ed25519_$HANDLE"
|
||||
SSH_GATEWAY="${COMPANION_SSH_GATEWAY:-ssh.os.companion.ai}"
|
||||
SSH_PORT="2222"
|
||||
PROXY_CMD="sh -c '( printf \"COMPANION:${HANDLE}\n\"; cat ) | nc ${SSH_GATEWAY} ${SSH_PORT}'"
|
||||
|
||||
SSH_CMD="ssh -i $KEY_PATH -o StrictHostKeyChecking=accept-new -o LogLevel=ERROR -o ProxyCommand=${PROXY_CMD}"
|
||||
RSYNC_SSH="ssh -i $KEY_PATH -o StrictHostKeyChecking=accept-new -o LogLevel=ERROR -o ProxyCommand=${PROXY_CMD}"
|
||||
|
||||
# --- Determine remote workspace ---
|
||||
echo "[offload] Checking remote workspace..."
|
||||
REMOTE_WORKSPACE=$(eval $SSH_CMD node@$HANDLE '[ -d /home/node/.openclaw/workspace ] && echo "/home/node/.openclaw/workspace" || ([ -d /home/node/.pi/workspace ] && echo "/home/node/.pi/workspace" || echo "")' 2>/dev/null || echo "")
|
||||
|
||||
if [ -z "$REMOTE_WORKSPACE" ]; then
|
||||
REMOTE_WORKSPACE="/home/node/.openclaw/workspace"
|
||||
echo "[offload] Creating workspace at $REMOTE_WORKSPACE"
|
||||
eval $SSH_CMD node@$HANDLE "mkdir -p $REMOTE_WORKSPACE"
|
||||
fi
|
||||
echo "[offload] Remote workspace: $REMOTE_WORKSPACE"
|
||||
|
||||
# --- Install Claude if needed ---
|
||||
echo "[offload] Checking for Claude Code on sandbox..."
|
||||
HAS_CLAUDE=$(eval $SSH_CMD node@$HANDLE 'command -v claude >/dev/null 2>&1 && echo "yes" || echo "no"' 2>/dev/null)
|
||||
if [ "$HAS_CLAUDE" = "no" ]; then
|
||||
echo "[offload] Installing Claude Code on sandbox..."
|
||||
eval $SSH_CMD node@$HANDLE 'curl -fsSL https://claude.ai/install.sh | sh 2>/dev/null || npm install -g @anthropic-ai/claude-code' || {
|
||||
echo "[offload] ERROR: Could not install Claude Code on sandbox."
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
|
||||
# --- Sync project ---
|
||||
echo "[offload] Syncing project to sandbox..."
|
||||
rsync -avz --progress \
|
||||
--exclude='node_modules' \
|
||||
--exclude='.git/objects' \
|
||||
--exclude='__pycache__' \
|
||||
--exclude='.venv' \
|
||||
--exclude='venv' \
|
||||
--exclude='.next' \
|
||||
--exclude='dist' \
|
||||
--exclude='build' \
|
||||
-e "$RSYNC_SSH" \
|
||||
"$LOCAL_DIR/" "node@$HANDLE:$REMOTE_WORKSPACE/"
|
||||
|
||||
# --- Sync Claude session ---
|
||||
if [ -d "$LOCAL_DIR/.claude" ]; then
|
||||
echo "[offload] Syncing Claude session data..."
|
||||
rsync -avz --progress \
|
||||
-e "$RSYNC_SSH" \
|
||||
"$LOCAL_DIR/.claude/" "node@$HANDLE:$REMOTE_WORKSPACE/.claude/"
|
||||
fi
|
||||
|
||||
# --- Transfer env vars ---
|
||||
echo "[offload] Transferring environment variables..."
|
||||
env | grep -v '^_=' | grep -v '^SHELL=' | grep -v '^TERM_' | grep -v '^SSH_' | \
|
||||
grep -v '^DISPLAY=' | grep -v '^HOME=' | grep -v '^USER=' | grep -v '^LOGNAME=' | \
|
||||
grep -v '^PATH=' | grep -v '^PWD=' | grep -v '^OLDPWD=' | grep -v '^SHLVL=' \
|
||||
> /tmp/companion_env_export.txt 2>/dev/null || true
|
||||
rsync -avz -e "$RSYNC_SSH" /tmp/companion_env_export.txt "node@$HANDLE:$REMOTE_WORKSPACE/.companion_env" 2>/dev/null
|
||||
rm -f /tmp/companion_env_export.txt
|
||||
|
||||
# --- Launch Claude ---
|
||||
echo "[offload] Launching Claude Code on sandbox..."
|
||||
echo ""
|
||||
|
||||
if [ -n "$TASK_PROMPT" ]; then
|
||||
eval $SSH_CMD -t node@$HANDLE "cd $REMOTE_WORKSPACE && set -a && source .companion_env 2>/dev/null; set +a && claude --dangerously-skip-permissions --prompt '$TASK_PROMPT'"
|
||||
else
|
||||
eval $SSH_CMD -t node@$HANDLE "cd $REMOTE_WORKSPACE && set -a && source .companion_env 2>/dev/null; set +a && claude --dangerously-skip-permissions --continue"
|
||||
fi
|
||||
Loading…
Add table
Add a link
Reference in a new issue