ec780b2e73
- docker-compose.prod.yml + .env.prod.example: one container per agent, Quince first; deployed under /opt/gu_agents and wired into the server's start/stop.sh. - Route API + git via host-gateway (containers can't hairpin the host's public IP). - Dockerfile: drop the base image's uid-1000 user before creating quince. - entrypoint: pip install --break-system-packages (Debian bookworm PEP 668). Verified on prod: image builds, API reachable (200), PyPI egress ok, gutask installs and runs, git clone from ramanujan works via host-gateway. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
74 lines
3.4 KiB
Bash
Executable File
74 lines
3.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Quince entrypoint: bootstrap the persistent self, then sleep-and-wake forever.
|
|
set -euo pipefail
|
|
|
|
HOME_DIR="/home/quince"
|
|
GUTASK_DIR="$HOME_DIR/gutasktool"
|
|
GUTASK_REPO="ssh://git@ramanujan.glitch.university:2222/glitch-university/gutasktool.git"
|
|
WAKE_TIME="${WAKE_TIME:-09:00}"
|
|
|
|
log() { echo "[entrypoint $(date '+%Y-%m-%d %H:%M:%S %Z')] $*"; }
|
|
|
|
# --- 0. Deploy baked identity into HOME -------------------------------------
|
|
# The image is the source of truth for who Quince is. Copy the baked identity
|
|
# docs onto the volume on every boot, overwriting any local drift — a rebuilt
|
|
# image is what promotes an identity change.
|
|
IDENTITY_SRC="/opt/quince/identity"
|
|
mkdir -p "$HOME_DIR/.claude"
|
|
if [ -d "$IDENTITY_SRC" ]; then
|
|
cp -f "$IDENTITY_SRC/CLAUDE.md" "$HOME_DIR/CLAUDE.md"
|
|
cp -f "$IDENTITY_SRC/GOALS.md" "$HOME_DIR/GOALS.md"
|
|
cp -f "$IDENTITY_SRC/claude-settings.json" "$HOME_DIR/.claude/settings.json"
|
|
log "Identity deployed from image ($IDENTITY_SRC) → $HOME_DIR."
|
|
else
|
|
log "WARNING: no baked identity at $IDENTITY_SRC."
|
|
fi
|
|
|
|
# --- 1. SSH: lock down perms and trust ramanujan ----------------------------
|
|
mkdir -p "$HOME_DIR/.ssh" "$HOME_DIR/workspace" "$HOME_DIR/notes" "$HOME_DIR/logs"
|
|
chmod 700 "$HOME_DIR/.ssh" || true
|
|
if [ -f "$HOME_DIR/.ssh/id_ed25519" ]; then
|
|
chmod 600 "$HOME_DIR/.ssh/id_ed25519" || true
|
|
ssh-keyscan -p 2222 ramanujan.glitch.university >> "$HOME_DIR/.ssh/known_hosts" 2>/dev/null || true
|
|
sort -u "$HOME_DIR/.ssh/known_hosts" -o "$HOME_DIR/.ssh/known_hosts" 2>/dev/null || true
|
|
else
|
|
log "WARNING: no SSH key at ~/.ssh/id_ed25519 — repo clone/push to ramanujan will fail."
|
|
fi
|
|
|
|
# --- 2. gutasktool: clone if missing, then editable-install -----------------
|
|
if [ ! -d "$GUTASK_DIR/.git" ]; then
|
|
log "gutasktool not on volume — cloning from ramanujan…"
|
|
GIT_SSH_COMMAND="ssh -i $HOME_DIR/.ssh/id_ed25519 -o IdentitiesOnly=yes" \
|
|
git clone "$GUTASK_REPO" "$GUTASK_DIR" || log "WARNING: clone failed (check SSH key)."
|
|
fi
|
|
if [ -d "$GUTASK_DIR" ]; then
|
|
log "Installing gutasktool (editable)…"
|
|
# --break-system-packages: the Debian base marks its Python externally-managed
|
|
# (PEP 668); this is an isolated container, so installing to --user is fine.
|
|
python3 -m pip install --user --break-system-packages -e "$GUTASK_DIR" -q || \
|
|
log "WARNING: gutasktool install failed."
|
|
fi
|
|
|
|
# --- 3. Sanity: identity present? -------------------------------------------
|
|
: "${AGENT_ID:?AGENT_ID not set — check .env / env_file}"
|
|
: "${CONTENT_API_KEY:?CONTENT_API_KEY not set — check .env / env_file}"
|
|
: "${ANTHROPIC_API_KEY:?ANTHROPIC_API_KEY not set — Claude Code cannot authenticate}"
|
|
log "Quince online. Identity AGENT_ID=$AGENT_ID, wake time $WAKE_TIME ($TZ)."
|
|
|
|
# --- 4. Optional immediate run (first deploy / testing) ---------------------
|
|
if [ "${RUN_ON_START:-0}" = "1" ]; then
|
|
log "RUN_ON_START=1 — triggering one awakening now."
|
|
/usr/local/bin/wake.sh || log "wake.sh exited non-zero."
|
|
fi
|
|
|
|
# --- 5. Sleep until the next WAKE_TIME, wake, repeat ------------------------
|
|
while true; do
|
|
now=$(date +%s)
|
|
target=$(date -d "today $WAKE_TIME" +%s)
|
|
[ "$target" -le "$now" ] && target=$(date -d "tomorrow $WAKE_TIME" +%s)
|
|
secs=$(( target - now ))
|
|
log "Sleeping ${secs}s until next awakening at $(date -d "@$target" '+%Y-%m-%d %H:%M:%S %Z')."
|
|
sleep "$secs"
|
|
/usr/local/bin/wake.sh || log "wake.sh exited non-zero; will try again next cycle."
|
|
done
|