- Add scripts/lib/common.sh (ORG/BOT/FORGE_BACKEND, logging, process_running) - discover.sh: dedupe review-requested+assignee PRs; JSON+summary formats - discover.sh: FORGE_BACKEND=forgejo via Forgejo API + stoke token - Fix poll-loop process detection (basename, not install path) - health-check: jq-safe JSON; backend-aware auth (gh or stoke) - install-live: require rsync/jq/tmux; soft-warn missing gh/stoke - self-test.sh offline smoke; restore/poll-once use shared config - Docs: Forgejo table, watcher.env, OPERATIONS troubleshooting
123 lines
4.4 KiB
Bash
Executable file
123 lines
4.4 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Restore the heavy-duty org watcher after reboot / session loss.
|
|
# Usage:
|
|
# ./scripts/restore.sh # create/attach tmux + resume Grok
|
|
# ./scripts/restore.sh --attach # attach only
|
|
# ./scripts/restore.sh --status # health snapshot
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
# shellcheck source=lib/common.sh
|
|
source "${SCRIPT_DIR}/lib/common.sh"
|
|
|
|
mkdir -p "${LOG_DIR}"
|
|
|
|
session_id() {
|
|
if [[ -f "${STATE_FILE}" ]] && command -v jq >/dev/null 2>&1; then
|
|
local id
|
|
id="$(jq -r '.session_id // empty' "${STATE_FILE}" 2>/dev/null || true)"
|
|
if [[ -n "${id}" && "${id}" != "null" ]]; then
|
|
echo "${id}"
|
|
return
|
|
fi
|
|
fi
|
|
echo ""
|
|
}
|
|
|
|
status() {
|
|
echo "=== heavy-duty-watcher status ==="
|
|
echo "dir: ${WATCHER_DIR}"
|
|
echo "backend: ${FORGE_BACKEND} org: ${ORG} bot: ${BOT_LOGIN}"
|
|
if [[ -f "${STATE_FILE}" ]]; then
|
|
jq '{scope, org, bot_login, session_id, last_poll_at, last_poll_summary, health, items: (.items|keys)}' \
|
|
"${STATE_FILE}" 2>/dev/null || cat "${STATE_FILE}"
|
|
else
|
|
echo "(missing state.json)"
|
|
fi
|
|
echo
|
|
tmux ls 2>/dev/null || echo "(no tmux sessions)"
|
|
echo
|
|
case "${FORGE_BACKEND}" in
|
|
github)
|
|
echo "gh user: $(gh api user --jq .login 2>/dev/null || echo 'not authenticated')"
|
|
;;
|
|
forgejo)
|
|
echo "stoke: $(stoke auth status 2>/dev/null | head -n 4 | tr '\n' ' ' || echo 'not authenticated')"
|
|
;;
|
|
esac
|
|
echo "session_id: $(session_id || true)"
|
|
[[ -f "${LOG_DIR}/poll.log" ]] && { echo; echo "last poll log:"; tail -n 5 "${LOG_DIR}/poll.log"; }
|
|
[[ -f "${LOG_DIR}/health.log" ]] && { echo; echo "last health log:"; tail -n 5 "${LOG_DIR}/health.log"; }
|
|
bash "${WATCHER_DIR}/scripts/health-check.sh" || true
|
|
}
|
|
|
|
attach_only() {
|
|
if tmux has-session -t "${TMUX_SESSION}" 2>/dev/null; then
|
|
exec tmux attach -t "${TMUX_SESSION}"
|
|
fi
|
|
echo "tmux session '${TMUX_SESSION}' not running. Run without --attach to start."
|
|
exit 1
|
|
}
|
|
|
|
start_or_attach() {
|
|
local sid resume_prompt
|
|
sid="$(session_id)"
|
|
resume_prompt="$(cat <<EOF
|
|
Resume the heavy-duty org watcher (ALL ${ORG}/* repos, backend=${FORGE_BACKEND}).
|
|
|
|
1. Read ${WATCHER_DIR}/POLL_INSTRUCTIONS.md and ${WATCHER_DIR}/state.json
|
|
2. Run one full org-wide poll cycle immediately (discover assigned/review-requested across org, read all comments, review until you agree)
|
|
3. Re-arm durable schedulers:
|
|
- 15m poll (org-wide, durable true)
|
|
- 1h health watchdog (durable true)
|
|
4. Confirm poll-loop + health-loop windows are running
|
|
5. Report status
|
|
|
|
Do not merge, close, or reassign unless explicitly asked.
|
|
EOF
|
|
)"
|
|
|
|
if tmux has-session -t "${TMUX_SESSION}" 2>/dev/null; then
|
|
if ! tmux list-windows -t "${TMUX_SESSION}" -F '#{window_name}' 2>/dev/null | grep -qx 'poll-loop'; then
|
|
tmux new-window -t "${TMUX_SESSION}" -n poll-loop -c "${WATCHER_DIR}" \
|
|
"bash ${WATCHER_DIR}/scripts/poll-loop.sh"
|
|
fi
|
|
if ! tmux list-windows -t "${TMUX_SESSION}" -F '#{window_name}' 2>/dev/null | grep -qx 'health-loop'; then
|
|
tmux new-window -t "${TMUX_SESSION}" -n health-loop -c "${WATCHER_DIR}" \
|
|
"bash ${WATCHER_DIR}/scripts/health-loop.sh"
|
|
fi
|
|
echo "tmux session '${TMUX_SESSION}' exists — attaching."
|
|
exec tmux attach -t "${TMUX_SESSION}"
|
|
fi
|
|
|
|
echo "Creating tmux session '${TMUX_SESSION}'..."
|
|
local grok_cmd
|
|
if [[ -n "${sid}" ]]; then
|
|
grok_cmd="grok -r ${sid} --cwd ${HOME} $(printf %q "${resume_prompt}") || grok -c --cwd ${HOME} $(printf %q "${resume_prompt}") || grok --cwd ${HOME} $(printf %q "${resume_prompt}")"
|
|
else
|
|
grok_cmd="grok --cwd ${HOME} $(printf %q "${resume_prompt}")"
|
|
fi
|
|
|
|
tmux new-session -d -s "${TMUX_SESSION}" -n grok -c "${HOME}" \
|
|
"${grok_cmd}; echo; echo '[heavy-duty-watcher] Grok exited.'; exec bash"
|
|
|
|
tmux new-window -t "${TMUX_SESSION}" -n poll-loop -c "${WATCHER_DIR}" \
|
|
"bash ${WATCHER_DIR}/scripts/poll-loop.sh"
|
|
|
|
tmux new-window -t "${TMUX_SESSION}" -n health-loop -c "${WATCHER_DIR}" \
|
|
"bash ${WATCHER_DIR}/scripts/health-loop.sh"
|
|
|
|
tmux new-window -t "${TMUX_SESSION}" -n tools -c "${WATCHER_DIR}" \
|
|
"echo 'tools: ./scripts/discover.sh | ./scripts/health-check.sh | tail -f logs/poll.log'; exec bash"
|
|
|
|
tmux select-window -t "${TMUX_SESSION}:grok"
|
|
watcher_log "${LOG_DIR}/poll.log" "restore.sh started ${TMUX_SESSION}"
|
|
exec tmux attach -t "${TMUX_SESSION}"
|
|
}
|
|
|
|
case "${1:-}" in
|
|
--status|-s) status ;;
|
|
--attach|-a) attach_only ;;
|
|
--help|-h) sed -n '2,7p' "$0" ;;
|
|
*) start_or_attach ;;
|
|
esac
|