No error is a dead end: crash, click, and your agent is already looking
Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
+142
@@ -0,0 +1,142 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Refresh the agent usage records the bar panel watches.
|
||||
#
|
||||
# Each panama-agent-usage-<agent> collector prints one display-ready JSON
|
||||
# record; this runs the enabled ones in parallel and writes their output to
|
||||
# $XDG_STATE_HOME/panama/agents/usage/<agent>.json. Adding an agent is adding a
|
||||
# collector -- the panel picks up any record that appears in that directory, and
|
||||
# no QML changes.
|
||||
#
|
||||
# Adapted from Omarchy (bin/omarchy-agent-usage-update).
|
||||
#
|
||||
# Copyright (c) David Heinemeier Hansson
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
#
|
||||
# ── Why this reads settings.json rather than being told ──────────────────────
|
||||
#
|
||||
# Per-agent collection is a preference, and a preference is read at the moment
|
||||
# it matters -- the same rule the power button follows. A collector the user
|
||||
# turned off must not run at all: it is the thing that reads a credential file
|
||||
# and makes a network call, so "off" has to mean "no process", not "a process
|
||||
# whose output is discarded".
|
||||
#
|
||||
# ── Why a disabled agent's record is deleted ─────────────────────────────────
|
||||
#
|
||||
# The panel watches the directory. Leaving yesterday's record behind after the
|
||||
# collector is switched off would keep showing numbers nothing is refreshing,
|
||||
# which is worse than an empty tab.
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
self_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
# Seams. The contract points all three somewhere hermetic; nothing else does.
|
||||
COLLECTOR_DIR="${PANAMA_AGENT_USAGE_COLLECTORS:-$self_dir}"
|
||||
USAGE_DIR="${PANAMA_AGENT_USAGE_DIR:-${XDG_STATE_HOME:-$HOME/.local/state}/panama/agents/usage}"
|
||||
SETTINGS="${PANAMA_SETTINGS:-${XDG_CONFIG_HOME:-$HOME/.config}/panama/settings.json}"
|
||||
|
||||
command -v jq >/dev/null 2>&1 || {
|
||||
printf 'panama-agent-usage-update: jq is required\n' >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
mkdir -p "$USAGE_DIR" || exit 1
|
||||
|
||||
flags=()
|
||||
only=()
|
||||
|
||||
while (( $# > 0 )); do
|
||||
case "$1" in
|
||||
--force | --limits-only) flags+=("$1") ;;
|
||||
--help | -h)
|
||||
printf 'usage: panama-agent-usage-update [--force] [--limits-only] [agent...]\n'
|
||||
exit 0
|
||||
;;
|
||||
-*)
|
||||
printf 'panama-agent-usage-update: unknown option %s\n' "$1" >&2
|
||||
exit 2
|
||||
;;
|
||||
*) only+=("$1") ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# `.key // true` is wrong here: jq's alternative operator fires on false as well
|
||||
# as on null, so a collector the user switched off would read as enabled. Absent
|
||||
# is the only case that means "use the default".
|
||||
enabled() {
|
||||
local agent="$1" key answer
|
||||
key="agentUsage$(tr '[:lower:]' '[:upper:]' <<<"${agent:0:1}")${agent:1}"
|
||||
[[ -r "$SETTINGS" ]] || return 0
|
||||
answer="$(jq -r --arg key "$key" \
|
||||
'if has($key) then (.[$key] | tostring) else "true" end' "$SETTINGS" 2>/dev/null)" || return 0
|
||||
[[ "$answer" == "true" ]]
|
||||
}
|
||||
|
||||
requested() {
|
||||
local agent="$1" candidate
|
||||
(( ${#only[@]} == 0 )) && return 0
|
||||
for candidate in "${only[@]}"; do
|
||||
[[ "$candidate" == "$agent" ]] && return 0
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# A record is replaced only by a whole, parseable one. A collector that dies
|
||||
# halfway, or prints a stack trace, leaves the previous numbers standing rather
|
||||
# than blanking the panel: mktemp + mv makes the swap atomic, so a reader never
|
||||
# sees a half-written file, and the jq gate makes sure the thing being moved
|
||||
# into place is a record at all.
|
||||
collect() {
|
||||
local collector="$1" agent="$2" record tmp
|
||||
if ! record="$("$collector" "${flags[@]}" 2>/dev/null)" \
|
||||
|| [[ -z "$record" ]] \
|
||||
|| ! jq -e . >/dev/null 2>&1 <<<"$record"; then
|
||||
printf 'panama-agent-usage-update: the %s collector produced no usable record\n' "$agent" >&2
|
||||
return 1
|
||||
fi
|
||||
tmp="$(mktemp "$USAGE_DIR/.$agent.XXXXXX")" || return 1
|
||||
printf '%s\n' "$record" >"$tmp" || { rm -f "$tmp"; return 1; }
|
||||
chmod 644 "$tmp"
|
||||
mv "$tmp" "$USAGE_DIR/$agent.json"
|
||||
}
|
||||
|
||||
pids=()
|
||||
status=0
|
||||
|
||||
for collector in "$COLLECTOR_DIR"/panama-agent-usage-*; do
|
||||
[[ -x "$collector" ]] || continue
|
||||
agent="${collector##*/panama-agent-usage-}"
|
||||
[[ "$agent" == "update" ]] && continue
|
||||
requested "$agent" || continue
|
||||
if ! enabled "$agent"; then
|
||||
rm -f "$USAGE_DIR/$agent.json"
|
||||
continue
|
||||
fi
|
||||
collect "$collector" "$agent" &
|
||||
pids+=($!)
|
||||
done
|
||||
|
||||
for pid in "${pids[@]}"; do
|
||||
wait "$pid" || status=1
|
||||
done
|
||||
|
||||
exit "$status"
|
||||
Reference in New Issue
Block a user