Last active
May 16, 2026 09:26
-
-
Save ardianys/52482c4e3ef818c96ed577293f842ed5 to your computer and use it in GitHub Desktop.
Claude Code Usage Monitor — installer for team members' laptops (POST daily ccusage data to Rails webhook)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Claude Code Usage Monitor — installer untuk laptop programmer | |
| # Cara pakai: bash install.sh <nama> <webhook_url> [bearer_token] | |
| # Contoh : bash install.sh budi "https://app.rumatkita.com/ccusage_reports" "secret123" | |
| set -e | |
| NAME="$1" | |
| WEBHOOK="$2" | |
| TOKEN="$3" | |
| if [ -z "$NAME" ] || [ -z "$WEBHOOK" ]; then | |
| echo "Usage: bash install.sh <nama> <webhook_url> [bearer_token]" | |
| exit 1 | |
| fi | |
| command -v python3 >/dev/null 2>&1 || { echo "Butuh python3 (sudah default di macOS). Install: brew install python"; exit 1; } | |
| mkdir -p "$HOME/.claude" | |
| PY="$HOME/.claude/ccusage-report.py" | |
| SCRIPT="$HOME/.claude/ccusage-report.sh" | |
| # --- Python parser --- | |
| cat > "$PY" <<'PYEOF' | |
| #!/usr/bin/env python3 | |
| """Aggregate Claude Code daily usage from ~/.claude/projects/**/*.jsonl | |
| Output (ccusage-compatible): | |
| {"daily": [{"date": "YYYY-MM-DD", "inputTokens": N, ...}]} | |
| """ | |
| import json, os, sys, glob, argparse | |
| from datetime import datetime, timezone, timedelta | |
| from collections import defaultdict | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument('--days', type=int, default=1, | |
| help='Jumlah hari ke belakang yang dimasukkan (default 1 = hari ini saja)') | |
| args = ap.parse_args() | |
| today = datetime.now(timezone.utc).date() | |
| since_date = today - timedelta(days=args.days - 1) | |
| home = os.path.expanduser('~') | |
| pattern = os.path.join(home, '.claude', 'projects', '**', '*.jsonl') | |
| files = glob.glob(pattern, recursive=True) | |
| daily = defaultdict(lambda: { | |
| 'in': 0, 'out': 0, 'cwrite': 0, 'cread': 0, 'models': set() | |
| }) | |
| seen = set() | |
| for f in files: | |
| try: | |
| with open(f, 'r', errors='ignore') as fp: | |
| for line in fp: | |
| line = line.strip() | |
| if not line: | |
| continue | |
| try: | |
| e = json.loads(line) | |
| except json.JSONDecodeError: | |
| continue | |
| msg = e.get('message') | |
| if not isinstance(msg, dict): | |
| continue | |
| usage = msg.get('usage') | |
| if not isinstance(usage, dict): | |
| continue | |
| mid = msg.get('id') | |
| if mid and mid in seen: | |
| continue | |
| if mid: | |
| seen.add(mid) | |
| ts = e.get('timestamp') | |
| if not ts: | |
| continue | |
| try: | |
| dt = datetime.fromisoformat(ts.replace('Z', '+00:00')).date() | |
| except ValueError: | |
| continue | |
| if dt < since_date: | |
| continue | |
| key = dt.isoformat() | |
| daily[key]['in'] += int(usage.get('input_tokens') or 0) | |
| daily[key]['out'] += int(usage.get('output_tokens') or 0) | |
| daily[key]['cwrite'] += int(usage.get('cache_creation_input_tokens') or 0) | |
| daily[key]['cread'] += int(usage.get('cache_read_input_tokens') or 0) | |
| model = msg.get('model') | |
| if model: | |
| daily[key]['models'].add(model) | |
| except (IOError, OSError): | |
| continue | |
| out = { | |
| 'daily': [ | |
| { | |
| 'date': d, | |
| 'inputTokens': v['in'], | |
| 'outputTokens': v['out'], | |
| 'cacheCreationTokens': v['cwrite'], | |
| 'cacheReadTokens': v['cread'], | |
| 'totalCost': 0, | |
| 'modelsUsed': sorted(v['models']), | |
| } | |
| for d, v in sorted(daily.items()) | |
| ] | |
| } | |
| print(json.dumps(out)) | |
| if __name__ == '__main__': | |
| main() | |
| PYEOF | |
| chmod +x "$PY" | |
| # --- Shell wrapper for cron --- | |
| cat > "$SCRIPT" <<EOF | |
| #!/bin/bash | |
| # Auto-generated. Jangan diedit manual. | |
| NAME="$NAME" | |
| WEBHOOK="$WEBHOOK" | |
| TOKEN="$TOKEN" | |
| DAILY=\$(python3 "$PY" --days 1 2>/dev/null) | |
| if [ -z "\$DAILY" ]; then | |
| echo "Tidak ada data usage hari ini" | |
| exit 0 | |
| fi | |
| TS=\$(date -u +%Y-%m-%dT%H:%M:%SZ) | |
| PAYLOAD=\$(printf '{"user":"%s","timestamp":"%s","daily":%s}' "\$NAME" "\$TS" "\$DAILY") | |
| AUTH_HEADER=() | |
| if [ -n "\$TOKEN" ]; then | |
| AUTH_HEADER=(-H "Authorization: Bearer \$TOKEN") | |
| fi | |
| curl -s -X POST "\$WEBHOOK" \\ | |
| -A "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" \\ | |
| -H "Content-Type: application/json" \\ | |
| "\${AUTH_HEADER[@]}" \\ | |
| -d "\$PAYLOAD" | |
| echo "" | |
| EOF | |
| chmod +x "$SCRIPT" | |
| # --- Cron: tiap hari setiap jam (menit 0) --- | |
| CRON_LINE="0 * * * * $SCRIPT >> $HOME/.claude/ccusage-report.log 2>&1" | |
| ( crontab -l 2>/dev/null | grep -v 'ccusage-report.sh' ; echo "$CRON_LINE" ) | crontab - | |
| echo "" | |
| echo "Terpasang:" | |
| echo " Parser : $PY" | |
| echo " Wrapper: $SCRIPT" | |
| echo " Cron : tiap hari setiap jam (menit 0)" | |
| echo "" | |
| echo "Test sekarang (kirim data hari ini):" | |
| echo " $SCRIPT" | |
| echo "" | |
| echo "Cek data minggu terakhir (tanpa kirim):" | |
| echo " python3 $PY --days 7" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment