thumbgate 1.28.0 → 1.28.2
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/plugin.json +2 -1
- package/.well-known/mcp/server-card.json +1 -1
- package/README.md +96 -122
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/mcp/server-stdio.js +54 -34
- package/adapters/opencode/opencode.json +1 -1
- package/bin/cli.js +100 -77
- package/config/gates/default.json +2 -2
- package/config/github-about.json +2 -5
- package/config/mcp-allowlists.json +5 -0
- package/config/post-deploy-marketing-pages.json +1 -1
- package/hooks/hooks.json +38 -0
- package/package.json +19 -8
- package/public/chatgpt-app.html +2 -2
- package/public/codex-enterprise.html +1 -1
- package/public/codex-plugin.html +1 -1
- package/public/diagnostic.html +23 -1
- package/public/guide.html +1 -1
- package/public/index.html +172 -201
- package/public/numbers.html +2 -2
- package/public/partner-intake.html +198 -0
- package/public/pricing.html +51 -14
- package/scripts/auto-wire-hooks.js +58 -2
- package/scripts/billing.js +815 -137
- package/scripts/checkout-attribution-reference.js +85 -0
- package/scripts/claude-feedback-sync.js +23 -6
- package/scripts/cli-feedback.js +20 -10
- package/scripts/feedback-history-distiller.js +385 -0
- package/scripts/feedback-loop.js +290 -1
- package/scripts/feedback-quality.js +25 -26
- package/scripts/feedback-sanitizer.js +151 -3
- package/scripts/gates-engine.js +128 -49
- package/scripts/hosted-config.js +9 -2
- package/scripts/mailer/resend-mailer.js +11 -5
- package/scripts/secret-scanner.js +363 -68
- package/scripts/self-protection.js +21 -36
- package/src/api/server.js +121 -12
- package/public/assets/brand/github-social-preview.png +0 -0
package/src/api/server.js
CHANGED
|
@@ -10,6 +10,7 @@ const {
|
|
|
10
10
|
createUnavailableAsyncOperation,
|
|
11
11
|
loadOptionalModule,
|
|
12
12
|
} = require('../../scripts/private-core-boundary');
|
|
13
|
+
const { redactSecrets, redactSecretsDeep } = require('../../scripts/secret-redaction');
|
|
13
14
|
|
|
14
15
|
const POSTHOG_API_PATHS = new Set(['/capture', '/batch', '/decide', '/e', '/engage']);
|
|
15
16
|
const POSTHOG_INGEST_HOST = 'us.i.posthog.com';
|
|
@@ -24,8 +25,12 @@ const PRO_CHECKOUT_URL = 'https://buy.stripe.com/8x2dR91M84r4cSd9uj3sI3f';
|
|
|
24
25
|
const FIRST_FAILURE_RULE_CHECKOUT_URL = 'https://buy.stripe.com/7sY6oHaiEbTw6tP5e33sI3e';
|
|
25
26
|
const QUICK_READ_CHECKOUT_URL = 'https://buy.stripe.com/5kQ7sL76s1eSaK55e33sI2H';
|
|
26
27
|
const WORKFLOW_TEARDOWN_CHECKOUT_URL = 'https://buy.stripe.com/8x214n2Qc4r44lHayn3sI2I';
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
// Verified live amounts (2026-07-13): diagnostic Stripe page contains $499;
|
|
29
|
+
// sprint code-default Stripe slug previously pointed at a $499 diagnostic product.
|
|
30
|
+
// Production sprint rail is PayPal at $1500 — pin that as the code fallback so a
|
|
31
|
+
// missing Railway env cannot silently charge $499 under a $1500 label.
|
|
32
|
+
const SPRINT_DIAGNOSTIC_CHECKOUT_URL = 'https://buy.stripe.com/9B69ATbmI4r4aK5eOD3sI3k';
|
|
33
|
+
const WORKFLOW_SPRINT_CHECKOUT_URL = 'https://www.paypal.com/ncp/payment/LTQFR7P9AR3QG';
|
|
29
34
|
|
|
30
35
|
function getPosthogProxyPath(pathname) {
|
|
31
36
|
return pathname.slice('/ingest'.length) || '/';
|
|
@@ -214,6 +219,7 @@ const {
|
|
|
214
219
|
const { sendProblem, PROBLEM_TYPES } = require('../../scripts/problem-detail');
|
|
215
220
|
const { TOOLS: MCP_TOOLS } = require('../../scripts/tool-registry');
|
|
216
221
|
const mcpOauth = require('../../scripts/mcp-oauth');
|
|
222
|
+
const { packCheckoutReference } = require('../../scripts/checkout-attribution-reference');
|
|
217
223
|
// OAuth 2.1 (PKCE) authorization-server state for the remote MCP connector
|
|
218
224
|
// (Claude Connectors Directory requires OAuth for authenticated services).
|
|
219
225
|
const oauthStore = mcpOauth.createStore();
|
|
@@ -247,6 +253,7 @@ const FEDERAL_PAGE_PATH = path.resolve(__dirname, '../../public/federal.html');
|
|
|
247
253
|
const PRICING_PAGE_PATH = path.resolve(__dirname, '../../public/pricing.html');
|
|
248
254
|
const ABOUT_PAGE_PATH = path.resolve(__dirname, '../../public/about.html');
|
|
249
255
|
const DIAGNOSTIC_PAGE_PATH = path.resolve(__dirname, '../../public/diagnostic.html');
|
|
256
|
+
const PARTNER_INTAKE_PAGE_PATH = path.resolve(__dirname, '../../public/partner-intake.html');
|
|
250
257
|
const INSTALL_PAGE_PATH = path.resolve(__dirname, '../../public/install.html');
|
|
251
258
|
const LEARN_DIR = path.resolve(__dirname, '../../public/learn');
|
|
252
259
|
const GUIDES_DIR = path.resolve(__dirname, '../../public/guides');
|
|
@@ -1431,7 +1438,6 @@ function buildCheckoutAttributionMetadata(body, req, traceId) {
|
|
|
1431
1438
|
: 1;
|
|
1432
1439
|
|
|
1433
1440
|
return {
|
|
1434
|
-
...rawMetadata,
|
|
1435
1441
|
traceId,
|
|
1436
1442
|
acquisitionId: pickFirstText(rawMetadata.acquisitionId, body.acquisitionId),
|
|
1437
1443
|
visitorId: pickFirstText(rawMetadata.visitorId, body.visitorId),
|
|
@@ -2102,6 +2108,10 @@ function buildCheckoutFallbackUrl(baseUrl, metadata = {}) {
|
|
|
2102
2108
|
appendQueryParam(url, 'seat_count', metadata.seatCount);
|
|
2103
2109
|
appendQueryParam(url, 'landing_path', metadata.landingPath);
|
|
2104
2110
|
appendQueryParam(url, 'referrer_host', metadata.referrerHost);
|
|
2111
|
+
// External Stripe Payment Links drop utm_* query params but preserve
|
|
2112
|
+
// client_reference_id — pack attribution into it so marketplace-attributed paid
|
|
2113
|
+
// checkouts (e.g. utm_source=aiventyx) survive to the webhook and can be credited.
|
|
2114
|
+
appendQueryParam(url, 'client_reference_id', packCheckoutReference(metadata));
|
|
2105
2115
|
return restoreStripeCheckoutPlaceholder(url.toString());
|
|
2106
2116
|
}
|
|
2107
2117
|
|
|
@@ -2546,10 +2556,18 @@ function serveTrackedLinkRedirect({ req, res, parsed, hostedConfig, isHeadReques
|
|
|
2546
2556
|
const { FEEDBACK_DIR } = getFeedbackPaths();
|
|
2547
2557
|
const journeyState = resolveJourneyState(req, parsed);
|
|
2548
2558
|
const destinationUrl = buildTrackedLinkDestination(target, hostedConfig, parsed);
|
|
2559
|
+
const attribution = buildTrackedLinkAttribution(target, parsed, req, journeyState, destinationUrl);
|
|
2560
|
+
if (target.external) {
|
|
2561
|
+
appendQueryParam(
|
|
2562
|
+
destinationUrl,
|
|
2563
|
+
'client_reference_id',
|
|
2564
|
+
packCheckoutReference(attribution)
|
|
2565
|
+
);
|
|
2566
|
+
}
|
|
2549
2567
|
if (!isHeadRequest) {
|
|
2550
2568
|
appendBestEffortTelemetry(
|
|
2551
2569
|
FEEDBACK_DIR,
|
|
2552
|
-
|
|
2570
|
+
attribution,
|
|
2553
2571
|
req.headers,
|
|
2554
2572
|
`tracked_link_redirect:${target.slug}`
|
|
2555
2573
|
);
|
|
@@ -2877,6 +2895,11 @@ function loadPublicMarketingTemplateHtml(templatePath, runtimeConfig, pageContex
|
|
|
2877
2895
|
'__SERVER_SESSION_ID__': pageContext.serverSessionId || '',
|
|
2878
2896
|
'__SERVER_ACQUISITION_ID__': pageContext.serverAcquisitionId || '',
|
|
2879
2897
|
'__SERVER_TELEMETRY_CAPTURED__': pageContext.serverTelemetryCaptured ? 'true' : 'false',
|
|
2898
|
+
'__SERVER_ATTRIBUTION_SOURCE__': escapeHtmlAttribute(pageContext.serverAttributionSource || 'partner'),
|
|
2899
|
+
'__SERVER_UTM_SOURCE__': escapeHtmlAttribute(pageContext.serverUtmSource || 'partner'),
|
|
2900
|
+
'__SERVER_UTM_MEDIUM__': escapeHtmlAttribute(pageContext.serverUtmMedium || 'partner'),
|
|
2901
|
+
'__SERVER_UTM_CAMPAIGN__': escapeHtmlAttribute(pageContext.serverUtmCampaign || 'hosted_listing'),
|
|
2902
|
+
'__SERVER_UTM_CONTENT__': escapeHtmlAttribute(pageContext.serverUtmContent || 'partner_intake'),
|
|
2880
2903
|
'__VERIFICATION_URL__': 'https://github.com/IgorGanapolsky/ThumbGate/blob/main/docs/VERIFICATION_EVIDENCE.md',
|
|
2881
2904
|
'__COMPATIBILITY_REPORT_URL__': 'https://github.com/IgorGanapolsky/ThumbGate/blob/main/docs/VERIFICATION_EVIDENCE.md',
|
|
2882
2905
|
'__AUTOMATION_REPORT_URL__': 'https://github.com/IgorGanapolsky/ThumbGate/blob/main/docs/VERIFICATION_EVIDENCE.md',
|
|
@@ -2898,6 +2921,10 @@ function loadDiagnosticPageHtml(runtimeConfig, pageContext = {}) {
|
|
|
2898
2921
|
return loadPublicMarketingTemplateHtml(DIAGNOSTIC_PAGE_PATH, runtimeConfig, pageContext);
|
|
2899
2922
|
}
|
|
2900
2923
|
|
|
2924
|
+
function loadPartnerIntakePageHtml(runtimeConfig, pageContext = {}) {
|
|
2925
|
+
return loadPublicMarketingTemplateHtml(PARTNER_INTAKE_PAGE_PATH, runtimeConfig, pageContext);
|
|
2926
|
+
}
|
|
2927
|
+
|
|
2901
2928
|
function loadInstallPageHtml(runtimeConfig, pageContext = {}) {
|
|
2902
2929
|
return loadPublicMarketingTemplateHtml(INSTALL_PAGE_PATH, runtimeConfig, pageContext);
|
|
2903
2930
|
}
|
|
@@ -3717,6 +3744,11 @@ function servePublicMarketingPage({
|
|
|
3717
3744
|
serverSessionId: journeyState.sessionId,
|
|
3718
3745
|
serverAcquisitionId: journeyState.acquisitionId,
|
|
3719
3746
|
serverTelemetryCaptured: landingTelemetryCaptured,
|
|
3747
|
+
serverAttributionSource: landingAttribution.source,
|
|
3748
|
+
serverUtmSource: landingAttribution.utmSource,
|
|
3749
|
+
serverUtmMedium: landingAttribution.utmMedium,
|
|
3750
|
+
serverUtmCampaign: landingAttribution.utmCampaign,
|
|
3751
|
+
serverUtmContent: landingAttribution.utmContent,
|
|
3720
3752
|
requestHost,
|
|
3721
3753
|
});
|
|
3722
3754
|
|
|
@@ -4521,6 +4553,19 @@ function normalizeLeadEmail(value) {
|
|
|
4521
4553
|
return email.toLowerCase();
|
|
4522
4554
|
}
|
|
4523
4555
|
|
|
4556
|
+
function buildIntakeAlertRateLimitKey(req, env = process.env) {
|
|
4557
|
+
const transportPeer = String(req.socket?.remoteAddress || 'unknown');
|
|
4558
|
+
const railwayRealIp = String(req.headers['x-real-ip'] || '').trim();
|
|
4559
|
+
const runningBehindRailway = Boolean(env.RAILWAY_ENVIRONMENT_ID || env.RAILWAY_PROJECT_ID);
|
|
4560
|
+
const hasValidRailwayIp = /^[0-9a-f:.]{3,64}$/i.test(railwayRealIp);
|
|
4561
|
+
// Railway overwrites X-Real-IP at its ingress. Outside that explicit trust
|
|
4562
|
+
// boundary, forwarding headers remain caller-controlled and are ignored.
|
|
4563
|
+
const clientAddress = runningBehindRailway && hasValidRailwayIp
|
|
4564
|
+
? railwayRealIp
|
|
4565
|
+
: transportPeer;
|
|
4566
|
+
return crypto.createHash('sha256').update(clientAddress).digest('hex');
|
|
4567
|
+
}
|
|
4568
|
+
|
|
4524
4569
|
function normalizeHttpUrl(value, fieldName) {
|
|
4525
4570
|
const raw = normalizeNullableText(value);
|
|
4526
4571
|
if (!raw) throw createHttpError(400, `${fieldName} is required`);
|
|
@@ -5629,6 +5674,16 @@ async function addContext(){
|
|
|
5629
5674
|
return;
|
|
5630
5675
|
}
|
|
5631
5676
|
|
|
5677
|
+
// /docs is a common buyer URL; route to the public setup guide instead of 404.
|
|
5678
|
+
if (isGetLikeRequest && (pathname === '/docs' || pathname === '/docs/' || pathname === '/documentation' || pathname === '/documentation/')) {
|
|
5679
|
+
res.writeHead(302, {
|
|
5680
|
+
Location: '/guide',
|
|
5681
|
+
'Cache-Control': 'public, max-age=300',
|
|
5682
|
+
});
|
|
5683
|
+
res.end();
|
|
5684
|
+
return;
|
|
5685
|
+
}
|
|
5686
|
+
|
|
5632
5687
|
if (isGetLikeRequest && (pathname === '/guide' || pathname === '/guide.html')) {
|
|
5633
5688
|
try {
|
|
5634
5689
|
servePublicMarketingPage({
|
|
@@ -5764,6 +5819,25 @@ async function addContext(){
|
|
|
5764
5819
|
return;
|
|
5765
5820
|
}
|
|
5766
5821
|
|
|
5822
|
+
if (isGetLikeRequest && (pathname === '/partner-intake' || pathname === '/partner-intake.html')) {
|
|
5823
|
+
try {
|
|
5824
|
+
servePublicMarketingPage({
|
|
5825
|
+
req,
|
|
5826
|
+
res,
|
|
5827
|
+
parsed,
|
|
5828
|
+
hostedConfig,
|
|
5829
|
+
isHeadRequest,
|
|
5830
|
+
renderHtml: loadPartnerIntakePageHtml,
|
|
5831
|
+
extraTelemetry: {
|
|
5832
|
+
pageType: 'partner_intake',
|
|
5833
|
+
},
|
|
5834
|
+
});
|
|
5835
|
+
} catch (err) {
|
|
5836
|
+
sendText(res, 500, err.message || 'Partner intake page unavailable');
|
|
5837
|
+
}
|
|
5838
|
+
return;
|
|
5839
|
+
}
|
|
5840
|
+
|
|
5767
5841
|
// Natural marketing URLs for the Workflow Hardening Diagnostic/Sprint.
|
|
5768
5842
|
// High-intent outreach refers to "the diagnostic", "the sprint", or
|
|
5769
5843
|
// "workflow hardening"; serve a focused buyer page instead of sending
|
|
@@ -7437,7 +7511,7 @@ a{color:#8b9}</style></head><body><form class="card" method="post" action="/oaut
|
|
|
7437
7511
|
? await parseFormBody(req, 24 * 1024)
|
|
7438
7512
|
: await parseJsonBody(req, 24 * 1024);
|
|
7439
7513
|
const workflowSprintIntake = requirePrivateApiModule('workflowSprintIntake', 'Workflow sprint intake');
|
|
7440
|
-
const
|
|
7514
|
+
const leadPayload = {
|
|
7441
7515
|
...body,
|
|
7442
7516
|
traceId: body.traceId || traceId,
|
|
7443
7517
|
acquisitionId: body.acquisitionId || journeyState.acquisitionId,
|
|
@@ -7462,7 +7536,17 @@ a{color:#8b9}</style></head><body><form class="card" method="post" action="/oaut
|
|
|
7462
7536
|
offerCode: body.offerCode || referrerAttribution.offerCode || null,
|
|
7463
7537
|
referrerHost: body.referrerHost || referrerAttribution.referrerHost || null,
|
|
7464
7538
|
referrer: body.referrer || referrerAttribution.referrer || null,
|
|
7465
|
-
}
|
|
7539
|
+
};
|
|
7540
|
+
const intakeReservation = workflowSprintIntake.reserveWorkflowSprintIntake(leadPayload, {
|
|
7541
|
+
feedbackDir: FEEDBACK_DIR,
|
|
7542
|
+
rateLimitKey: buildIntakeAlertRateLimitKey(req),
|
|
7543
|
+
});
|
|
7544
|
+
if (!intakeReservation.allowed) {
|
|
7545
|
+
const error = new Error('Too many workflow-intake submissions. Try again later.');
|
|
7546
|
+
error.statusCode = intakeReservation.statusCode || 429;
|
|
7547
|
+
throw error;
|
|
7548
|
+
}
|
|
7549
|
+
const lead = workflowSprintIntake.appendWorkflowSprintLead(leadPayload, { feedbackDir: FEEDBACK_DIR });
|
|
7466
7550
|
|
|
7467
7551
|
appendBestEffortTelemetry(FEEDBACK_DIR, {
|
|
7468
7552
|
eventType: 'workflow_sprint_lead_submitted',
|
|
@@ -7493,6 +7577,11 @@ a{color:#8b9}</style></head><body><form class="card" method="post" action="/oaut
|
|
|
7493
7577
|
referrer: lead.attribution.referrer,
|
|
7494
7578
|
}, req.headers, 'workflow_sprint_lead_submitted');
|
|
7495
7579
|
|
|
7580
|
+
const leadNotification = await workflowSprintIntake.notifyWorkflowSprintLead(lead, {
|
|
7581
|
+
sendEmailImpl: resendMailer.sendEmail,
|
|
7582
|
+
rateLimitKey: buildIntakeAlertRateLimitKey(req),
|
|
7583
|
+
});
|
|
7584
|
+
|
|
7496
7585
|
if (isFormSubmission && !wantsJson(req, parsed)) {
|
|
7497
7586
|
sendHtml(
|
|
7498
7587
|
res,
|
|
@@ -7513,6 +7602,8 @@ a{color:#8b9}</style></head><body><form class="card" method="post" action="/oaut
|
|
|
7513
7602
|
status: lead.status,
|
|
7514
7603
|
offer: lead.offer,
|
|
7515
7604
|
nextStep: 'review_proof_pack',
|
|
7605
|
+
operatorAlertSent: leadNotification.sent === true,
|
|
7606
|
+
operatorAlertReason: leadNotification.reason || null,
|
|
7516
7607
|
proofPackUrl: 'https://github.com/IgorGanapolsky/ThumbGate/blob/main/docs/VERIFICATION_EVIDENCE.md',
|
|
7517
7608
|
sprintBriefUrl: 'https://github.com/IgorGanapolsky/ThumbGate/blob/main/docs/WORKFLOW_HARDENING_SPRINT.md',
|
|
7518
7609
|
}, {
|
|
@@ -7520,7 +7611,7 @@ a{color:#8b9}</style></head><body><form class="card" method="post" action="/oaut
|
|
|
7520
7611
|
...(journeyState.setCookieHeaders.length ? { 'Set-Cookie': journeyState.setCookieHeaders } : {}),
|
|
7521
7612
|
});
|
|
7522
7613
|
} catch (err) {
|
|
7523
|
-
appendBestEffortTelemetry(FEEDBACK_DIR, {
|
|
7614
|
+
appendBestEffortTelemetry(FEEDBACK_DIR, redactSecretsDeep({
|
|
7524
7615
|
eventType: 'workflow_sprint_lead_failed',
|
|
7525
7616
|
clientType: 'web',
|
|
7526
7617
|
traceId,
|
|
@@ -7543,10 +7634,10 @@ a{color:#8b9}</style></head><body><form class="card" method="post" action="/oaut
|
|
|
7543
7634
|
page: referrerAttribution.page || '/#workflow-sprint-intake',
|
|
7544
7635
|
landingPath: referrerAttribution.landingPath || '/',
|
|
7545
7636
|
referrerHost: referrerAttribution.referrerHost,
|
|
7546
|
-
referrer: referrerAttribution.referrer,
|
|
7547
|
-
failureCode: err?.message ? err.message : 'workflow_sprint_lead_failed',
|
|
7637
|
+
referrer: redactSecrets(referrerAttribution.referrer),
|
|
7638
|
+
failureCode: redactSecrets(err?.message ? err.message : 'workflow_sprint_lead_failed'),
|
|
7548
7639
|
httpStatus: err && err.statusCode ? err.statusCode : null,
|
|
7549
|
-
}, req.headers, 'workflow_sprint_lead_failed');
|
|
7640
|
+
}), redactSecretsDeep(req.headers), 'workflow_sprint_lead_failed');
|
|
7550
7641
|
if (isFormSubmission && !wantsJson(req, parsed)) {
|
|
7551
7642
|
sendHtml(
|
|
7552
7643
|
res,
|
|
@@ -7832,6 +7923,15 @@ a{color:#8b9}</style></head><body><form class="card" method="post" action="/oaut
|
|
|
7832
7923
|
});
|
|
7833
7924
|
return;
|
|
7834
7925
|
}
|
|
7926
|
+
if (result && result.retryable === true) {
|
|
7927
|
+
sendProblem(res, {
|
|
7928
|
+
type: PROBLEM_TYPES.INTERNAL,
|
|
7929
|
+
title: 'Fulfillment temporarily unavailable',
|
|
7930
|
+
status: 503,
|
|
7931
|
+
detail: 'The payment was received but fulfillment did not complete. Stripe should retry this event.',
|
|
7932
|
+
});
|
|
7933
|
+
return;
|
|
7934
|
+
}
|
|
7835
7935
|
sendJson(res, 200, result);
|
|
7836
7936
|
|
|
7837
7937
|
} catch (err) {
|
|
@@ -7964,16 +8064,24 @@ a{color:#8b9}</style></head><body><form class="card" method="post" action="/oaut
|
|
|
7964
8064
|
}
|
|
7965
8065
|
|
|
7966
8066
|
const resolvedTraceId = result.traceId || requestedTraceId;
|
|
8067
|
+
const nextSteps = result.apiKey
|
|
8068
|
+
? {
|
|
8069
|
+
env: `THUMBGATE_API_KEY=${result.apiKey}\nTHUMBGATE_API_BASE_URL=${hostedConfig.billingApiBaseUrl}`,
|
|
8070
|
+
curl: `curl -X POST ${hostedConfig.billingApiBaseUrl}/v1/feedback/capture \\\n+ -H 'Authorization: Bearer ${result.apiKey}' \\\n+ -H 'Content-Type: application/json' \\\n+ -d '{"signal":"down","context":"example","whatWentWrong":"example","whatToChange":"example"}'`,
|
|
8071
|
+
}
|
|
8072
|
+
: result.offerKind === 'workflow_hardening_diagnostic'
|
|
8073
|
+
? { fulfillment: 'Payment confirmed. Diagnostic fulfillment is processing; an email will arrive when it completes.' }
|
|
8074
|
+
: {};
|
|
7967
8075
|
|
|
7968
8076
|
sendJson(res, 200, {
|
|
7969
8077
|
...result,
|
|
7970
8078
|
traceId: resolvedTraceId || null,
|
|
7971
8079
|
appOrigin: hostedConfig.appOrigin,
|
|
7972
8080
|
apiBaseUrl: hostedConfig.billingApiBaseUrl,
|
|
7973
|
-
nextSteps: {
|
|
8081
|
+
nextSteps: result.apiKey ? {
|
|
7974
8082
|
env: `THUMBGATE_API_KEY=${result.apiKey || ''}\nTHUMBGATE_API_BASE_URL=${hostedConfig.billingApiBaseUrl}`,
|
|
7975
8083
|
curl: `curl -X POST ${hostedConfig.billingApiBaseUrl}/v1/feedback/capture \\\n -H 'Authorization: Bearer ${result.apiKey || ''}' \\\n -H 'Content-Type: application/json' \\\n -d '{"signal":"down","context":"example","whatWentWrong":"example","whatToChange":"example"}'`,
|
|
7976
|
-
},
|
|
8084
|
+
} : nextSteps,
|
|
7977
8085
|
}, getPublicBillingHeaders(resolvedTraceId));
|
|
7978
8086
|
} catch (err) {
|
|
7979
8087
|
const requestedTraceId = parsed.searchParams.get('traceId') || '';
|
|
@@ -9857,6 +9965,7 @@ module.exports = {
|
|
|
9857
9965
|
answerEnterpriseDataChat,
|
|
9858
9966
|
answerEnterpriseDialogflowChat,
|
|
9859
9967
|
buildLossAnalyticsResponse,
|
|
9968
|
+
buildIntakeAlertRateLimitKey,
|
|
9860
9969
|
sanitizeHtmlUnsafeJsonValue,
|
|
9861
9970
|
},
|
|
9862
9971
|
};
|
|
Binary file
|