thumbgate 1.16.19 → 1.16.21
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/README.md +3 -2
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/mcp/server-stdio.js +1 -1
- package/adapters/opencode/opencode.json +1 -1
- package/bench/programbench-smoke.json +71 -0
- package/bench/thumbgate-bench.json +131 -0
- package/bin/cli.js +64 -1
- package/bin/postinstall.js +2 -2
- package/package.json +16 -8
- package/public/dashboard.html +1 -1
- package/public/guide.html +5 -3
- package/public/index.html +53 -41
- package/public/lessons.html +1 -1
- package/public/numbers.html +6 -6
- package/public/pro.html +34 -91
- package/scripts/billing.js +21 -5
- package/scripts/commercial-offer.js +1 -1
- package/scripts/harness-selector.js +188 -0
- package/scripts/rag-precision-guardrails.js +63 -1
- package/scripts/rate-limiter.js +1 -1
- package/scripts/reasoning-efficiency-guardrails.js +73 -1
- package/scripts/telemetry-analytics.js +77 -0
- package/scripts/thumbgate-bench.js +707 -0
- package/src/api/server.js +180 -48
package/src/api/server.js
CHANGED
|
@@ -13,6 +13,9 @@ const {
|
|
|
13
13
|
const POSTHOG_API_PATHS = new Set(['/capture', '/batch', '/decide', '/e', '/engage']);
|
|
14
14
|
const POSTHOG_INGEST_HOST = 'us.i.posthog.com';
|
|
15
15
|
const POSTHOG_STATIC_PATH_PREFIX = '/static/';
|
|
16
|
+
const FIRST_FAILURE_RULE_CHECKOUT_URL = 'https://buy.stripe.com/4gM6oHgH2bTw4lH6i73sI0z';
|
|
17
|
+
const QUICK_READ_CHECKOUT_URL = 'https://buy.stripe.com/aFa8wPgH29Lo4lH35V3sI0w';
|
|
18
|
+
const WORKFLOW_TEARDOWN_CHECKOUT_URL = 'https://buy.stripe.com/7sYfZhgH29LodWhdKz3sI0v';
|
|
16
19
|
|
|
17
20
|
function getPosthogProxyPath(pathname) {
|
|
18
21
|
return pathname.slice('/ingest'.length) || '/';
|
|
@@ -1453,6 +1456,61 @@ function buildCheckoutFallbackUrl(baseUrl, metadata = {}) {
|
|
|
1453
1456
|
return restoreStripeCheckoutPlaceholder(url.toString());
|
|
1454
1457
|
}
|
|
1455
1458
|
|
|
1459
|
+
function buildCheckoutIntentHref(baseUrl, metadata = {}, overrides = {}) {
|
|
1460
|
+
return buildCheckoutFallbackUrl(baseUrl, {
|
|
1461
|
+
...metadata,
|
|
1462
|
+
...overrides,
|
|
1463
|
+
});
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
function renderCheckoutIntentPage({
|
|
1467
|
+
confirmHref,
|
|
1468
|
+
firstRuleCheckoutHref,
|
|
1469
|
+
quickReadCheckoutHref,
|
|
1470
|
+
workflowTeardownCheckoutHref,
|
|
1471
|
+
workflowIntakeHref,
|
|
1472
|
+
teamOptionsHref,
|
|
1473
|
+
diagnosticCheckoutHref,
|
|
1474
|
+
sprintCheckoutHref,
|
|
1475
|
+
sprintDiagnosticPriceDollars = 499,
|
|
1476
|
+
workflowSprintPriceDollars = 1500,
|
|
1477
|
+
}) {
|
|
1478
|
+
const safeConfirmHref = escapeHtmlAttribute(confirmHref);
|
|
1479
|
+
const safeFirstRuleCheckoutHref = firstRuleCheckoutHref
|
|
1480
|
+
? escapeHtmlAttribute(firstRuleCheckoutHref)
|
|
1481
|
+
: '';
|
|
1482
|
+
const safeQuickReadCheckoutHref = quickReadCheckoutHref
|
|
1483
|
+
? escapeHtmlAttribute(quickReadCheckoutHref)
|
|
1484
|
+
: '';
|
|
1485
|
+
const safeWorkflowTeardownCheckoutHref = workflowTeardownCheckoutHref
|
|
1486
|
+
? escapeHtmlAttribute(workflowTeardownCheckoutHref)
|
|
1487
|
+
: '';
|
|
1488
|
+
const safeWorkflowIntakeHref = escapeHtmlAttribute(workflowIntakeHref);
|
|
1489
|
+
const safeTeamOptionsHref = escapeHtmlAttribute(teamOptionsHref);
|
|
1490
|
+
const safeDiagnosticCheckoutHref = diagnosticCheckoutHref
|
|
1491
|
+
? escapeHtmlAttribute(diagnosticCheckoutHref)
|
|
1492
|
+
: '';
|
|
1493
|
+
const safeSprintCheckoutHref = sprintCheckoutHref
|
|
1494
|
+
? escapeHtmlAttribute(sprintCheckoutHref)
|
|
1495
|
+
: '';
|
|
1496
|
+
const diagnosticAction = safeDiagnosticCheckoutHref
|
|
1497
|
+
? `<a data-i="sprint_diagnostic_checkout" href="${safeDiagnosticCheckoutHref}">Book $${sprintDiagnosticPriceDollars} diagnostic</a>`
|
|
1498
|
+
: '';
|
|
1499
|
+
const sprintAction = safeSprintCheckoutHref
|
|
1500
|
+
? `<a data-i="workflow_sprint_checkout" href="${safeSprintCheckoutHref}">Start $${workflowSprintPriceDollars} sprint</a>`
|
|
1501
|
+
: '';
|
|
1502
|
+
const firstRuleAction = safeFirstRuleCheckoutHref
|
|
1503
|
+
? `<a data-i="first_failure_rule_checkout" href="${safeFirstRuleCheckoutHref}">Pay $1 first rule</a>`
|
|
1504
|
+
: '';
|
|
1505
|
+
const quickReadAction = safeQuickReadCheckoutHref
|
|
1506
|
+
? `<a data-i="quick_read_checkout" href="${safeQuickReadCheckoutHref}">Pay $19 quick read</a>`
|
|
1507
|
+
: '';
|
|
1508
|
+
const teardownAction = safeWorkflowTeardownCheckoutHref
|
|
1509
|
+
? `<a data-i="workflow_teardown_checkout" href="${safeWorkflowTeardownCheckoutHref}">Pay $99 teardown</a>`
|
|
1510
|
+
: '';
|
|
1511
|
+
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}.high-ticket{border-color:#4ade80;color:#4ade80}</style><div><h1>Choose the right paid path.</h1><p>For one repeated workflow failure, start with the diagnostic or sprint. Use Pro only when you need the local self-serve dashboard.</p>${diagnosticAction.replace('<a ', '<a class="high-ticket" ')}${sprintAction.replace('<a ', '<a class="high-ticket" ')}<a class="primary" data-i="pro_checkout_confirmed" href="${safeConfirmHref}">Pay in Stripe</a>${teardownAction}${quickReadAction}${firstRuleAction}<a data-i="workflow_sprint_intake" href="${safeWorkflowIntakeHref}">Send workflow first</a><a data-i="team_paid_path" href="${safeTeamOptionsHref}">See 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>`;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1456
1514
|
function buildCheckoutBootstrapBody(parsed, req, journeyState = resolveJourneyState(req, parsed)) {
|
|
1457
1515
|
const params = parsed.searchParams;
|
|
1458
1516
|
const traceId = pickFirstText(params.get('trace_id')) || createJourneyId('checkout');
|
|
@@ -1505,6 +1563,26 @@ function buildCheckoutConfirmHref(parsed) {
|
|
|
1505
1563
|
return `${confirmUrl.pathname}${confirmUrl.search}`;
|
|
1506
1564
|
}
|
|
1507
1565
|
|
|
1566
|
+
function normalizeCheckoutCustomerEmail(value) {
|
|
1567
|
+
const email = (normalizeNullableText(value) || '').toLowerCase();
|
|
1568
|
+
const atIndex = email.indexOf('@');
|
|
1569
|
+
const domain = email.slice(atIndex + 1);
|
|
1570
|
+
if (!email || email.length > 254 || atIndex <= 0 || atIndex !== email.lastIndexOf('@') || !domain || !domain.includes('.') || domain.startsWith('.') || domain.endsWith('.') || domain.includes('..')) return null;
|
|
1571
|
+
for (const ch of email) if (ch <= ' ' || ch === '<' || ch === '>' || ch === '"') return null;
|
|
1572
|
+
return email;
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
function renderCheckoutIntentGate(parsed, responseHeaders = {}) {
|
|
1576
|
+
let hiddenInputs = '';
|
|
1577
|
+
for (const [key, value] of parsed.searchParams.entries()) {
|
|
1578
|
+
if (key !== 'confirm' && key !== 'customer_email') hiddenInputs += `<input type=hidden name=${escapeHtmlAttribute(key)} value=${escapeHtmlAttribute(value)}>`;
|
|
1579
|
+
}
|
|
1580
|
+
return {
|
|
1581
|
+
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>`,
|
|
1582
|
+
headers: responseHeaders,
|
|
1583
|
+
};
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1508
1586
|
function normalizeTrackedLinkSlug(value) {
|
|
1509
1587
|
return String(value || '').trim().toLowerCase().replace(/[^a-z0-9-]/g, '');
|
|
1510
1588
|
}
|
|
@@ -1746,9 +1824,7 @@ function appendBestEffortTelemetry(feedbackDir, payload, headers, context) {
|
|
|
1746
1824
|
evidence: [err && err.message ? err.message : 'unknown_error'],
|
|
1747
1825
|
},
|
|
1748
1826
|
});
|
|
1749
|
-
} catch (_) {
|
|
1750
|
-
// Public telemetry remains best-effort even when diagnostics fail.
|
|
1751
|
-
}
|
|
1827
|
+
} catch (_) {}
|
|
1752
1828
|
return false;
|
|
1753
1829
|
}
|
|
1754
1830
|
}
|
|
@@ -2055,7 +2131,6 @@ a{color:#22d3ee;text-decoration:none}</style></head><body>
|
|
|
2055
2131
|
const timestamp = merged.timestamp ? new Date(merged.timestamp).toLocaleString() : '';
|
|
2056
2132
|
const isoTimestamp = merged.timestamp || '';
|
|
2057
2133
|
|
|
2058
|
-
// Technical metadata
|
|
2059
2134
|
const failureType = merged.failureType || null;
|
|
2060
2135
|
const skill = merged.skill || null;
|
|
2061
2136
|
const source = merged.source || fb.source || null;
|
|
@@ -2066,19 +2141,12 @@ a{color:#22d3ee;text-decoration:none}</style></head><body>
|
|
|
2066
2141
|
const guardrails = merged.guardrails || null;
|
|
2067
2142
|
const rubricScores = merged.rubricScores || null;
|
|
2068
2143
|
|
|
2069
|
-
// Structured rule
|
|
2070
2144
|
const rule = merged.structuredRule || merged.rule || null;
|
|
2071
|
-
// Conversation window
|
|
2072
2145
|
const convoWindow = merged.conversationWindow || merged.chatHistory || [];
|
|
2073
|
-
// Reflector analysis
|
|
2074
2146
|
const reflector = merged.reflectorAnalysis || merged.reflector || null;
|
|
2075
|
-
// Diagnosis
|
|
2076
2147
|
const diagnosis = merged.diagnosis || null;
|
|
2077
|
-
// Rubric
|
|
2078
2148
|
const rubric = merged.rubricEvaluation || merged.rubric || null;
|
|
2079
|
-
// Synthesis
|
|
2080
2149
|
const synthesis = merged.synthesis || null;
|
|
2081
|
-
// Bayesian
|
|
2082
2150
|
const bayesian = merged.bayesianBelief || merged.bayesian || null;
|
|
2083
2151
|
|
|
2084
2152
|
function sectionCard(titleText, content, id) {
|
|
@@ -2501,14 +2569,6 @@ function servePublicMarketingPage({
|
|
|
2501
2569
|
'landing_page_view'
|
|
2502
2570
|
);
|
|
2503
2571
|
|
|
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
2572
|
try {
|
|
2513
2573
|
appendFunnelEvent({
|
|
2514
2574
|
stage: 'discovery',
|
|
@@ -2927,6 +2987,9 @@ function renderCheckoutSuccessPage(runtimeConfig) {
|
|
|
2927
2987
|
}
|
|
2928
2988
|
|
|
2929
2989
|
function renderCheckoutCancelledPage(runtimeConfig) {
|
|
2990
|
+
const firstFailureRuleCheckoutUrl = escapeHtmlAttribute(FIRST_FAILURE_RULE_CHECKOUT_URL);
|
|
2991
|
+
const quickReadCheckoutUrl = escapeHtmlAttribute(QUICK_READ_CHECKOUT_URL);
|
|
2992
|
+
const workflowTeardownCheckoutUrl = escapeHtmlAttribute(WORKFLOW_TEARDOWN_CHECKOUT_URL);
|
|
2930
2993
|
const diagnosticCheckoutUrl = runtimeConfig.sprintDiagnosticCheckoutUrl
|
|
2931
2994
|
? escapeHtmlAttribute(runtimeConfig.sprintDiagnosticCheckoutUrl)
|
|
2932
2995
|
: '';
|
|
@@ -2937,18 +3000,21 @@ function renderCheckoutCancelledPage(runtimeConfig) {
|
|
|
2937
3000
|
const workflowSprintPriceDollars = runtimeConfig.workflowSprintPriceDollars || 1500;
|
|
2938
3001
|
const workflowSprintIntakeUrl = `${escapeHtmlAttribute(runtimeConfig.appOrigin)}/#workflow-sprint-intake`;
|
|
2939
3002
|
const recoveryOfferLinks = [
|
|
2940
|
-
`<a id="send-workflow-first" href="${workflowSprintIntakeUrl}" data-recovery-offer="workflow_sprint_intake" data-offer-price="0">Send workflow first</a>`,
|
|
2941
3003
|
diagnosticCheckoutUrl
|
|
2942
3004
|
? `<a href="${diagnosticCheckoutUrl}" data-recovery-offer="sprint_diagnostic" data-offer-price="${sprintDiagnosticPriceDollars}">Book $${sprintDiagnosticPriceDollars} diagnostic</a>`
|
|
2943
3005
|
: '',
|
|
2944
3006
|
workflowSprintCheckoutUrl
|
|
2945
3007
|
? `<a href="${workflowSprintCheckoutUrl}" data-recovery-offer="workflow_sprint" data-offer-price="${workflowSprintPriceDollars}">Start $${workflowSprintPriceDollars} sprint</a>`
|
|
2946
3008
|
: '',
|
|
3009
|
+
`<a href="${workflowTeardownCheckoutUrl}" data-recovery-offer="workflow_teardown" data-offer-price="99">Pay $99 teardown</a>`,
|
|
3010
|
+
`<a href="${quickReadCheckoutUrl}" data-recovery-offer="quick_read" data-offer-price="19">Pay $19 quick read</a>`,
|
|
3011
|
+
`<a href="${firstFailureRuleCheckoutUrl}" data-recovery-offer="first_failure_rule" data-offer-price="1">Pay $1 first rule</a>`,
|
|
3012
|
+
`<a id="send-workflow-first" href="${workflowSprintIntakeUrl}" data-recovery-offer="workflow_sprint_intake" data-offer-price="0">Send workflow first</a>`,
|
|
2947
3013
|
].filter(Boolean).join('\n ');
|
|
2948
3014
|
const recoveryOfferCard = recoveryOfferLinks
|
|
2949
3015
|
? `<div class="card recovery-card">
|
|
2950
3016
|
<h2>Need help deciding?</h2>
|
|
2951
|
-
<p>If Pro is not the right next step, send the workflow first. We can qualify the blocker, confirm the proof plan, and route you to the diagnostic or sprint only when the scope is real.</p>
|
|
3017
|
+
<p>If Pro is not the right next step, start smaller or send the workflow first. We can qualify the blocker, confirm the proof plan, and route you to the diagnostic or sprint only when the scope is real.</p>
|
|
2952
3018
|
<div class="actions">
|
|
2953
3019
|
${recoveryOfferLinks}
|
|
2954
3020
|
</div>
|
|
@@ -3070,7 +3136,7 @@ function renderCheckoutCancelledPage(runtimeConfig) {
|
|
|
3070
3136
|
</div>
|
|
3071
3137
|
<div class="actions">
|
|
3072
3138
|
<button type="button" id="submit-reason">Send feedback</button>
|
|
3073
|
-
<a id="retry-checkout" href="/checkout/pro" class="secondary" data-recovery-offer="
|
|
3139
|
+
<a id="retry-checkout" href="/checkout/pro" class="secondary" data-recovery-offer="pro_pay_now_retry" data-offer-price="19">Restart $19 Pro checkout</a>
|
|
3074
3140
|
<a href="${runtimeConfig.appOrigin}" class="secondary">Return to Context Gateway</a>
|
|
3075
3141
|
</div>
|
|
3076
3142
|
<p class="note" id="status">No feedback sent yet.</p>
|
|
@@ -3361,10 +3427,8 @@ function isAuthorized(req, expected) {
|
|
|
3361
3427
|
if (!expected) return true;
|
|
3362
3428
|
const token = extractApiKey(req);
|
|
3363
3429
|
|
|
3364
|
-
// Check static THUMBGATE_API_KEY first
|
|
3365
3430
|
if (token === expected) return true;
|
|
3366
3431
|
|
|
3367
|
-
// Also accept any valid provisioned billing key
|
|
3368
3432
|
if (token) {
|
|
3369
3433
|
const result = validateApiKey(token);
|
|
3370
3434
|
return result.valid === true;
|
|
@@ -3373,9 +3437,6 @@ function isAuthorized(req, expected) {
|
|
|
3373
3437
|
return false;
|
|
3374
3438
|
}
|
|
3375
3439
|
|
|
3376
|
-
/**
|
|
3377
|
-
* Extract the Bearer token from a request (returns '' if absent).
|
|
3378
|
-
*/
|
|
3379
3440
|
function extractBearerToken(req) {
|
|
3380
3441
|
const auth = req.headers.authorization || '';
|
|
3381
3442
|
return auth.startsWith('Bearer ') ? auth.slice(7) : '';
|
|
@@ -3537,15 +3598,6 @@ function createApiServer() {
|
|
|
3537
3598
|
const expectedApiKey = getExpectedApiKey();
|
|
3538
3599
|
const expectedOperatorKey = getExpectedOperatorKey();
|
|
3539
3600
|
|
|
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
3601
|
const eventBus = new EventEmitter();
|
|
3550
3602
|
eventBus.setMaxListeners(200);
|
|
3551
3603
|
|
|
@@ -4386,37 +4438,117 @@ async function addContext(){
|
|
|
4386
4438
|
? { 'Set-Cookie': journeyState.setCookieHeaders }
|
|
4387
4439
|
: {};
|
|
4388
4440
|
|
|
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
4441
|
const botClassification = classifyRequester(req.headers);
|
|
4396
4442
|
const confirmParam = parsed?.searchParams?.get('confirm') ?? null;
|
|
4397
4443
|
const isConfirmedCheckout = confirmParam === '1'
|
|
4398
4444
|
|| confirmParam === 'true'
|
|
4399
4445
|
|| req.method === 'POST';
|
|
4400
|
-
if (botClassification.isBot
|
|
4446
|
+
if (!isConfirmedCheckout && botClassification.isBot) {
|
|
4447
|
+
const eventType = 'checkout_bot_deflected';
|
|
4401
4448
|
appendBestEffortTelemetry(FEEDBACK_DIR, {
|
|
4402
|
-
eventType
|
|
4449
|
+
eventType,
|
|
4403
4450
|
clientType: 'web',
|
|
4404
4451
|
traceId,
|
|
4452
|
+
acquisitionId: analyticsMetadata.acquisitionId,
|
|
4453
|
+
visitorId: analyticsMetadata.visitorId,
|
|
4454
|
+
sessionId: analyticsMetadata.sessionId,
|
|
4405
4455
|
utmSource: analyticsMetadata.utmSource,
|
|
4406
4456
|
utmMedium: analyticsMetadata.utmMedium,
|
|
4407
4457
|
utmCampaign: analyticsMetadata.utmCampaign,
|
|
4458
|
+
utmContent: analyticsMetadata.utmContent,
|
|
4459
|
+
utmTerm: analyticsMetadata.utmTerm,
|
|
4408
4460
|
referrer: analyticsMetadata.referrer,
|
|
4409
4461
|
referrerHost: analyticsMetadata.referrerHost,
|
|
4410
4462
|
page: '/checkout/pro',
|
|
4463
|
+
ctaId: analyticsMetadata.ctaId,
|
|
4464
|
+
ctaPlacement: analyticsMetadata.ctaPlacement,
|
|
4411
4465
|
planId: analyticsMetadata.planId,
|
|
4412
4466
|
reason: botClassification.reason,
|
|
4413
|
-
}, req.headers,
|
|
4414
|
-
const
|
|
4415
|
-
|
|
4467
|
+
}, req.headers, eventType);
|
|
4468
|
+
const workflowIntakeHref = buildCheckoutIntentHref(`${hostedConfig.appOrigin}/#workflow-sprint-intake`, analyticsMetadata, {
|
|
4469
|
+
utmMedium: 'checkout_interstitial_recovery',
|
|
4470
|
+
utmCampaign: analyticsMetadata.utmCampaign || 'checkout_interstitial_workflow_sprint',
|
|
4471
|
+
ctaId: 'checkout_interstitial_workflow_sprint_intake',
|
|
4472
|
+
ctaPlacement: 'checkout_interstitial',
|
|
4473
|
+
planId: 'team',
|
|
4474
|
+
});
|
|
4475
|
+
const teamOptionsHref = buildCheckoutIntentHref(`${hostedConfig.appOrigin}/guides/ai-agent-governance-sprint`, analyticsMetadata, {
|
|
4476
|
+
utmMedium: 'checkout_interstitial_paid_path',
|
|
4477
|
+
utmCampaign: analyticsMetadata.utmCampaign || 'checkout_interstitial_team_paid_path',
|
|
4478
|
+
ctaId: 'checkout_interstitial_team_paid_path',
|
|
4479
|
+
ctaPlacement: 'checkout_interstitial',
|
|
4480
|
+
planId: 'team',
|
|
4481
|
+
});
|
|
4482
|
+
const firstRuleCheckoutHref = buildCheckoutIntentHref(FIRST_FAILURE_RULE_CHECKOUT_URL, analyticsMetadata, {
|
|
4483
|
+
utmMedium: 'checkout_interstitial_paid_path',
|
|
4484
|
+
utmCampaign: analyticsMetadata.utmCampaign || 'checkout_interstitial_first_failure_rule',
|
|
4485
|
+
ctaId: 'checkout_interstitial_first_failure_rule_checkout',
|
|
4486
|
+
ctaPlacement: 'checkout_interstitial',
|
|
4487
|
+
planId: 'first_failure_rule',
|
|
4488
|
+
});
|
|
4489
|
+
const quickReadCheckoutHref = buildCheckoutIntentHref(QUICK_READ_CHECKOUT_URL, analyticsMetadata, {
|
|
4490
|
+
utmMedium: 'checkout_interstitial_paid_path',
|
|
4491
|
+
utmCampaign: analyticsMetadata.utmCampaign || 'checkout_interstitial_quick_read',
|
|
4492
|
+
ctaId: 'checkout_interstitial_quick_read_checkout',
|
|
4493
|
+
ctaPlacement: 'checkout_interstitial',
|
|
4494
|
+
planId: 'quick_read',
|
|
4495
|
+
});
|
|
4496
|
+
const workflowTeardownCheckoutHref = buildCheckoutIntentHref(WORKFLOW_TEARDOWN_CHECKOUT_URL, analyticsMetadata, {
|
|
4497
|
+
utmMedium: 'checkout_interstitial_paid_path',
|
|
4498
|
+
utmCampaign: analyticsMetadata.utmCampaign || 'checkout_interstitial_workflow_teardown',
|
|
4499
|
+
ctaId: 'checkout_interstitial_workflow_teardown_checkout',
|
|
4500
|
+
ctaPlacement: 'checkout_interstitial',
|
|
4501
|
+
planId: 'workflow_teardown',
|
|
4502
|
+
});
|
|
4503
|
+
const diagnosticCheckoutHref = hostedConfig.sprintDiagnosticCheckoutUrl
|
|
4504
|
+
? buildCheckoutIntentHref(hostedConfig.sprintDiagnosticCheckoutUrl, analyticsMetadata, {
|
|
4505
|
+
utmMedium: 'checkout_interstitial_paid_path',
|
|
4506
|
+
utmCampaign: analyticsMetadata.utmCampaign || 'checkout_interstitial_diagnostic',
|
|
4507
|
+
ctaId: 'checkout_interstitial_sprint_diagnostic_checkout',
|
|
4508
|
+
ctaPlacement: 'checkout_interstitial',
|
|
4509
|
+
planId: 'sprint_diagnostic',
|
|
4510
|
+
})
|
|
4511
|
+
: '';
|
|
4512
|
+
const sprintCheckoutHref = hostedConfig.workflowSprintCheckoutUrl
|
|
4513
|
+
? buildCheckoutIntentHref(hostedConfig.workflowSprintCheckoutUrl, analyticsMetadata, {
|
|
4514
|
+
utmMedium: 'checkout_interstitial_paid_path',
|
|
4515
|
+
utmCampaign: analyticsMetadata.utmCampaign || 'checkout_interstitial_workflow_sprint',
|
|
4516
|
+
ctaId: 'checkout_interstitial_workflow_sprint_checkout',
|
|
4517
|
+
ctaPlacement: 'checkout_interstitial',
|
|
4518
|
+
planId: 'workflow_sprint',
|
|
4519
|
+
})
|
|
4520
|
+
: '';
|
|
4521
|
+
const html = renderCheckoutIntentPage({
|
|
4522
|
+
confirmHref: buildCheckoutConfirmHref(parsed),
|
|
4523
|
+
firstRuleCheckoutHref,
|
|
4524
|
+
quickReadCheckoutHref,
|
|
4525
|
+
workflowTeardownCheckoutHref,
|
|
4526
|
+
workflowIntakeHref,
|
|
4527
|
+
teamOptionsHref,
|
|
4528
|
+
diagnosticCheckoutHref,
|
|
4529
|
+
sprintCheckoutHref,
|
|
4530
|
+
sprintDiagnosticPriceDollars: hostedConfig.sprintDiagnosticPriceDollars || 499,
|
|
4531
|
+
workflowSprintPriceDollars: hostedConfig.workflowSprintPriceDollars || 1500,
|
|
4532
|
+
botClassification,
|
|
4533
|
+
});
|
|
4416
4534
|
sendHtml(res, 200, html, responseHeaders);
|
|
4417
4535
|
return;
|
|
4418
4536
|
}
|
|
4419
4537
|
|
|
4538
|
+
const rawCheckoutEmail = normalizeNullableText(bootstrapBody.customerEmail);
|
|
4539
|
+
const normalizedCheckoutEmail = normalizeCheckoutCustomerEmail(rawCheckoutEmail);
|
|
4540
|
+
if (!normalizedCheckoutEmail) {
|
|
4541
|
+
appendBestEffortTelemetry(FEEDBACK_DIR, {
|
|
4542
|
+
eventType: 'checkout_email_deferred_to_stripe',
|
|
4543
|
+
clientType: 'web',
|
|
4544
|
+
traceId,
|
|
4545
|
+
page: '/checkout/pro',
|
|
4546
|
+
planId: analyticsMetadata.planId,
|
|
4547
|
+
reason: rawCheckoutEmail ? 'invalid_customer_email' : 'missing_customer_email',
|
|
4548
|
+
}, req.headers, 'checkout_email_deferred_to_stripe');
|
|
4549
|
+
}
|
|
4550
|
+
bootstrapBody.customerEmail = normalizedCheckoutEmail || undefined;
|
|
4551
|
+
|
|
4420
4552
|
appendBestEffortTelemetry(FEEDBACK_DIR, {
|
|
4421
4553
|
eventType: 'checkout_bootstrap',
|
|
4422
4554
|
clientType: 'web',
|
|
@@ -4792,7 +4924,7 @@ async function addContext(){
|
|
|
4792
4924
|
.map(l => { try { return JSON.parse(l); } catch(_e) { return null; } })
|
|
4793
4925
|
.filter(Boolean);
|
|
4794
4926
|
}
|
|
4795
|
-
} catch
|
|
4927
|
+
} catch { entries = []; }
|
|
4796
4928
|
|
|
4797
4929
|
const now = Date.now();
|
|
4798
4930
|
const sevenDaysAgo = now - 7 * 24 * 60 * 60 * 1000;
|