thumbgate 1.16.19 โ 1.16.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.well-known/mcp/server-card.json +1 -1
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/mcp/server-stdio.js +1 -1
- package/adapters/opencode/opencode.json +1 -1
- package/bin/postinstall.js +2 -2
- package/package.json +1 -1
- package/public/index.html +12 -12
- package/public/numbers.html +11 -11
- package/public/pro.html +5 -5
- package/scripts/billing.js +18 -2
- package/scripts/commercial-offer.js +1 -1
- package/scripts/telemetry-analytics.js +77 -0
- package/src/api/server.js +124 -45
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thumbgate-marketplace",
|
|
3
|
-
"version": "1.16.
|
|
3
|
+
"version": "1.16.20",
|
|
4
4
|
"owner": {
|
|
5
5
|
"name": "Igor Ganapolsky",
|
|
6
6
|
"email": "ig5973700@gmail.com"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"source": "npm",
|
|
14
14
|
"package": "thumbgate"
|
|
15
15
|
},
|
|
16
|
-
"version": "1.16.
|
|
16
|
+
"version": "1.16.20",
|
|
17
17
|
"author": {
|
|
18
18
|
"name": "Igor Ganapolsky"
|
|
19
19
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thumbgate",
|
|
3
3
|
"description": "Type ๐ or ๐ on any agent action. ThumbGate captures it, distills a lesson, and blocks the pattern from repeating. One thumbs-down = the agent physically cannot make that mistake again. 33 pre-action checks, budget enforcement, self-protection, and NIST/SOC2 compliance tags.",
|
|
4
|
-
"version": "1.16.
|
|
4
|
+
"version": "1.16.20",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Igor Ganapolsky"
|
|
7
7
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thumbgate",
|
|
3
|
-
"version": "1.16.
|
|
3
|
+
"version": "1.16.20",
|
|
4
4
|
"description": "ThumbGate โ ๐๐ feedback that teaches your AI agent. Thumbs down a mistake, it never happens again.",
|
|
5
5
|
"homepage": "https://thumbgate-production.up.railway.app",
|
|
6
6
|
"transport": "stdio",
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
"mcpServers": {
|
|
3
3
|
"thumbgate": {
|
|
4
4
|
"command": "npx",
|
|
5
|
-
"args": ["--yes", "--package", "thumbgate@1.16.
|
|
5
|
+
"args": ["--yes", "--package", "thumbgate@1.16.20", "thumbgate", "serve"]
|
|
6
6
|
}
|
|
7
7
|
},
|
|
8
8
|
"hooks": {
|
|
9
9
|
"preToolUse": {
|
|
10
10
|
"command": "npx",
|
|
11
|
-
"args": ["--yes", "--package", "thumbgate@1.16.
|
|
11
|
+
"args": ["--yes", "--package", "thumbgate@1.16.20", "thumbgate", "gate-check"]
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
}
|
|
@@ -216,7 +216,7 @@ const {
|
|
|
216
216
|
finalizeSession: finalizeFeedbackSession,
|
|
217
217
|
} = require('../../scripts/feedback-session');
|
|
218
218
|
|
|
219
|
-
const SERVER_INFO = { name: 'thumbgate-mcp', version: '1.16.
|
|
219
|
+
const SERVER_INFO = { name: 'thumbgate-mcp', version: '1.16.20' };
|
|
220
220
|
const COMMERCE_CATEGORIES = [
|
|
221
221
|
'product_recommendation',
|
|
222
222
|
'brand_compliance',
|
package/bin/postinstall.js
CHANGED
|
@@ -20,8 +20,8 @@ const {
|
|
|
20
20
|
|
|
21
21
|
// Tracked click-through path: /go/pro โ /checkout/pro โ Stripe.
|
|
22
22
|
// This captures UTM attribution in our funnel before handing off to Stripe.
|
|
23
|
-
const PRO_CTA_URL = 'https://thumbgate
|
|
24
|
-
const WORKFLOW_SPRINT_URL = 'https://thumbgate
|
|
23
|
+
const PRO_CTA_URL = 'https://thumbgate.ai/go/pro?utm_source=npm&utm_medium=postinstall&utm_campaign=first_dollar';
|
|
24
|
+
const WORKFLOW_SPRINT_URL = 'https://thumbgate.ai/#workflow-sprint-intake';
|
|
25
25
|
|
|
26
26
|
process.stderr.write(`
|
|
27
27
|
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thumbgate",
|
|
3
|
-
"version": "1.16.
|
|
3
|
+
"version": "1.16.20",
|
|
4
4
|
"description": "Self-improving agent governance: type thumbs-up or thumbs-down on any AI agent action. ThumbGate turns every mistake into a prevention rule and blocks the pattern from repeating. One thumbs-down, never again. 33 pre-action checks, budget enforcement, and self-protection for Claude Code, Cursor, Codex, Gemini CLI, and Amp.",
|
|
5
5
|
"homepage": "https://thumbgate-production.up.railway.app",
|
|
6
6
|
"repository": {
|
package/public/index.html
CHANGED
|
@@ -696,7 +696,7 @@ __GA_BOOTSTRAP__
|
|
|
696
696
|
<div id="pro-pitch" style="max-width:820px;margin:0 auto 32px;padding:24px 28px;background:linear-gradient(180deg,rgba(17,17,19,0.94) 0%,rgba(22,22,24,0.94) 100%);border:1px solid rgba(34,211,238,0.32);border-radius:20px;box-shadow:0 0 0 1px rgba(34,211,238,0.18),0 24px 64px rgba(0,0,0,0.35);text-align:left;">
|
|
697
697
|
<div style="display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;margin-bottom:16px;">
|
|
698
698
|
<div>
|
|
699
|
-
<div style="display:inline-flex;align-items:center;gap:8px;padding:6px 12px;border-radius:999px;border:1px solid rgba(34,211,238,0.25);background:var(--cyan-dim);color:var(--cyan);font-size:11px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;">
|
|
699
|
+
<div style="display:inline-flex;align-items:center;gap:8px;padding:6px 12px;border-radius:999px;border:1px solid rgba(34,211,238,0.25);background:var(--cyan-dim);color:var(--cyan);font-size:11px;font-weight:700;letter-spacing:0.08em;text-transform:uppercase;">Pay-now Pro</div>
|
|
700
700
|
<h2 style="margin:10px 0 4px;font-size:22px;letter-spacing:-0.025em;line-height:1.2;">Go Pro โ one correction, every agent, every session.</h2>
|
|
701
701
|
<p style="margin:0;font-size:13px;color:var(--text-muted);">Personal local dashboard ยท DPO export from real corrections ยท founder support on risky flows.</p>
|
|
702
702
|
</div>
|
|
@@ -717,7 +717,7 @@ __GA_BOOTSTRAP__
|
|
|
717
717
|
<a href="/checkout/pro?utm_source=website&utm_medium=hero_pricing_card&utm_campaign=pro_pack&cta_id=hero_pro_annual&cta_placement=hero_pricing&plan_id=pro&billing_cycle=annual&landing_path=%2F" onclick="posthog.capture('hero_pricing_annual_click',{cta:'choose_annual'})" style="display:inline-flex;align-items:center;gap:6px;padding:11px 20px;background:rgba(17,17,19,0.75);color:var(--text);border:1px solid var(--border);border-radius:999px;text-decoration:none;font-weight:700;font-size:14px;white-space:nowrap;">Choose annual โ</a>
|
|
718
718
|
</div>
|
|
719
719
|
</div>
|
|
720
|
-
<p style="margin:14px 0 0;font-size:11px;color:var(--text-muted);text-align:center;">
|
|
720
|
+
<p style="margin:14px 0 0;font-size:11px;color:var(--text-muted);text-align:center;">Card required ยท billed today ยท cancel anytime ยท your rules and captures stay local. <a href="/go/install" style="color:var(--cyan);text-decoration:none;">Prefer free? Install CLI โ</a></p>
|
|
721
721
|
</div>
|
|
722
722
|
|
|
723
723
|
<div class="hero-paid-path" aria-label="Paid AI agent governance sprint checkout options">
|
|
@@ -773,7 +773,7 @@ __GA_BOOTSTRAP__
|
|
|
773
773
|
<span class="copy-hint">click to copy</span>
|
|
774
774
|
</div>
|
|
775
775
|
<a href="/go/install?utm_source=website&utm_medium=hero_cta&utm_campaign=install_free&cta_id=hero_install_cli&cta_placement=hero" onclick="posthog.capture('hero_install_click',{cta:'install_cli'})" class="btn-gpt-page btn-install-hero" style="font-size:18px;padding:16px 36px;">Install Free CLI</a>
|
|
776
|
-
<a href="/go/pro?utm_source=website&utm_medium=hero_cta&utm_campaign=pro_upgrade&cta_id=hero_go_pro&cta_placement=hero&plan_id=pro&landing_path=%2F" onclick="posthog.capture('hero_pro_click',{cta:'go_pro'})" class="btn-pro-page" style="font-size:18px;padding:16px 32px;background:linear-gradient(135deg,#f59e0b 0%,#ef4444 100%);color:#fff;font-weight:700;border-radius:10px;box-shadow:0 0 32px rgba(239,68,68,0.35);display:inline-flex;align-items:center;gap:8px;">Start
|
|
776
|
+
<a href="/go/pro?utm_source=website&utm_medium=hero_cta&utm_campaign=pro_upgrade&cta_id=hero_go_pro&cta_placement=hero&plan_id=pro&landing_path=%2F" onclick="posthog.capture('hero_pro_click',{cta:'go_pro'})" class="btn-pro-page" style="font-size:18px;padding:16px 32px;background:linear-gradient(135deg,#f59e0b 0%,#ef4444 100%);color:#fff;font-weight:700;border-radius:10px;box-shadow:0 0 32px rgba(239,68,68,0.35);display:inline-flex;align-items:center;gap:8px;">Start Pro โ $19/mo <span style="font-size:12px;opacity:0.85;font-weight:500;">โ</span></a>
|
|
777
777
|
<div class="hero-secondary-ctas" style="display:flex;gap:10px;flex-wrap:wrap;justify-content:center;margin-top:8px;opacity:0.7;">
|
|
778
778
|
<a href="https://github.com/IgorGanapolsky/ThumbGate/releases/latest/download/thumbgate-claude-desktop.mcpb" class="btn-gpt-page" target="_blank" rel="noopener" onclick="posthog.capture('hero_claude_extension_click',{cta:'install_claude_extension'})" style="font-size:12px;padding:8px 16px;background:#d97706;color:#fff;box-shadow:none;">Install Claude Extension</a>
|
|
779
779
|
<a href="/go/github?utm_source=website&utm_medium=hero_cta&utm_campaign=github_repo&cta_id=hero_star_github&cta_placement=hero" target="_blank" rel="noopener" class="btn-free" style="display:inline-flex;align-items:center;gap:6px;padding:8px 14px;border-radius:999px;font-size:12px;">โญ Star on GitHub</a>
|
|
@@ -792,7 +792,7 @@ __GA_BOOTSTRAP__
|
|
|
792
792
|
<div id="demo" style="margin:28px auto 0;max-width:560px;text-align:center;" onclick="posthog.capture('hero_demo_view')">
|
|
793
793
|
<div style="font-size:13px;color:var(--text-muted);margin-bottom:8px;letter-spacing:0.04em;text-transform:uppercase;">โถ 90-second demo ยท force-push โ ๐ โ blocked</div>
|
|
794
794
|
<video src="/assets/tiktok-agent-memory.mp4" controls playsinline preload="metadata" poster="/assets/instagram-card.png" style="width:100%;max-width:560px;border-radius:12px;border:1px solid var(--border);background:#000;box-shadow:0 10px 40px rgba(0,0,0,0.4);"></video>
|
|
795
|
-
<a href="/checkout/pro?utm_source=website&utm_medium=hero_demo&utm_campaign=pro_trial&cta_id=hero_post_demo" onclick="posthog.capture('hero_cta_click',{cta:'start_trial_post_demo'})" style="display:inline-block;margin-top:14px;color:var(--cyan);font-size:14px;font-weight:700;text-decoration:none;">โ Start
|
|
795
|
+
<a href="/checkout/pro?utm_source=website&utm_medium=hero_demo&utm_campaign=pro_trial&cta_id=hero_post_demo" onclick="posthog.capture('hero_cta_click',{cta:'start_trial_post_demo'})" style="display:inline-block;margin-top:14px;color:var(--cyan);font-size:14px;font-weight:700;text-decoration:none;">โ Start Pro</a>
|
|
796
796
|
</div>
|
|
797
797
|
<p style="font-size:13px;color:var(--text-muted);margin:8px auto 28px;max-width:560px;">Your agent has no memory. Every session, the same wrong pattern runs. ThumbGate turns a single correction into a permanent block โ before the next tool call fires. <a href="#pricing" style="color:var(--cyan);text-decoration:none;">See all plans โ</a></p>
|
|
798
798
|
<div class="first-check-card" id="first-check">
|
|
@@ -1300,7 +1300,7 @@ __GA_BOOTSTRAP__
|
|
|
1300
1300
|
</div>
|
|
1301
1301
|
<div class="autoresearch-cta">
|
|
1302
1302
|
<a class="primary" href="/guides/autoresearch-agent-safety?utm_source=website&utm_medium=autoresearch_pack&utm_campaign=autoresearch_safety&cta_id=autoresearch_guide&cta_placement=autoresearch_pack">Read the Autoresearch guide</a>
|
|
1303
|
-
<a class="secondary" href="/checkout/pro?utm_source=website&utm_medium=autoresearch_pack&utm_campaign=autoresearch_safety&cta_id=autoresearch_pro_trial&cta_placement=autoresearch_pack&plan_id=pro&landing_path=%2F">Start Pro
|
|
1303
|
+
<a class="secondary" href="/checkout/pro?utm_source=website&utm_medium=autoresearch_pack&utm_campaign=autoresearch_safety&cta_id=autoresearch_pro_trial&cta_placement=autoresearch_pack&plan_id=pro&landing_path=%2F">Start Pro</a>
|
|
1304
1304
|
</div>
|
|
1305
1305
|
</div>
|
|
1306
1306
|
</div>
|
|
@@ -1309,7 +1309,7 @@ __GA_BOOTSTRAP__
|
|
|
1309
1309
|
<!-- HOW IT WORKS -->
|
|
1310
1310
|
<section class="how-it-works" id="how-it-works">
|
|
1311
1311
|
<div class="container">
|
|
1312
|
-
<div class="section-label">New in v1.16.
|
|
1312
|
+
<div class="section-label">New in v1.16.20</div>
|
|
1313
1313
|
<h2 class="section-title">Three steps to stop repeated AI failures</h2>
|
|
1314
1314
|
<div class="steps">
|
|
1315
1315
|
<div class="step">
|
|
@@ -1481,7 +1481,7 @@ __GA_BOOTSTRAP__
|
|
|
1481
1481
|
<div class="price-sub">or $149/yr (save 35%) ยท Personal dashboard + enforcement proof</div>
|
|
1482
1482
|
<p style="font-size:13px;color:var(--cyan);margin-bottom:16px;font-weight:500;">Unlimited captures, unlimited rules, full recall. $19/mo costs less than 20 minutes of re-fixing a mistake your agent already learned to avoid.</p>
|
|
1483
1483
|
<div class="pro-upgrade-triggers" style="font-size:12px;color:#aaa;margin-bottom:12px;">
|
|
1484
|
-
<strong style="color:#fff;">
|
|
1484
|
+
<strong style="color:#fff;">Card required.</strong> Billed today. Cancel anytime. Your rules and captures stay local.
|
|
1485
1485
|
</div>
|
|
1486
1486
|
<div class="dashboard-preview" style="margin-bottom:16px;border:1px solid #333;border-radius:8px;overflow:hidden;">
|
|
1487
1487
|
<div style="background:linear-gradient(135deg,#1a1a2e 0%,#16213e 100%);padding:16px;text-align:center;">
|
|
@@ -1504,13 +1504,13 @@ __GA_BOOTSTRAP__
|
|
|
1504
1504
|
<li>Personal local dashboard โ every Pro user gets a localhost dashboard without extra cloud setup</li>
|
|
1505
1505
|
<li>Review-ready workflow support โ we help you wire the riskiest flows first: migrations, force-pushes, deploys, and CI</li>
|
|
1506
1506
|
</ul>
|
|
1507
|
-
<div class="trial-badge" style="background:var(--cyan);color:#000;display:inline-block;padding:4px 12px;border-radius:12px;font-size:12px;font-weight:700;margin-bottom:12px;">
|
|
1507
|
+
<div class="trial-badge" style="background:var(--cyan);color:#000;display:inline-block;padding:4px 12px;border-radius:12px;font-size:12px;font-weight:700;margin-bottom:12px;">PAY-NOW PRO</div>
|
|
1508
1508
|
<div style="display:flex;gap:8px;margin-bottom:12px;">
|
|
1509
1509
|
<input type="email" id="pro-email" data-buyer-email placeholder="you@company.com" style="flex:1;padding:10px 12px;border:1px solid var(--border);border-radius:8px;background:var(--bg-raised);color:var(--text);font-size:14px;">
|
|
1510
|
-
<a href="/go/pro?utm_source=website&utm_medium=pricing_card&utm_campaign=pro_upgrade&cta_id=pricing_pro_upgrade&cta_placement=pricing&plan_id=pro&landing_path=%2F" id="pro-checkout-link" class="btn-pro" onclick="handleProTrial();return false;" style="display:flex;align-items:center;padding:10px 20px;font-size:14px;white-space:nowrap;">
|
|
1510
|
+
<a href="/go/pro?utm_source=website&utm_medium=pricing_card&utm_campaign=pro_upgrade&cta_id=pricing_pro_upgrade&cta_placement=pricing&plan_id=pro&landing_path=%2F" id="pro-checkout-link" class="btn-pro" onclick="handleProTrial();return false;" style="display:flex;align-items:center;padding:10px 20px;font-size:14px;white-space:nowrap;">Upgrade now</a>
|
|
1511
1511
|
</div>
|
|
1512
1512
|
<a href="/go/pro?utm_source=website&utm_medium=pricing_card&utm_campaign=pro_upgrade&cta_id=pricing_pro_upgrade&cta_placement=pricing&plan_id=pro&landing_path=%2F" class="btn-pro" onclick="posthog.capture('pricing_cta_click',{cta:'pro_upgrade',plan:'pro'})" style="display:block;width:100%;text-align:center;padding:12px;font-size:15px;">Upgrade to Pro โ $19/mo</a>
|
|
1513
|
-
<p style="font-size:11px;color:#666;margin-top:8px;">
|
|
1513
|
+
<p style="font-size:11px;color:#666;margin-top:8px;">Card required. Billed today. Cancel anytime. Your rules and captures stay local.</p>
|
|
1514
1514
|
</div>
|
|
1515
1515
|
<div class="price-card team">
|
|
1516
1516
|
<div class="tier">Team</div>
|
|
@@ -1656,7 +1656,7 @@ __GA_BOOTSTRAP__
|
|
|
1656
1656
|
</div>
|
|
1657
1657
|
<div class="faq-item">
|
|
1658
1658
|
<div class="faq-q" role="button" tabindex="0" aria-expanded="false" onclick="toggleFaq(this)" onkeydown="handleFaqKeydown(event)">What does Pro cost?</div>
|
|
1659
|
-
<div class="faq-a">Pro is $19/mo or $149/yr for individual operators and
|
|
1659
|
+
<div class="faq-a">Pro is $19/mo or $149/yr for individual operators and bills immediately through Stripe so the self-serve path can create revenue today. Team is $49/seat/mo with a 3-seat minimum and starts with the Workflow Hardening Sprint intake, not a self-serve trial.</div>
|
|
1660
1660
|
</div>
|
|
1661
1661
|
</div>
|
|
1662
1662
|
</div>
|
|
@@ -1708,7 +1708,7 @@ __GA_BOOTSTRAP__
|
|
|
1708
1708
|
<a href="https://www.linkedin.com/in/igorganapolsky" target="_blank" rel="noopener">LinkedIn</a>
|
|
1709
1709
|
<a href="/blog">Blog</a>
|
|
1710
1710
|
</div>
|
|
1711
|
-
<span class="footer-copy">ยฉ 2026 Max Smith KDP LLC ยท MIT License ยท v1.16.
|
|
1711
|
+
<span class="footer-copy">ยฉ 2026 Max Smith KDP LLC ยท MIT License ยท v1.16.20</span>
|
|
1712
1712
|
</div>
|
|
1713
1713
|
</footer>
|
|
1714
1714
|
|
package/public/numbers.html
CHANGED
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"alternateName": "thumbgate",
|
|
26
26
|
"applicationCategory": "DeveloperApplication",
|
|
27
27
|
"operatingSystem": "Cross-platform, Node.js >=18.18.0",
|
|
28
|
-
"softwareVersion": "1.16.
|
|
28
|
+
"softwareVersion": "1.16.20",
|
|
29
29
|
"url": "https://thumbgate-production.up.railway.app/numbers",
|
|
30
|
-
"dateModified": "2026-05-
|
|
30
|
+
"dateModified": "2026-05-05",
|
|
31
31
|
"creator": {
|
|
32
32
|
"@type": "Person",
|
|
33
33
|
"name": "Igor Ganapolsky",
|
|
@@ -57,8 +57,8 @@
|
|
|
57
57
|
"https://www.linkedin.com/in/igorganapolsky"
|
|
58
58
|
]
|
|
59
59
|
},
|
|
60
|
-
"dateModified": "2026-05-
|
|
61
|
-
"datePublished": "2026-05-
|
|
60
|
+
"dateModified": "2026-05-05",
|
|
61
|
+
"datePublished": "2026-05-05",
|
|
62
62
|
"keywords": [
|
|
63
63
|
"AI agent gates",
|
|
64
64
|
"LLM token savings",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
{
|
|
71
71
|
"@type": "PropertyValue",
|
|
72
72
|
"name": "active_gates",
|
|
73
|
-
"value":
|
|
73
|
+
"value": 37
|
|
74
74
|
},
|
|
75
75
|
{
|
|
76
76
|
"@type": "PropertyValue",
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
{
|
|
102
102
|
"@type": "PropertyValue",
|
|
103
103
|
"name": "bayes_error_rate",
|
|
104
|
-
"value":
|
|
104
|
+
"value": 0
|
|
105
105
|
}
|
|
106
106
|
]
|
|
107
107
|
}
|
|
@@ -190,14 +190,14 @@
|
|
|
190
190
|
<main class="container">
|
|
191
191
|
<h1>The Numbers</h1>
|
|
192
192
|
<p class="subtitle">Generated first-party operational data from the ThumbGate runtime. No surveys or projections โ this page is a release-time snapshot produced by the same local scripts that power the CLI and dashboard.</p>
|
|
193
|
-
<div class="freshness">Updated: 2026-05-
|
|
193
|
+
<div class="freshness">Updated: 2026-05-05 ยท Version 1.16.20</div>
|
|
194
194
|
|
|
195
195
|
<h2>Gate enforcement</h2>
|
|
196
196
|
<div class="stats-grid">
|
|
197
197
|
<div class="stat-card">
|
|
198
198
|
<div class="stat-label">Active gates</div>
|
|
199
|
-
<div class="stat-value">
|
|
200
|
-
<div class="stat-sub">36 manual ยท
|
|
199
|
+
<div class="stat-value">37</div>
|
|
200
|
+
<div class="stat-sub">36 manual ยท 1 auto-promoted</div>
|
|
201
201
|
<a class="stat-source" href="https://github.com/IgorGanapolsky/ThumbGate/blob/main/scripts/gate-stats.js">source: gate-stats.js</a>
|
|
202
202
|
</div>
|
|
203
203
|
<div class="stat-card">
|
|
@@ -242,7 +242,7 @@
|
|
|
242
242
|
</div>
|
|
243
243
|
<div class="stat-card">
|
|
244
244
|
<div class="stat-label">Scorer Bayes error</div>
|
|
245
|
-
<div class="stat-value">
|
|
245
|
+
<div class="stat-value">0.0%</div>
|
|
246
246
|
<div class="stat-sub">irreducible error given current feature set</div>
|
|
247
247
|
<a class="stat-source" href="https://github.com/IgorGanapolsky/ThumbGate/blob/main/scripts/bayes-optimal-gate.js">source: bayes-optimal-gate.js</a>
|
|
248
248
|
</div>
|
|
@@ -264,7 +264,7 @@
|
|
|
264
264
|
<div class="cta">
|
|
265
265
|
<a href="https://www.npmjs.com/package/thumbgate">Install ThumbGate โ npx thumbgate init</a>
|
|
266
266
|
<div class="footer-note">Prefer the raw feed? See <a href="https://github.com/IgorGanapolsky/ThumbGate">GitHub</a> or run <code>npm run gate:stats</code> locally.</div>
|
|
267
|
-
<div class="footer-note">Generated at 2026-05-
|
|
267
|
+
<div class="footer-note">Generated at 2026-05-05T14:49:47.056Z UTC.</div>
|
|
268
268
|
</div>
|
|
269
269
|
</main>
|
|
270
270
|
</body>
|
package/public/pro.html
CHANGED
|
@@ -796,7 +796,7 @@ __GA_BOOTSTRAP__
|
|
|
796
796
|
<a href="#pricing">Pricing</a>
|
|
797
797
|
<a href="#faq">FAQ</a>
|
|
798
798
|
<a href="/dashboard">Demo</a>
|
|
799
|
-
<a class="nav-cta btn-pro-checkout" href="/checkout/pro?utm_source=website&utm_medium=pro_page_nav&utm_campaign=pro_pack&cta_id=pro_page_nav&cta_placement=nav&plan_id=pro&landing_path=%2Fpro">Start
|
|
799
|
+
<a class="nav-cta btn-pro-checkout" href="/checkout/pro?utm_source=website&utm_medium=pro_page_nav&utm_campaign=pro_pack&cta_id=pro_page_nav&cta_placement=nav&plan_id=pro&landing_path=%2Fpro">Start Pro Now</a>
|
|
800
800
|
</div>
|
|
801
801
|
</div>
|
|
802
802
|
</nav>
|
|
@@ -816,7 +816,7 @@ __GA_BOOTSTRAP__
|
|
|
816
816
|
<div class="proof-pill">Founder support on risky flows</div>
|
|
817
817
|
</div>
|
|
818
818
|
<div class="hero-actions">
|
|
819
|
-
<a class="btn-primary btn-pro-checkout" href="/checkout/pro?utm_source=website&utm_medium=pro_page_hero&utm_campaign=pro_pack&cta_id=pro_page_primary&cta_placement=hero&plan_id=pro&landing_path=%2Fpro">Start
|
|
819
|
+
<a class="btn-primary btn-pro-checkout" href="/checkout/pro?utm_source=website&utm_medium=pro_page_hero&utm_campaign=pro_pack&cta_id=pro_page_primary&cta_placement=hero&plan_id=pro&landing_path=%2Fpro">Start Pro Now</a>
|
|
820
820
|
<a class="btn-secondary btn-demo" href="/dashboard?utm_source=website&utm_medium=pro_page&utm_campaign=pro_pack">Open dashboard demo</a>
|
|
821
821
|
<a class="btn-ghost btn-free-path" href="/guide?utm_source=website&utm_medium=pro_page&utm_campaign=free_install">Stay on Free and install locally</a>
|
|
822
822
|
</div>
|
|
@@ -860,7 +860,7 @@ __GA_BOOTSTRAP__
|
|
|
860
860
|
<input type="email" name="email" data-buyer-email placeholder="you@company.com" required>
|
|
861
861
|
<div class="buyer-form-actions">
|
|
862
862
|
<button type="submit" class="btn-secondary">Email me the demo</button>
|
|
863
|
-
<button type="button" class="btn-primary btn-email-checkout">Start
|
|
863
|
+
<button type="button" class="btn-primary btn-email-checkout">Start Pro with this email</button>
|
|
864
864
|
</div>
|
|
865
865
|
<p class="buyer-form-note">No sales deck. Just the Pro demo path, launch updates, and a prefilled checkout when you are ready.</p>
|
|
866
866
|
<p class="buyer-form-status" data-newsletter-status aria-live="polite"></p>
|
|
@@ -962,7 +962,7 @@ __GA_BOOTSTRAP__
|
|
|
962
962
|
<div class="section-label" style="text-align:left;margin-bottom:8px;">Pricing</div>
|
|
963
963
|
<h3>ThumbGate Pro</h3>
|
|
964
964
|
<div class="price">$19<span>/mo</span></div>
|
|
965
|
-
<div class="annual">$149/year available ยท
|
|
965
|
+
<div class="annual">$149/year available ยท Card required ยท billed today</div>
|
|
966
966
|
<p class="pricing-note">For the individual operator who wants a personal local dashboard, DPO export, review-ready evidence, and founder help on the first risky workflow.</p>
|
|
967
967
|
<ul>
|
|
968
968
|
<li><strong>Personal local dashboard</strong> โ inspect blocked actions, active checks, and lesson evidence without a cloud account.</li>
|
|
@@ -971,7 +971,7 @@ __GA_BOOTSTRAP__
|
|
|
971
971
|
<li><strong>Founder support</strong> โ get help hardening the first force-push, deploy, migration, or CI failure that keeps repeating.</li>
|
|
972
972
|
</ul>
|
|
973
973
|
<div class="pricing-actions">
|
|
974
|
-
<a class="btn-primary btn-pro-checkout" href="/checkout/pro?utm_source=website&utm_medium=pro_page_pricing&utm_campaign=pro_pack&cta_id=pricing_pro&cta_placement=pricing&plan_id=pro&landing_path=%2Fpro">Start
|
|
974
|
+
<a class="btn-primary btn-pro-checkout" href="/checkout/pro?utm_source=website&utm_medium=pro_page_pricing&utm_campaign=pro_pack&cta_id=pricing_pro&cta_placement=pricing&plan_id=pro&landing_path=%2Fpro">Start Pro Now</a>
|
|
975
975
|
<a class="btn-secondary btn-pro-checkout" href="/checkout/pro?utm_source=website&utm_medium=pro_page_pricing&utm_campaign=pro_pack&cta_id=pricing_pro_annual&cta_placement=pricing&plan_id=pro&billing_cycle=annual&landing_path=%2Fpro">Choose annual</a>
|
|
976
976
|
</div>
|
|
977
977
|
<div class="pricing-meta">Best for one operator with one repeated failure to prove. Stay on Free if you only need the local install; buy Pro when the dashboard, export, and proof trail save you time.</div>
|
package/scripts/billing.js
CHANGED
|
@@ -2523,6 +2523,23 @@ function buildCheckoutSessionPayload({ successUrl, cancelUrl, customerEmail, che
|
|
|
2523
2523
|
quantity: checkoutSelection.quantity,
|
|
2524
2524
|
}];
|
|
2525
2525
|
|
|
2526
|
+
const explicitTrialDays = normalizeInteger(
|
|
2527
|
+
checkoutMetadata?.trialPeriodDays
|
|
2528
|
+
?? checkoutMetadata?.trial_period_days
|
|
2529
|
+
?? checkoutMetadata?.trialDays
|
|
2530
|
+
?? checkoutMetadata?.trial_days
|
|
2531
|
+
);
|
|
2532
|
+
const trialFlag = (normalizeText(checkoutMetadata?.trial) || '').toLowerCase();
|
|
2533
|
+
const shouldStartTrial = !pack && (
|
|
2534
|
+
explicitTrialDays > 0
|
|
2535
|
+
|| trialFlag === '1'
|
|
2536
|
+
|| trialFlag === 'true'
|
|
2537
|
+
|| trialFlag === 'yes'
|
|
2538
|
+
);
|
|
2539
|
+
const trialPeriodDays = explicitTrialDays > 0
|
|
2540
|
+
? Math.min(explicitTrialDays, 30)
|
|
2541
|
+
: 7;
|
|
2542
|
+
|
|
2526
2543
|
const sessionPayload = {
|
|
2527
2544
|
success_url: successUrl,
|
|
2528
2545
|
cancel_url: cancelUrl,
|
|
@@ -2539,10 +2556,9 @@ function buildCheckoutSessionPayload({ successUrl, cancelUrl, customerEmail, che
|
|
|
2539
2556
|
packId: pack ? pack.id : null,
|
|
2540
2557
|
credits: pack ? pack.credits : null,
|
|
2541
2558
|
}),
|
|
2542
|
-
// Keep the trial, but require a payment method so trials can convert without dunning.
|
|
2543
2559
|
...(pack ? {} : {
|
|
2544
|
-
subscription_data: { trial_period_days: 7 },
|
|
2545
2560
|
payment_method_collection: 'always',
|
|
2561
|
+
...(shouldStartTrial ? { subscription_data: { trial_period_days: trialPeriodDays } } : {}),
|
|
2546
2562
|
}),
|
|
2547
2563
|
};
|
|
2548
2564
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const PRO_MONTHLY_PAYMENT_LINK = 'https://thumbgate
|
|
3
|
+
const PRO_MONTHLY_PAYMENT_LINK = 'https://thumbgate.ai/go/pro?utm_source=offer';
|
|
4
4
|
const PRO_ANNUAL_PAYMENT_LINK = 'https://buy.stripe.com/3cI8wPfCYaPs2dzdKz3sI07';
|
|
5
5
|
|
|
6
6
|
const PRO_MONTHLY_PRICE_ID = 'price_1THQY7GGBpd520QYHoS7RG0J';
|
|
@@ -19,6 +19,7 @@ const MARKETING_CLICK_EVENT_TYPES = new Set([
|
|
|
19
19
|
'cta_click',
|
|
20
20
|
'checkout_start',
|
|
21
21
|
'checkout_bootstrap',
|
|
22
|
+
'checkout_interstitial_cta_clicked',
|
|
22
23
|
'chatgpt_gpt_open',
|
|
23
24
|
'chatgpt_gpt_click',
|
|
24
25
|
'install_guide_click',
|
|
@@ -324,6 +325,7 @@ function sanitizeTelemetryPayload(payload = {}, headers = {}) {
|
|
|
324
325
|
maxScrollPercent: normalizeInteger(raw.maxScrollPercent ?? raw.scrollPercent),
|
|
325
326
|
buyerEmailFocused: Boolean(raw.buyerEmailFocused),
|
|
326
327
|
buyerEmailCaptured: Boolean(raw.buyerEmailCaptured),
|
|
328
|
+
checkoutIntentClassification: pickFirstText(raw.checkoutIntentClassification),
|
|
327
329
|
trafficChannel: inferTrafficChannel(raw, referrerHost),
|
|
328
330
|
failureCode: pickFirstText(raw.failureCode),
|
|
329
331
|
httpStatus: normalizeInteger(raw.httpStatus),
|
|
@@ -490,6 +492,14 @@ function getTelemetrySummary(feedbackDir, options = {}) {
|
|
|
490
492
|
let ctaClicks = 0;
|
|
491
493
|
let ctaImpressions = 0;
|
|
492
494
|
let checkoutStarts = 0;
|
|
495
|
+
let checkoutInterstitialViews = 0;
|
|
496
|
+
let checkoutBotDeflections = 0;
|
|
497
|
+
let checkoutInterstitialClicks = 0;
|
|
498
|
+
let checkoutInterstitialProConfirms = 0;
|
|
499
|
+
let checkoutInterstitialWorkflowIntakeClicks = 0;
|
|
500
|
+
let checkoutInterstitialTeamPathClicks = 0;
|
|
501
|
+
let checkoutInterstitialDiagnosticCheckoutClicks = 0;
|
|
502
|
+
let checkoutInterstitialWorkflowSprintCheckoutClicks = 0;
|
|
493
503
|
let checkoutFailures = 0;
|
|
494
504
|
let checkoutCancelled = 0;
|
|
495
505
|
let checkoutAbandoned = 0;
|
|
@@ -561,6 +571,29 @@ function getTelemetrySummary(feedbackDir, options = {}) {
|
|
|
561
571
|
}
|
|
562
572
|
}
|
|
563
573
|
|
|
574
|
+
if ((entry.eventType || entry.event) === 'checkout_interstitial_view') {
|
|
575
|
+
checkoutInterstitialViews += 1;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
if ((entry.eventType || entry.event) === 'checkout_bot_deflected') {
|
|
579
|
+
checkoutBotDeflections += 1;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
if ((entry.eventType || entry.event) === 'checkout_interstitial_cta_clicked') {
|
|
583
|
+
checkoutInterstitialClicks += 1;
|
|
584
|
+
if (entry.ctaId === 'pro_checkout_confirmed') {
|
|
585
|
+
checkoutInterstitialProConfirms += 1;
|
|
586
|
+
} else if (entry.ctaId === 'workflow_sprint_intake') {
|
|
587
|
+
checkoutInterstitialWorkflowIntakeClicks += 1;
|
|
588
|
+
} else if (entry.ctaId === 'team_paid_path') {
|
|
589
|
+
checkoutInterstitialTeamPathClicks += 1;
|
|
590
|
+
} else if (entry.ctaId === 'sprint_diagnostic_checkout') {
|
|
591
|
+
checkoutInterstitialDiagnosticCheckoutClicks += 1;
|
|
592
|
+
} else if (entry.ctaId === 'workflow_sprint_checkout') {
|
|
593
|
+
checkoutInterstitialWorkflowSprintCheckoutClicks += 1;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
564
597
|
if ((entry.eventType || entry.event) === 'checkout_start' || (entry.eventType || entry.event) === 'checkout_bootstrap') {
|
|
565
598
|
checkoutStarts += 1;
|
|
566
599
|
incrementCounter(checkoutStartsBySource, entry.source);
|
|
@@ -737,11 +770,15 @@ function getTelemetrySummary(feedbackDir, options = {}) {
|
|
|
737
770
|
installCopies,
|
|
738
771
|
gptOpens,
|
|
739
772
|
checkoutStarts,
|
|
773
|
+
checkoutInterstitialViews,
|
|
774
|
+
checkoutInterstitialClicks,
|
|
740
775
|
trialEmails,
|
|
741
776
|
proConversions,
|
|
742
777
|
landingToInstallCopyRate: safeRate(installCopies, pageViews),
|
|
743
778
|
landingToGptOpenRate: safeRate(gptOpens, pageViews),
|
|
744
779
|
landingToCheckoutRate: safeRate(checkoutStarts, pageViews),
|
|
780
|
+
checkoutInterstitialClickRate: safeRate(checkoutInterstitialClicks, checkoutInterstitialViews),
|
|
781
|
+
checkoutInterstitialProConfirmRate: safeRate(checkoutInterstitialProConfirms, checkoutInterstitialViews),
|
|
745
782
|
checkoutToTrialEmailRate: safeRate(trialEmails, checkoutStarts),
|
|
746
783
|
checkoutToProConversionRate: safeRate(proConversions, checkoutStarts),
|
|
747
784
|
},
|
|
@@ -753,6 +790,14 @@ function getTelemetrySummary(feedbackDir, options = {}) {
|
|
|
753
790
|
pageViews,
|
|
754
791
|
ctaClicks,
|
|
755
792
|
checkoutStarts,
|
|
793
|
+
checkoutInterstitialViews,
|
|
794
|
+
checkoutBotDeflections,
|
|
795
|
+
checkoutInterstitialClicks,
|
|
796
|
+
checkoutInterstitialProConfirms,
|
|
797
|
+
checkoutInterstitialWorkflowIntakeClicks,
|
|
798
|
+
checkoutInterstitialTeamPathClicks,
|
|
799
|
+
checkoutInterstitialDiagnosticCheckoutClicks,
|
|
800
|
+
checkoutInterstitialWorkflowSprintCheckoutClicks,
|
|
756
801
|
checkoutFailures,
|
|
757
802
|
checkoutCancelled,
|
|
758
803
|
checkoutAbandoned,
|
|
@@ -915,6 +960,14 @@ function getTelemetryAnalytics(feedbackDir, options = {}) {
|
|
|
915
960
|
ctas: {
|
|
916
961
|
totalClicks: summary.web.ctaClicks,
|
|
917
962
|
checkoutStarts: summary.web.checkoutStarts,
|
|
963
|
+
checkoutInterstitialViews: summary.web.checkoutInterstitialViews,
|
|
964
|
+
checkoutBotDeflections: summary.web.checkoutBotDeflections,
|
|
965
|
+
checkoutInterstitialClicks: summary.web.checkoutInterstitialClicks,
|
|
966
|
+
checkoutInterstitialProConfirms: summary.web.checkoutInterstitialProConfirms,
|
|
967
|
+
checkoutInterstitialWorkflowIntakeClicks: summary.web.checkoutInterstitialWorkflowIntakeClicks,
|
|
968
|
+
checkoutInterstitialTeamPathClicks: summary.web.checkoutInterstitialTeamPathClicks,
|
|
969
|
+
checkoutInterstitialDiagnosticCheckoutClicks: summary.web.checkoutInterstitialDiagnosticCheckoutClicks,
|
|
970
|
+
checkoutInterstitialWorkflowSprintCheckoutClicks: summary.web.checkoutInterstitialWorkflowSprintCheckoutClicks,
|
|
918
971
|
uniqueCheckoutStarters: summary.web.uniqueCheckoutStarters,
|
|
919
972
|
checkoutFailures: summary.web.checkoutFailures,
|
|
920
973
|
checkoutCancelled: summary.web.checkoutCancelled,
|
|
@@ -950,6 +1003,30 @@ function getTelemetryAnalytics(feedbackDir, options = {}) {
|
|
|
950
1003
|
pageViewToCheckoutRate: summary.web.pageViewToCheckoutRate,
|
|
951
1004
|
visitorToCheckoutRate: summary.web.visitorToCheckoutRate,
|
|
952
1005
|
clickToCheckoutRate: safeRate(summary.web.checkoutStarts, summary.web.ctaClicks),
|
|
1006
|
+
checkoutInterstitialClickRate: safeRate(
|
|
1007
|
+
summary.web.checkoutInterstitialClicks,
|
|
1008
|
+
summary.web.checkoutInterstitialViews
|
|
1009
|
+
),
|
|
1010
|
+
checkoutInterstitialProConfirmRate: safeRate(
|
|
1011
|
+
summary.web.checkoutInterstitialProConfirms,
|
|
1012
|
+
summary.web.checkoutInterstitialViews
|
|
1013
|
+
),
|
|
1014
|
+
checkoutInterstitialWorkflowIntakeRate: safeRate(
|
|
1015
|
+
summary.web.checkoutInterstitialWorkflowIntakeClicks,
|
|
1016
|
+
summary.web.checkoutInterstitialViews
|
|
1017
|
+
),
|
|
1018
|
+
checkoutInterstitialTeamPathRate: safeRate(
|
|
1019
|
+
summary.web.checkoutInterstitialTeamPathClicks,
|
|
1020
|
+
summary.web.checkoutInterstitialViews
|
|
1021
|
+
),
|
|
1022
|
+
checkoutInterstitialDiagnosticCheckoutRate: safeRate(
|
|
1023
|
+
summary.web.checkoutInterstitialDiagnosticCheckoutClicks,
|
|
1024
|
+
summary.web.checkoutInterstitialViews
|
|
1025
|
+
),
|
|
1026
|
+
checkoutInterstitialWorkflowSprintCheckoutRate: safeRate(
|
|
1027
|
+
summary.web.checkoutInterstitialWorkflowSprintCheckoutClicks,
|
|
1028
|
+
summary.web.checkoutInterstitialViews
|
|
1029
|
+
),
|
|
953
1030
|
cancellationRate: safeRate(summary.web.checkoutCancelled, summary.web.checkoutStarts),
|
|
954
1031
|
abandonmentRate: safeRate(summary.web.checkoutAbandoned, summary.web.checkoutStarts),
|
|
955
1032
|
paidConfirmationRate: safeRate(summary.web.checkoutPaidConfirmations, summary.web.checkoutStarts),
|
package/src/api/server.js
CHANGED
|
@@ -1453,6 +1453,40 @@ function buildCheckoutFallbackUrl(baseUrl, metadata = {}) {
|
|
|
1453
1453
|
return restoreStripeCheckoutPlaceholder(url.toString());
|
|
1454
1454
|
}
|
|
1455
1455
|
|
|
1456
|
+
function buildCheckoutIntentHref(baseUrl, metadata = {}, overrides = {}) {
|
|
1457
|
+
return buildCheckoutFallbackUrl(baseUrl, {
|
|
1458
|
+
...metadata,
|
|
1459
|
+
...overrides,
|
|
1460
|
+
});
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
function renderCheckoutIntentPage({
|
|
1464
|
+
confirmHref,
|
|
1465
|
+
workflowIntakeHref,
|
|
1466
|
+
teamOptionsHref,
|
|
1467
|
+
diagnosticCheckoutHref,
|
|
1468
|
+
sprintCheckoutHref,
|
|
1469
|
+
sprintDiagnosticPriceDollars = 499,
|
|
1470
|
+
workflowSprintPriceDollars = 1500,
|
|
1471
|
+
}) {
|
|
1472
|
+
const safeConfirmHref = escapeHtmlAttribute(confirmHref);
|
|
1473
|
+
const safeWorkflowIntakeHref = escapeHtmlAttribute(workflowIntakeHref);
|
|
1474
|
+
const safeTeamOptionsHref = escapeHtmlAttribute(teamOptionsHref);
|
|
1475
|
+
const safeDiagnosticCheckoutHref = diagnosticCheckoutHref
|
|
1476
|
+
? escapeHtmlAttribute(diagnosticCheckoutHref)
|
|
1477
|
+
: '';
|
|
1478
|
+
const safeSprintCheckoutHref = sprintCheckoutHref
|
|
1479
|
+
? escapeHtmlAttribute(sprintCheckoutHref)
|
|
1480
|
+
: '';
|
|
1481
|
+
const diagnosticAction = safeDiagnosticCheckoutHref
|
|
1482
|
+
? `<a data-i="sprint_diagnostic_checkout" href="${safeDiagnosticCheckoutHref}">Book $${sprintDiagnosticPriceDollars} diagnostic</a>`
|
|
1483
|
+
: '';
|
|
1484
|
+
const sprintAction = safeSprintCheckoutHref
|
|
1485
|
+
? `<a data-i="workflow_sprint_checkout" href="${safeSprintCheckoutHref}">Start $${workflowSprintPriceDollars} sprint</a>`
|
|
1486
|
+
: '';
|
|
1487
|
+
return `<!doctype html><html lang="en"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><style>body{background:#0a0a0a;color:#eee;font-family:system-ui,sans-serif}div{max-width:560px;margin:12vh auto}a{display:block;margin:10px 0;padding:12px;border:1px solid #374151;color:inherit;text-align:center}.primary{background:#22d3ee;color:#000}</style><div><h1>Choose the right paid path.</h1><p>Pick Pro, diagnostic, sprint, or intake.</p><a class="primary" data-i="pro_checkout_confirmed" href="${safeConfirmHref}">Continue to Stripe</a>${diagnosticAction}${sprintAction}<a data-i="workflow_sprint_intake" href="${safeWorkflowIntakeHref}">Send workflow first</a><a data-i="team_paid_path" href="${safeTeamOptionsHref}">See diagnostic and sprint options</a><p>Stripe checkout.</p><a href="/">Back</a></div><script>addEventListener('click',e=>{let a=e.target.closest('[data-i]');if(a&&navigator.sendBeacon)navigator.sendBeacon('/v1/telemetry/ping',new Blob([JSON.stringify({eventType:'checkout_interstitial_cta_clicked',clientType:'web',page:'/checkout/pro',ctaId:a.dataset.i,ctaPlacement:'checkout_interstitial'})],{type:'application/json'}))})</script>`;
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1456
1490
|
function buildCheckoutBootstrapBody(parsed, req, journeyState = resolveJourneyState(req, parsed)) {
|
|
1457
1491
|
const params = parsed.searchParams;
|
|
1458
1492
|
const traceId = pickFirstText(params.get('trace_id')) || createJourneyId('checkout');
|
|
@@ -1505,6 +1539,26 @@ function buildCheckoutConfirmHref(parsed) {
|
|
|
1505
1539
|
return `${confirmUrl.pathname}${confirmUrl.search}`;
|
|
1506
1540
|
}
|
|
1507
1541
|
|
|
1542
|
+
function normalizeCheckoutCustomerEmail(value) {
|
|
1543
|
+
const email = (normalizeNullableText(value) || '').toLowerCase();
|
|
1544
|
+
const atIndex = email.indexOf('@');
|
|
1545
|
+
const domain = email.slice(atIndex + 1);
|
|
1546
|
+
if (!email || email.length > 254 || atIndex <= 0 || atIndex !== email.lastIndexOf('@') || !domain || !domain.includes('.') || domain.startsWith('.') || domain.endsWith('.') || domain.includes('..')) return null;
|
|
1547
|
+
for (const ch of email) if (ch <= ' ' || ch === '<' || ch === '>' || ch === '"') return null;
|
|
1548
|
+
return email;
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
function renderCheckoutIntentGate(parsed, responseHeaders = {}) {
|
|
1552
|
+
let hiddenInputs = '';
|
|
1553
|
+
for (const [key, value] of parsed.searchParams.entries()) {
|
|
1554
|
+
if (key !== 'confirm' && key !== 'customer_email') hiddenInputs += `<input type=hidden name=${escapeHtmlAttribute(key)} value=${escapeHtmlAttribute(value)}>`;
|
|
1555
|
+
}
|
|
1556
|
+
return {
|
|
1557
|
+
html: `<!doctype html><h1>Email for Stripe receipt</h1><form action=/checkout/pro>${hiddenInputs}<input type=hidden name=confirm value=1><input name=customer_email type=email required><button>Continue</button></form>`,
|
|
1558
|
+
headers: responseHeaders,
|
|
1559
|
+
};
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1508
1562
|
function normalizeTrackedLinkSlug(value) {
|
|
1509
1563
|
return String(value || '').trim().toLowerCase().replace(/[^a-z0-9-]/g, '');
|
|
1510
1564
|
}
|
|
@@ -1746,9 +1800,7 @@ function appendBestEffortTelemetry(feedbackDir, payload, headers, context) {
|
|
|
1746
1800
|
evidence: [err && err.message ? err.message : 'unknown_error'],
|
|
1747
1801
|
},
|
|
1748
1802
|
});
|
|
1749
|
-
} catch (_) {
|
|
1750
|
-
// Public telemetry remains best-effort even when diagnostics fail.
|
|
1751
|
-
}
|
|
1803
|
+
} catch (_) {}
|
|
1752
1804
|
return false;
|
|
1753
1805
|
}
|
|
1754
1806
|
}
|
|
@@ -2055,7 +2107,6 @@ a{color:#22d3ee;text-decoration:none}</style></head><body>
|
|
|
2055
2107
|
const timestamp = merged.timestamp ? new Date(merged.timestamp).toLocaleString() : '';
|
|
2056
2108
|
const isoTimestamp = merged.timestamp || '';
|
|
2057
2109
|
|
|
2058
|
-
// Technical metadata
|
|
2059
2110
|
const failureType = merged.failureType || null;
|
|
2060
2111
|
const skill = merged.skill || null;
|
|
2061
2112
|
const source = merged.source || fb.source || null;
|
|
@@ -2066,19 +2117,12 @@ a{color:#22d3ee;text-decoration:none}</style></head><body>
|
|
|
2066
2117
|
const guardrails = merged.guardrails || null;
|
|
2067
2118
|
const rubricScores = merged.rubricScores || null;
|
|
2068
2119
|
|
|
2069
|
-
// Structured rule
|
|
2070
2120
|
const rule = merged.structuredRule || merged.rule || null;
|
|
2071
|
-
// Conversation window
|
|
2072
2121
|
const convoWindow = merged.conversationWindow || merged.chatHistory || [];
|
|
2073
|
-
// Reflector analysis
|
|
2074
2122
|
const reflector = merged.reflectorAnalysis || merged.reflector || null;
|
|
2075
|
-
// Diagnosis
|
|
2076
2123
|
const diagnosis = merged.diagnosis || null;
|
|
2077
|
-
// Rubric
|
|
2078
2124
|
const rubric = merged.rubricEvaluation || merged.rubric || null;
|
|
2079
|
-
// Synthesis
|
|
2080
2125
|
const synthesis = merged.synthesis || null;
|
|
2081
|
-
// Bayesian
|
|
2082
2126
|
const bayesian = merged.bayesianBelief || merged.bayesian || null;
|
|
2083
2127
|
|
|
2084
2128
|
function sectionCard(titleText, content, id) {
|
|
@@ -2501,14 +2545,6 @@ function servePublicMarketingPage({
|
|
|
2501
2545
|
'landing_page_view'
|
|
2502
2546
|
);
|
|
2503
2547
|
|
|
2504
|
-
// Funnel-ledger write (2026-04-21): populate funnel-events.jsonl with a
|
|
2505
|
-
// discovery-stage event on every landing-page view so UTM-tagged social
|
|
2506
|
-
// traffic becomes visible in `npm run feedback:summary` and
|
|
2507
|
-
// `bin/cli.js cfo --today`. Prior to this wire, landing views wrote only
|
|
2508
|
-
// to telemetry-pings.jsonl (invisible to the CEO-facing revenue surface),
|
|
2509
|
-
// leaving funnel-events.jsonl empty despite 404 published Zernio posts.
|
|
2510
|
-
// Best-effort: wrapped in try/catch so a billing-ledger hiccup never
|
|
2511
|
-
// breaks a page render.
|
|
2512
2548
|
try {
|
|
2513
2549
|
appendFunnelEvent({
|
|
2514
2550
|
stage: 'discovery',
|
|
@@ -3361,10 +3397,8 @@ function isAuthorized(req, expected) {
|
|
|
3361
3397
|
if (!expected) return true;
|
|
3362
3398
|
const token = extractApiKey(req);
|
|
3363
3399
|
|
|
3364
|
-
// Check static THUMBGATE_API_KEY first
|
|
3365
3400
|
if (token === expected) return true;
|
|
3366
3401
|
|
|
3367
|
-
// Also accept any valid provisioned billing key
|
|
3368
3402
|
if (token) {
|
|
3369
3403
|
const result = validateApiKey(token);
|
|
3370
3404
|
return result.valid === true;
|
|
@@ -3373,9 +3407,6 @@ function isAuthorized(req, expected) {
|
|
|
3373
3407
|
return false;
|
|
3374
3408
|
}
|
|
3375
3409
|
|
|
3376
|
-
/**
|
|
3377
|
-
* Extract the Bearer token from a request (returns '' if absent).
|
|
3378
|
-
*/
|
|
3379
3410
|
function extractBearerToken(req) {
|
|
3380
3411
|
const auth = req.headers.authorization || '';
|
|
3381
3412
|
return auth.startsWith('Bearer ') ? auth.slice(7) : '';
|
|
@@ -3537,15 +3568,6 @@ function createApiServer() {
|
|
|
3537
3568
|
const expectedApiKey = getExpectedApiKey();
|
|
3538
3569
|
const expectedOperatorKey = getExpectedOperatorKey();
|
|
3539
3570
|
|
|
3540
|
-
// Live-event bus. Feedback captures, prevention-rule regenerations, and
|
|
3541
|
-
// gate decisions push to this emitter; the /v1/events SSE endpoint streams
|
|
3542
|
-
// those events to connected dashboard clients so they render in real time
|
|
3543
|
-
// instead of waiting for the next manual refresh.
|
|
3544
|
-
//
|
|
3545
|
-
// See .changeset/dashboard-sse-live.md for the ROI rationale โ this is a
|
|
3546
|
-
// direct application of the "persistent channel beats per-turn HTTP" pattern
|
|
3547
|
-
// to ThumbGate's dashboard surface (the primary UI for watching team
|
|
3548
|
-
// feedback flow).
|
|
3549
3571
|
const eventBus = new EventEmitter();
|
|
3550
3572
|
eventBus.setMaxListeners(200);
|
|
3551
3573
|
|
|
@@ -4386,37 +4408,94 @@ async function addContext(){
|
|
|
4386
4408
|
? { 'Set-Cookie': journeyState.setCookieHeaders }
|
|
4387
4409
|
: {};
|
|
4388
4410
|
|
|
4389
|
-
// โโ Bot guard โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
4390
|
-
// Creating a Stripe Checkout session on every GET means crawlers,
|
|
4391
|
-
// link-preview fetchers, and LLM scrapers inflate "sessions opened"
|
|
4392
|
-
// while completions stay at zero. Serve bots an interstitial HTML
|
|
4393
|
-
// page instead โ no Stripe session created, no funnel pollution.
|
|
4394
|
-
// The `?confirm=1` query param or POST below is the real-user path.
|
|
4395
4411
|
const botClassification = classifyRequester(req.headers);
|
|
4396
4412
|
const confirmParam = parsed?.searchParams?.get('confirm') ?? null;
|
|
4397
4413
|
const isConfirmedCheckout = confirmParam === '1'
|
|
4398
4414
|
|| confirmParam === 'true'
|
|
4399
4415
|
|| req.method === 'POST';
|
|
4400
|
-
if (
|
|
4416
|
+
if (!isConfirmedCheckout) {
|
|
4417
|
+
const eventType = botClassification.isBot ? 'checkout_bot_deflected' : 'checkout_interstitial_view';
|
|
4401
4418
|
appendBestEffortTelemetry(FEEDBACK_DIR, {
|
|
4402
|
-
eventType
|
|
4419
|
+
eventType,
|
|
4403
4420
|
clientType: 'web',
|
|
4404
4421
|
traceId,
|
|
4422
|
+
acquisitionId: analyticsMetadata.acquisitionId,
|
|
4423
|
+
visitorId: analyticsMetadata.visitorId,
|
|
4424
|
+
sessionId: analyticsMetadata.sessionId,
|
|
4405
4425
|
utmSource: analyticsMetadata.utmSource,
|
|
4406
4426
|
utmMedium: analyticsMetadata.utmMedium,
|
|
4407
4427
|
utmCampaign: analyticsMetadata.utmCampaign,
|
|
4428
|
+
utmContent: analyticsMetadata.utmContent,
|
|
4429
|
+
utmTerm: analyticsMetadata.utmTerm,
|
|
4408
4430
|
referrer: analyticsMetadata.referrer,
|
|
4409
4431
|
referrerHost: analyticsMetadata.referrerHost,
|
|
4410
4432
|
page: '/checkout/pro',
|
|
4433
|
+
ctaId: analyticsMetadata.ctaId,
|
|
4434
|
+
ctaPlacement: analyticsMetadata.ctaPlacement,
|
|
4411
4435
|
planId: analyticsMetadata.planId,
|
|
4412
4436
|
reason: botClassification.reason,
|
|
4413
|
-
}, req.headers,
|
|
4414
|
-
const
|
|
4415
|
-
|
|
4437
|
+
}, req.headers, eventType);
|
|
4438
|
+
const workflowIntakeHref = buildCheckoutIntentHref(`${hostedConfig.appOrigin}/#workflow-sprint-intake`, analyticsMetadata, {
|
|
4439
|
+
utmMedium: 'checkout_interstitial_recovery',
|
|
4440
|
+
utmCampaign: analyticsMetadata.utmCampaign || 'checkout_interstitial_workflow_sprint',
|
|
4441
|
+
ctaId: 'checkout_interstitial_workflow_sprint_intake',
|
|
4442
|
+
ctaPlacement: 'checkout_interstitial',
|
|
4443
|
+
planId: 'team',
|
|
4444
|
+
});
|
|
4445
|
+
const teamOptionsHref = buildCheckoutIntentHref(`${hostedConfig.appOrigin}/guides/ai-agent-governance-sprint`, analyticsMetadata, {
|
|
4446
|
+
utmMedium: 'checkout_interstitial_paid_path',
|
|
4447
|
+
utmCampaign: analyticsMetadata.utmCampaign || 'checkout_interstitial_team_paid_path',
|
|
4448
|
+
ctaId: 'checkout_interstitial_team_paid_path',
|
|
4449
|
+
ctaPlacement: 'checkout_interstitial',
|
|
4450
|
+
planId: 'team',
|
|
4451
|
+
});
|
|
4452
|
+
const diagnosticCheckoutHref = hostedConfig.sprintDiagnosticCheckoutUrl
|
|
4453
|
+
? buildCheckoutIntentHref(hostedConfig.sprintDiagnosticCheckoutUrl, analyticsMetadata, {
|
|
4454
|
+
utmMedium: 'checkout_interstitial_paid_path',
|
|
4455
|
+
utmCampaign: analyticsMetadata.utmCampaign || 'checkout_interstitial_diagnostic',
|
|
4456
|
+
ctaId: 'checkout_interstitial_sprint_diagnostic_checkout',
|
|
4457
|
+
ctaPlacement: 'checkout_interstitial',
|
|
4458
|
+
planId: 'sprint_diagnostic',
|
|
4459
|
+
})
|
|
4460
|
+
: '';
|
|
4461
|
+
const sprintCheckoutHref = hostedConfig.workflowSprintCheckoutUrl
|
|
4462
|
+
? buildCheckoutIntentHref(hostedConfig.workflowSprintCheckoutUrl, analyticsMetadata, {
|
|
4463
|
+
utmMedium: 'checkout_interstitial_paid_path',
|
|
4464
|
+
utmCampaign: analyticsMetadata.utmCampaign || 'checkout_interstitial_workflow_sprint',
|
|
4465
|
+
ctaId: 'checkout_interstitial_workflow_sprint_checkout',
|
|
4466
|
+
ctaPlacement: 'checkout_interstitial',
|
|
4467
|
+
planId: 'workflow_sprint',
|
|
4468
|
+
})
|
|
4469
|
+
: '';
|
|
4470
|
+
const html = renderCheckoutIntentPage({
|
|
4471
|
+
confirmHref: buildCheckoutConfirmHref(parsed),
|
|
4472
|
+
workflowIntakeHref,
|
|
4473
|
+
teamOptionsHref,
|
|
4474
|
+
diagnosticCheckoutHref,
|
|
4475
|
+
sprintCheckoutHref,
|
|
4476
|
+
sprintDiagnosticPriceDollars: hostedConfig.sprintDiagnosticPriceDollars || 499,
|
|
4477
|
+
workflowSprintPriceDollars: hostedConfig.workflowSprintPriceDollars || 1500,
|
|
4478
|
+
botClassification,
|
|
4479
|
+
});
|
|
4416
4480
|
sendHtml(res, 200, html, responseHeaders);
|
|
4417
4481
|
return;
|
|
4418
4482
|
}
|
|
4419
4483
|
|
|
4484
|
+
const normalizedCheckoutEmail = normalizeCheckoutCustomerEmail(bootstrapBody.customerEmail);
|
|
4485
|
+
if (!normalizedCheckoutEmail) {
|
|
4486
|
+
appendBestEffortTelemetry(FEEDBACK_DIR, {
|
|
4487
|
+
eventType: 'checkout_email_gate_shown',
|
|
4488
|
+
clientType: 'web',
|
|
4489
|
+
traceId,
|
|
4490
|
+
page: '/checkout/pro',
|
|
4491
|
+
planId: analyticsMetadata.planId,
|
|
4492
|
+
}, req.headers, 'checkout_email_gate_shown');
|
|
4493
|
+
const { html, headers } = renderCheckoutIntentGate(parsed, responseHeaders);
|
|
4494
|
+
sendHtml(res, 200, html, headers);
|
|
4495
|
+
return;
|
|
4496
|
+
}
|
|
4497
|
+
bootstrapBody.customerEmail = normalizedCheckoutEmail;
|
|
4498
|
+
|
|
4420
4499
|
appendBestEffortTelemetry(FEEDBACK_DIR, {
|
|
4421
4500
|
eventType: 'checkout_bootstrap',
|
|
4422
4501
|
clientType: 'web',
|
|
@@ -4792,7 +4871,7 @@ async function addContext(){
|
|
|
4792
4871
|
.map(l => { try { return JSON.parse(l); } catch(_e) { return null; } })
|
|
4793
4872
|
.filter(Boolean);
|
|
4794
4873
|
}
|
|
4795
|
-
} catch
|
|
4874
|
+
} catch { entries = []; }
|
|
4796
4875
|
|
|
4797
4876
|
const now = Date.now();
|
|
4798
4877
|
const sevenDaysAgo = now - 7 * 24 * 60 * 60 * 1000;
|