From ab2556360f1a740bd0f1b0a968d9508b6043fc12 Mon Sep 17 00:00:00 2001 From: yuhao Date: Fri, 24 Apr 2026 10:15:28 +0000 Subject: [PATCH] Fix analytics --- docs/hub/index-modern.html | 95 ++++++++++---------------------- docs/hub/index.html | 107 ++++++++++++------------------------- 2 files changed, 62 insertions(+), 140 deletions(-) diff --git a/docs/hub/index-modern.html b/docs/hub/index-modern.html index 408e24452..43d747748 100644 --- a/docs/hub/index-modern.html +++ b/docs/hub/index-modern.html @@ -2097,30 +2097,30 @@
Website Visits
Total - 21,123 + ...
Human - 7,689 + ...
Agent - 13,434 + ...
-
CLI-Hub Calls
+
CLI-Hub Usage
Total - 16,361 + ...
Human - 3,237 + ...
Agent - 13,124 + ...
@@ -2633,13 +2633,11 @@ const ANALYTICS_PROVIDER = 'posthog'; const ANALYTICS_SITE = 'clianything.cc'; const COUNTED_HOSTS = new Set(['clianything.cc', 'www.clianything.cc']); - const BASELINE_STATS = { - total: 21123, - human: 7689, - agent: 13434, - cli_hub_total: 16361, - cli_hub_human: 3237, - cli_hub_agent: 13124, + // Fixed Umami baseline from remote main. Runtime PostHog counts are + // queried live and added to this. + const UMAMI_BASELINE_STATS = { + human: 7689, agent: 13434, + cli_hub_human: 3237, cli_hub_agent: 13124, }; const ANALYTICS_TEST_RUN = new URLSearchParams(location.search).get('analytics_test_run') || ''; const POSTHOG_CONFIG = { @@ -2910,20 +2908,14 @@ }); // ── Live analytics via Cloudflare Worker proxy ── - // PostHog key and Umami key live as Worker secrets (repo: - // ~/clianything-analytics-worker). Both sources are queried every - // refresh and summed, so pre-migration Umami events and post-migration - // PostHog events stay counted forever. + // PostHog key lives as a Worker secret (repo: + // ~/clianything-analytics-worker). Runtime PostHog counts are queried + // every refresh and added to the fixed pre-migration Umami baseline. const ANALYTICS_PROXY_URL = 'https://analytics.clianything.cc/'; - const UMAMI_SITE_IDS = [ - '07082d05-efd3-4f85-a7a1-b426b0e8bfaa', // hkuds.github.io - 'a076c661-bed1-405c-a522-813794e688b4', // clianything.cc - ]; const LIVE_STATS_REFRESH_MS = 60_000; const ZERO_STATS = { human: 0, agent: 0, cli_hub_human: 0, cli_hub_agent: 0, - cli_install: 0, cli_uninstall: 0, cli_launch: 0, }; async function fetchPosthogTotals() { @@ -2932,10 +2924,7 @@ "countIf(event = 'visit-human' and properties.source = 'web' and properties.site = 'clianything.cc') as human, " + "countIf(event = 'visit-agent' and properties.source = 'web' and properties.site = 'clianything.cc') as agent, " + "countIf(event = 'cli-hub call' and properties.source = 'cli' and properties.channel = 'cli-hub' and properties.is_agent = false) as cli_hub_human, " + - "countIf(event = 'cli-hub call' and properties.source = 'cli' and properties.channel = 'cli-hub' and properties.is_agent = true) as cli_hub_agent, " + - "countIf((event = 'cli-install' or like(event, 'cli-install:%')) and properties.source = 'cli') as cli_install, " + - "countIf((event = 'cli-uninstall' or like(event, 'cli-uninstall:%')) and properties.source = 'cli') as cli_uninstall, " + - "countIf(event = 'cli-launch' and properties.source = 'cli') as cli_launch " + + "countIf(event = 'cli-hub call' and properties.source = 'cli' and properties.channel = 'cli-hub' and properties.is_agent = true) as cli_hub_agent " + "from events" ); const response = await fetch(ANALYTICS_PROXY_URL, { @@ -2945,40 +2934,15 @@ }); if (!response.ok) throw new Error('posthog query http ' + response.status); const data = await response.json(); - const row = (data.results && data.results[0]) || [0, 0, 0, 0, 0, 0, 0]; + const row = (data.results && data.results[0]) || [0, 0, 0, 0]; return { human: Number(row[0]) || 0, agent: Number(row[1]) || 0, cli_hub_human: Number(row[2]) || 0, cli_hub_agent: Number(row[3]) || 0, - cli_install: Number(row[4]) || 0, - cli_uninstall: Number(row[5]) || 0, - cli_launch: Number(row[6]) || 0, }; } - async function fetchUmamiTotals() { - const totals = { ...ZERO_STATS }; - const responses = await Promise.all(UMAMI_SITE_IDS.map((site) => - fetch(ANALYTICS_PROXY_URL + 'umami/events?site=' + encodeURIComponent(site)) - .then((r) => r.ok ? r.json() : []) - .catch(() => []) - )); - for (const rows of responses) { - if (!Array.isArray(rows)) continue; - for (const row of rows) { - const name = row && row.x; - const count = Number(row && row.y) || 0; - if (!name || !count) continue; - if (name === 'visit-human') totals.human += count; - else if (name === 'visit-agent') totals.agent += count; - else if (name === 'cli-install' || (typeof name === 'string' && name.startsWith('cli-install:'))) totals.cli_install += count; - else if (name === 'cli-uninstall' || (typeof name === 'string' && name.startsWith('cli-uninstall:'))) totals.cli_uninstall += count; - } - } - return totals; - } - function sumStats(a, b) { const out = {}; for (const key of Object.keys(ZERO_STATS)) out[key] = (a[key] || 0) + (b[key] || 0); @@ -2986,31 +2950,30 @@ } function withDerivedTotals(s) { - // cli_hub_total bundles all cli-hub-sourced events so the footer's - // "cli-hub" number reflects end-to-end activity, not just raw - // invocations. The human/agent split stays tied to cli-hub call - // only, because Umami install/uninstall events don't carry is_agent. + // cli-hub call is emitted once per invocation, so action events like + // install/launch must not be added here or usage would be double-counted. return { ...s, total: s.human + s.agent, - cli_hub_total: s.cli_hub_human + s.cli_hub_agent + s.cli_install + s.cli_uninstall + s.cli_launch, + cli_hub_total: s.cli_hub_human + s.cli_hub_agent, }; } + function hasAnyStats(s) { + return Object.keys(ZERO_STATS).some((key) => (s[key] || 0) > 0); + } + async function refreshVisitorStats() { - const [posthog, umami] = await Promise.allSettled([fetchPosthogTotals(), fetchUmamiTotals()]); - const ph = posthog.status === 'fulfilled' ? posthog.value : null; - const um = umami.status === 'fulfilled' ? umami.value : null; - if (!ph && !um) return false; - const merged = sumStats(ph || ZERO_STATS, um || ZERO_STATS); + const posthog = await fetchPosthogTotals(); + const merged = sumStats(UMAMI_BASELINE_STATS, posthog); + if (!hasAnyStats(merged)) return false; applyVisitorStats(withDerivedTotals(merged)); return true; } async function loadVisitorStats() { - applyVisitorStats(BASELINE_STATS); - await refreshVisitorStats(); - setInterval(refreshVisitorStats, LIVE_STATS_REFRESH_MS); + await refreshVisitorStats().catch(() => false); + setInterval(() => { refreshVisitorStats().catch(() => false); }, LIVE_STATS_REFRESH_MS); } loadVisitorStats(); diff --git a/docs/hub/index.html b/docs/hub/index.html index 1ec1fee1d..b0fec1999 100644 --- a/docs/hub/index.html +++ b/docs/hub/index.html @@ -2087,37 +2087,37 @@
Website visits - 21,123 + ...
Human
-
7,68936%
+
......
Agent
-
13,43464%
+
......
- CLI-Hub calls - 16,361 + CLI-Hub usage + ...
Human
-
3,23720%
+
......
Agent
-
13,12480%
+
......
@@ -2827,13 +2827,11 @@ const ANALYTICS_PROVIDER = 'posthog'; const ANALYTICS_SITE = 'clianything.cc'; const COUNTED_HOSTS = new Set(['clianything.cc', 'www.clianything.cc']); - const BASELINE_STATS = { - total: 21123, - human: 7689, - agent: 13434, - cli_hub_total: 16361, - cli_hub_human: 3237, - cli_hub_agent: 13124, + // Fixed Umami baseline from remote main. Runtime PostHog counts are + // queried live and added to this. + const UMAMI_BASELINE_STATS = { + human: 7689, agent: 13434, + cli_hub_human: 3237, cli_hub_agent: 13124, }; const ANALYTICS_TEST_RUN = new URLSearchParams(location.search).get('analytics_test_run') || ''; const POSTHOG_CONFIG = { @@ -3124,21 +3122,14 @@ }); // ── Live analytics via Cloudflare Worker proxy ── - // PostHog personal key and Umami API key live as Worker secrets - // (repo: ~/clianything-analytics-worker). Both sources are queried - // every refresh and summed, so pre-migration Umami events and - // post-migration PostHog events both stay counted forever. - // BASELINE_STATS is a last-resort fallback when both live fetches fail. + // PostHog personal key lives as a Worker secret (repo: + // ~/clianything-analytics-worker). Runtime PostHog counts are queried + // every refresh and added to the fixed pre-migration Umami baseline. const ANALYTICS_PROXY_URL = 'https://analytics.clianything.cc/'; - const UMAMI_SITE_IDS = [ - '07082d05-efd3-4f85-a7a1-b426b0e8bfaa', // hkuds.github.io - 'a076c661-bed1-405c-a522-813794e688b4', // clianything.cc - ]; const LIVE_STATS_REFRESH_MS = 60_000; const ZERO_STATS = { human: 0, agent: 0, cli_hub_human: 0, cli_hub_agent: 0, - cli_install: 0, cli_uninstall: 0, cli_launch: 0, }; async function fetchPosthogTotals() { @@ -3147,10 +3138,7 @@ "countIf(event = 'visit-human' and properties.source = 'web' and properties.site = 'clianything.cc') as human, " + "countIf(event = 'visit-agent' and properties.source = 'web' and properties.site = 'clianything.cc') as agent, " + "countIf(event = 'cli-hub call' and properties.source = 'cli' and properties.channel = 'cli-hub' and properties.is_agent = false) as cli_hub_human, " + - "countIf(event = 'cli-hub call' and properties.source = 'cli' and properties.channel = 'cli-hub' and properties.is_agent = true) as cli_hub_agent, " + - "countIf((event = 'cli-install' or like(event, 'cli-install:%')) and properties.source = 'cli') as cli_install, " + - "countIf((event = 'cli-uninstall' or like(event, 'cli-uninstall:%')) and properties.source = 'cli') as cli_uninstall, " + - "countIf(event = 'cli-launch' and properties.source = 'cli') as cli_launch " + + "countIf(event = 'cli-hub call' and properties.source = 'cli' and properties.channel = 'cli-hub' and properties.is_agent = true) as cli_hub_agent " + "from events" ); const response = await fetch(ANALYTICS_PROXY_URL, { @@ -3160,43 +3148,15 @@ }); if (!response.ok) throw new Error('posthog query http ' + response.status); const data = await response.json(); - const row = (data.results && data.results[0]) || [0, 0, 0, 0, 0, 0, 0]; + const row = (data.results && data.results[0]) || [0, 0, 0, 0]; return { human: Number(row[0]) || 0, agent: Number(row[1]) || 0, cli_hub_human: Number(row[2]) || 0, cli_hub_agent: Number(row[3]) || 0, - cli_install: Number(row[4]) || 0, - cli_uninstall: Number(row[5]) || 0, - cli_launch: Number(row[6]) || 0, }; } - async function fetchUmamiTotals() { - // Umami events/series returns [{x: eventName, t: timestamp, y: count}, ...]. - // Pre-migration we only emitted visit-human, visit-agent, and - // cli-install:{name} / cli-uninstall:{name} — no cli-hub call, no cli-launch. - const totals = { ...ZERO_STATS }; - const responses = await Promise.all(UMAMI_SITE_IDS.map((site) => - fetch(ANALYTICS_PROXY_URL + 'umami/events?site=' + encodeURIComponent(site)) - .then((r) => r.ok ? r.json() : []) - .catch(() => []) - )); - for (const rows of responses) { - if (!Array.isArray(rows)) continue; - for (const row of rows) { - const name = row && row.x; - const count = Number(row && row.y) || 0; - if (!name || !count) continue; - if (name === 'visit-human') totals.human += count; - else if (name === 'visit-agent') totals.agent += count; - else if (name === 'cli-install' || (typeof name === 'string' && name.startsWith('cli-install:'))) totals.cli_install += count; - else if (name === 'cli-uninstall' || (typeof name === 'string' && name.startsWith('cli-uninstall:'))) totals.cli_uninstall += count; - } - } - return totals; - } - function sumStats(a, b) { const out = {}; for (const key of Object.keys(ZERO_STATS)) out[key] = (a[key] || 0) + (b[key] || 0); @@ -3204,31 +3164,30 @@ } function withDerivedTotals(s) { - // cli_hub_total bundles all cli-hub-sourced events so the footer's - // "cli-hub" number reflects end-to-end activity, not just raw - // invocations. The human/agent split stays tied to cli-hub call - // only, because Umami install/uninstall events don't carry is_agent. + // cli-hub call is emitted once per invocation, so action events like + // install/launch must not be added here or usage would be double-counted. return { ...s, total: s.human + s.agent, - cli_hub_total: s.cli_hub_human + s.cli_hub_agent + s.cli_install + s.cli_uninstall + s.cli_launch, + cli_hub_total: s.cli_hub_human + s.cli_hub_agent, }; } + function hasAnyStats(s) { + return Object.keys(ZERO_STATS).some((key) => (s[key] || 0) > 0); + } + async function refreshVisitorStats() { - const [posthog, umami] = await Promise.allSettled([fetchPosthogTotals(), fetchUmamiTotals()]); - const ph = posthog.status === 'fulfilled' ? posthog.value : null; - const um = umami.status === 'fulfilled' ? umami.value : null; - if (!ph && !um) return false; - const merged = sumStats(ph || ZERO_STATS, um || ZERO_STATS); + const posthog = await fetchPosthogTotals(); + const merged = sumStats(UMAMI_BASELINE_STATS, posthog); + if (!hasAnyStats(merged)) return false; applyVisitorStats(withDerivedTotals(merged)); return true; } async function loadVisitorStats() { - applyVisitorStats(BASELINE_STATS); - await refreshVisitorStats(); - setInterval(refreshVisitorStats, LIVE_STATS_REFRESH_MS); + await refreshVisitorStats().catch(() => false); + setInterval(() => { refreshVisitorStats().catch(() => false); }, LIVE_STATS_REFRESH_MS); } loadVisitorStats();