thumbgate 1.4.3 → 1.4.4
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/README.md +1 -1
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/codex/config.toml +2 -2
- package/adapters/mcp/server-stdio.js +1 -1
- package/adapters/opencode/opencode.json +1 -1
- package/package.json +157 -9
- package/scripts/statusline.sh +1 -0
- package/src/api/server.js +113 -16
- package/src/index.js +3 -0
- package/.claude-plugin/bundle/icon.png +0 -0
- package/.claude-plugin/bundle/icon.svg +0 -18
- package/.claude-plugin/bundle/server/index.js +0 -24
- package/adapters/chatgpt/INSTALL.md +0 -158
- package/adapters/perplexity/.mcp.json +0 -36
- package/adapters/perplexity/config.toml +0 -16
- package/adapters/perplexity/opencode.json +0 -29
- package/bin/memory.sh +0 -64
- package/bin/obsidian-sync.sh +0 -20
- package/plugins/amp-skill/INSTALL.md +0 -52
- package/plugins/amp-skill/SKILL.md +0 -64
- package/plugins/claude-codex-bridge/.claude-plugin/plugin.json +0 -22
- package/plugins/claude-codex-bridge/.mcp.json +0 -14
- package/plugins/claude-codex-bridge/INSTALL.md +0 -43
- package/plugins/claude-codex-bridge/README.md +0 -46
- package/plugins/claude-codex-bridge/scripts/codex-bridge.js +0 -286
- package/plugins/claude-codex-bridge/skills/adversarial-review/SKILL.md +0 -24
- package/plugins/claude-codex-bridge/skills/result/SKILL.md +0 -22
- package/plugins/claude-codex-bridge/skills/review/SKILL.md +0 -28
- package/plugins/claude-codex-bridge/skills/second-pass/SKILL.md +0 -27
- package/plugins/claude-codex-bridge/skills/setup/SKILL.md +0 -21
- package/plugins/claude-codex-bridge/skills/status/SKILL.md +0 -19
- package/plugins/claude-skill/INSTALL.md +0 -55
- package/plugins/claude-skill/SKILL.md +0 -46
- package/plugins/codex-profile/.codex-plugin/plugin.json +0 -43
- package/plugins/codex-profile/.mcp.json +0 -14
- package/plugins/codex-profile/AGENTS.md +0 -20
- package/plugins/codex-profile/INSTALL.md +0 -89
- package/plugins/codex-profile/README.md +0 -61
- package/plugins/cursor-marketplace/.cursor-plugin/plugin.json +0 -23
- package/plugins/cursor-marketplace/CHANGELOG.md +0 -30
- package/plugins/cursor-marketplace/LICENSE +0 -21
- package/plugins/cursor-marketplace/README.md +0 -124
- package/plugins/cursor-marketplace/agents/reliability-reviewer.md +0 -31
- package/plugins/cursor-marketplace/assets/logo-400x400.png +0 -0
- package/plugins/cursor-marketplace/commands/capture-feedback.md +0 -33
- package/plugins/cursor-marketplace/commands/check-gates.md +0 -25
- package/plugins/cursor-marketplace/commands/show-lessons.md +0 -27
- package/plugins/cursor-marketplace/hooks/hooks.json +0 -10
- package/plugins/cursor-marketplace/mcp.json +0 -14
- package/plugins/cursor-marketplace/rules/feedback-capture.mdc +0 -34
- package/plugins/cursor-marketplace/rules/pre-action-gates.mdc +0 -30
- package/plugins/cursor-marketplace/rules/session-continuity.mdc +0 -28
- package/plugins/cursor-marketplace/scripts/gate-check.sh +0 -21
- package/plugins/cursor-marketplace/skills/capture-feedback/SKILL.md +0 -48
- package/plugins/cursor-marketplace/skills/prevention-rules/SKILL.md +0 -31
- package/plugins/cursor-marketplace/skills/recall-context/SKILL.md +0 -30
- package/plugins/cursor-marketplace/skills/search-lessons/SKILL.md +0 -33
- package/plugins/gemini-extension/INSTALL.md +0 -92
- package/plugins/gemini-extension/gemini_prompt.txt +0 -14
- package/plugins/gemini-extension/tool_contract.json +0 -45
- package/plugins/opencode-profile/INSTALL.md +0 -57
- package/public/assets/instagram-card.png +0 -0
- package/public/assets/tiktok-agent-memory.mp4 +0 -0
- package/public/blog.html +0 -474
- package/public/compare/mem0.html +0 -189
- package/public/compare/speclock.html +0 -180
- package/public/compare.html +0 -310
- package/public/dashboard.html +0 -1100
- package/public/guide.html +0 -317
- package/public/guides/claude-code-prevent-repeated-mistakes.html +0 -161
- package/public/guides/codex-cli-guardrails.html +0 -158
- package/public/guides/cursor-prevent-repeated-mistakes.html +0 -161
- package/public/guides/pre-action-gates.html +0 -162
- package/public/guides/stop-repeated-ai-agent-mistakes.html +0 -159
- package/public/index.html +0 -1225
- package/public/js/buyer-intent.js +0 -252
- package/public/learn/agent-harness-pattern.html +0 -180
- package/public/learn/ai-agent-persistent-memory.html +0 -203
- package/public/learn/learn.css +0 -45
- package/public/learn/mcp-pre-action-gates-explained.html +0 -172
- package/public/learn/stop-ai-agent-force-push.html +0 -134
- package/public/learn/vibe-coding-safety-net.html +0 -142
- package/public/learn.html +0 -274
- package/public/lessons.html +0 -967
- package/public/llm-context.md +0 -156
- package/public/pro.html +0 -1087
- package/public/vercel.json +0 -8
- package/scripts/a2ui-engine.js +0 -73
- package/scripts/adk-consolidator.js +0 -274
- package/scripts/agent-security-hardening.js +0 -225
- package/scripts/ai-search-visibility.js +0 -116
- package/scripts/autonomous-sales-agent.js +0 -39
- package/scripts/autoresearch-runner.js +0 -216
- package/scripts/background-agent-governance.js +0 -229
- package/scripts/behavioral-extraction.js +0 -93
- package/scripts/budget-enforcer.js +0 -173
- package/scripts/budget-guard.js +0 -173
- package/scripts/build-claude-mcpb.js +0 -255
- package/scripts/build-codex-plugin.js +0 -152
- package/scripts/capture-railway-diagnostics.sh +0 -97
- package/scripts/changeset-check.js +0 -372
- package/scripts/check-congruence.js +0 -443
- package/scripts/computer-use-firewall.js +0 -280
- package/scripts/content-engine/linkedin-content-generator.js +0 -154
- package/scripts/content-engine/output/linkedin-memento-validation.md +0 -17
- package/scripts/content-engine/output/linkedin-posts-2026-04-09.md +0 -175
- package/scripts/content-engine/reddit-thread-finder.js +0 -154
- package/scripts/context-engine.js +0 -710
- package/scripts/daily-digest.js +0 -11
- package/scripts/data-governance.js +0 -173
- package/scripts/deploy-gcp.sh +0 -44
- package/scripts/deploy-policy.js +0 -249
- package/scripts/disagreement-mining.js +0 -315
- package/scripts/dpo-optimizer.js +0 -206
- package/scripts/ensure-repo-bootstrap.js +0 -130
- package/scripts/ephemeral-agent-store.js +0 -212
- package/scripts/eval-harness.js +0 -56
- package/scripts/export-kto-pairs.js +0 -309
- package/scripts/export-training.js +0 -446
- package/scripts/feedback-fallback.js +0 -111
- package/scripts/feedback-inbox-read.js +0 -162
- package/scripts/feedback-root-consolidator.js +0 -233
- package/scripts/feedback-to-memory.js +0 -185
- package/scripts/gate-satisfy.js +0 -42
- package/scripts/generate-paperbanana-diagrams.sh +0 -99
- package/scripts/generate-pretool-hook.sh +0 -40
- package/scripts/github-about.js +0 -430
- package/scripts/github-outreach.js +0 -65
- package/scripts/gtm-revenue-loop.js +0 -535
- package/scripts/hallucination-detector.js +0 -226
- package/scripts/hf-papers.js +0 -317
- package/scripts/hook-auto-capture.sh +0 -100
- package/scripts/hook-stop-pr-thread-check.sh +0 -68
- package/scripts/hook-stop-self-score.sh +0 -51
- package/scripts/hook-stop-verify-deploy.sh +0 -31
- package/scripts/hook-verify-before-done.sh +0 -20
- package/scripts/managed-dpo-export.js +0 -91
- package/scripts/markdown-escape.js +0 -12
- package/scripts/marketing-experiment.js +0 -657
- package/scripts/memalign-recall.js +0 -111
- package/scripts/memory-migration.js +0 -296
- package/scripts/meta-policy.js +0 -190
- package/scripts/metered-billing.js +0 -16
- package/scripts/model-tier-router.js +0 -310
- package/scripts/money-watcher.js +0 -218
- package/scripts/multi-hop-recall.js +0 -240
- package/scripts/per-step-scoring.js +0 -163
- package/scripts/perplexity-command-center.js +0 -644
- package/scripts/perplexity-marketing.js +0 -454
- package/scripts/pii-scanner.js +0 -153
- package/scripts/plan-gate.js +0 -154
- package/scripts/post-everywhere.js +0 -341
- package/scripts/post-to-x-retry.sh +0 -22
- package/scripts/post-to-x.js +0 -369
- package/scripts/pr-manager.js +0 -421
- package/scripts/principle-extractor.js +0 -162
- package/scripts/pro-features.js +0 -41
- package/scripts/prompt-dlp.js +0 -222
- package/scripts/prove-adapters.js +0 -860
- package/scripts/prove-attribution.js +0 -361
- package/scripts/prove-automation.js +0 -651
- package/scripts/prove-autoresearch.js +0 -304
- package/scripts/prove-claim-verification.js +0 -277
- package/scripts/prove-cloudflare-sandbox.js +0 -161
- package/scripts/prove-data-pipeline.js +0 -408
- package/scripts/prove-data-quality.js +0 -227
- package/scripts/prove-evolution.js +0 -352
- package/scripts/prove-harnesses.js +0 -287
- package/scripts/prove-intelligence.js +0 -257
- package/scripts/prove-lancedb.js +0 -425
- package/scripts/prove-local-intelligence.js +0 -340
- package/scripts/prove-loop-closure.js +0 -263
- package/scripts/prove-packaged-runtime.js +0 -327
- package/scripts/prove-predictive-insights.js +0 -355
- package/scripts/prove-runtime.js +0 -363
- package/scripts/prove-seo-gsd.js +0 -234
- package/scripts/prove-settings.js +0 -279
- package/scripts/prove-subway-upgrades.js +0 -277
- package/scripts/prove-tessl.js +0 -229
- package/scripts/prove-training-export.js +0 -325
- package/scripts/prove-workflow-contract.js +0 -112
- package/scripts/prove-xmemory.js +0 -332
- package/scripts/publish-decision.js +0 -159
- package/scripts/ralph-loop.js +0 -376
- package/scripts/ralph-mode-ci.js +0 -434
- package/scripts/reddit-dm-outreach.js +0 -192
- package/scripts/reddit-monitor-cron.sh +0 -26
- package/scripts/reminder-engine.js +0 -132
- package/scripts/revenue-status.js +0 -472
- package/scripts/rotate-stripe-webhook-secret.js +0 -314
- package/scripts/schedule-manager.js +0 -249
- package/scripts/self-healing-check.js +0 -193
- package/scripts/session-analyzer.js +0 -533
- package/scripts/shieldcortex-memory-firewall-runner.mjs +0 -53
- package/scripts/skill-exporter.js +0 -260
- package/scripts/skill-materializer.js +0 -134
- package/scripts/skill-packs.js +0 -136
- package/scripts/skill-proposer.js +0 -99
- package/scripts/skill-quality-tracker.js +0 -282
- package/scripts/slow-loop.js +0 -72
- package/scripts/social-analytics/db/marketing-db.js +0 -179
- package/scripts/social-analytics/db/schema.sql +0 -55
- package/scripts/social-analytics/digest.js +0 -256
- package/scripts/social-analytics/engagement-audit.js +0 -185
- package/scripts/social-analytics/generate-instagram-card.js +0 -123
- package/scripts/social-analytics/generate-slides.js +0 -268
- package/scripts/social-analytics/instagram-thumbgate-post.js +0 -111
- package/scripts/social-analytics/install-growth-automation.js +0 -114
- package/scripts/social-analytics/load-env.js +0 -77
- package/scripts/social-analytics/mcp-server.js +0 -289
- package/scripts/social-analytics/normalizer.js +0 -580
- package/scripts/social-analytics/notify.js +0 -162
- package/scripts/social-analytics/poll-all.js +0 -107
- package/scripts/social-analytics/pollers/github.js +0 -195
- package/scripts/social-analytics/pollers/instagram.js +0 -253
- package/scripts/social-analytics/pollers/linkedin.js +0 -340
- package/scripts/social-analytics/pollers/plausible.js +0 -245
- package/scripts/social-analytics/pollers/reddit.js +0 -306
- package/scripts/social-analytics/pollers/threads.js +0 -233
- package/scripts/social-analytics/pollers/tiktok.js +0 -203
- package/scripts/social-analytics/pollers/x.js +0 -227
- package/scripts/social-analytics/pollers/youtube.js +0 -304
- package/scripts/social-analytics/pollers/zernio.js +0 -183
- package/scripts/social-analytics/post-video.js +0 -316
- package/scripts/social-analytics/publish-instagram-thumbgate.js +0 -104
- package/scripts/social-analytics/publish-thumbgate-launch.js +0 -322
- package/scripts/social-analytics/publishers/devto.js +0 -122
- package/scripts/social-analytics/publishers/instagram.js +0 -317
- package/scripts/social-analytics/publishers/linkedin.js +0 -294
- package/scripts/social-analytics/publishers/reddit.js +0 -385
- package/scripts/social-analytics/publishers/threads.js +0 -275
- package/scripts/social-analytics/publishers/tiktok.js +0 -217
- package/scripts/social-analytics/publishers/x.js +0 -259
- package/scripts/social-analytics/publishers/youtube.js +0 -223
- package/scripts/social-analytics/publishers/zernio.js +0 -568
- package/scripts/social-analytics/reconcile-thumbgate-campaign.js +0 -165
- package/scripts/social-analytics/run-digest.js +0 -34
- package/scripts/social-analytics/schedule-thumbgate-campaign.js +0 -275
- package/scripts/social-analytics/store.js +0 -455
- package/scripts/social-analytics/sync-launch-assets.js +0 -185
- package/scripts/social-analytics/utm.js +0 -143
- package/scripts/social-pipeline.js +0 -2626
- package/scripts/social-post-hourly.js +0 -228
- package/scripts/social-quality-gate.js +0 -134
- package/scripts/social-reply-monitor.js +0 -592
- package/scripts/status-dashboard.js +0 -155
- package/scripts/stripe-live-status.js +0 -115
- package/scripts/subagent-profiles.js +0 -79
- package/scripts/sync-branch-protection.js +0 -340
- package/scripts/sync-gh-secrets-from-env.sh +0 -70
- package/scripts/sync-github-about.js +0 -55
- package/scripts/sync-version.js +0 -479
- package/scripts/synthetic-dpo.js +0 -234
- package/scripts/tessl-export.js +0 -369
- package/scripts/test-coverage.js +0 -128
- package/scripts/thumbgate-bench.js +0 -494
- package/scripts/thumbgate_session_start.sh +0 -32
- package/scripts/train_from_feedback.py +0 -929
- package/scripts/validate-feedback.js +0 -581
- package/scripts/verify-obsidian-setup.sh +0 -269
- package/scripts/verify-run.js +0 -269
- package/scripts/weekly-auto-post.js +0 -124
- package/scripts/x-autonomous-marketing.js +0 -139
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
|
|
6
|
-
const PROJECT_ROOT = path.join(__dirname, '..');
|
|
7
|
-
const DEFAULT_STATE_PATH = path.join(PROJECT_ROOT, '.thumbgate', 'reminder-state.json');
|
|
8
|
-
|
|
9
|
-
const REMINDER_TEMPLATES = {
|
|
10
|
-
guardrail_spike: 'Safety guardrails triggered {{count}} times. Re-apply rule: {{rule}}',
|
|
11
|
-
iteration_limit: 'Approaching max iterations ({{count}}/{{limit}}). Prioritize essential actions only.',
|
|
12
|
-
tool_misuse: 'Tool misuse detected {{count}} times for: {{tools}}. Verify tool schemas before calling.',
|
|
13
|
-
error_cascade: 'Repeated errors ({{count}}). Switch strategy: {{suggestion}}',
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const DEFAULT_THRESHOLDS = {
|
|
17
|
-
guardrail_spike: 3,
|
|
18
|
-
iteration_limit: 1,
|
|
19
|
-
tool_misuse: 2,
|
|
20
|
-
error_cascade: 3,
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
function getStatePath(stateFile) {
|
|
24
|
-
return stateFile || DEFAULT_STATE_PATH;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function loadState(stateFile) {
|
|
28
|
-
const p = getStatePath(stateFile);
|
|
29
|
-
try {
|
|
30
|
-
if (fs.existsSync(p)) return JSON.parse(fs.readFileSync(p, 'utf-8'));
|
|
31
|
-
} catch {
|
|
32
|
-
// corrupted — start fresh
|
|
33
|
-
}
|
|
34
|
-
return { counts: {} };
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function saveState(state, stateFile) {
|
|
38
|
-
const p = getStatePath(stateFile);
|
|
39
|
-
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
40
|
-
fs.writeFileSync(p, JSON.stringify(state, null, 2));
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Increment the event counter for a given event type.
|
|
45
|
-
* @param {string} eventType - One of the keys in REMINDER_TEMPLATES
|
|
46
|
-
* @param {string} [stateFile] - Path to state JSON (default: .thumbgate/reminder-state.json)
|
|
47
|
-
* @returns {number} New count after incrementing
|
|
48
|
-
*/
|
|
49
|
-
function trackEvent(eventType, stateFile) {
|
|
50
|
-
const state = loadState(stateFile);
|
|
51
|
-
state.counts[eventType] = (state.counts[eventType] || 0) + 1;
|
|
52
|
-
saveState(state, stateFile);
|
|
53
|
-
return state.counts[eventType];
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Get the current event count without modifying state.
|
|
58
|
-
* @param {string} eventType
|
|
59
|
-
* @param {string} [stateFile]
|
|
60
|
-
* @returns {number}
|
|
61
|
-
*/
|
|
62
|
-
function getEventCount(eventType, stateFile) {
|
|
63
|
-
return loadState(stateFile).counts[eventType] || 0;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Return true if the event count meets or exceeds its threshold.
|
|
68
|
-
* @param {string} eventType
|
|
69
|
-
* @param {number} [threshold] - Defaults to DEFAULT_THRESHOLDS[eventType] or 3
|
|
70
|
-
* @param {string} [stateFile]
|
|
71
|
-
* @returns {boolean}
|
|
72
|
-
*/
|
|
73
|
-
function shouldInjectReminder(eventType, threshold, stateFile) {
|
|
74
|
-
const t = typeof threshold === 'number' ? threshold : (DEFAULT_THRESHOLDS[eventType] || 3);
|
|
75
|
-
return getEventCount(eventType, stateFile) >= t;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Render a reminder template with context variable substitution.
|
|
80
|
-
* @param {string} eventType
|
|
81
|
-
* @param {object} ctx - Variables to substitute into {{var}} placeholders
|
|
82
|
-
* @returns {string}
|
|
83
|
-
*/
|
|
84
|
-
function renderTemplate(eventType, ctx) {
|
|
85
|
-
const template = REMINDER_TEMPLATES[eventType];
|
|
86
|
-
if (!template) return `[Reminder] Event: ${eventType}`;
|
|
87
|
-
return template.replace(/\{\{(\w+)\}\}/g, (_, key) => (ctx && ctx[key] !== undefined ? ctx[key] : `{${key}}`));
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Append a system reminder to a turns array without modifying state.
|
|
92
|
-
* Callers are responsible for calling trackEvent before/after as needed.
|
|
93
|
-
* @param {object[]} turns - Existing turns array
|
|
94
|
-
* @param {string} eventType
|
|
95
|
-
* @param {object} ctx - Template variables (count will be added automatically)
|
|
96
|
-
* @param {string} [stateFile]
|
|
97
|
-
* @returns {object[]} New turns array with reminder appended
|
|
98
|
-
*/
|
|
99
|
-
function injectReminder(turns, eventType, ctx, stateFile) {
|
|
100
|
-
const count = getEventCount(eventType, stateFile);
|
|
101
|
-
const message = renderTemplate(eventType, { ...ctx, count });
|
|
102
|
-
const reminder = {
|
|
103
|
-
role: 'user',
|
|
104
|
-
content: `[System Reminder] ${message}`,
|
|
105
|
-
injectedAt: new Date().toISOString(),
|
|
106
|
-
eventType,
|
|
107
|
-
};
|
|
108
|
-
return [...turns, reminder];
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Reset the event counter for a given event type (e.g., after a reminder is acted on).
|
|
113
|
-
* @param {string} eventType
|
|
114
|
-
* @param {string} [stateFile]
|
|
115
|
-
*/
|
|
116
|
-
function resetEvent(eventType, stateFile) {
|
|
117
|
-
const state = loadState(stateFile);
|
|
118
|
-
state.counts[eventType] = 0;
|
|
119
|
-
saveState(state, stateFile);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
module.exports = {
|
|
123
|
-
REMINDER_TEMPLATES,
|
|
124
|
-
DEFAULT_THRESHOLDS,
|
|
125
|
-
DEFAULT_STATE_PATH,
|
|
126
|
-
trackEvent,
|
|
127
|
-
getEventCount,
|
|
128
|
-
shouldInjectReminder,
|
|
129
|
-
renderTemplate,
|
|
130
|
-
injectReminder,
|
|
131
|
-
resetEvent,
|
|
132
|
-
};
|
|
@@ -1,472 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
const crypto = require('node:crypto');
|
|
5
|
-
const { spawnSync } = require('node:child_process');
|
|
6
|
-
const {
|
|
7
|
-
DEFAULT_PUBLIC_APP_ORIGIN,
|
|
8
|
-
} = require('./hosted-config');
|
|
9
|
-
|
|
10
|
-
const DEFAULT_REPO = 'IgorGanapolsky/ThumbGate';
|
|
11
|
-
const DEFAULT_RAILWAY_SERVICE = 'thumbgate';
|
|
12
|
-
const HOSTED_WINDOWS = ['today', '30d', 'lifetime'];
|
|
13
|
-
const RUNTIME_KEYS = [
|
|
14
|
-
'THUMBGATE_FEEDBACK_DIR',
|
|
15
|
-
'THUMBGATE_API_KEY',
|
|
16
|
-
'THUMBGATE_PUBLIC_APP_ORIGIN',
|
|
17
|
-
'THUMBGATE_BILLING_API_BASE_URL',
|
|
18
|
-
'THUMBGATE_GA_MEASUREMENT_ID',
|
|
19
|
-
'THUMBGATE_CHECKOUT_FALLBACK_URL',
|
|
20
|
-
'STRIPE_SECRET_KEY',
|
|
21
|
-
];
|
|
22
|
-
|
|
23
|
-
function parseArgs(argv = []) {
|
|
24
|
-
const options = {
|
|
25
|
-
json: false,
|
|
26
|
-
repo: process.env.THUMBGATE_GITHUB_REPO || DEFAULT_REPO,
|
|
27
|
-
timeZone: process.env.TZ || 'America/New_York',
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
for (const arg of argv) {
|
|
31
|
-
if (arg === '--json') {
|
|
32
|
-
options.json = true;
|
|
33
|
-
continue;
|
|
34
|
-
}
|
|
35
|
-
if (arg.startsWith('--repo=')) {
|
|
36
|
-
options.repo = arg.slice('--repo='.length).trim() || options.repo;
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
if (arg.startsWith('--timezone=')) {
|
|
40
|
-
options.timeZone = arg.slice('--timezone='.length).trim() || options.timeZone;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return options;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function parseGhVariableList(stdout = '') {
|
|
48
|
-
const variables = {};
|
|
49
|
-
for (const line of String(stdout).split(/\r?\n/)) {
|
|
50
|
-
if (!line.trim()) continue;
|
|
51
|
-
const [name, value] = line.split('\t');
|
|
52
|
-
if (!name || value === undefined) continue;
|
|
53
|
-
variables[name.trim()] = value.trim();
|
|
54
|
-
}
|
|
55
|
-
return variables;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function parseHtmlSignals(html = '') {
|
|
59
|
-
const body = String(html);
|
|
60
|
-
return {
|
|
61
|
-
plausibleScript: body.includes('/js/analytics.js'),
|
|
62
|
-
gaLoaderScript: body.includes('googletagmanager.com/gtag/js'),
|
|
63
|
-
gaEventHook: body.includes('window.gtag('),
|
|
64
|
-
gaPlaceholderPresent: body.includes('__GA_MEASUREMENT_ID__'),
|
|
65
|
-
telemetryEndpoint: body.includes('/v1/telemetry/ping'),
|
|
66
|
-
workflowSprintIntake: body.includes('workflow-sprint-intake'),
|
|
67
|
-
webmcpBadge: body.includes('WebMCP-ready'),
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function centsToDollars(value) {
|
|
72
|
-
return `$${(Number(value || 0) / 100).toFixed(2)}`;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function formatRatio(value) {
|
|
76
|
-
return Number.isFinite(Number(value)) ? Number(value).toFixed(4) : '0.0000';
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function windowSnapshot(summary = {}) {
|
|
80
|
-
return {
|
|
81
|
-
trafficMetrics: summary.trafficMetrics || {},
|
|
82
|
-
signups: summary.signups || {},
|
|
83
|
-
revenue: summary.revenue || {},
|
|
84
|
-
pipeline: summary.pipeline || {},
|
|
85
|
-
dataQuality: summary.dataQuality || {},
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function buildDiagnosis({ publicProbe, hostedAudit }) {
|
|
90
|
-
const today = hostedAudit && hostedAudit.summaries ? hostedAudit.summaries.today : null;
|
|
91
|
-
const trailing30 = hostedAudit && hostedAudit.summaries ? hostedAudit.summaries['30d'] : null;
|
|
92
|
-
const runtimePresence = hostedAudit ? hostedAudit.runtimePresence : {};
|
|
93
|
-
const traffic30 = trailing30 && trailing30.trafficMetrics ? trailing30.trafficMetrics : {};
|
|
94
|
-
const revenue30 = trailing30 && trailing30.revenue ? trailing30.revenue : {};
|
|
95
|
-
|
|
96
|
-
const trackingImplemented = Boolean(
|
|
97
|
-
publicProbe &&
|
|
98
|
-
publicProbe.root &&
|
|
99
|
-
publicProbe.root.signals &&
|
|
100
|
-
publicProbe.root.signals.telemetryEndpoint &&
|
|
101
|
-
publicProbe.root.signals.plausibleScript
|
|
102
|
-
);
|
|
103
|
-
const telemetryIngressWorking = Boolean(publicProbe && publicProbe.telemetryPing && publicProbe.telemetryPing.status === 204);
|
|
104
|
-
const hostedSummaryWorking = Boolean(today && today.status === 200 && trailing30 && trailing30.status === 200);
|
|
105
|
-
const hostedTrafficObserved = Number(traffic30.visitors || 0) > 0 || Number(traffic30.pageViews || 0) > 0;
|
|
106
|
-
const hostedRevenueObserved = Number(revenue30.paidOrders || 0) > 0 || Number(revenue30.bookedRevenueCents || 0) > 0;
|
|
107
|
-
|
|
108
|
-
let primaryIssue = 'inconclusive';
|
|
109
|
-
if (trackingImplemented && telemetryIngressWorking && hostedSummaryWorking && hostedTrafficObserved) {
|
|
110
|
-
primaryIssue = 'operator_blind_spot_local_fallback';
|
|
111
|
-
} else if (trackingImplemented && telemetryIngressWorking && hostedSummaryWorking) {
|
|
112
|
-
primaryIssue = 'low_traffic';
|
|
113
|
-
} else if (trackingImplemented && telemetryIngressWorking) {
|
|
114
|
-
primaryIssue = 'hosted_summary_access_or_config_gap';
|
|
115
|
-
} else if (trackingImplemented) {
|
|
116
|
-
primaryIssue = 'telemetry_ingestion_gap';
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const gaps = [];
|
|
120
|
-
if (!runtimePresence.THUMBGATE_GA_MEASUREMENT_ID) {
|
|
121
|
-
gaps.push('GA4 runtime env is missing in Railway');
|
|
122
|
-
}
|
|
123
|
-
if (!runtimePresence.THUMBGATE_PUBLIC_APP_ORIGIN) {
|
|
124
|
-
gaps.push('THUMBGATE_PUBLIC_APP_ORIGIN is not explicitly set in Railway runtime');
|
|
125
|
-
}
|
|
126
|
-
if (!runtimePresence.THUMBGATE_BILLING_API_BASE_URL) {
|
|
127
|
-
gaps.push('THUMBGATE_BILLING_API_BASE_URL is not explicitly set in Railway runtime');
|
|
128
|
-
}
|
|
129
|
-
if (trackingImplemented && !publicProbe.root.signals.gaLoaderScript) {
|
|
130
|
-
gaps.push('GA event hooks exist in the page, but the GA loader script is absent');
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return {
|
|
134
|
-
trackingImplemented,
|
|
135
|
-
telemetryIngressWorking,
|
|
136
|
-
hostedSummaryWorking,
|
|
137
|
-
hostedTrafficObserved,
|
|
138
|
-
hostedRevenueObserved,
|
|
139
|
-
primaryIssue,
|
|
140
|
-
gaps,
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
function formatWindowBlock(label, summary = {}) {
|
|
145
|
-
const traffic = summary.trafficMetrics || {};
|
|
146
|
-
const revenue = summary.revenue || {};
|
|
147
|
-
const signups = summary.signups || {};
|
|
148
|
-
const pipeline = summary.pipeline || {};
|
|
149
|
-
const sprintLeads = pipeline.workflowSprintLeads || {};
|
|
150
|
-
|
|
151
|
-
return [
|
|
152
|
-
`${label}: visitors ${traffic.visitors || 0}, pageViews ${traffic.pageViews || 0}, checkoutStarts ${traffic.checkoutStarts || 0}, paidOrders ${revenue.paidOrders || 0}, bookedRevenue ${centsToDollars(revenue.bookedRevenueCents || 0)}, sprintLeads ${sprintLeads.total || 0}, signups ${signups.uniqueLeads || 0}`,
|
|
153
|
-
];
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function formatReport(report) {
|
|
157
|
-
const lines = [];
|
|
158
|
-
lines.push(`Revenue Status @ ${report.generatedAt}`);
|
|
159
|
-
lines.push(`Source: ${report.source}`);
|
|
160
|
-
lines.push(`Primary issue: ${report.diagnosis.primaryIssue}`);
|
|
161
|
-
lines.push(`Tracking implemented: ${report.diagnosis.trackingImplemented ? 'yes' : 'no'}`);
|
|
162
|
-
lines.push(`Telemetry ingress working: ${report.diagnosis.telemetryIngressWorking ? 'yes' : 'no'}`);
|
|
163
|
-
lines.push(`Hosted summary working: ${report.diagnosis.hostedSummaryWorking ? 'yes' : 'no'}`);
|
|
164
|
-
lines.push(`Hosted traffic observed: ${report.diagnosis.hostedTrafficObserved ? 'yes' : 'no'}`);
|
|
165
|
-
lines.push(`Hosted revenue observed: ${report.diagnosis.hostedRevenueObserved ? 'yes' : 'no'}`);
|
|
166
|
-
lines.push('');
|
|
167
|
-
lines.push(`Public health: ${report.publicProbe.health.status} (${report.publicProbe.health.version || 'unknown version'})`);
|
|
168
|
-
lines.push(`Telemetry ping probe: ${report.publicProbe.telemetryPing.status}`);
|
|
169
|
-
lines.push(`Runtime flags: ${RUNTIME_KEYS.map((key) => `${key}=${report.hostedAudit.runtimePresence[key] ? 'set' : 'missing'}`).join(', ')}`);
|
|
170
|
-
lines.push('');
|
|
171
|
-
lines.push(...formatWindowBlock('Today', report.hostedAudit.summaries.today));
|
|
172
|
-
lines.push(...formatWindowBlock('30d', report.hostedAudit.summaries['30d']));
|
|
173
|
-
lines.push(...formatWindowBlock('Lifetime', report.hostedAudit.summaries.lifetime));
|
|
174
|
-
lines.push('');
|
|
175
|
-
lines.push(`30d attribution coverage: ${formatRatio(report.hostedAudit.summaries['30d'].dataQuality.attributionCoverage)}`);
|
|
176
|
-
lines.push(`30d telemetry coverage: ${formatRatio(report.hostedAudit.summaries['30d'].dataQuality.telemetryCoverage)}`);
|
|
177
|
-
|
|
178
|
-
if (report.diagnosis.gaps.length) {
|
|
179
|
-
lines.push('');
|
|
180
|
-
lines.push('Gaps:');
|
|
181
|
-
for (const gap of report.diagnosis.gaps) {
|
|
182
|
-
lines.push(`- ${gap}`);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return `${lines.join('\n')}\n`;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function runCommand(command, args, options = {}) {
|
|
190
|
-
const result = spawnSync(command, args, {
|
|
191
|
-
encoding: 'utf8',
|
|
192
|
-
...options,
|
|
193
|
-
});
|
|
194
|
-
return {
|
|
195
|
-
status: result.status,
|
|
196
|
-
stdout: result.stdout || '',
|
|
197
|
-
stderr: result.stderr || '',
|
|
198
|
-
error: result.error || null,
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function requireCommandSuccess(name, result) {
|
|
203
|
-
if (result.error) {
|
|
204
|
-
throw result.error;
|
|
205
|
-
}
|
|
206
|
-
if (result.status !== 0) {
|
|
207
|
-
const message = result.stderr.trim() || result.stdout.trim() || `${name} failed`;
|
|
208
|
-
throw new Error(message);
|
|
209
|
-
}
|
|
210
|
-
return result.stdout;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function getRepoVariables({ repo = DEFAULT_REPO, runCommandFn = runCommand } = {}) {
|
|
214
|
-
const stdout = requireCommandSuccess(
|
|
215
|
-
'gh variable list',
|
|
216
|
-
runCommandFn('gh', ['variable', 'list', '-R', repo])
|
|
217
|
-
);
|
|
218
|
-
return parseGhVariableList(stdout);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
async function probePublicRuntime(appOrigin) {
|
|
222
|
-
const healthUrl = new URL('/health', appOrigin);
|
|
223
|
-
const rootUrl = new URL('/', appOrigin);
|
|
224
|
-
const telemetryUrl = new URL('/v1/telemetry/ping', appOrigin);
|
|
225
|
-
|
|
226
|
-
const healthRes = await fetch(healthUrl);
|
|
227
|
-
const healthJson = await healthRes.json();
|
|
228
|
-
const rootRes = await fetch(rootUrl);
|
|
229
|
-
const rootHtml = await rootRes.text();
|
|
230
|
-
|
|
231
|
-
const probeId = crypto.randomUUID();
|
|
232
|
-
const telemetryRes = await fetch(telemetryUrl, {
|
|
233
|
-
method: 'POST',
|
|
234
|
-
headers: {
|
|
235
|
-
'content-type': 'application/json',
|
|
236
|
-
},
|
|
237
|
-
body: JSON.stringify({
|
|
238
|
-
eventType: 'ops_live_audit_probe',
|
|
239
|
-
clientType: 'web',
|
|
240
|
-
installId: `audit_install_${probeId}`,
|
|
241
|
-
visitorId: `audit_visitor_${probeId}`,
|
|
242
|
-
sessionId: `audit_session_${probeId}`,
|
|
243
|
-
acquisitionId: `audit_acq_${probeId}`,
|
|
244
|
-
page: '/',
|
|
245
|
-
pageTitle: 'Ops Live Audit Probe',
|
|
246
|
-
source: 'website',
|
|
247
|
-
utmSource: 'website',
|
|
248
|
-
utmMedium: 'ops_audit',
|
|
249
|
-
utmCampaign: 'ops_live_audit',
|
|
250
|
-
ctaId: 'ops_live_audit',
|
|
251
|
-
}),
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
return {
|
|
255
|
-
health: {
|
|
256
|
-
status: healthRes.status,
|
|
257
|
-
version: healthJson.version || null,
|
|
258
|
-
deployment: healthJson.deployment || null,
|
|
259
|
-
},
|
|
260
|
-
root: {
|
|
261
|
-
status: rootRes.status,
|
|
262
|
-
signals: parseHtmlSignals(rootHtml),
|
|
263
|
-
},
|
|
264
|
-
telemetryPing: {
|
|
265
|
-
status: telemetryRes.status,
|
|
266
|
-
},
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
function buildRailwayAuditSnippet({ appOrigin, timeZone }) {
|
|
271
|
-
return `
|
|
272
|
-
(async () => {
|
|
273
|
-
const base = ${JSON.stringify(appOrigin)};
|
|
274
|
-
const runtimePresence = {};
|
|
275
|
-
for (const key of ${JSON.stringify(RUNTIME_KEYS)}) {
|
|
276
|
-
runtimePresence[key] = Boolean(process.env[key]);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
const summaries = {};
|
|
280
|
-
for (const window of ${JSON.stringify(HOSTED_WINDOWS)}) {
|
|
281
|
-
const url = new URL('/v1/billing/summary', base);
|
|
282
|
-
url.searchParams.set('window', window);
|
|
283
|
-
url.searchParams.set('timezone', ${JSON.stringify(timeZone)});
|
|
284
|
-
const response = await fetch(url, {
|
|
285
|
-
headers: {
|
|
286
|
-
authorization: 'Bearer ' + process.env.THUMBGATE_API_KEY,
|
|
287
|
-
accept: 'application/json',
|
|
288
|
-
},
|
|
289
|
-
});
|
|
290
|
-
const payload = await response.json();
|
|
291
|
-
summaries[window] = {
|
|
292
|
-
status: response.status,
|
|
293
|
-
trafficMetrics: payload.trafficMetrics || {},
|
|
294
|
-
signups: payload.signups || {},
|
|
295
|
-
revenue: payload.revenue || {},
|
|
296
|
-
pipeline: payload.pipeline || {},
|
|
297
|
-
dataQuality: payload.dataQuality || {},
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
console.log(JSON.stringify({ runtimePresence, summaries }, null, 2));
|
|
302
|
-
})().catch((error) => {
|
|
303
|
-
console.error(error && error.stack ? error.stack : error);
|
|
304
|
-
process.exit(1);
|
|
305
|
-
});
|
|
306
|
-
`;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
function getHostedAuditViaRailway({
|
|
310
|
-
projectId,
|
|
311
|
-
environmentId,
|
|
312
|
-
service = DEFAULT_RAILWAY_SERVICE,
|
|
313
|
-
appOrigin = DEFAULT_PUBLIC_APP_ORIGIN,
|
|
314
|
-
timeZone = 'America/New_York',
|
|
315
|
-
runCommandFn = runCommand,
|
|
316
|
-
} = {}) {
|
|
317
|
-
const snippet = buildRailwayAuditSnippet({ appOrigin, timeZone });
|
|
318
|
-
const stdout = requireCommandSuccess(
|
|
319
|
-
'railway run',
|
|
320
|
-
runCommandFn('railway', [
|
|
321
|
-
'run',
|
|
322
|
-
'-p',
|
|
323
|
-
projectId,
|
|
324
|
-
'-e',
|
|
325
|
-
environmentId,
|
|
326
|
-
'-s',
|
|
327
|
-
service,
|
|
328
|
-
'--',
|
|
329
|
-
'node',
|
|
330
|
-
'-e',
|
|
331
|
-
snippet,
|
|
332
|
-
])
|
|
333
|
-
);
|
|
334
|
-
return JSON.parse(stdout);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
async function getLocalFallback(timeZone) {
|
|
338
|
-
const {
|
|
339
|
-
getOperationalBillingSummary,
|
|
340
|
-
} = require('./operational-summary');
|
|
341
|
-
const result = await getOperationalBillingSummary({
|
|
342
|
-
window: 'today',
|
|
343
|
-
timeZone,
|
|
344
|
-
});
|
|
345
|
-
return {
|
|
346
|
-
source: 'local',
|
|
347
|
-
fallbackReason: result.fallbackReason,
|
|
348
|
-
summary: result.summary,
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
async function generateRevenueStatusReport({
|
|
353
|
-
repo = DEFAULT_REPO,
|
|
354
|
-
timeZone = 'America/New_York',
|
|
355
|
-
runCommandFn = runCommand,
|
|
356
|
-
fetchPublicProbe = probePublicRuntime,
|
|
357
|
-
} = {}) {
|
|
358
|
-
let repoVars = {};
|
|
359
|
-
let repoVarError = null;
|
|
360
|
-
try {
|
|
361
|
-
repoVars = getRepoVariables({ repo, runCommandFn });
|
|
362
|
-
} catch (error) {
|
|
363
|
-
repoVarError = error;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
const appOrigin = repoVars.THUMBGATE_PUBLIC_APP_ORIGIN || DEFAULT_PUBLIC_APP_ORIGIN;
|
|
367
|
-
const publicProbe = await fetchPublicProbe(appOrigin);
|
|
368
|
-
|
|
369
|
-
try {
|
|
370
|
-
if (!repoVars.RAILWAY_PROJECT_ID || !repoVars.RAILWAY_ENVIRONMENT_ID) {
|
|
371
|
-
throw repoVarError || new Error('GitHub repo variables for Railway are unavailable.');
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
const hostedAudit = getHostedAuditViaRailway({
|
|
375
|
-
projectId: repoVars.RAILWAY_PROJECT_ID,
|
|
376
|
-
environmentId: repoVars.RAILWAY_ENVIRONMENT_ID,
|
|
377
|
-
service: repoVars.RAILWAY_SERVICE || DEFAULT_RAILWAY_SERVICE,
|
|
378
|
-
appOrigin: repoVars.THUMBGATE_BILLING_API_BASE_URL || appOrigin,
|
|
379
|
-
timeZone,
|
|
380
|
-
runCommandFn,
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
return {
|
|
384
|
-
generatedAt: new Date().toISOString(),
|
|
385
|
-
repo,
|
|
386
|
-
source: 'hosted-via-railway-env',
|
|
387
|
-
repoVars: {
|
|
388
|
-
RAILWAY_PROJECT_ID: Boolean(repoVars.RAILWAY_PROJECT_ID),
|
|
389
|
-
RAILWAY_ENVIRONMENT_ID: Boolean(repoVars.RAILWAY_ENVIRONMENT_ID),
|
|
390
|
-
RAILWAY_SERVICE: repoVars.RAILWAY_SERVICE || DEFAULT_RAILWAY_SERVICE,
|
|
391
|
-
THUMBGATE_PUBLIC_APP_ORIGIN: appOrigin,
|
|
392
|
-
THUMBGATE_BILLING_API_BASE_URL: repoVars.THUMBGATE_BILLING_API_BASE_URL || appOrigin,
|
|
393
|
-
},
|
|
394
|
-
publicProbe,
|
|
395
|
-
hostedAudit,
|
|
396
|
-
diagnosis: buildDiagnosis({
|
|
397
|
-
publicProbe,
|
|
398
|
-
hostedAudit,
|
|
399
|
-
}),
|
|
400
|
-
};
|
|
401
|
-
} catch (error) {
|
|
402
|
-
const fallback = await getLocalFallback(timeZone);
|
|
403
|
-
return {
|
|
404
|
-
generatedAt: new Date().toISOString(),
|
|
405
|
-
repo,
|
|
406
|
-
source: 'local-fallback',
|
|
407
|
-
repoVars: {
|
|
408
|
-
RAILWAY_PROJECT_ID: Boolean(repoVars.RAILWAY_PROJECT_ID),
|
|
409
|
-
RAILWAY_ENVIRONMENT_ID: Boolean(repoVars.RAILWAY_ENVIRONMENT_ID),
|
|
410
|
-
RAILWAY_SERVICE: repoVars.RAILWAY_SERVICE || DEFAULT_RAILWAY_SERVICE,
|
|
411
|
-
THUMBGATE_PUBLIC_APP_ORIGIN: appOrigin,
|
|
412
|
-
THUMBGATE_BILLING_API_BASE_URL: repoVars.THUMBGATE_BILLING_API_BASE_URL || appOrigin,
|
|
413
|
-
},
|
|
414
|
-
publicProbe,
|
|
415
|
-
hostedAudit: {
|
|
416
|
-
runtimePresence: {},
|
|
417
|
-
summaries: {
|
|
418
|
-
today: windowSnapshot(fallback.summary),
|
|
419
|
-
'30d': windowSnapshot(),
|
|
420
|
-
lifetime: windowSnapshot(),
|
|
421
|
-
},
|
|
422
|
-
error: error.message,
|
|
423
|
-
},
|
|
424
|
-
diagnosis: {
|
|
425
|
-
trackingImplemented: Boolean(publicProbe.root && publicProbe.root.signals && publicProbe.root.signals.telemetryEndpoint),
|
|
426
|
-
telemetryIngressWorking: Boolean(publicProbe.telemetryPing && publicProbe.telemetryPing.status === 204),
|
|
427
|
-
hostedSummaryWorking: false,
|
|
428
|
-
hostedTrafficObserved: false,
|
|
429
|
-
hostedRevenueObserved: false,
|
|
430
|
-
primaryIssue: 'hosted_summary_access_or_config_gap',
|
|
431
|
-
gaps: [repoVarError && repoVarError.message, error.message, fallback.fallbackReason].filter(Boolean),
|
|
432
|
-
},
|
|
433
|
-
};
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
async function main(argv = process.argv.slice(2)) {
|
|
438
|
-
const options = parseArgs(argv);
|
|
439
|
-
const report = await generateRevenueStatusReport({
|
|
440
|
-
repo: options.repo,
|
|
441
|
-
timeZone: options.timeZone,
|
|
442
|
-
});
|
|
443
|
-
|
|
444
|
-
if (options.json) {
|
|
445
|
-
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
446
|
-
return;
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
process.stdout.write(formatReport(report));
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
module.exports = {
|
|
453
|
-
DEFAULT_REPO,
|
|
454
|
-
DEFAULT_RAILWAY_SERVICE,
|
|
455
|
-
HOSTED_WINDOWS,
|
|
456
|
-
RUNTIME_KEYS,
|
|
457
|
-
parseArgs,
|
|
458
|
-
parseGhVariableList,
|
|
459
|
-
parseHtmlSignals,
|
|
460
|
-
centsToDollars,
|
|
461
|
-
buildDiagnosis,
|
|
462
|
-
formatReport,
|
|
463
|
-
buildRailwayAuditSnippet,
|
|
464
|
-
generateRevenueStatusReport,
|
|
465
|
-
};
|
|
466
|
-
|
|
467
|
-
if (require.main === module) {
|
|
468
|
-
main().catch((error) => {
|
|
469
|
-
process.stderr.write(`${error && error.message ? error.message : String(error)}\n`);
|
|
470
|
-
process.exit(1);
|
|
471
|
-
});
|
|
472
|
-
}
|