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
- 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();