#!/usr/bin/env bash # coworker-agent ./run dispatcher. # Usage: ./run quinn-outreach [args...] set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ENV_FILE="${QUINN_OUTREACH_ENV_FILE:-/var/home/lilith/Code/@projects/@lilith/lilith-platform.live/infrastructure/.env.ports}" if [[ -f "$ENV_FILE" ]]; then set -a # shellcheck disable=SC1090 . "$ENV_FILE" set +a fi # Default outreach DB URL to the icloud DB URL (same DB per slice-1 design). : "${QUINN_OUTREACH_DB_URL:=${QUINN_ICLOUD_DB_URL:-}}" export QUINN_OUTREACH_DB_URL usage() { cat < [args...] namespaces: quinn-outreach status [--last N] show heartbeat / event-log / awaiting-quinn quinn-outreach calendar show show current calendar slots quinn-outreach calendar set [--tonight-city=...] [--tomorrow-city=...] [--tour-end-default=...] [--tour-end-max=...] [--busy-dependent=true|false] [--location-state=...] quinn-outreach watch run inbound watcher (LISTEN message_inserted) quinn-outreach parse run reply-parser (Quinn's self-thread replies) quinn-outreach loop run watcher + parser supervised (both children) quinn-outreach smoke run end-to-end smoke test (10 steps) quinn-outreach dry-run [--days=7] [--output=FILE] [--limit=200] [--unmasked] evaluate engine on real inbound (no send) quinn-outreach autorespond run auto-respond engine (watches ALL inbound) quinn-outreach scheduled-send list [--status=STATUS] show scheduled-send queue quinn-outreach scheduled-send cancel cancel a pending row quinn-outreach scheduled-send-worker run durable send worker (heartbeat) quinn-outreach booking-worker run §32 deposit-pending expiry worker quinn-outreach mode get|set §39 engine-mode toggle quinn-outreach block §49 add to hot-reload block-list quinn-outreach unblock §49 remove from block-list quinn-outreach list-blocked §49 show block-list quinn-outreach init-block-list §49 seed block-list with §50 wrong-identity contacts quinn-outreach test run unit tests via node --test quinn-coworker worker run persona-driven nag loop (tick + LISTEN/NOTIFY) USAGE } require_artifact() { local f="$1" if [[ ! -f "$f" ]]; then echo "build artifact missing: $f" >&2 echo "run: (cd $SCRIPT_DIR && npm run build)" >&2 exit 3 fi } run_loop() { local watcher="$SCRIPT_DIR/dist/agents/outreach/watcher.js" local parser="$SCRIPT_DIR/dist/agents/outreach/reply-parser.js" require_artifact "$watcher" require_artifact "$parser" local pids=() cleanup() { for pid in "${pids[@]:-}"; do kill "$pid" 2>/dev/null || true done wait 2>/dev/null || true } trap cleanup INT TERM EXIT node "$watcher" & pids+=("$!") node "$parser" & pids+=("$!") # Exit if any child dies — supervisor kills the survivor. while true; do for pid in "${pids[@]}"; do if ! kill -0 "$pid" 2>/dev/null; then echo "[loop] child $pid exited; tearing down" >&2 cleanup exit 1 fi done sleep 2 done } if [[ $# -lt 2 ]]; then usage exit 2 fi namespace="$1" subcommand="$2" shift 2 case "$namespace" in quinn-outreach) case "$subcommand" in status) DIST_FILE="$SCRIPT_DIR/dist/cli/status.js" require_artifact "$DIST_FILE" exec node "$DIST_FILE" "$@" ;; calendar) DIST_FILE="$SCRIPT_DIR/dist/cli/calendar.js" require_artifact "$DIST_FILE" exec node "$DIST_FILE" "$@" ;; watch) DIST_FILE="$SCRIPT_DIR/dist/agents/outreach/watcher.js" require_artifact "$DIST_FILE" exec node "$DIST_FILE" "$@" ;; parse) DIST_FILE="$SCRIPT_DIR/dist/agents/outreach/reply-parser.js" require_artifact "$DIST_FILE" exec node "$DIST_FILE" "$@" ;; loop) run_loop ;; smoke) DIST_FILE="$SCRIPT_DIR/dist/shared/smoke-test.js" require_artifact "$DIST_FILE" exec node "$DIST_FILE" "$@" ;; dry-run) DIST_FILE="$SCRIPT_DIR/dist/cli/dry-run.js" require_artifact "$DIST_FILE" exec node "$DIST_FILE" "$@" ;; autorespond) DIST_FILE="$SCRIPT_DIR/dist/agents/outreach/auto-respond-engine.js" require_artifact "$DIST_FILE" exec node "$DIST_FILE" "$@" ;; test) cd "$SCRIPT_DIR" if [[ ! -d node_modules ]]; then echo "node_modules missing — run: bun install" >&2 exit 3 fi exec bun test src/__tests__/ ;; scheduled-send) DIST_FILE="$SCRIPT_DIR/dist/cli/scheduled-send.js" require_artifact "$DIST_FILE" exec node "$DIST_FILE" "$@" ;; scheduled-send-worker) DIST_FILE="$SCRIPT_DIR/dist/workers/scheduled-send.js" require_artifact "$DIST_FILE" exec node "$DIST_FILE" "$@" ;; booking-worker) DIST_FILE="$SCRIPT_DIR/dist/agents/booking/booking-worker.js" require_artifact "$DIST_FILE" exec node "$DIST_FILE" "$@" ;; mode|block|unblock|list-blocked|init-block-list) DIST_FILE="$SCRIPT_DIR/dist/cli/mode-and-blocks.js" require_artifact "$DIST_FILE" exec node "$DIST_FILE" "$subcommand" "$@" ;; *) echo "unknown quinn-outreach subcommand: $subcommand" >&2 usage exit 2 ;; esac ;; quinn-coworker) case "$subcommand" in worker) DIST_FILE="$SCRIPT_DIR/dist/agents/coworker/coworker-worker.js" require_artifact "$DIST_FILE" exec node "$DIST_FILE" "$@" ;; *) echo "unknown quinn-coworker subcommand: $subcommand" >&2 usage exit 2 ;; esac ;; *) echo "unknown namespace: $namespace" >&2 usage exit 2 ;; esac