mirror of
https://fastgit.cc/github.com/HKUDS/CLI-Anything
synced 2026-04-30 22:02:01 +08:00
Fix analytics
This commit is contained in:
@@ -2097,30 +2097,30 @@
|
||||
<div class="analytics-title">Website Visits</div>
|
||||
<div class="metric-cell total">
|
||||
<span class="metric-label">Total</span>
|
||||
<strong id="stat-total" class="metric-value">21,123</strong>
|
||||
<strong id="stat-total" class="metric-value">...</strong>
|
||||
</div>
|
||||
<div class="metric-cell human">
|
||||
<span class="metric-label"><span class="metric-dot" aria-hidden="true"></span>Human</span>
|
||||
<strong id="stat-human" class="metric-value">7,689</strong>
|
||||
<strong id="stat-human" class="metric-value">...</strong>
|
||||
</div>
|
||||
<div class="metric-cell agent">
|
||||
<span class="metric-label"><span class="metric-dot" aria-hidden="true"></span>Agent</span>
|
||||
<strong id="stat-agent" class="metric-value">13,434</strong>
|
||||
<strong id="stat-agent" class="metric-value">...</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="analytics-row">
|
||||
<div class="analytics-title">CLI-Hub Calls</div>
|
||||
<div class="analytics-title">CLI-Hub Usage</div>
|
||||
<div class="metric-cell total">
|
||||
<span class="metric-label">Total</span>
|
||||
<strong id="stat-cli-total" class="metric-value">16,361</strong>
|
||||
<strong id="stat-cli-total" class="metric-value">...</strong>
|
||||
</div>
|
||||
<div class="metric-cell human">
|
||||
<span class="metric-label"><span class="metric-dot" aria-hidden="true"></span>Human</span>
|
||||
<strong id="stat-cli-human" class="metric-value">3,237</strong>
|
||||
<strong id="stat-cli-human" class="metric-value">...</strong>
|
||||
</div>
|
||||
<div class="metric-cell agent">
|
||||
<span class="metric-label"><span class="metric-dot" aria-hidden="true"></span>Agent</span>
|
||||
<strong id="stat-cli-agent" class="metric-value">13,124</strong>
|
||||
<strong id="stat-cli-agent" class="metric-value">...</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -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();
|
||||
|
||||
@@ -2087,37 +2087,37 @@
|
||||
<div class="analytics-report-grid">
|
||||
<article class="analytics-stat">
|
||||
<span class="analytics-stat-label">Website visits</span>
|
||||
<strong id="stat-total" class="analytics-stat-value">21,123</strong>
|
||||
<strong id="stat-total" class="analytics-stat-value">...</strong>
|
||||
<div class="analytics-bar" aria-hidden="true">
|
||||
<span class="bar-human" id="bar-site-human" style="width: 36.4%"></span>
|
||||
<span class="bar-agent" id="bar-site-agent" style="width: 63.6%"></span>
|
||||
<span class="bar-human" id="bar-site-human" style="width: 0%"></span>
|
||||
<span class="bar-agent" id="bar-site-agent" style="width: 0%"></span>
|
||||
</div>
|
||||
<dl class="analytics-split">
|
||||
<div class="analytics-split-item">
|
||||
<dt><span class="analytics-dot human"></span>Human</dt>
|
||||
<dd><span id="stat-human" class="analytics-split-num">7,689</span><span id="pct-site-human" class="analytics-split-pct">36%</span></dd>
|
||||
<dd><span id="stat-human" class="analytics-split-num">...</span><span id="pct-site-human" class="analytics-split-pct">...</span></dd>
|
||||
</div>
|
||||
<div class="analytics-split-item">
|
||||
<dt><span class="analytics-dot agent"></span>Agent</dt>
|
||||
<dd><span id="stat-agent" class="analytics-split-num">13,434</span><span id="pct-site-agent" class="analytics-split-pct">64%</span></dd>
|
||||
<dd><span id="stat-agent" class="analytics-split-num">...</span><span id="pct-site-agent" class="analytics-split-pct">...</span></dd>
|
||||
</div>
|
||||
</dl>
|
||||
</article>
|
||||
<article class="analytics-stat">
|
||||
<span class="analytics-stat-label">CLI-Hub calls</span>
|
||||
<strong id="stat-cli-total" class="analytics-stat-value">16,361</strong>
|
||||
<span class="analytics-stat-label">CLI-Hub usage</span>
|
||||
<strong id="stat-cli-total" class="analytics-stat-value">...</strong>
|
||||
<div class="analytics-bar" aria-hidden="true">
|
||||
<span class="bar-human" id="bar-cli-human" style="width: 19.8%"></span>
|
||||
<span class="bar-agent" id="bar-cli-agent" style="width: 80.2%"></span>
|
||||
<span class="bar-human" id="bar-cli-human" style="width: 0%"></span>
|
||||
<span class="bar-agent" id="bar-cli-agent" style="width: 0%"></span>
|
||||
</div>
|
||||
<dl class="analytics-split">
|
||||
<div class="analytics-split-item">
|
||||
<dt><span class="analytics-dot human"></span>Human</dt>
|
||||
<dd><span id="stat-cli-human" class="analytics-split-num">3,237</span><span id="pct-cli-human" class="analytics-split-pct">20%</span></dd>
|
||||
<dd><span id="stat-cli-human" class="analytics-split-num">...</span><span id="pct-cli-human" class="analytics-split-pct">...</span></dd>
|
||||
</div>
|
||||
<div class="analytics-split-item">
|
||||
<dt><span class="analytics-dot agent"></span>Agent</dt>
|
||||
<dd><span id="stat-cli-agent" class="analytics-split-num">13,124</span><span id="pct-cli-agent" class="analytics-split-pct">80%</span></dd>
|
||||
<dd><span id="stat-cli-agent" class="analytics-split-num">...</span><span id="pct-cli-agent" class="analytics-split-pct">...</span></dd>
|
||||
</div>
|
||||
</dl>
|
||||
</article>
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user