90 lines
2.8 KiB
Bash
90 lines
2.8 KiB
Bash
|
|
#!/bin/bash
|
||
|
|
#
|
||
|
|
# update-costs.sh — Update the landing page with latest project cost
|
||
|
|
# estimates from the impact log.
|
||
|
|
#
|
||
|
|
# Reads .claude/impact/impact-log.jsonl, takes the latest snapshot per
|
||
|
|
# session, sums totals, and updates index.html in place.
|
||
|
|
#
|
||
|
|
# Usage: ./update-costs.sh
|
||
|
|
#
|
||
|
|
# Run after each conversation or as a cron job.
|
||
|
|
|
||
|
|
set -euo pipefail
|
||
|
|
|
||
|
|
IMPACT_LOG="/home/claude/claude-dir/.claude/impact/impact-log.jsonl"
|
||
|
|
|
||
|
|
if [ ! -f "$IMPACT_LOG" ]; then
|
||
|
|
echo "No impact log found at $IMPACT_LOG"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
python3 << 'PYEOF'
|
||
|
|
import json, re, sys
|
||
|
|
|
||
|
|
IMPACT_LOG = "/home/claude/claude-dir/.claude/impact/impact-log.jsonl"
|
||
|
|
PAGES = ["/home/claude/www/index.html", "/home/claude/claude-dir/www/index.html"]
|
||
|
|
|
||
|
|
# Read all entries, keep latest per session
|
||
|
|
sessions = {}
|
||
|
|
with open(IMPACT_LOG) as f:
|
||
|
|
for line in f:
|
||
|
|
line = line.strip()
|
||
|
|
if not line:
|
||
|
|
continue
|
||
|
|
d = json.loads(line)
|
||
|
|
sid = d.get("session_id", "")
|
||
|
|
if sid == "test-123" or not sid:
|
||
|
|
continue
|
||
|
|
ts = d.get("timestamp", "")
|
||
|
|
if sid not in sessions or ts > sessions[sid]["timestamp"]:
|
||
|
|
sessions[sid] = d
|
||
|
|
|
||
|
|
if not sessions:
|
||
|
|
print("No sessions found in impact log")
|
||
|
|
sys.exit(0)
|
||
|
|
|
||
|
|
# Sum across sessions
|
||
|
|
n = len(sessions)
|
||
|
|
total_energy = sum(d.get("energy_wh", 0) for d in sessions.values())
|
||
|
|
total_co2 = sum(d.get("co2_g", 0) for d in sessions.values())
|
||
|
|
total_cost_cents = sum(d.get("cost_cents", 0) for d in sessions.values())
|
||
|
|
|
||
|
|
# Format cost
|
||
|
|
if total_cost_cents >= 100:
|
||
|
|
cost_display = f"${total_cost_cents // 100}"
|
||
|
|
else:
|
||
|
|
cost_display = f"{total_cost_cents}c"
|
||
|
|
|
||
|
|
# Pluralize
|
||
|
|
session_word = "session" if n == 1 else "sessions"
|
||
|
|
|
||
|
|
# Build replacement paragraph
|
||
|
|
new_para = (
|
||
|
|
f"How this was made:</strong>\n"
|
||
|
|
f" This project was developed by a human\n"
|
||
|
|
f" directing <a href=\"https://claude.ai\">Claude</a> (Anthropic's AI assistant)\n"
|
||
|
|
f" across multiple conversations. The methodology was applied to itself:\n"
|
||
|
|
f" across {n} tracked {session_word}, the project has consumed\n"
|
||
|
|
f" ~{total_energy} Wh of energy, ~{total_co2}g of CO2, and ~{cost_display} in\n"
|
||
|
|
f" compute. Whether it produces enough value to justify those costs depends\n"
|
||
|
|
f" on whether anyone finds it useful. We are\n"
|
||
|
|
f' <a href="/forge/claude/ai-conversation-impact/src/branch/main/plans/measure-project-impact.md">tracking that question</a>.\n'
|
||
|
|
f" </div>"
|
||
|
|
)
|
||
|
|
|
||
|
|
# Read and replace in all landing page copies
|
||
|
|
pattern = r"How this was made:</strong>.*?</div>"
|
||
|
|
for page in PAGES:
|
||
|
|
try:
|
||
|
|
with open(page) as f:
|
||
|
|
html = f.read()
|
||
|
|
new_html = re.sub(pattern, new_para, html, flags=re.DOTALL)
|
||
|
|
with open(page, "w") as f:
|
||
|
|
f.write(new_html)
|
||
|
|
except FileNotFoundError:
|
||
|
|
pass
|
||
|
|
|
||
|
|
print(f"Updated: {n} {session_word}, {total_energy} Wh, {total_co2}g CO2, {cost_display}")
|
||
|
|
PYEOF
|