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,173 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Budget Enforcer — action count, token, and time limits for agent sessions.
|
|
6
|
-
*
|
|
7
|
-
* Competitive parity with LaneKeep's budget system. Tracks:
|
|
8
|
-
* - max_actions: total tool calls allowed per session
|
|
9
|
-
* - max_time_minutes: wall-clock session duration cap
|
|
10
|
-
* - action_count: running count of tool calls in current session
|
|
11
|
-
*
|
|
12
|
-
* Budget state persists to ~/.thumbgate/budget-state.json.
|
|
13
|
-
* Config lives in config/budget.json or can be set via env vars.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
const fs = require('fs');
|
|
17
|
-
const path = require('path');
|
|
18
|
-
|
|
19
|
-
const BUDGET_STATE_PATH = process.env.THUMBGATE_BUDGET_STATE_PATH || path.join(
|
|
20
|
-
process.env.HOME || '/tmp',
|
|
21
|
-
'.thumbgate',
|
|
22
|
-
'budget-state.json'
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
const DEFAULT_BUDGET_CONFIG_PATH = process.env.THUMBGATE_BUDGET_CONFIG_PATH || path.join(
|
|
26
|
-
__dirname, '..', 'config', 'budget.json'
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
const DEFAULT_BUDGET = {
|
|
30
|
-
max_actions: 2000,
|
|
31
|
-
max_time_minutes: 600, // 10 hours
|
|
32
|
-
profiles: {
|
|
33
|
-
strict: { max_actions: 500, max_time_minutes: 150 },
|
|
34
|
-
guided: { max_actions: 2000, max_time_minutes: 600 },
|
|
35
|
-
autonomous: { max_actions: 5000, max_time_minutes: 1200 },
|
|
36
|
-
},
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
function loadBudgetConfig() {
|
|
40
|
-
// 1. Environment overrides
|
|
41
|
-
const envProfile = process.env.THUMBGATE_BUDGET_PROFILE;
|
|
42
|
-
const envMaxActions = process.env.THUMBGATE_MAX_ACTIONS;
|
|
43
|
-
const envMaxTime = process.env.THUMBGATE_MAX_TIME_MINUTES;
|
|
44
|
-
|
|
45
|
-
// 2. Config file
|
|
46
|
-
let fileConfig = {};
|
|
47
|
-
try {
|
|
48
|
-
if (fs.existsSync(DEFAULT_BUDGET_CONFIG_PATH)) {
|
|
49
|
-
fileConfig = JSON.parse(fs.readFileSync(DEFAULT_BUDGET_CONFIG_PATH, 'utf8'));
|
|
50
|
-
}
|
|
51
|
-
} catch { /* use defaults */ }
|
|
52
|
-
|
|
53
|
-
const merged = { ...DEFAULT_BUDGET, ...fileConfig };
|
|
54
|
-
|
|
55
|
-
// Apply profile if set
|
|
56
|
-
if (envProfile && merged.profiles && merged.profiles[envProfile]) {
|
|
57
|
-
Object.assign(merged, merged.profiles[envProfile]);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Env overrides take final precedence
|
|
61
|
-
if (envMaxActions) {
|
|
62
|
-
const parsedMaxActions = parseInt(envMaxActions, 10);
|
|
63
|
-
if (Number.isFinite(parsedMaxActions) && parsedMaxActions > 0) {
|
|
64
|
-
merged.max_actions = parsedMaxActions;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
if (envMaxTime) {
|
|
68
|
-
const parsedMaxTime = parseInt(envMaxTime, 10);
|
|
69
|
-
if (Number.isFinite(parsedMaxTime) && parsedMaxTime > 0) {
|
|
70
|
-
merged.max_time_minutes = parsedMaxTime;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return merged;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function loadBudgetState() {
|
|
78
|
-
try {
|
|
79
|
-
if (fs.existsSync(BUDGET_STATE_PATH)) {
|
|
80
|
-
return JSON.parse(fs.readFileSync(BUDGET_STATE_PATH, 'utf8'));
|
|
81
|
-
}
|
|
82
|
-
} catch { /* corrupted state — reset */ }
|
|
83
|
-
return { action_count: 0, session_start: new Date().toISOString() };
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function saveBudgetState(state) {
|
|
87
|
-
const dir = path.dirname(BUDGET_STATE_PATH);
|
|
88
|
-
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
89
|
-
fs.writeFileSync(BUDGET_STATE_PATH, JSON.stringify(state, null, 2));
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function resetBudget() {
|
|
93
|
-
const state = { action_count: 0, session_start: new Date().toISOString() };
|
|
94
|
-
saveBudgetState(state);
|
|
95
|
-
return state;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Evaluate budget limits. Called before every gate evaluation.
|
|
100
|
-
* Returns null if within budget, or a deny result if budget exceeded.
|
|
101
|
-
*/
|
|
102
|
-
function evaluateBudget(toolName, toolInput) {
|
|
103
|
-
const config = loadBudgetConfig();
|
|
104
|
-
const state = loadBudgetState();
|
|
105
|
-
|
|
106
|
-
// Increment action count
|
|
107
|
-
state.action_count = (state.action_count || 0) + 1;
|
|
108
|
-
saveBudgetState(state);
|
|
109
|
-
|
|
110
|
-
// Check action limit
|
|
111
|
-
if (config.max_actions && state.action_count > config.max_actions) {
|
|
112
|
-
return {
|
|
113
|
-
decision: 'deny',
|
|
114
|
-
gate: 'budget-action-limit',
|
|
115
|
-
message: `Budget exceeded: ${state.action_count}/${config.max_actions} actions used. Session budget is exhausted.`,
|
|
116
|
-
severity: 'critical',
|
|
117
|
-
reasoning: `Tool call #${state.action_count} exceeds the configured max_actions limit of ${config.max_actions}.`,
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Check time limit
|
|
122
|
-
if (config.max_time_minutes && state.session_start) {
|
|
123
|
-
const elapsedMs = Date.now() - new Date(state.session_start).getTime();
|
|
124
|
-
const elapsedMinutes = elapsedMs / (60 * 1000);
|
|
125
|
-
if (elapsedMinutes > config.max_time_minutes) {
|
|
126
|
-
return {
|
|
127
|
-
decision: 'deny',
|
|
128
|
-
gate: 'budget-time-limit',
|
|
129
|
-
message: `Budget exceeded: session has run ${Math.round(elapsedMinutes)}min, limit is ${config.max_time_minutes}min.`,
|
|
130
|
-
severity: 'critical',
|
|
131
|
-
reasoning: `Session duration (${Math.round(elapsedMinutes)}min) exceeds max_time_minutes (${config.max_time_minutes}).`,
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return null; // Within budget
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Get current budget status for dashboard/reporting.
|
|
141
|
-
*/
|
|
142
|
-
function getBudgetStatus() {
|
|
143
|
-
const config = loadBudgetConfig();
|
|
144
|
-
const state = loadBudgetState();
|
|
145
|
-
const elapsedMs = state.session_start
|
|
146
|
-
? Date.now() - new Date(state.session_start).getTime()
|
|
147
|
-
: 0;
|
|
148
|
-
const elapsedMinutes = Math.round(elapsedMs / (60 * 1000));
|
|
149
|
-
|
|
150
|
-
return {
|
|
151
|
-
action_count: state.action_count || 0,
|
|
152
|
-
max_actions: config.max_actions,
|
|
153
|
-
actions_remaining: Math.max(0, (config.max_actions || Infinity) - (state.action_count || 0)),
|
|
154
|
-
actions_pct: config.max_actions ? Math.round(((state.action_count || 0) / config.max_actions) * 100) : 0,
|
|
155
|
-
elapsed_minutes: elapsedMinutes,
|
|
156
|
-
max_time_minutes: config.max_time_minutes,
|
|
157
|
-
time_remaining_minutes: Math.max(0, (config.max_time_minutes || Infinity) - elapsedMinutes),
|
|
158
|
-
time_pct: config.max_time_minutes ? Math.round((elapsedMinutes / config.max_time_minutes) * 100) : 0,
|
|
159
|
-
session_start: state.session_start,
|
|
160
|
-
profile: process.env.THUMBGATE_BUDGET_PROFILE || 'guided',
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
module.exports = {
|
|
165
|
-
evaluateBudget,
|
|
166
|
-
getBudgetStatus,
|
|
167
|
-
loadBudgetConfig,
|
|
168
|
-
loadBudgetState,
|
|
169
|
-
saveBudgetState,
|
|
170
|
-
resetBudget,
|
|
171
|
-
BUDGET_STATE_PATH,
|
|
172
|
-
DEFAULT_BUDGET_CONFIG_PATH,
|
|
173
|
-
};
|
package/scripts/budget-guard.js
DELETED
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
const { resolveFeedbackDir } = require('./feedback-paths');
|
|
5
|
-
|
|
6
|
-
const FEEDBACK_DIR = resolveFeedbackDir();
|
|
7
|
-
const LEDGER_PATH = path.join(FEEDBACK_DIR, 'budget-ledger.json');
|
|
8
|
-
const LOCK_PATH = `${LEDGER_PATH}.lock`;
|
|
9
|
-
|
|
10
|
-
function parseMonthlyBudget(rawValue) {
|
|
11
|
-
const parsed = Number(rawValue);
|
|
12
|
-
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
13
|
-
throw new Error(`Invalid THUMBGATE_MONTHLY_BUDGET_USD value: '${rawValue}'`);
|
|
14
|
-
}
|
|
15
|
-
return parsed;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function getMonthlyBudget() {
|
|
19
|
-
const rawValue = process.env.THUMBGATE_MONTHLY_BUDGET_USD || '10';
|
|
20
|
-
return parseMonthlyBudget(rawValue);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function currentMonthKey() {
|
|
24
|
-
const now = new Date();
|
|
25
|
-
return `${now.getUTCFullYear()}-${String(now.getUTCMonth() + 1).padStart(2, '0')}`;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function loadLedger() {
|
|
29
|
-
if (!fs.existsSync(LEDGER_PATH)) return { months: {} };
|
|
30
|
-
return JSON.parse(fs.readFileSync(LEDGER_PATH, 'utf-8'));
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function saveLedger(ledger) {
|
|
34
|
-
fs.mkdirSync(path.dirname(LEDGER_PATH), { recursive: true });
|
|
35
|
-
fs.writeFileSync(LEDGER_PATH, `${JSON.stringify(ledger, null, 2)}\n`);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function blockMs(ms) {
|
|
39
|
-
const start = Date.now();
|
|
40
|
-
while (Date.now() - start < ms) {
|
|
41
|
-
// Intentional synchronous short wait while lock clears.
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function acquireLock({ timeoutMs = 5000, staleMs = 15000 } = {}) {
|
|
46
|
-
const startedAt = Date.now();
|
|
47
|
-
fs.mkdirSync(path.dirname(LOCK_PATH), { recursive: true });
|
|
48
|
-
|
|
49
|
-
while (true) {
|
|
50
|
-
try {
|
|
51
|
-
return fs.openSync(LOCK_PATH, 'wx');
|
|
52
|
-
} catch (err) {
|
|
53
|
-
if (err.code !== 'EEXIST') throw err;
|
|
54
|
-
|
|
55
|
-
try {
|
|
56
|
-
const stat = fs.statSync(LOCK_PATH);
|
|
57
|
-
if (Date.now() - stat.mtimeMs > staleMs) {
|
|
58
|
-
fs.rmSync(LOCK_PATH, { force: true });
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
} catch {
|
|
62
|
-
// lock disappeared between retries
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (Date.now() - startedAt > timeoutMs) {
|
|
66
|
-
throw new Error('Could not acquire budget ledger lock');
|
|
67
|
-
}
|
|
68
|
-
blockMs(20);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function releaseLock(lockFd) {
|
|
74
|
-
try {
|
|
75
|
-
fs.closeSync(lockFd);
|
|
76
|
-
} finally {
|
|
77
|
-
fs.rmSync(LOCK_PATH, { force: true });
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function addSpend({ amountUsd, source, note }) {
|
|
82
|
-
if (!Number.isFinite(amountUsd) || amountUsd < 0) {
|
|
83
|
-
throw new Error('amountUsd must be a non-negative number');
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const budgetUsd = getMonthlyBudget();
|
|
87
|
-
const lockFd = acquireLock();
|
|
88
|
-
try {
|
|
89
|
-
const ledger = loadLedger();
|
|
90
|
-
const month = currentMonthKey();
|
|
91
|
-
if (!ledger.months[month]) {
|
|
92
|
-
ledger.months[month] = {
|
|
93
|
-
totalUsd: 0,
|
|
94
|
-
entries: [],
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const nextTotal = ledger.months[month].totalUsd + amountUsd;
|
|
99
|
-
if (nextTotal > budgetUsd) {
|
|
100
|
-
throw new Error(`Budget exceeded: ${nextTotal.toFixed(2)} > ${budgetUsd.toFixed(2)} USD/month`);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
ledger.months[month].totalUsd = nextTotal;
|
|
104
|
-
ledger.months[month].entries.push({
|
|
105
|
-
ts: new Date().toISOString(),
|
|
106
|
-
source: source || 'unknown',
|
|
107
|
-
note: note || '',
|
|
108
|
-
amountUsd,
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
saveLedger(ledger);
|
|
112
|
-
return {
|
|
113
|
-
month,
|
|
114
|
-
totalUsd: ledger.months[month].totalUsd,
|
|
115
|
-
budgetUsd,
|
|
116
|
-
};
|
|
117
|
-
} finally {
|
|
118
|
-
releaseLock(lockFd);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function getBudgetStatus() {
|
|
123
|
-
const budgetUsd = getMonthlyBudget();
|
|
124
|
-
const ledger = loadLedger();
|
|
125
|
-
const month = currentMonthKey();
|
|
126
|
-
const total = ledger.months[month] ? ledger.months[month].totalUsd : 0;
|
|
127
|
-
return {
|
|
128
|
-
month,
|
|
129
|
-
totalUsd: total,
|
|
130
|
-
budgetUsd,
|
|
131
|
-
remainingUsd: Math.max(0, budgetUsd - total),
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function runCli() {
|
|
136
|
-
const args = process.argv.slice(2);
|
|
137
|
-
if (args.includes('--status')) {
|
|
138
|
-
console.log(JSON.stringify(getBudgetStatus(), null, 2));
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const addArg = args.find((a) => a.startsWith('--add='));
|
|
143
|
-
if (!addArg) {
|
|
144
|
-
console.log('Usage: node scripts/budget-guard.js --status');
|
|
145
|
-
console.log('Usage: node scripts/budget-guard.js --add=0.15 --source=paperbanana --note="diagram generation"');
|
|
146
|
-
process.exit(1);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const amountUsd = Number(addArg.replace('--add=', ''));
|
|
150
|
-
const sourceArg = args.find((a) => a.startsWith('--source='));
|
|
151
|
-
const noteArg = args.find((a) => a.startsWith('--note='));
|
|
152
|
-
|
|
153
|
-
const result = addSpend({
|
|
154
|
-
amountUsd,
|
|
155
|
-
source: sourceArg ? sourceArg.replace('--source=', '') : 'unknown',
|
|
156
|
-
note: noteArg ? noteArg.replace('--note=', '') : '',
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
console.log(JSON.stringify(result, null, 2));
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
module.exports = {
|
|
163
|
-
addSpend,
|
|
164
|
-
getBudgetStatus,
|
|
165
|
-
getMonthlyBudget,
|
|
166
|
-
parseMonthlyBudget,
|
|
167
|
-
LEDGER_PATH,
|
|
168
|
-
LOCK_PATH,
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
if (require.main === module) {
|
|
172
|
-
runCli();
|
|
173
|
-
}
|
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
const path = require('path');
|
|
6
|
-
const { execFileSync } = require('child_process');
|
|
7
|
-
const {
|
|
8
|
-
getClaudePluginReviewVersionedAssetName,
|
|
9
|
-
getClaudePluginVersionedAssetName,
|
|
10
|
-
} = require('./distribution-surfaces');
|
|
11
|
-
|
|
12
|
-
const PROJECT_ROOT = path.join(__dirname, '..');
|
|
13
|
-
const DEFAULT_OUTPUT_DIR = path.join(PROJECT_ROOT, '.artifacts', 'claude-desktop');
|
|
14
|
-
const RUNTIME_COPY_PATHS = [
|
|
15
|
-
'bin',
|
|
16
|
-
'src',
|
|
17
|
-
'scripts',
|
|
18
|
-
'adapters',
|
|
19
|
-
'config',
|
|
20
|
-
'plugins',
|
|
21
|
-
'skills',
|
|
22
|
-
'openapi',
|
|
23
|
-
'public',
|
|
24
|
-
'.well-known',
|
|
25
|
-
'.claude-plugin',
|
|
26
|
-
'README.md',
|
|
27
|
-
'LICENSE',
|
|
28
|
-
'SECURITY.md',
|
|
29
|
-
'server.json',
|
|
30
|
-
];
|
|
31
|
-
const REVIEW_PACKET_COPY_PATHS = [
|
|
32
|
-
'.claude-plugin',
|
|
33
|
-
'docs/CLAUDE_DESKTOP_EXTENSION.md',
|
|
34
|
-
'README.md',
|
|
35
|
-
'LICENSE',
|
|
36
|
-
'SECURITY.md',
|
|
37
|
-
'server.json',
|
|
38
|
-
];
|
|
39
|
-
|
|
40
|
-
function readJson(relativePath) {
|
|
41
|
-
return JSON.parse(fs.readFileSync(path.join(PROJECT_ROOT, relativePath), 'utf8'));
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function readText(relativePath) {
|
|
45
|
-
return fs.readFileSync(path.join(PROJECT_ROOT, relativePath), 'utf8');
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function copyEntry(relativePath, stageDir) {
|
|
49
|
-
const sourcePath = path.join(PROJECT_ROOT, relativePath);
|
|
50
|
-
if (!fs.existsSync(sourcePath)) return;
|
|
51
|
-
|
|
52
|
-
const targetPath = path.join(stageDir, relativePath);
|
|
53
|
-
const stat = fs.statSync(sourcePath);
|
|
54
|
-
if (stat.isDirectory()) {
|
|
55
|
-
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
56
|
-
fs.cpSync(sourcePath, targetPath, { recursive: true });
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
61
|
-
fs.copyFileSync(sourcePath, targetPath);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function exec(command, args, options = {}) {
|
|
65
|
-
return execFileSync(command, args, {
|
|
66
|
-
cwd: PROJECT_ROOT,
|
|
67
|
-
stdio: 'inherit',
|
|
68
|
-
...options,
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function buildClaudeMcpbManifest() {
|
|
73
|
-
const packageJson = readJson('package.json');
|
|
74
|
-
const pluginManifest = readJson('.claude-plugin/plugin.json');
|
|
75
|
-
const marketplace = readJson('.claude-plugin/marketplace.json');
|
|
76
|
-
const { TOOLS } = require(path.join(PROJECT_ROOT, 'scripts', 'tool-registry'));
|
|
77
|
-
|
|
78
|
-
const repositoryUrl = String(pluginManifest.repository || packageJson.repository.url).replace(/\.git$/, '');
|
|
79
|
-
const privacyPolicyUrl = `${packageJson.homepage}/privacy`;
|
|
80
|
-
const marketplaceEntry = marketplace.plugins[0];
|
|
81
|
-
const readme = readText('.claude-plugin/README.md')
|
|
82
|
-
.split('\n')
|
|
83
|
-
.slice(0, 6)
|
|
84
|
-
.join(' ')
|
|
85
|
-
.replace(/\s+/g, ' ')
|
|
86
|
-
.trim();
|
|
87
|
-
|
|
88
|
-
return {
|
|
89
|
-
manifest_version: '0.3',
|
|
90
|
-
name: pluginManifest.name,
|
|
91
|
-
display_name: 'ThumbGate',
|
|
92
|
-
version: packageJson.version,
|
|
93
|
-
description: marketplaceEntry.description,
|
|
94
|
-
long_description: readme,
|
|
95
|
-
author: {
|
|
96
|
-
name: pluginManifest.author.name,
|
|
97
|
-
url: repositoryUrl,
|
|
98
|
-
},
|
|
99
|
-
repository: {
|
|
100
|
-
type: 'git',
|
|
101
|
-
url: repositoryUrl,
|
|
102
|
-
},
|
|
103
|
-
homepage: packageJson.homepage,
|
|
104
|
-
documentation: `${repositoryUrl}/blob/main/docs/CLAUDE_DESKTOP_EXTENSION.md`,
|
|
105
|
-
support: `${repositoryUrl}/issues`,
|
|
106
|
-
icon: 'icon.png',
|
|
107
|
-
server: {
|
|
108
|
-
type: 'node',
|
|
109
|
-
entry_point: 'server/index.js',
|
|
110
|
-
mcp_config: {
|
|
111
|
-
command: 'node',
|
|
112
|
-
args: ['${__dirname}/server/index.js'],
|
|
113
|
-
env: {},
|
|
114
|
-
},
|
|
115
|
-
},
|
|
116
|
-
tools: TOOLS.map((tool) => ({
|
|
117
|
-
name: tool.name,
|
|
118
|
-
description: tool.description,
|
|
119
|
-
})),
|
|
120
|
-
tools_generated: true,
|
|
121
|
-
keywords: pluginManifest.keywords,
|
|
122
|
-
license: packageJson.license,
|
|
123
|
-
privacy_policies: [privacyPolicyUrl],
|
|
124
|
-
};
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function stageClaudeMcpbBundle(outputDir = DEFAULT_OUTPUT_DIR) {
|
|
128
|
-
const packageJson = readJson('package.json');
|
|
129
|
-
const stageDir = path.join(outputDir, 'bundle');
|
|
130
|
-
const outputFile = path.join(outputDir, getClaudePluginVersionedAssetName(packageJson.version));
|
|
131
|
-
|
|
132
|
-
fs.rmSync(outputDir, { recursive: true, force: true });
|
|
133
|
-
fs.mkdirSync(path.join(stageDir, 'server'), { recursive: true });
|
|
134
|
-
|
|
135
|
-
for (const relativePath of RUNTIME_COPY_PATHS) {
|
|
136
|
-
copyEntry(relativePath, stageDir);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
copyEntry('package.json', stageDir);
|
|
140
|
-
copyEntry('package-lock.json', stageDir);
|
|
141
|
-
|
|
142
|
-
fs.writeFileSync(
|
|
143
|
-
path.join(stageDir, 'server', 'index.js'),
|
|
144
|
-
readText('.claude-plugin/bundle/server/index.js')
|
|
145
|
-
);
|
|
146
|
-
fs.writeFileSync(
|
|
147
|
-
path.join(stageDir, 'icon.png'),
|
|
148
|
-
fs.readFileSync(path.join(PROJECT_ROOT, '.claude-plugin', 'bundle', 'icon.png'))
|
|
149
|
-
);
|
|
150
|
-
fs.writeFileSync(
|
|
151
|
-
path.join(stageDir, 'README.md'),
|
|
152
|
-
readText('.claude-plugin/README.md')
|
|
153
|
-
);
|
|
154
|
-
fs.writeFileSync(
|
|
155
|
-
path.join(stageDir, 'manifest.json'),
|
|
156
|
-
JSON.stringify(buildClaudeMcpbManifest(), null, 2) + '\n'
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
return {
|
|
160
|
-
stageDir,
|
|
161
|
-
outputFile,
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function buildClaudeMcpb(outputDir = DEFAULT_OUTPUT_DIR) {
|
|
166
|
-
const { stageDir, outputFile } = stageClaudeMcpbBundle(outputDir);
|
|
167
|
-
|
|
168
|
-
exec('npm', ['ci', '--omit=dev'], { cwd: stageDir });
|
|
169
|
-
exec('npx', ['-y', '@anthropic-ai/mcpb', 'pack', stageDir, outputFile], { cwd: PROJECT_ROOT });
|
|
170
|
-
|
|
171
|
-
const info = execFileSync('npx', ['-y', '@anthropic-ai/mcpb', 'info', outputFile], {
|
|
172
|
-
cwd: PROJECT_ROOT,
|
|
173
|
-
encoding: 'utf8',
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
process.stdout.write(info);
|
|
177
|
-
|
|
178
|
-
return {
|
|
179
|
-
stageDir,
|
|
180
|
-
outputFile,
|
|
181
|
-
info,
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function buildClaudeReviewZip(outputDir = DEFAULT_OUTPUT_DIR) {
|
|
186
|
-
const packageJson = readJson('package.json');
|
|
187
|
-
const reviewRoot = path.join(outputDir, 'review');
|
|
188
|
-
const reviewDirName = 'thumbgate-claude-plugin-review';
|
|
189
|
-
const stageDir = path.join(reviewRoot, reviewDirName);
|
|
190
|
-
const outputFile = path.join(outputDir, getClaudePluginReviewVersionedAssetName(packageJson.version));
|
|
191
|
-
|
|
192
|
-
fs.rmSync(reviewRoot, { recursive: true, force: true });
|
|
193
|
-
fs.mkdirSync(stageDir, { recursive: true });
|
|
194
|
-
|
|
195
|
-
for (const relativePath of REVIEW_PACKET_COPY_PATHS) {
|
|
196
|
-
copyEntry(relativePath, stageDir);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
fs.rmSync(outputFile, { force: true });
|
|
200
|
-
exec('zip', ['-qr', outputFile, reviewDirName], { cwd: reviewRoot });
|
|
201
|
-
|
|
202
|
-
return {
|
|
203
|
-
stageDir,
|
|
204
|
-
outputFile,
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function resolveBuildRequest(args = [], cwd = process.cwd()) {
|
|
209
|
-
const mode = args.includes('--review-zip')
|
|
210
|
-
? 'review-zip'
|
|
211
|
-
: args.includes('--all')
|
|
212
|
-
? 'all'
|
|
213
|
-
: 'mcpb';
|
|
214
|
-
const outputArg = args.find((arg) => !arg.startsWith('--'));
|
|
215
|
-
const outputDir = outputArg
|
|
216
|
-
? path.resolve(cwd, outputArg)
|
|
217
|
-
: DEFAULT_OUTPUT_DIR;
|
|
218
|
-
|
|
219
|
-
return { mode, outputDir };
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
function runBuildRequest({ mode, outputDir }, deps = { buildClaudeMcpb, buildClaudeReviewZip }) {
|
|
223
|
-
if (mode === 'review-zip') {
|
|
224
|
-
const { outputFile } = deps.buildClaudeReviewZip(outputDir);
|
|
225
|
-
return [`Built Claude plugin review zip: ${outputFile}`];
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
if (mode === 'all') {
|
|
229
|
-
const { outputFile } = deps.buildClaudeMcpb(outputDir);
|
|
230
|
-
const { outputFile: reviewOutputFile } = deps.buildClaudeReviewZip(outputDir);
|
|
231
|
-
return [
|
|
232
|
-
`Built Claude Desktop bundle: ${outputFile}`,
|
|
233
|
-
`Built Claude plugin review zip: ${reviewOutputFile}`,
|
|
234
|
-
];
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
const { outputFile } = deps.buildClaudeMcpb(outputDir);
|
|
238
|
-
return [`Built Claude Desktop bundle: ${outputFile}`];
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if (require.main === module) {
|
|
242
|
-
for (const line of runBuildRequest(resolveBuildRequest(process.argv.slice(2)))) {
|
|
243
|
-
console.log(line);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
module.exports = {
|
|
248
|
-
DEFAULT_OUTPUT_DIR,
|
|
249
|
-
buildClaudeMcpbManifest,
|
|
250
|
-
buildClaudeReviewZip,
|
|
251
|
-
stageClaudeMcpbBundle,
|
|
252
|
-
buildClaudeMcpb,
|
|
253
|
-
resolveBuildRequest,
|
|
254
|
-
runBuildRequest,
|
|
255
|
-
};
|