mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-18 18:32:28 +08:00
merge: incorporate origin/main into community-mode branch
Resolved 10 conflicted files:
- VERSION/package.json: kept 0.12.0.0 (feature branch version)
- CHANGELOG.md: preserved both branch entry and main's new entries
- supabase/config.sh: kept GSTACK_WEB_URL, accepted TELEMETRY_ENDPOINT removal
- bin/gstack-{community-dashboard,telemetry-log,telemetry-sync,update-check}:
took main's improved versions (edge function approach, safe cursor, UUID gen)
- supabase/functions/community-pulse: took main's count-based approach
- test/telemetry.test.ts: took main's structure with fingerprint field name
Post-merge fixes:
- Removed shadowed local RESOLVERS/functions in gen-skill-docs.ts (main's
resolver imports now take precedence for tier-based preamble, coverage gates)
- Added 3 missing E2E_TIERS entries (ship-plan-*, review-plan-completion)
- Updated telemetry test to match current prompt text
- Regenerated all SKILL.md files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# gstack-community-dashboard — community usage stats from Supabase
|
||||
#
|
||||
# Queries the Supabase REST API to show community-wide gstack usage:
|
||||
# Calls the community-pulse edge function for aggregated stats:
|
||||
# skill popularity, crash clusters, version distribution, retention.
|
||||
#
|
||||
# Env overrides (for testing):
|
||||
@@ -30,46 +30,40 @@ if [ -z "$SUPABASE_URL" ] || [ -z "$ANON_KEY" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ─── Helper: query Supabase REST API ─────────────────────────
|
||||
query() {
|
||||
local table="$1"
|
||||
local params="${2:-}"
|
||||
curl -sf --max-time 10 \
|
||||
"${SUPABASE_URL}/rest/v1/${table}?${params}" \
|
||||
-H "apikey: ${ANON_KEY}" \
|
||||
-H "Authorization: Bearer ${ANON_KEY}" \
|
||||
2>/dev/null || echo "[]"
|
||||
}
|
||||
# ─── Fetch aggregated stats from edge function ────────────────
|
||||
DATA="$(curl -sf --max-time 15 \
|
||||
"${SUPABASE_URL}/functions/v1/community-pulse" \
|
||||
-H "apikey: ${ANON_KEY}" \
|
||||
2>/dev/null || echo "{}")"
|
||||
|
||||
echo "gstack community dashboard"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
# ─── Weekly active installs ──────────────────────────────────
|
||||
WEEK_AGO="$(date -u -v-7d +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || echo "")"
|
||||
if [ -n "$WEEK_AGO" ]; then
|
||||
# Direct REST query (replaces unreliable community-pulse edge function)
|
||||
WEEKLY="$(curl -sf --max-time 10 \
|
||||
"${SUPABASE_URL}/rest/v1/update_checks?select=install_fingerprint&checked_at=gte.${WEEK_AGO}&source=eq.live" \
|
||||
-H "apikey: ${ANON_KEY}" \
|
||||
-H "Authorization: Bearer ${ANON_KEY}" \
|
||||
2>/dev/null | grep -o '"install_fingerprint":"[^"]*"' | sort -u | wc -l | tr -d ' ')"
|
||||
WEEKLY="${WEEKLY:-0}"
|
||||
WEEKLY="$(echo "$DATA" | grep -o '"weekly_active":[0-9]*' | grep -o '[0-9]*' || echo "0")"
|
||||
CHANGE="$(echo "$DATA" | grep -o '"change_pct":[0-9-]*' | grep -o '[0-9-]*' || echo "0")"
|
||||
|
||||
echo "Weekly active installs: ${WEEKLY} unique"
|
||||
echo ""
|
||||
echo "Weekly active installs: ${WEEKLY}"
|
||||
if [ "$CHANGE" -gt 0 ] 2>/dev/null; then
|
||||
echo " Change: +${CHANGE}%"
|
||||
elif [ "$CHANGE" -lt 0 ] 2>/dev/null; then
|
||||
echo " Change: ${CHANGE}%"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ─── Skill popularity (top 10) ───────────────────────────────
|
||||
echo "Top skills (last 7 days)"
|
||||
echo "────────────────────────"
|
||||
|
||||
# Query telemetry_events, group by skill
|
||||
EVENTS="$(query "telemetry_events" "select=skill,gstack_version,session_id&event_type=eq.skill_run&event_timestamp=gte.${WEEK_AGO}&source=eq.live&limit=1000" 2>/dev/null || echo "[]")"
|
||||
|
||||
if [ "$EVENTS" != "[]" ] && [ -n "$EVENTS" ]; then
|
||||
echo "$EVENTS" | grep -o '"skill":"[^"]*"' | awk -F'"' '{print $4}' | sort | uniq -c | sort -rn | head -10 | while read -r COUNT SKILL; do
|
||||
printf " /%-20s %d runs\n" "$SKILL" "$COUNT"
|
||||
# Parse top_skills array from JSON
|
||||
SKILLS="$(echo "$DATA" | grep -o '"top_skills":\[[^]]*\]' || echo "")"
|
||||
if [ -n "$SKILLS" ] && [ "$SKILLS" != '"top_skills":[]' ]; then
|
||||
# Parse each object — handle any key order (JSONB doesn't preserve order)
|
||||
echo "$SKILLS" | grep -o '{[^}]*}' | while read -r OBJ; do
|
||||
SKILL="$(echo "$OBJ" | grep -o '"skill":"[^"]*"' | awk -F'"' '{print $4}')"
|
||||
COUNT="$(echo "$OBJ" | grep -o '"count":[0-9]*' | grep -o '[0-9]*')"
|
||||
[ -n "$SKILL" ] && [ -n "$COUNT" ] && printf " /%-20s %s runs\n" "$SKILL" "$COUNT"
|
||||
done
|
||||
else
|
||||
echo " No data yet"
|
||||
@@ -80,31 +74,13 @@ echo ""
|
||||
echo "Top errors (last 7 days)"
|
||||
echo "────────────────────────"
|
||||
|
||||
ERRORS="$(query "telemetry_events" "select=skill,error_class,error_message,failed_step,duration_s,session_id&outcome=eq.error&event_timestamp=gte.${WEEK_AGO}&source=eq.live&order=event_timestamp.desc&limit=200" 2>/dev/null || echo "[]")"
|
||||
|
||||
if [ "$ERRORS" != "[]" ] && [ -n "$ERRORS" ]; then
|
||||
# Group by skill + error_class, show count and example message
|
||||
echo "$ERRORS" | grep -o '"skill":"[^"]*"[^}]*"error_class":"[^"]*"' | \
|
||||
sed 's/.*"skill":"//;s/".*"error_class":"/\t/' | sed 's/"$//' | \
|
||||
sort | uniq -c | sort -rn | head -8 | while read -r COUNT COMBO; do
|
||||
SKILL="$(echo "$COMBO" | cut -f1)"
|
||||
ERR="$(echo "$COMBO" | cut -f2)"
|
||||
# Find an example error_message for this combo
|
||||
MSG="$(echo "$ERRORS" | grep -o "\"skill\":\"${SKILL}\"[^}]*\"error_message\":\"[^\"]*\"" | \
|
||||
grep -o '"error_message":"[^"]*"' | head -1 | sed 's/"error_message":"//;s/"$//' || true)"
|
||||
# Find an example failed_step
|
||||
STEP="$(echo "$ERRORS" | grep -o "\"skill\":\"${SKILL}\"[^}]*\"failed_step\":\"[^\"]*\"" | \
|
||||
grep -o '"failed_step":"[^"]*"' | head -1 | sed 's/"failed_step":"//;s/"$//' || true)"
|
||||
|
||||
printf " /%-12s %-18s %3d errors\n" "$SKILL" "${ERR:-unknown}" "$COUNT"
|
||||
[ -n "$STEP" ] && printf " step: %s\n" "$STEP"
|
||||
[ -n "$MSG" ] && printf " e.g.: %s\n" "$(echo "$MSG" | head -c 80)"
|
||||
done
|
||||
|
||||
# Show how many unique sessions have errors
|
||||
ERR_SESSIONS="$(echo "$ERRORS" | grep -o '"session_id":"[^"]*"' | sort -u | wc -l | tr -d ' ')"
|
||||
echo ""
|
||||
echo " ${ERR_SESSIONS} unique session(s) with errors"
|
||||
CRASHES="$(echo "$DATA" | grep -o '"crashes":\[[^]]*\]' || echo "")"
|
||||
if [ -n "$CRASHES" ] && [ "$CRASHES" != '"crashes":[]' ]; then
|
||||
echo "$CRASHES" | grep -o '{[^}]*}' | head -5 | while read -r OBJ; do
|
||||
ERR="$(echo "$OBJ" | grep -o '"error_class":"[^"]*"' | awk -F'"' '{print $4}')"
|
||||
C="$(echo "$OBJ" | grep -o '"total_occurrences":[0-9]*' | grep -o '[0-9]*')"
|
||||
[ -n "$ERR" ] && printf " %-30s %s occurrences\n" "$ERR" "${C:-?}"
|
||||
done
|
||||
else
|
||||
echo " No errors reported"
|
||||
fi
|
||||
@@ -114,49 +90,17 @@ echo ""
|
||||
echo "Version distribution (last 7 days)"
|
||||
echo "───────────────────────────────────"
|
||||
|
||||
if [ "$EVENTS" != "[]" ] && [ -n "$EVENTS" ]; then
|
||||
echo "$EVENTS" | grep -o '"gstack_version":"[^"]*"' | awk -F'"' '{print $4}' | sort | uniq -c | sort -rn | head -5 | while read -r COUNT VER; do
|
||||
printf " v%-15s %d events\n" "$VER" "$COUNT"
|
||||
VERSIONS="$(echo "$DATA" | grep -o '"versions":\[[^]]*\]' || echo "")"
|
||||
if [ -n "$VERSIONS" ] && [ "$VERSIONS" != '"versions":[]' ]; then
|
||||
echo "$VERSIONS" | grep -o '{[^}]*}' | head -5 | while read -r OBJ; do
|
||||
VER="$(echo "$OBJ" | grep -o '"version":"[^"]*"' | awk -F'"' '{print $4}')"
|
||||
COUNT="$(echo "$OBJ" | grep -o '"count":[0-9]*' | grep -o '[0-9]*')"
|
||||
[ -n "$VER" ] && [ -n "$COUNT" ] && printf " v%-15s %s events\n" "$VER" "$COUNT"
|
||||
done
|
||||
else
|
||||
echo " No data yet"
|
||||
fi
|
||||
|
||||
# ─── Sessions (distinct session_id, works for all tiers) ────
|
||||
echo "Sessions (last 7 days)"
|
||||
echo "──────────────────────"
|
||||
|
||||
if [ "$EVENTS" != "[]" ] && [ -n "$EVENTS" ]; then
|
||||
SESSION_COUNT="$(echo "$EVENTS" | grep -o '"session_id":"[^"]*"' | sort -u | wc -l | tr -d ' ')"
|
||||
echo " ${SESSION_COUNT} unique sessions"
|
||||
else
|
||||
echo " No session data"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ─── Skill recommendations ─────────────────────────────────
|
||||
# Fetch top skills for recommendations
|
||||
TOP_SKILLS="$(echo "$EVENTS" | grep -o '"skill":"[^"]*"' | awk -F'"' '{print $4}' | sort | uniq -c | sort -rn | head -3 | awk '{print $2}' | tr '\n' ',' | sed 's/,$//')"
|
||||
|
||||
if [ -n "$TOP_SKILLS" ]; then
|
||||
RECS="$(curl -sf --max-time 10 \
|
||||
"${SUPABASE_URL}/functions/v1/community-recommendations?skills=${TOP_SKILLS}" \
|
||||
-H "Authorization: Bearer ${ANON_KEY}" \
|
||||
2>/dev/null || echo '{"recommendations":[]}')"
|
||||
|
||||
REC_LIST="$(echo "$RECS" | grep -o '"skill":"[^"]*"' | awk -F'"' '{print $4}')"
|
||||
REC_REASONS="$(echo "$RECS" | grep -o '"reason":"[^"]*"' | awk -F'"' '{print $4}')"
|
||||
|
||||
if [ -n "$REC_LIST" ]; then
|
||||
echo "Skills you might like"
|
||||
echo "─────────────────────"
|
||||
paste <(echo "$REC_LIST") <(echo "$REC_REASONS") 2>/dev/null | while IFS=$'\t' read -r SKILL REASON; do
|
||||
[ -z "$SKILL" ] && continue
|
||||
printf " /%-20s %s\n" "$SKILL" "${REASON:-}"
|
||||
done
|
||||
echo ""
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "For local analytics: gstack-analytics"
|
||||
echo "For benchmarks: gstack-community-benchmarks"
|
||||
|
||||
Reference in New Issue
Block a user