@vibecheckai/cli 3.2.6 → 3.4.0

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.
Files changed (89) hide show
  1. package/bin/registry.js +306 -90
  2. package/bin/runners/lib/agent-firewall/change-packet/builder.js +280 -6
  3. package/bin/runners/lib/agent-firewall/critic/index.js +151 -0
  4. package/bin/runners/lib/agent-firewall/critic/judge.js +432 -0
  5. package/bin/runners/lib/agent-firewall/critic/prompts.js +305 -0
  6. package/bin/runners/lib/agent-firewall/lawbook/distributor.js +465 -0
  7. package/bin/runners/lib/agent-firewall/lawbook/evaluator.js +604 -0
  8. package/bin/runners/lib/agent-firewall/lawbook/index.js +304 -0
  9. package/bin/runners/lib/agent-firewall/lawbook/registry.js +514 -0
  10. package/bin/runners/lib/agent-firewall/lawbook/schema.js +420 -0
  11. package/bin/runners/lib/agent-firewall/logger.js +141 -0
  12. package/bin/runners/lib/agent-firewall/policy/loader.js +312 -4
  13. package/bin/runners/lib/agent-firewall/policy/rules/ghost-env.js +113 -1
  14. package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +133 -6
  15. package/bin/runners/lib/agent-firewall/proposal/extractor.js +394 -0
  16. package/bin/runners/lib/agent-firewall/proposal/index.js +212 -0
  17. package/bin/runners/lib/agent-firewall/proposal/schema.js +251 -0
  18. package/bin/runners/lib/agent-firewall/proposal/validator.js +386 -0
  19. package/bin/runners/lib/agent-firewall/reality/index.js +332 -0
  20. package/bin/runners/lib/agent-firewall/reality/state.js +625 -0
  21. package/bin/runners/lib/agent-firewall/reality/watcher.js +322 -0
  22. package/bin/runners/lib/agent-firewall/risk/index.js +173 -0
  23. package/bin/runners/lib/agent-firewall/risk/scorer.js +328 -0
  24. package/bin/runners/lib/agent-firewall/risk/thresholds.js +321 -0
  25. package/bin/runners/lib/agent-firewall/risk/vectors.js +421 -0
  26. package/bin/runners/lib/agent-firewall/simulator/diff-simulator.js +472 -0
  27. package/bin/runners/lib/agent-firewall/simulator/import-resolver.js +346 -0
  28. package/bin/runners/lib/agent-firewall/simulator/index.js +181 -0
  29. package/bin/runners/lib/agent-firewall/simulator/route-validator.js +380 -0
  30. package/bin/runners/lib/agent-firewall/time-machine/incident-correlator.js +661 -0
  31. package/bin/runners/lib/agent-firewall/time-machine/index.js +267 -0
  32. package/bin/runners/lib/agent-firewall/time-machine/replay-engine.js +436 -0
  33. package/bin/runners/lib/agent-firewall/time-machine/state-reconstructor.js +490 -0
  34. package/bin/runners/lib/agent-firewall/time-machine/timeline-builder.js +530 -0
  35. package/bin/runners/lib/analyzers.js +136 -141
  36. package/bin/runners/lib/authority-badge.js +425 -0
  37. package/bin/runners/lib/cli-output.js +7 -1
  38. package/bin/runners/lib/entitlements-v2.js +96 -505
  39. package/bin/runners/lib/error-handler.js +16 -9
  40. package/bin/runners/lib/exit-codes.js +275 -0
  41. package/bin/runners/lib/global-flags.js +37 -0
  42. package/bin/runners/lib/help-formatter.js +413 -0
  43. package/bin/runners/lib/logger.js +38 -0
  44. package/bin/runners/lib/scan-output.js +18 -19
  45. package/bin/runners/lib/ship-output.js +18 -25
  46. package/bin/runners/lib/unified-cli-output.js +604 -0
  47. package/bin/runners/lib/upsell.js +105 -205
  48. package/bin/runners/runApprove.js +1200 -0
  49. package/bin/runners/runAuth.js +324 -95
  50. package/bin/runners/runCheckpoint.js +39 -21
  51. package/bin/runners/runClassify.js +859 -0
  52. package/bin/runners/runContext.js +136 -24
  53. package/bin/runners/runDoctor.js +108 -68
  54. package/bin/runners/runFix.js +6 -5
  55. package/bin/runners/runGuard.js +212 -118
  56. package/bin/runners/runInit.js +3 -2
  57. package/bin/runners/runMcp.js +130 -52
  58. package/bin/runners/runPolish.js +43 -20
  59. package/bin/runners/runProve.js +1 -2
  60. package/bin/runners/runReport.js +3 -2
  61. package/bin/runners/runScan.js +77 -45
  62. package/bin/runners/runShip.js +3 -4
  63. package/bin/runners/runValidate.js +19 -2
  64. package/bin/runners/runWatch.js +104 -53
  65. package/bin/vibecheck.js +103 -21
  66. package/mcp-server/HARDENING_SUMMARY.md +299 -0
  67. package/mcp-server/agent-firewall-interceptor.js +367 -31
  68. package/mcp-server/authority-tools.js +569 -0
  69. package/mcp-server/conductor/conflict-resolver.js +588 -0
  70. package/mcp-server/conductor/execution-planner.js +544 -0
  71. package/mcp-server/conductor/index.js +377 -0
  72. package/mcp-server/conductor/lock-manager.js +615 -0
  73. package/mcp-server/conductor/request-queue.js +550 -0
  74. package/mcp-server/conductor/session-manager.js +500 -0
  75. package/mcp-server/conductor/tools.js +510 -0
  76. package/mcp-server/index.js +1152 -856
  77. package/mcp-server/lib/api-client.cjs +13 -0
  78. package/mcp-server/lib/logger.cjs +30 -0
  79. package/mcp-server/logger.js +173 -0
  80. package/mcp-server/package.json +2 -2
  81. package/mcp-server/premium-tools.js +2 -2
  82. package/mcp-server/tier-auth.js +194 -383
  83. package/mcp-server/tools-v3.js +495 -533
  84. package/mcp-server/truth-firewall-tools.js +145 -15
  85. package/mcp-server/vibecheck-tools.js +2 -2
  86. package/package.json +2 -3
  87. package/mcp-server/index.old.js +0 -4137
  88. package/mcp-server/lib/api-client.js +0 -269
  89. package/mcp-server/package-lock.json +0 -165
@@ -1,475 +1,286 @@
1
1
  /**
2
- * MCP Server Tier Authentication & Authorization
2
+ * MCP Server Tier Authentication
3
3
  *
4
- * UNIFIED AUTHORITY SYSTEM for MCP tools.
4
+ * Simple 2-tier model:
5
+ * - FREE ($0): Inspect & Observe
6
+ * - PRO ($69/mo): Fix, Prove & Enforce
5
7
  *
6
- * Authority Rules:
7
- * - FREE: May see problems, never resolve certainty. Read-only context tools.
8
- * - STARTER: May fix, but not prove. Read tools + advisory actions.
9
- * - PRO: May enforce reality. Full MCP access with proof generation.
10
- * - ENTERPRISE: All features + compliance tools.
11
- *
12
- * Verdict authority is paid. Evidence beats explanations.
8
+ * PRO includes:
9
+ * - Authority System (verdicts, approvals)
10
+ * - Agent Conductor (multi-agent coordination)
11
+ * - Agent Firewall (enforce mode)
13
12
  */
14
13
 
15
14
  import fs from "fs/promises";
16
15
  import path from "path";
17
16
  import os from "os";
18
17
 
19
- // ═══════════════════════════════════════════════════════════════════════════════
20
- // TIER DEFINITIONS - UNIFIED AUTHORITY SYSTEM
21
- // Synchronized with packages/core/src/tier-config.ts
22
- //
23
- // Authority Rules:
24
- // - FREE: May see problems, never resolve certainty
25
- // - STARTER: May fix, but not prove (Advisory verdicts only)
26
- // - PRO: May enforce reality (Full verdict authority)
27
- // ═══════════════════════════════════════════════════════════════════════════════
18
+ // ============================================================================
19
+ // TIERS
20
+ // ============================================================================
28
21
  export const TIERS = {
29
- free: {
30
- name: 'FREE',
31
- price: 0,
32
- order: 0,
33
- // Verdict authority
34
- verdictAuthority: 'NONE',
35
- canIssueVerdicts: false,
36
- canEnforceCI: false,
37
- canGenerateProof: false,
38
- // Limits
39
- limits: {
40
- scans: -1, // Unlimited scans
41
- realityMaxPages: 5,
42
- realityMaxClicks: 20,
43
- mcpRateLimit: 10,
44
- },
45
- // MCP tools allowed on FREE (read-only context only)
46
- mcpTools: [
47
- 'vibecheck.get_truthpack',
48
- 'vibecheck.compile_context',
49
- 'vibecheck.search_evidence',
50
- ],
51
- },
52
- starter: {
53
- name: 'STARTER',
54
- price: 39,
55
- order: 1,
56
- // Verdict authority - ADVISORY (verdicts not enforced)
57
- verdictAuthority: 'ADVISORY',
58
- canIssueVerdicts: true, // SHIP, WARN only
59
- canEnforceCI: false, // Cannot block builds
60
- canGenerateProof: false, // Cannot generate proof
61
- // Limits
62
- limits: {
63
- scans: -1, // Unlimited scans
64
- realityMaxPages: -1, // Unlimited browser testing
65
- realityMaxClicks: -1,
66
- mcpRateLimit: 60,
67
- },
68
- // MCP tools allowed on STARTER (read + advisory actions)
69
- mcpTools: [
70
- 'vibecheck.ctx',
71
- 'vibecheck.scan',
72
- 'vibecheck.ship', // Advisory verdicts only
73
- 'vibecheck.get_truthpack',
74
- 'vibecheck.validate_claim',
75
- 'vibecheck.compile_context',
76
- 'vibecheck.search_evidence',
77
- 'vibecheck.find_counterexamples',
78
- 'vibecheck.check_invariants',
79
- 'vibecheck.fix', // Plan mode only
80
- ],
81
- },
82
- pro: {
83
- name: 'PRO',
84
- price: 99,
85
- order: 2,
86
- // Verdict authority - ENFORCED (verdicts block CI/PRs)
87
- verdictAuthority: 'ENFORCED',
88
- canIssueVerdicts: true, // SHIP, WARN, BLOCK
89
- canEnforceCI: true, // Can block builds
90
- canGenerateProof: true, // Can generate cryptographic proof
91
- // Limits
92
- limits: {
93
- scans: -1,
94
- realityMaxPages: -1,
95
- realityMaxClicks: -1,
96
- mcpRateLimit: -1, // Unlimited
97
- },
98
- // MCP tools - FULL ACCESS
99
- mcpTools: ['*'], // All tools unlocked
100
- },
101
- enterprise: {
102
- name: 'ENTERPRISE',
103
- price: 0, // Custom pricing
104
- order: 3,
105
- // Verdict authority - ENFORCED + Compliance
106
- verdictAuthority: 'ENFORCED',
107
- canIssueVerdicts: true,
108
- canEnforceCI: true,
109
- canGenerateProof: true,
110
- // Limits
111
- limits: {
112
- scans: -1,
113
- realityMaxPages: -1,
114
- realityMaxClicks: -1,
115
- mcpRateLimit: -1,
116
- },
117
- // MCP tools - FULL ACCESS + Compliance
118
- mcpTools: ['*'],
119
- }
22
+ free: { name: 'FREE', price: 0 },
23
+ pro: { name: 'PRO', price: 69 },
120
24
  };
121
25
 
122
- // ═══════════════════════════════════════════════════════════════════════════════
123
- // MCP TOOL TIER MAPPING (Unified Authority System)
124
- // Aligned with packages/core/src/features.ts
125
- // ═══════════════════════════════════════════════════════════════════════════════
126
- export const MCP_TOOL_TIERS = {
127
- // FREE - Read-only context tools ONLY
128
- // Free users may see problems, never resolve certainty
129
- 'vibecheck.get_truthpack': 'free',
130
- 'vibecheck.compile_context': 'free',
131
- 'vibecheck.search_evidence': 'free',
132
-
133
- // STARTER - Advisory verdict authority
134
- // Starter users may fix, but not prove
135
- 'vibecheck.ctx': 'starter',
136
- 'vibecheck.scan': 'starter',
137
- 'vibecheck.ship': 'starter', // SHIP/WARN (advisory, not enforced)
138
- 'vibecheck.fix': 'starter', // Plan mode only (no apply)
139
- 'vibecheck.validate_claim': 'starter',
140
- 'vibecheck.find_counterexamples': 'starter',
141
- 'vibecheck.check_invariants': 'starter',
142
- 'vibecheck.gate': 'starter', // Advisory gates
143
- 'vibecheck.badge': 'starter', // Unverified badges
144
-
145
- // PRO - Full verdict authority & enforcement
146
- // Pro users may enforce reality
147
- 'vibecheck.prove': 'pro', // Cryptographic proof generation
148
- 'vibecheck.fix.apply': 'pro', // Apply fixes
149
- 'vibecheck.fix.verify': 'pro', // Verify fixes with proof
150
- 'vibecheck.reality': 'pro', // Full browser testing
151
- 'vibecheck.reality.prove': 'pro', // Prove runtime behavior
152
- 'vibecheck.enforce': 'pro', // Enforce verdicts in CI
153
- 'vibecheck.ai_test': 'pro', // AI agent testing
154
- 'vibecheck.autopilot': 'pro', // Autonomous protection
155
- 'vibecheck.report': 'pro', // Advanced reporting
156
- 'vibecheck.allowlist': 'pro', // Manage allowlists
157
- 'vibecheck.status': 'pro', // Advanced status
158
- };
26
+ // ============================================================================
27
+ // MCP TOOLS - 15 Core + PRO Features
28
+ // ============================================================================
159
29
 
160
30
  /**
161
- * Load user configuration from ~/.vibecheck/credentials.json
31
+ * FREE TOOLS (7) - Inspect & Observe
162
32
  */
163
- async function loadUserConfig() {
164
- try {
165
- const configPath = path.join(os.homedir(), '.vibecheck', 'credentials.json');
166
- const configData = await fs.readFile(configPath, 'utf-8');
167
- return JSON.parse(configData);
168
- } catch (error) {
169
- return null;
170
- }
171
- }
33
+ export const FREE_TOOLS = [
34
+ // Core FREE tools
35
+ 'vibecheck.scan',
36
+ 'vibecheck.ctx',
37
+ 'vibecheck.verify',
38
+ 'vibecheck.report',
39
+ 'vibecheck.status',
40
+ 'vibecheck.doctor',
41
+ 'vibecheck.firewall', // Observe mode only
42
+ // Authority (read-only)
43
+ 'authority.list',
44
+ 'authority.classify',
45
+ // Conductor (status only)
46
+ 'vibecheck_conductor_status',
47
+ ];
172
48
 
173
49
  /**
174
- * Determine tier from API key
175
- * Matches CLI entitlements-v2.js logic
50
+ * PRO TOOLS (8 Core + Authority + Conductor + Firewall) - Fix, Prove & Enforce
176
51
  */
177
- async function getTierFromApiKey(apiKey) {
178
- if (!apiKey) return null; // No API key = no access
52
+ export const PRO_TOOLS = [
53
+ // Core PRO tools
54
+ 'vibecheck.ship',
55
+ 'vibecheck.fix',
56
+ 'vibecheck.prove',
57
+ 'vibecheck.gate',
58
+ 'vibecheck.badge',
59
+ 'vibecheck.reality',
60
+ 'vibecheck.ai_test',
61
+ 'vibecheck.share',
179
62
 
180
- // Check API key prefix patterns (matches CLI)
181
- if (apiKey.startsWith('gr_starter_')) return 'starter';
182
- if (apiKey.startsWith('gr_pro_')) return 'pro';
183
- if (apiKey.startsWith('gr_enterprise_') || apiKey.startsWith('gr_ent_')) return 'enterprise';
184
- if (apiKey.startsWith('gr_free_')) return 'free';
63
+ // Authority System (full)
64
+ 'authority.approve',
65
+ 'authority.enforce',
185
66
 
186
- // Try to validate with API
67
+ // Agent Conductor (full multi-agent coordination)
68
+ 'vibecheck_conductor_register',
69
+ 'vibecheck_conductor_acquire_lock',
70
+ 'vibecheck_conductor_release_lock',
71
+ 'vibecheck_conductor_propose',
72
+ 'vibecheck_conductor_terminate',
73
+
74
+ // Agent Firewall (enforce mode)
75
+ 'vibecheck_agent_firewall_intercept',
76
+ 'vibecheck.firewall.enforce',
77
+ ];
78
+
79
+ export const ALL_TOOLS = [...FREE_TOOLS, ...PRO_TOOLS];
80
+
81
+ // ============================================================================
82
+ // TIER CACHE
83
+ // ============================================================================
84
+ const tierCache = new Map();
85
+ const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
86
+
87
+ function hashKey(apiKey) {
88
+ const crypto = require('crypto');
89
+ return crypto.createHash('sha256').update(apiKey).digest('hex').slice(0, 16);
90
+ }
91
+
92
+ // ============================================================================
93
+ // TIER VALIDATION
94
+ // ============================================================================
95
+
96
+ export async function getTierFromApiKey(apiKey) {
97
+ if (!apiKey || typeof apiKey !== 'string' || apiKey.length < 10) {
98
+ return null;
99
+ }
100
+
101
+ const keyHash = hashKey(apiKey);
102
+ const now = Date.now();
103
+
104
+ // Check cache
105
+ const cached = tierCache.get(keyHash);
106
+ if (cached && cached.expiresAt > now) {
107
+ return cached.tier;
108
+ }
109
+
110
+ // Validate with API
187
111
  try {
188
112
  const response = await fetch('https://api.vibecheckai.dev/whoami', {
189
- headers: {
190
- 'Authorization': `Bearer ${apiKey}`,
191
- 'Content-Type': 'application/json',
192
- },
113
+ headers: { 'Authorization': `Bearer ${apiKey}` },
114
+ signal: AbortSignal.timeout(10000),
193
115
  });
194
116
 
195
117
  if (!response.ok) {
196
- return null; // Invalid API key
118
+ return null;
197
119
  }
198
120
 
199
121
  const data = await response.json();
122
+ const plan = data.plan?.toLowerCase() || 'free';
200
123
 
201
- // Map API response to tier
202
- switch (data.plan?.toLowerCase()) {
203
- case 'starter':
204
- return 'starter';
205
- case 'pro':
206
- return 'pro';
207
- case 'enterprise':
208
- return 'enterprise';
209
- default:
210
- return 'free';
211
- }
212
- } catch (error) {
213
- console.error('API validation failed:', error);
214
- return null; // On error, deny access
124
+ // Any paid plan = pro
125
+ const tier = (plan === 'free') ? 'free' : 'pro';
126
+
127
+ tierCache.set(keyHash, { tier, expiresAt: now + CACHE_TTL });
128
+ return tier;
129
+
130
+ } catch {
131
+ // Network error - check stale cache
132
+ if (cached) return cached.tier;
133
+ return null;
215
134
  }
216
- } // default for unknown keys
135
+ }
136
+
137
+ // ============================================================================
138
+ // ACCESS CONTROL
139
+ // ============================================================================
140
+
141
+ export function isPro(tier) {
142
+ return tier === 'pro';
143
+ }
144
+
145
+ export function canAccessTool(tier, toolName) {
146
+ // PRO gets everything
147
+ if (tier === 'pro') return true;
148
+
149
+ // FREE can access FREE tools
150
+ return FREE_TOOLS.includes(toolName);
151
+ }
217
152
 
218
153
  /**
219
- * Check if user has access to a specific feature
220
- * Matches CLI entitlements-v2.js logic
154
+ * Get firewall mode based on tier
155
+ * - FREE: observe (log only)
156
+ * - PRO: enforce (block violations)
221
157
  */
222
- export async function getFeatureAccessStatus(featureName, providedApiKey = null) {
223
- // Try to load user config
224
- const userConfig = await loadUserConfig();
225
- const apiKey = providedApiKey || userConfig?.apiKey;
226
-
227
- if (!apiKey) {
228
- return {
229
- hasAccess: false,
230
- tier: null,
231
- reason: 'No API key provided. Please set your API key with `vibecheck login`.',
232
- upgradeUrl: 'https://vibecheckai.dev'
233
- };
234
- }
235
-
236
- const currentTier = await getTierFromApiKey(apiKey);
237
-
238
- if (!currentTier) {
239
- return {
240
- hasAccess: false,
241
- tier: null,
242
- reason: 'Invalid API key. Please check your API key or get a new one at https://vibecheckai.dev',
243
- upgradeUrl: 'https://vibecheckai.dev'
244
- };
245
- }
246
- const currentTierConfig = TIERS[currentTier];
247
-
248
- // Find which tier has this feature
249
- let requiredTier = null;
250
- let requiredTierConfig = null;
251
-
252
- for (const [tierName, tierConfig] of Object.entries(TIERS)) {
253
- if (tierConfig.features.includes(featureName)) {
254
- requiredTier = tierName;
255
- requiredTierConfig = tierConfig;
256
- break;
257
- }
258
- }
259
-
260
- // If feature not found in any tier, deny access
261
- if (!requiredTier) {
262
- return {
263
- hasAccess: false,
264
- tier: currentTier,
265
- reason: `${featureName} is not available in any tier`,
266
- upgradeUrl: 'https://vibecheckai.dev'
267
- };
268
- }
269
-
270
- // Check if current tier meets minimum requirement (using order)
271
- const hasAccess = currentTierConfig.order >= requiredTierConfig.order;
272
-
273
- if (!hasAccess) {
274
- return {
275
- hasAccess: false,
276
- tier: currentTier,
277
- requiredTier,
278
- reason: `${featureName} requires ${requiredTierConfig.name} tier ($${requiredTierConfig.price}/mo) or higher. Current tier: ${currentTierConfig.name}`,
279
- upgradeUrl: 'https://vibecheckai.dev'
280
- };
281
- }
282
-
283
- return {
284
- hasAccess: true,
285
- tier: currentTier,
286
- reason: 'Access granted'
287
- };
158
+ export function getFirewallMode(tier) {
159
+ return tier === 'pro' ? 'enforce' : 'observe';
288
160
  }
289
161
 
290
162
  /**
291
- * Middleware for MCP tool handlers
163
+ * Check if user can use full conductor features
292
164
  */
293
- export function withTierCheck(featureName, handler) {
294
- return async (args) => {
295
- const access = await checkFeatureAccess(featureName, args?.apiKey);
296
-
297
- if (!access.hasAccess) {
298
- return {
299
- content: [{
300
- type: "text",
301
- text: `🚫 UPGRADE REQUIRED\n\n${access.reason}\n\nUpgrade at: ${access.upgradeUrl}`
302
- }],
303
- isError: true
304
- };
305
- }
306
-
307
- // Add tier info to args for the handler
308
- args._tier = access.tier;
309
- return handler(args);
310
- };
165
+ export function canUseCondcutor(tier) {
166
+ return tier === 'pro';
311
167
  }
312
168
 
313
169
  /**
314
- * Check if user has access to a specific MCP tool
315
- * MCP tools have specific tier requirements separate from CLI features
170
+ * Check if user can approve authorities
316
171
  */
317
- export async function getMcpToolAccess(toolName, providedApiKey = null) {
318
- const userConfig = await loadUserConfig();
319
- const apiKey = providedApiKey || userConfig?.apiKey;
320
-
172
+ export function canApproveAuthority(tier) {
173
+ return tier === 'pro';
174
+ }
175
+
176
+ export async function getMcpToolAccess(toolName, apiKey) {
321
177
  if (!apiKey) {
322
178
  return {
323
- hasAccess: false,
324
- tier: null,
325
- reason: 'No API key provided. Please set your API key with `vibecheck login`.',
326
- upgradeUrl: 'https://vibecheckai.dev'
179
+ hasAccess: FREE_TOOLS.includes(toolName),
180
+ tier: 'free',
181
+ reason: FREE_TOOLS.includes(toolName)
182
+ ? 'Access granted (free tool)'
183
+ : 'This tool requires Pro. Set API key with `vibecheck login`.',
327
184
  };
328
185
  }
329
186
 
330
- const currentTier = await getTierFromApiKey(apiKey);
187
+ const tier = await getTierFromApiKey(apiKey);
331
188
 
332
- if (!currentTier) {
189
+ if (!tier) {
333
190
  return {
334
191
  hasAccess: false,
335
192
  tier: null,
336
- reason: 'Invalid API key. Please check your API key or get a new one at https://vibecheckai.dev',
337
- upgradeUrl: 'https://vibecheckai.dev'
193
+ reason: 'Invalid API key.',
338
194
  };
339
195
  }
340
196
 
341
- const currentTierConfig = TIERS[currentTier];
342
-
343
- // Check if tool is allowed for current tier
344
- const allowedTools = [];
345
-
346
- // Accumulate tools from current tier and all lower tiers
347
- for (const [tierName, tierConfig] of Object.entries(TIERS)) {
348
- if (tierConfig.order <= currentTierConfig.order) {
349
- if (tierConfig.mcpTools) {
350
- if (tierConfig.mcpTools.includes('*')) {
351
- // Compliance tier - all tools allowed
352
- return {
353
- hasAccess: true,
354
- tier: currentTier,
355
- reason: 'Full MCP access'
356
- };
357
- }
358
- allowedTools.push(...tierConfig.mcpTools);
359
- }
360
- }
361
- }
362
-
363
- // Check if tool is in allowed list
364
- const hasAccess = allowedTools.includes(toolName);
365
-
366
- if (!hasAccess) {
367
- // Find which tier has this tool
368
- let requiredTier = null;
369
- for (const [tierName, tierConfig] of Object.entries(TIERS)) {
370
- if (tierConfig.mcpTools?.includes(toolName) || tierConfig.mcpTools?.includes('*')) {
371
- requiredTier = tierName;
372
- break;
373
- }
374
- }
375
-
376
- const requiredTierConfig = requiredTier ? TIERS[requiredTier] : null;
377
-
378
- return {
379
- hasAccess: false,
380
- tier: currentTier,
381
- requiredTier,
382
- reason: requiredTierConfig
383
- ? `${toolName} requires ${requiredTierConfig.name} tier ($${requiredTierConfig.price}/mo). Current: ${currentTierConfig.name}`
384
- : `${toolName} is not available`,
385
- upgradeUrl: 'https://vibecheckai.dev'
386
- };
387
- }
197
+ const hasAccess = canAccessTool(tier, toolName);
388
198
 
389
199
  return {
390
- hasAccess: true,
391
- tier: currentTier,
392
- reason: 'Access granted'
200
+ hasAccess,
201
+ tier,
202
+ firewallMode: getFirewallMode(tier),
203
+ reason: hasAccess
204
+ ? 'Access granted'
205
+ : `${toolName} requires Pro ($69/mo). Upgrade at https://vibecheckai.dev/pricing`,
393
206
  };
394
207
  }
395
208
 
396
- /**
397
- * Middleware for MCP tool handlers with tool-specific checking
398
- */
399
- export function withMcpToolCheck(toolName, handler) {
209
+ // ============================================================================
210
+ // MIDDLEWARE
211
+ // ============================================================================
212
+
213
+ export function withTierCheck(toolName, handler) {
400
214
  return async (args) => {
401
- const access = await checkMcpToolAccess(toolName, args?.apiKey);
215
+ const access = await getMcpToolAccess(toolName, args?.apiKey);
402
216
 
403
217
  if (!access.hasAccess) {
404
218
  return {
405
219
  content: [{
406
220
  type: "text",
407
- text: `🚫 UPGRADE REQUIRED\n\n${access.reason}\n\nUpgrade at: ${access.upgradeUrl}`
221
+ text: `This tool requires Pro.\n\n${toolName} is a Pro feature.\n\nUpgrade to Pro ($69/mo) to unlock:\n- Authority System (verdicts & approvals)\n- Agent Conductor (multi-agent coordination)\n- Agent Firewall (enforce mode)\n\nhttps://vibecheckai.dev/pricing`
408
222
  }],
409
223
  isError: true
410
224
  };
411
225
  }
412
226
 
413
- // Add tier info to args for the handler
414
227
  args._tier = access.tier;
228
+ args._firewallMode = access.firewallMode;
415
229
  return handler(args);
416
230
  };
417
231
  }
418
232
 
419
- /**
420
- * Get current user info
421
- */
233
+ // ============================================================================
234
+ // USER INFO
235
+ // ============================================================================
236
+
237
+ async function loadUserConfig() {
238
+ try {
239
+ const configPath = path.join(os.homedir(), '.vibecheck', 'credentials.json');
240
+ const data = await fs.readFile(configPath, 'utf-8');
241
+ return JSON.parse(data);
242
+ } catch {
243
+ return null;
244
+ }
245
+ }
246
+
422
247
  export async function getUserInfo() {
423
248
  const config = await loadUserConfig();
424
- if (!config) {
249
+
250
+ if (!config?.apiKey) {
425
251
  return {
426
252
  authenticated: false,
427
253
  tier: 'free',
428
- message: 'Not authenticated. Run: vibecheck auth --key YOUR_API_KEY'
254
+ tools: FREE_TOOLS,
255
+ firewallMode: 'observe',
429
256
  };
430
257
  }
431
258
 
432
- const tier = getTierFromApiKey(config.apiKey);
433
- const tierConfig = TIERS[tier];
259
+ const tier = await getTierFromApiKey(config.apiKey);
434
260
 
435
261
  return {
436
262
  authenticated: true,
437
- tier,
263
+ tier: tier || 'free',
438
264
  email: config.email,
439
- authenticatedAt: config.authenticatedAt,
440
- features: tierConfig.features,
441
- limits: tierConfig.limits,
442
- mcpTools: tierConfig.mcpTools,
265
+ tools: tier === 'pro' ? ALL_TOOLS : FREE_TOOLS,
266
+ firewallMode: getFirewallMode(tier || 'free'),
443
267
  };
444
268
  }
445
269
 
446
- /**
447
- * Get list of MCP tools available for current tier
448
- */
449
- export async function getAvailableMcpTools(providedApiKey = null) {
450
- const userConfig = await loadUserConfig();
451
- const apiKey = providedApiKey || userConfig?.apiKey;
452
-
453
- const currentTier = getTierFromApiKey(apiKey);
454
- const currentTierConfig = TIERS[currentTier];
455
-
456
- const allowedTools = new Set();
457
-
458
- // Accumulate tools from current tier and all lower tiers
459
- for (const [tierName, tierConfig] of Object.entries(TIERS)) {
460
- if (tierConfig.order <= currentTierConfig.order) {
461
- if (tierConfig.mcpTools) {
462
- if (tierConfig.mcpTools.includes('*')) {
463
- return { tier: currentTier, tools: ['*'], unlimited: true };
464
- }
465
- tierConfig.mcpTools.forEach(t => allowedTools.add(t));
466
- }
467
- }
468
- }
469
-
270
+ export async function getAvailableMcpTools(apiKey) {
271
+ const tier = apiKey ? await getTierFromApiKey(apiKey) : 'free';
470
272
  return {
471
- tier: currentTier,
472
- tools: Array.from(allowedTools),
473
- unlimited: false
273
+ tier: tier || 'free',
274
+ tools: (tier === 'pro') ? ALL_TOOLS : FREE_TOOLS,
275
+ firewallMode: getFirewallMode(tier || 'free'),
474
276
  };
475
277
  }
278
+
279
+ // Legacy exports for backward compatibility
280
+ export async function getFeatureAccessStatus(featureName, apiKey) {
281
+ return getMcpToolAccess(featureName, apiKey);
282
+ }
283
+
284
+ export function withMcpToolCheck(toolName, handler) {
285
+ return withTierCheck(toolName, handler);
286
+ }