feat(scripts): fleet-logs.sh to tail/inspect a Devin fleet job's logs
Convenience CLI over the agent-queue factory logs: resolves the agent-queue checkout (AQ override or sibling default), takes a full/partial job id (defaults to newest), and exposes ls/status/tail/steps/watch/full/path over the runner .log and the live Devin transcript (.devin-export.json steps[]). Referenced from the §8 Observe section of the fleet run runbook. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
e6611cae1a
commit
4ac5a747d1
@ -356,8 +356,22 @@ it **stops at the human merge gate**.
|
||||
|
||||
## 8. Observe progress
|
||||
|
||||
- **Factory/agent logs (the live Devin transcript):** use the helper
|
||||
`scripts/fleet-logs.sh` (auto-finds the agent-queue logs; takes a full or partial job id,
|
||||
defaults to the newest job):
|
||||
```bash
|
||||
scripts/fleet-logs.sh ls # list jobs: slot + step count
|
||||
scripts/fleet-logs.sh status 3c0586ce # steps count + slot + latest step
|
||||
scripts/fleet-logs.sh steps 3c0586ce 20 # last 20 transcript steps
|
||||
scripts/fleet-logs.sh watch 3c0586ce # live-refresh the tail
|
||||
scripts/fleet-logs.sh tail 3c0586ce # follow the runner lifecycle .log
|
||||
scripts/fleet-logs.sh full 3c0586ce # all agent messages in your pager
|
||||
```
|
||||
(Override the factory location with `AQ=/path/to/agent-queue`. Needs `jq` for the
|
||||
transcript commands.)
|
||||
- **tracker-web:** `http://localhost:3003/dashboard/fleet/jobs/<jobId>` — live event
|
||||
stream (SSE), runs, PR link + state.
|
||||
stream (SSE), runs, PR link + state. (Select the job's **product** in the UI first, or
|
||||
it shows "job does not exist" — every call is scoped by `X-Product-Id`.)
|
||||
- **Events/API:**
|
||||
```bash
|
||||
curl -s http://localhost:4003/api/fleet/jobs/<jobId>/events \
|
||||
|
||||
141
scripts/fleet-logs.sh
Executable file
141
scripts/fleet-logs.sh
Executable file
@ -0,0 +1,141 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# fleet-logs.sh — quickly inspect a fleet factory job's logs (agent-queue).
|
||||
#
|
||||
# The factory (learning_ai_devops_tools/agent-queue) writes two artifacts per job
|
||||
# under queue/logs/:
|
||||
# • <base>.log — sparse runner lifecycle (launch, PR open, verify, errors)
|
||||
# • <base>.devin-export.json — the live Devin transcript (steps[]: source + message)
|
||||
# where <base> looks like 20260602-003202__fleet-<jobId>.
|
||||
#
|
||||
# Usage:
|
||||
# scripts/fleet-logs.sh [command] [job]
|
||||
#
|
||||
# Commands:
|
||||
# status [job] steps count + slot (building/review/...) + latest step (default)
|
||||
# tail [job] follow the runner .log (-f)
|
||||
# steps [job] [N] last N transcript steps (default 12)
|
||||
# watch [job] [N] live-refresh the last N steps every few seconds
|
||||
# full [job] all agent messages through your pager ($PAGER/less)
|
||||
# path [job] print the resolved log file paths
|
||||
# ls list known jobs (newest first) with slot + step count
|
||||
#
|
||||
# `job` is a full or PARTIAL job id (e.g. `3c0586ce`). Omit it to use the most
|
||||
# recent job. Override the factory location with AQ=/path/to/agent-queue.
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
# ── Locate the agent-queue checkout ───────────────────────────────────────────
|
||||
resolve_aq() {
|
||||
if [[ -n "${AQ:-}" ]]; then printf '%s' "$AQ"; return; fi
|
||||
local here parent
|
||||
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
parent="$(cd "$here/../.." 2>/dev/null && pwd || true)" # …/<code root>
|
||||
for cand in \
|
||||
"$parent/learning_ai_devops_tools/agent-queue" \
|
||||
"$HOME/code/mygh/learning_ai_devops_tools/agent-queue" \
|
||||
"$HOME/code/learning_ai_devops_tools/agent-queue"; do
|
||||
[[ -d "$cand/queue/logs" ]] && { printf '%s' "$cand"; return; }
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
AQ_DIR="$(resolve_aq || true)"
|
||||
if [[ -z "$AQ_DIR" || ! -d "$AQ_DIR/queue/logs" ]]; then
|
||||
echo "error: agent-queue logs dir not found. Set AQ=/path/to/agent-queue." >&2
|
||||
exit 1
|
||||
fi
|
||||
LOGDIR="$AQ_DIR/queue/logs"
|
||||
QUEUE="$AQ_DIR/queue"
|
||||
|
||||
have_jq() { command -v jq >/dev/null 2>&1; }
|
||||
need_jq() { have_jq || { echo "error: 'jq' is required for this command (brew install jq)." >&2; exit 1; }; }
|
||||
|
||||
# ── Resolve a job's export file by (optional) substring; default = newest ──────
|
||||
resolve_export() {
|
||||
local pat="${1:-}" f
|
||||
if [[ -z "$pat" ]]; then
|
||||
f="$(ls -t "$LOGDIR"/*.devin-export.json 2>/dev/null | head -1 || true)"
|
||||
else
|
||||
f="$(ls -t "$LOGDIR"/*"$pat"*.devin-export.json 2>/dev/null | head -1 || true)"
|
||||
fi
|
||||
[[ -n "$f" ]] || { echo "error: no transcript matching '${pat:-<latest>}' in $LOGDIR" >&2; return 1; }
|
||||
printf '%s' "$f"
|
||||
}
|
||||
|
||||
base_of() { basename "$1" .devin-export.json; } # 20260602-…__fleet-<jobId>
|
||||
runner_log() { printf '%s/%s.log' "$LOGDIR" "$(base_of "$1")"; }
|
||||
|
||||
slot_of() {
|
||||
local base="$1" d
|
||||
for d in building review testing shipped failed inbox; do
|
||||
[[ -e "$QUEUE/$d/$base.md" ]] && { printf '%s' "$d"; return; }
|
||||
done
|
||||
printf 'unknown'
|
||||
}
|
||||
|
||||
steps_count() { have_jq && jq '.steps|length' "$1" 2>/dev/null || echo '?'; }
|
||||
|
||||
print_steps() {
|
||||
local exp="$1" n="${2:-12}"
|
||||
need_jq
|
||||
jq -r --argjson n "$n" '.steps[-$n:][] | "[\(.source)] \(.message[0:300])"' "$exp" 2>/dev/null \
|
||||
|| echo "(transcript mid-write — re-run)"
|
||||
}
|
||||
|
||||
cmd="${1:-status}"; shift || true
|
||||
|
||||
case "$cmd" in
|
||||
ls)
|
||||
printf '%-40s %-10s %-7s %s\n' "JOB (base)" "SLOT" "STEPS" "FILE"
|
||||
ls -t "$LOGDIR"/*.devin-export.json 2>/dev/null | while read -r f; do
|
||||
b="$(base_of "$f")"
|
||||
printf '%-40s %-10s %-7s %s\n' "$b" "$(slot_of "$b")" "$(steps_count "$f")" "$f"
|
||||
done
|
||||
;;
|
||||
path)
|
||||
exp="$(resolve_export "${1:-}")"
|
||||
echo "transcript: $exp"
|
||||
echo "runner log: $(runner_log "$exp")"
|
||||
;;
|
||||
status)
|
||||
exp="$(resolve_export "${1:-}")"; base="$(base_of "$exp")"
|
||||
echo "job: $base"
|
||||
echo "slot: $(slot_of "$base")"
|
||||
echo "steps: $(steps_count "$exp")"
|
||||
echo "transcript: $exp"
|
||||
echo "--- latest step ---"
|
||||
print_steps "$exp" 3
|
||||
;;
|
||||
tail)
|
||||
exp="$(resolve_export "${1:-}")"; log="$(runner_log "$exp")"
|
||||
echo "==> tail -f $log (Ctrl-C to stop)"
|
||||
tail -n +1 -f "$log"
|
||||
;;
|
||||
steps)
|
||||
exp="$(resolve_export "${1:-}")"
|
||||
print_steps "$exp" "${2:-12}"
|
||||
;;
|
||||
watch)
|
||||
exp="$(resolve_export "${1:-}")"; n="${2:-8}"
|
||||
echo "==> live last $n steps of $(base_of "$exp") (Ctrl-C to stop)"
|
||||
while true; do
|
||||
clear 2>/dev/null || true
|
||||
printf '== %s | slot=%s | steps=%s | %s ==\n\n' \
|
||||
"$(base_of "$exp")" "$(slot_of "$(base_of "$exp")")" "$(steps_count "$exp")" "$(date +%T)"
|
||||
print_steps "$exp" "$n"
|
||||
sleep 3
|
||||
done
|
||||
;;
|
||||
full)
|
||||
exp="$(resolve_export "${1:-}")"; need_jq
|
||||
jq -r '.steps[] | "== \(.source) ==\n\(.message)\n"' "$exp" | "${PAGER:-less}"
|
||||
;;
|
||||
-h|--help|help)
|
||||
sed -n '2,33p' "$0" | sed 's/^# \{0,1\}//'
|
||||
;;
|
||||
*)
|
||||
echo "unknown command '$cmd' (try: status|tail|steps|watch|full|path|ls; -h for help)" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Loading…
Reference in New Issue
Block a user