vibezcheck 0.4.0 → 0.4.1

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/dist/cli/index.js CHANGED
@@ -24,9 +24,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/cli/index.ts
27
- var import_fs = __toESM(require("fs"));
28
- var import_path = __toESM(require("path"));
29
- var import_readline = __toESM(require("readline"));
27
+ var import_fs2 = __toESM(require("fs"));
28
+ var import_path2 = __toESM(require("path"));
29
+ var import_readline2 = __toESM(require("readline"));
30
30
 
31
31
  // src/pricing/table.ts
32
32
  var MODEL_PRICING_TABLE = {
@@ -88,17 +88,298 @@ var MODEL_PRICING_TABLE = {
88
88
  "command-r": { inputPer1M: 0.15, outputPer1M: 0.6 }
89
89
  };
90
90
 
91
+ // src/cli/audit.ts
92
+ var import_fs = __toESM(require("fs"));
93
+ var import_path = __toESM(require("path"));
94
+ var import_readline = __toESM(require("readline"));
95
+ var IGNORED_DIRS = /* @__PURE__ */ new Set([
96
+ "node_modules",
97
+ ".next",
98
+ ".git",
99
+ "dist",
100
+ "build",
101
+ ".turbo",
102
+ "coverage",
103
+ ".cache",
104
+ ".vercel",
105
+ "tests",
106
+ "test",
107
+ "__tests__",
108
+ "examples",
109
+ "fixtures"
110
+ ]);
111
+ var EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs"]);
112
+ function findRouteFiles(rootDir) {
113
+ const targetSubdirs = [
114
+ import_path.default.join("app", "api"),
115
+ import_path.default.join("src", "app", "api"),
116
+ import_path.default.join("pages", "api"),
117
+ import_path.default.join("src", "pages", "api"),
118
+ "routes",
119
+ import_path.default.join("src", "routes"),
120
+ import_path.default.join("server", "api"),
121
+ import_path.default.join("server", "routes")
122
+ ];
123
+ const foundFiles = [];
124
+ let searchedDesignated = false;
125
+ for (const rel of targetSubdirs) {
126
+ const full = import_path.default.join(rootDir, rel);
127
+ if (import_fs.default.existsSync(full)) {
128
+ searchedDesignated = true;
129
+ scanDirRecursive(full, foundFiles);
130
+ }
131
+ }
132
+ if (foundFiles.length === 0) {
133
+ const appDir = import_fs.default.existsSync(import_path.default.join(rootDir, "app")) ? import_path.default.join(rootDir, "app") : import_fs.default.existsSync(import_path.default.join(rootDir, "src", "app")) ? import_path.default.join(rootDir, "src", "app") : rootDir;
134
+ scanDirRecursive(appDir, foundFiles);
135
+ }
136
+ return Array.from(new Set(foundFiles));
137
+ }
138
+ function scanDirRecursive(currentDir, results) {
139
+ try {
140
+ const entries = import_fs.default.readdirSync(currentDir, { withFileTypes: true });
141
+ for (const entry of entries) {
142
+ if (entry.isDirectory()) {
143
+ if (!IGNORED_DIRS.has(entry.name)) {
144
+ scanDirRecursive(import_path.default.join(currentDir, entry.name), results);
145
+ }
146
+ } else if (entry.isFile()) {
147
+ const ext = import_path.default.extname(entry.name);
148
+ if (EXTENSIONS.has(ext)) {
149
+ if (!entry.name.includes(".test.") && !entry.name.includes(".spec.")) {
150
+ results.push(import_path.default.join(currentDir, entry.name));
151
+ }
152
+ }
153
+ }
154
+ }
155
+ } catch {
156
+ }
157
+ }
158
+ function auditFile(filePath, rootDir) {
159
+ const relativePath = import_path.default.relative(rootDir, filePath).replace(/\\/g, "/");
160
+ let content = "";
161
+ try {
162
+ content = import_fs.default.readFileSync(filePath, "utf-8");
163
+ } catch {
164
+ return [];
165
+ }
166
+ const hasAiKeywords = content.includes("streamText") || content.includes("generateText") || content.includes("streamObject") || content.includes("generateObject") || content.includes("OpenAI") || content.includes("Anthropic") || content.includes("vibezcheck");
167
+ if (!hasAiKeywords) {
168
+ return [];
169
+ }
170
+ const lines = content.split(/\r?\n/);
171
+ const findings = [];
172
+ const aiSdkRegex = /\b(streamText|generateText|streamObject|generateObject)\s*\(\s*\{/g;
173
+ let match;
174
+ while ((match = aiSdkRegex.exec(content)) !== null) {
175
+ const matchIndex = match.index;
176
+ const lineNumber = content.substring(0, matchIndex).split(/\r?\n/).length;
177
+ const snippetLines = lines.slice(lineNumber - 1, lineNumber + 25);
178
+ const snippet = snippetLines.join("\n");
179
+ const modelLineMatch = snippet.match(/model\s*:\s*([^,\n}]+)/);
180
+ if (modelLineMatch) {
181
+ const modelExpr = modelLineMatch[1].trim();
182
+ const modelLineOffset = snippetLines.findIndex((l) => l.includes("model:"));
183
+ const actualLine = lineNumber + (modelLineOffset >= 0 ? modelLineOffset : 0);
184
+ const rawSnippet = lines[actualLine - 1] || modelLineMatch[0];
185
+ const isProtected = modelExpr.includes("vibezcheck(") || modelExpr.includes("session.model(") || modelExpr.includes("vz.model(");
186
+ if (isProtected) {
187
+ findings.push({
188
+ file: filePath,
189
+ relativePath,
190
+ status: "protected",
191
+ line: actualLine,
192
+ rawSnippet,
193
+ modelOrProvider: modelExpr,
194
+ details: "Metered with 0ms added latency and $0.50 safety fuse ceiling"
195
+ });
196
+ } else {
197
+ const suggestedFix = `model: vibezcheck(${modelExpr}),`;
198
+ findings.push({
199
+ file: filePath,
200
+ relativePath,
201
+ status: "unmetered",
202
+ line: actualLine,
203
+ rawSnippet,
204
+ suggestedFix,
205
+ modelOrProvider: modelExpr,
206
+ details: "Direct provider call without cost limit or token metering"
207
+ });
208
+ }
209
+ }
210
+ }
211
+ for (let i = 0; i < lines.length; i++) {
212
+ const line = lines[i];
213
+ if ((line.includes("new OpenAI(") || line.includes("new Anthropic(")) && !content.includes("vibezcheck") && !content.includes("wrapStream") && !content.includes("trackUsage")) {
214
+ findings.push({
215
+ file: filePath,
216
+ relativePath,
217
+ status: "unprotected_direct",
218
+ line: i + 1,
219
+ rawSnippet: line.trim(),
220
+ details: "Direct client instance without circuit breaker fuse"
221
+ });
222
+ break;
223
+ }
224
+ }
225
+ return findings;
226
+ }
227
+ async function runAudit(options = {}) {
228
+ const startTime = Date.now();
229
+ const rootDir = options.dir ? import_path.default.resolve(options.dir) : process.cwd();
230
+ const files = findRouteFiles(rootDir);
231
+ const allFindings = [];
232
+ for (const file of files) {
233
+ const findings = auditFile(file, rootDir);
234
+ allFindings.push(...findings);
235
+ }
236
+ const protectedCount = allFindings.filter((f) => f.status === "protected").length;
237
+ const unmeteredCount = allFindings.filter(
238
+ (f) => f.status === "unmetered" || f.status === "unprotected_direct"
239
+ ).length;
240
+ const summary = {
241
+ scannedFiles: files.length,
242
+ aiRoutesCount: allFindings.length,
243
+ protectedCount,
244
+ unmeteredCount,
245
+ scanTimeMs: Date.now() - startTime,
246
+ findings: allFindings
247
+ };
248
+ return summary;
249
+ }
250
+ function applyFixToFile(filePath) {
251
+ try {
252
+ const content = import_fs.default.readFileSync(filePath, "utf-8");
253
+ import_fs.default.writeFileSync(`${filePath}.bak`, content, "utf-8");
254
+ let updated = content;
255
+ if (!updated.includes("from 'vibezcheck'") && !updated.includes('from "vibezcheck"')) {
256
+ const importRegex = /^import\s+.*?;\s*$/gm;
257
+ let lastImportMatch = null;
258
+ let match;
259
+ while ((match = importRegex.exec(updated)) !== null) {
260
+ lastImportMatch = match;
261
+ }
262
+ const importStatement = "import { vibezcheck } from 'vibezcheck';\n";
263
+ if (lastImportMatch) {
264
+ const insertPos = lastImportMatch.index + lastImportMatch[0].length;
265
+ updated = updated.slice(0, insertPos) + "\n" + importStatement + updated.slice(insertPos);
266
+ } else {
267
+ updated = importStatement + updated;
268
+ }
269
+ }
270
+ updated = updated.replace(
271
+ /model\s*:\s*(?!vibezcheck\()([a-zA-Z0-9_$]+(?:\([^)]*\)|'[^']*'|"[^"]*"))/g,
272
+ "model: vibezcheck($1)"
273
+ );
274
+ import_fs.default.writeFileSync(filePath, updated, "utf-8");
275
+ return true;
276
+ } catch {
277
+ return false;
278
+ }
279
+ }
280
+ async function displayAuditReport(summary, options = {}) {
281
+ if (options.json) {
282
+ console.log(JSON.stringify(summary, null, 2));
283
+ if (options.ci && summary.unmeteredCount > 0) {
284
+ process.exit(1);
285
+ }
286
+ return;
287
+ }
288
+ console.log(`
289
+ \x1B[38;2;212;255;50m\u2726\x1B[0m \x1B[1mvibezcheck audit\x1B[0m \x1B[90m(${summary.scanTimeMs}ms)\x1B[0m
290
+ `);
291
+ if (summary.aiRoutesCount === 0) {
292
+ console.log(` \x1B[90mNo AI routes detected across ${summary.scannedFiles} files.\x1B[0m`);
293
+ console.log(` Ready to build your first metered route? Run: \x1B[36mnpx vibezcheck init\x1B[0m
294
+ `);
295
+ return;
296
+ }
297
+ if (summary.unmeteredCount === 0) {
298
+ const routeWord = summary.protectedCount === 1 ? "route" : "routes";
299
+ console.log(` \x1B[32m\u2713 All ${summary.protectedCount} AI ${routeWord} are metered with $0.50 safety fuses.\x1B[0m`);
300
+ console.log(` \x1B[90mYour wallet is protected. You're good to ship.\x1B[0m
301
+ `);
302
+ return;
303
+ }
304
+ const unmeteredFindings = summary.findings.filter((f) => f.status !== "protected");
305
+ const countWord = unmeteredFindings.length === 1 ? "route" : "routes";
306
+ console.log(
307
+ ` We noticed \x1B[33m${unmeteredFindings.length} ${countWord}\x1B[0m calling AI providers directly without a safety fuse:
308
+ `
309
+ );
310
+ for (const finding of unmeteredFindings) {
311
+ console.log(` \x1B[1m\u2192 ${finding.relativePath}:${finding.line}\x1B[0m`);
312
+ console.log(` \x1B[90mCurrent:\x1B[0m \x1B[31m${finding.rawSnippet.trim()}\x1B[0m`);
313
+ if (finding.suggestedFix) {
314
+ console.log(` \x1B[90m1-Line Fix:\x1B[0m \x1B[32m${finding.suggestedFix}\x1B[0m`);
315
+ }
316
+ console.log("");
317
+ }
318
+ console.log(` \x1B[1mWhy this matters:\x1B[0m`);
319
+ console.log(` \x1B[90mUnmetered routes bill directly to your credit card without runaway limits.`);
320
+ console.log(` Wrapping them adds 0ms token metering, $0.50 runaway fuses, and prompt cache discounts.\x1B[0m
321
+ `);
322
+ if (options.fix) {
323
+ let fixedCount = 0;
324
+ const uniqueFiles = Array.from(new Set(unmeteredFindings.map((f) => f.file)));
325
+ for (const file of uniqueFiles) {
326
+ if (applyFixToFile(file)) {
327
+ fixedCount++;
328
+ const rel = import_path.default.relative(process.cwd(), file).replace(/\\/g, "/");
329
+ console.log(` \x1B[32m\u2713 Safely wrapped ${rel}\x1B[0m \x1B[90m(backup saved as .bak)\x1B[0m`);
330
+ }
331
+ }
332
+ console.log(`
333
+ \x1B[32m\x1B[1m\u{1F389} All done!\x1B[0m \x1B[90mRun tests or build to verify.\x1B[0m
334
+ `);
335
+ return;
336
+ }
337
+ if (!options.ci && process.stdin.isTTY) {
338
+ const rl = import_readline.default.createInterface({
339
+ input: process.stdin,
340
+ output: process.stdout
341
+ });
342
+ const answer = await new Promise((resolve) => {
343
+ rl.question(
344
+ ` \x1B[38;2;212;255;50m\u26A1 Would you like VibezCheck to safely wrap these routes for you? (y/N): \x1B[0m`,
345
+ (ans) => {
346
+ rl.close();
347
+ resolve(ans.trim().toLowerCase());
348
+ }
349
+ );
350
+ });
351
+ if (answer === "y" || answer === "yes") {
352
+ const uniqueFiles = Array.from(new Set(unmeteredFindings.map((f) => f.file)));
353
+ for (const file of uniqueFiles) {
354
+ if (applyFixToFile(file)) {
355
+ const rel = import_path.default.relative(process.cwd(), file).replace(/\\/g, "/");
356
+ console.log(` \x1B[32m\u2713 Safely wrapped ${rel}\x1B[0m \x1B[90m(backup saved as .bak)\x1B[0m`);
357
+ }
358
+ }
359
+ console.log(`
360
+ \x1B[32m\x1B[1m\u{1F389} All done!\x1B[0m \x1B[90mRun tests or build to verify.\x1B[0m
361
+ `);
362
+ return;
363
+ }
364
+ }
365
+ if (options.ci) {
366
+ console.log(` \x1B[31m\u2716 CI check failed: ${summary.unmeteredCount} unmetered route(s) found.\x1B[0m
367
+ `);
368
+ process.exit(1);
369
+ }
370
+ }
371
+
91
372
  // src/cli/index.ts
92
373
  var args = process.argv.slice(2);
93
374
  var command = args[0] || "init";
94
375
  function printBanner() {
95
376
  console.log(`
96
- \x1B[36m\u26A1 \x1B[1mvibezcheck CLI\x1B[0m \x1B[90mv0.3.0\x1B[0m
377
+ \x1B[38;2;212;255;50m\u2726\x1B[0m \x1B[1mvibezcheck CLI\x1B[0m \x1B[90mv0.4.1\x1B[0m
97
378
  \x1B[90mThe 1-line Stripe Billing & Token Metering Engine for LLMs\x1B[0m
98
379
  `);
99
380
  }
100
381
  async function prompt(question, defaultVal = "") {
101
- const rl = import_readline.default.createInterface({
382
+ const rl = import_readline2.default.createInterface({
102
383
  input: process.stdin,
103
384
  output: process.stdout
104
385
  });
@@ -114,9 +395,9 @@ async function handleInit() {
114
395
  printBanner();
115
396
  console.log("\x1B[35m\u{1F680} Welcome to the VibezCheck Setup Wizard!\x1B[0m\n");
116
397
  const cwd = process.cwd();
117
- const isNextAppRouter = import_fs.default.existsSync(import_path.default.join(cwd, "app"));
118
- const isNextPagesRouter = import_fs.default.existsSync(import_path.default.join(cwd, "pages"));
119
- const isSrcDir = import_fs.default.existsSync(import_path.default.join(cwd, "src", "app"));
398
+ const isNextAppRouter = import_fs2.default.existsSync(import_path2.default.join(cwd, "app"));
399
+ const isNextPagesRouter = import_fs2.default.existsSync(import_path2.default.join(cwd, "pages"));
400
+ const isSrcDir = import_fs2.default.existsSync(import_path2.default.join(cwd, "src", "app"));
120
401
  console.log(`\x1B[90m\u{1F4C1} Project directory: ${cwd}\x1B[0m`);
121
402
  if (isNextAppRouter || isSrcDir) {
122
403
  console.log("\x1B[32m\u2713 Detected Next.js App Router project!\x1B[0m\n");
@@ -125,17 +406,17 @@ async function handleInit() {
125
406
  const gatewayKey = await prompt("Enter your AI Gateway API Key (or OpenAI key)", defaultGateway);
126
407
  const gatewayUrl = await prompt("Enter your AI Gateway Base URL", "https://ai-gateway.vercel.sh/v1");
127
408
  const stripeKey = await prompt("Enter your Stripe Secret Key (optional for test mode)", "");
128
- const envPath = import_path.default.join(cwd, ".env.local");
409
+ const envPath = import_path2.default.join(cwd, ".env.local");
129
410
  const envContent = `# VibezCheck AI Gateway & Stripe Configuration
130
411
  AI_GATEWAY_API_KEY=${gatewayKey}
131
412
  AI_GATEWAY_BASE_URL=${gatewayUrl}
132
413
  STRIPE_SECRET_KEY=${stripeKey}
133
414
  `;
134
- import_fs.default.writeFileSync(envPath, envContent, { flag: "w" });
415
+ import_fs2.default.writeFileSync(envPath, envContent, { flag: "w" });
135
416
  console.log(`\x1B[32m\u2713 Created/updated .env.local\x1B[0m`);
136
- const apiDir = isSrcDir ? import_path.default.join(cwd, "src", "app", "api", "chat") : import_path.default.join(cwd, "app", "api", "chat");
137
- import_fs.default.mkdirSync(apiDir, { recursive: true });
138
- const routePath = import_path.default.join(apiDir, "route.ts");
417
+ const apiDir = isSrcDir ? import_path2.default.join(cwd, "src", "app", "api", "chat") : import_path2.default.join(cwd, "app", "api", "chat");
418
+ import_fs2.default.mkdirSync(apiDir, { recursive: true });
419
+ const routePath = import_path2.default.join(apiDir, "route.ts");
139
420
  const routeContent = `import { streamText } from 'ai';
140
421
  import { vibezcheck } from 'vibezcheck';
141
422
 
@@ -159,8 +440,8 @@ export async function POST(req: Request) {
159
440
  return result.toTextStreamResponse();
160
441
  }
161
442
  `;
162
- import_fs.default.writeFileSync(routePath, routeContent, { flag: "w" });
163
- console.log(`\x1B[32m\u2713 Generated declarative API route: ${import_path.default.relative(cwd, routePath)}\x1B[0m`);
443
+ import_fs2.default.writeFileSync(routePath, routeContent, { flag: "w" });
444
+ console.log(`\x1B[32m\u2713 Generated declarative API route: ${import_path2.default.relative(cwd, routePath)}\x1B[0m`);
164
445
  console.log(`
165
446
  \x1B[32m\x1B[1m\u{1F389} Setup Complete!\x1B[0m
166
447
 
@@ -193,8 +474,8 @@ function handleDoctor() {
193
474
  printBanner();
194
475
  console.log("\x1B[1m\u{1FA7A} Running VibezCheck System Health Check...\x1B[0m\n");
195
476
  const cwd = process.cwd();
196
- const envPath = import_path.default.join(cwd, ".env.local");
197
- const hasEnv = import_fs.default.existsSync(envPath);
477
+ const envPath = import_path2.default.join(cwd, ".env.local");
478
+ const hasEnv = import_fs2.default.existsSync(envPath);
198
479
  console.log(`Node.js Version: \x1B[32m${process.version}\x1B[0m`);
199
480
  console.log(`Working Directory: \x1B[90m${cwd}\x1B[0m`);
200
481
  console.log(`Environment File: ${hasEnv ? "\x1B[32m\u2713 Found (.env.local)\x1B[0m" : "\x1B[33m\u26A0 Missing (.env.local)\x1B[0m"}`);
@@ -206,7 +487,24 @@ function handleDoctor() {
206
487
  \x1B[32m\u2713 VibezCheck engine is healthy and ready to meter!\x1B[0m
207
488
  `);
208
489
  }
490
+ async function handleAudit() {
491
+ const fix = args.includes("--fix") || args.includes("-f");
492
+ const ci = args.includes("--ci") || args.includes("-s") || args.includes("--strict");
493
+ const json = args.includes("--json") || args.includes("-j");
494
+ let dir = process.cwd();
495
+ const dirIndex = args.indexOf("--dir") !== -1 ? args.indexOf("--dir") : args.indexOf("-d");
496
+ if (dirIndex !== -1 && args[dirIndex + 1]) {
497
+ dir = args[dirIndex + 1];
498
+ }
499
+ const summary = await runAudit({ dir, fix, ci, json });
500
+ await displayAuditReport(summary, { dir, fix, ci, json });
501
+ }
209
502
  switch (command) {
503
+ case "audit":
504
+ case "check":
505
+ case "scan":
506
+ handleAudit();
507
+ break;
210
508
  case "init":
211
509
  handleInit();
212
510
  break;
@@ -223,6 +521,7 @@ switch (command) {
223
521
  Unknown command: \x1B[31m${command}\x1B[0m
224
522
 
225
523
  Available commands:
524
+ \x1B[36mvibezcheck audit\x1B[0m Scan project for unmetered AI routes and runaway loop risks
226
525
  \x1B[36mvibezcheck init\x1B[0m Interactive project setup wizard
227
526
  \x1B[36mvibezcheck prices\x1B[0m Display supported model pricing table
228
527
  \x1B[36mvibezcheck doctor\x1B[0m Diagnose environment and API configurations
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/index.ts","../../src/pricing/table.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport fs from 'fs';\nimport path from 'path';\nimport readline from 'readline';\nimport { MODEL_PRICING_TABLE } from '../pricing/table';\n\nconst args = process.argv.slice(2);\nconst command = args[0] || 'init';\n\nfunction printBanner() {\n console.log(`\n\\x1b[36m⚡ \\x1b[1mvibezcheck CLI\\x1b[0m \\x1b[90mv0.3.0\\x1b[0m\n\\x1b[90mThe 1-line Stripe Billing & Token Metering Engine for LLMs\\x1b[0m\n`);\n}\n\nasync function prompt(question: string, defaultVal: string = ''): Promise<string> {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n return new Promise((resolve) => {\n const promptText = defaultVal\n ? `\\x1b[32m?\\x1b[0m \\x1b[1m${question}\\x1b[0m \\x1b[90m(${defaultVal})\\x1b[0m: `\n : `\\x1b[32m?\\x1b[0m \\x1b[1m${question}\\x1b[0m: `;\n\n rl.question(promptText, (answer) => {\n rl.close();\n resolve(answer.trim() || defaultVal);\n });\n });\n}\n\n/**\n * Command: npx vibezcheck init\n */\nasync function handleInit() {\n printBanner();\n console.log('\\x1b[35m🚀 Welcome to the VibezCheck Setup Wizard!\\x1b[0m\\n');\n\n const cwd = process.cwd();\n\n // Detect project structure\n const isNextAppRouter = fs.existsSync(path.join(cwd, 'app'));\n const isNextPagesRouter = fs.existsSync(path.join(cwd, 'pages'));\n const isSrcDir = fs.existsSync(path.join(cwd, 'src', 'app'));\n\n console.log(`\\x1b[90m📁 Project directory: ${cwd}\\x1b[0m`);\n if (isNextAppRouter || isSrcDir) {\n console.log('\\x1b[32m✓ Detected Next.js App Router project!\\x1b[0m\\n');\n }\n\n // 1. Prompt for API Keys\n const defaultGateway = 'vck_demo_key';\n const gatewayKey = await prompt('Enter your AI Gateway API Key (or OpenAI key)', defaultGateway);\n const gatewayUrl = await prompt('Enter your AI Gateway Base URL', 'https://ai-gateway.vercel.sh/v1');\n const stripeKey = await prompt('Enter your Stripe Secret Key (optional for test mode)', '');\n\n // 2. Create / Update .env.local\n const envPath = path.join(cwd, '.env.local');\n const envContent = `# VibezCheck AI Gateway & Stripe Configuration\nAI_GATEWAY_API_KEY=${gatewayKey}\nAI_GATEWAY_BASE_URL=${gatewayUrl}\nSTRIPE_SECRET_KEY=${stripeKey}\n`;\n\n fs.writeFileSync(envPath, envContent, { flag: 'w' });\n console.log(`\\x1b[32m✓ Created/updated .env.local\\x1b[0m`);\n\n // 3. Create Sample API Route (app/api/chat/route.ts)\n const apiDir = isSrcDir\n ? path.join(cwd, 'src', 'app', 'api', 'chat')\n : path.join(cwd, 'app', 'api', 'chat');\n\n fs.mkdirSync(apiDir, { recursive: true });\n const routePath = path.join(apiDir, 'route.ts');\n\n const routeContent = `import { streamText } from 'ai';\nimport { vibezcheck } from 'vibezcheck';\n\nexport const runtime = 'nodejs';\nexport const dynamic = 'force-dynamic';\n\nexport async function POST(req: Request) {\n const { messages, customer = 'demo@example.com' } = await req.json();\n\n // ⚡ 1-Line Declarative Model Metering\n const result = streamText({\n model: vibezcheck('openai/gpt-4o-mini', {\n customer,\n onUsage: (event) => {\n console.log(\\`⚡ [vibezcheck] Tokens: \\${event.usage.totalTokens} | Cost: $\\${event.cost.totalUSD.toFixed(6)}\\`);\n },\n }),\n messages,\n });\n\n return result.toTextStreamResponse();\n}\n`;\n\n fs.writeFileSync(routePath, routeContent, { flag: 'w' });\n console.log(`\\x1b[32m✓ Generated declarative API route: ${path.relative(cwd, routePath)}\\x1b[0m`);\n\n console.log(`\n\\x1b[32m\\x1b[1m🎉 Setup Complete!\\x1b[0m\n\n\\x1b[1mNext Steps:\\x1b[0m\n 1. Add \\x1b[36m<VibezSessionWidget />\\x1b[0m to your layout:\n \\x1b[90mimport { VibezSessionProvider, VibezSessionWidget } from 'vibezcheck/react';\\x1b[0m\n\n 2. Use \\x1b[36museVibezChat()\\x1b[0m in your client component:\n \\x1b[90mconst { messages, input, handleSubmit } = useVibezChat();\\x1b[0m\n\n 3. Run your dev server:\n \\x1b[33mnpm run dev\\x1b[0m or \\x1b[33mpnpm dev\\x1b[0m\n`);\n}\n\n/**\n * Command: npx vibezcheck prices\n */\nfunction handlePrices() {\n printBanner();\n console.log('\\x1b[1m📊 Official Model Pricing Registry (USD per 1M Tokens):\\x1b[0m\\n');\n\n console.log(\n 'Model'.padEnd(32) +\n 'Input / 1M'.padEnd(16) +\n 'Output / 1M'.padEnd(16) +\n 'Cached / 1M'\n );\n console.log('-'.repeat(78));\n\n Object.entries(MODEL_PRICING_TABLE).forEach(([model, rates]) => {\n const input = `$${rates.inputPer1M.toFixed(3)}`.padEnd(16);\n const output = `$${rates.outputPer1M.toFixed(3)}`.padEnd(16);\n const cached = rates.cachedInputPer1M\n ? `$${rates.cachedInputPer1M.toFixed(3)}`\n : '—';\n\n console.log(model.padEnd(32) + input + output + cached);\n });\n}\n\n/**\n * Command: npx vibezcheck doctor\n */\nfunction handleDoctor() {\n printBanner();\n console.log('\\x1b[1m🩺 Running VibezCheck System Health Check...\\x1b[0m\\n');\n\n const cwd = process.cwd();\n const envPath = path.join(cwd, '.env.local');\n const hasEnv = fs.existsSync(envPath);\n\n console.log(`Node.js Version: \\x1b[32m${process.version}\\x1b[0m`);\n console.log(`Working Directory: \\x1b[90m${cwd}\\x1b[0m`);\n console.log(`Environment File: ${hasEnv ? '\\x1b[32m✓ Found (.env.local)\\x1b[0m' : '\\x1b[33m⚠ Missing (.env.local)\\x1b[0m'}`);\n\n const hasStripe = Boolean(process.env.STRIPE_SECRET_KEY);\n console.log(`Stripe Key: ${hasStripe ? '\\x1b[32m✓ Active\\x1b[0m' : '\\x1b[90m○ Free Local Mode (No Stripe key)\\x1b[0m'}`);\n\n const hasGateway = Boolean(process.env.AI_GATEWAY_API_KEY || process.env.OPENAI_API_KEY);\n console.log(`AI Provider Key: ${hasGateway ? '\\x1b[32m✓ Configured\\x1b[0m' : '\\x1b[33m⚠ Missing (Set AI_GATEWAY_API_KEY)\\x1b[0m'}`);\n\n console.log(`\\n\\x1b[32m✓ VibezCheck engine is healthy and ready to meter!\\x1b[0m\\n`);\n}\n\n// Router\nswitch (command) {\n case 'init':\n handleInit();\n break;\n case 'prices':\n case 'pricing':\n handlePrices();\n break;\n case 'doctor':\n case 'health':\n handleDoctor();\n break;\n default:\n console.log(`\nUnknown command: \\x1b[31m${command}\\x1b[0m\n\nAvailable commands:\n \\x1b[36mvibezcheck init\\x1b[0m Interactive project setup wizard\n \\x1b[36mvibezcheck prices\\x1b[0m Display supported model pricing table\n \\x1b[36mvibezcheck doctor\\x1b[0m Diagnose environment and API configurations\n`);\n break;\n}\n","import type { ModelPricingRates } from '../types';\n\n/**\n * Built-in Registry of Model Pricing (USD per 1 Million Tokens)\n * Sourced from official 2026 provider pricing tables.\n */\nexport const MODEL_PRICING_TABLE: Record<string, ModelPricingRates> = {\n // --- OpenAI ---\n 'gpt-5.6-sol': { inputPer1M: 4.0, outputPer1M: 20.0, cachedInputPer1M: 0.4 },\n 'gpt-5.6-terra': { inputPer1M: 2.0, outputPer1M: 12.0, cachedInputPer1M: 0.2 },\n 'gpt-5.6-luna': { inputPer1M: 0.2, outputPer1M: 1.2, cachedInputPer1M: 0.02 },\n 'gpt-5': { inputPer1M: 4.0, outputPer1M: 20.0, cachedInputPer1M: 0.4 },\n 'gpt-5-mini': { inputPer1M: 0.2, outputPer1M: 1.2, cachedInputPer1M: 0.02 },\n 'o1': { inputPer1M: 15.0, outputPer1M: 60.0, cachedInputPer1M: 7.5 },\n 'o1-mini': { inputPer1M: 1.1, outputPer1M: 4.4, cachedInputPer1M: 0.55 },\n 'o3': { inputPer1M: 15.0, outputPer1M: 60.0, cachedInputPer1M: 7.5 },\n 'o3-mini': { inputPer1M: 1.1, outputPer1M: 4.4, cachedInputPer1M: 0.55 },\n 'gpt-4o': { inputPer1M: 2.5, outputPer1M: 10.0, cachedInputPer1M: 1.25 },\n 'gpt-4o-mini': { inputPer1M: 0.15, outputPer1M: 0.6, cachedInputPer1M: 0.075 },\n 'gpt-4.1': { inputPer1M: 2.0, outputPer1M: 8.0, cachedInputPer1M: 1.0 },\n 'gpt-4.1-nano': { inputPer1M: 0.1, outputPer1M: 0.4, cachedInputPer1M: 0.05 },\n 'text-embedding-3-small': { inputPer1M: 0.02, outputPer1M: 0.0 },\n 'text-embedding-3-large': { inputPer1M: 0.13, outputPer1M: 0.0 },\n\n // --- Anthropic ---\n 'claude-3-7-sonnet': { inputPer1M: 0.59, outputPer1M: 2.93, cachedInputPer1M: 0.3 },\n 'claude-sonnet-5': { inputPer1M: 2.0, outputPer1M: 10.0, cachedInputPer1M: 0.3 },\n 'claude-3-5-sonnet': { inputPer1M: 3.0, outputPer1M: 15.0, cachedInputPer1M: 0.3 },\n 'claude-3-5-haiku': { inputPer1M: 0.8, outputPer1M: 4.0, cachedInputPer1M: 0.08 },\n 'haiku-4.5': { inputPer1M: 1.0, outputPer1M: 5.0, cachedInputPer1M: 0.1 },\n 'claude-opus-5': { inputPer1M: 5.0, outputPer1M: 25.0, cachedInputPer1M: 1.5 },\n 'claude-3-opus': { inputPer1M: 15.0, outputPer1M: 75.0, cachedInputPer1M: 1.5 },\n\n // --- Google Gemini ---\n 'gemini-3.7-flash': { inputPer1M: 0.75, outputPer1M: 3.75, cachedInputPer1M: 0.18 },\n 'gemini-3.1-pro': { inputPer1M: 2.0, outputPer1M: 12.0, cachedInputPer1M: 0.5 },\n 'gemini-3.5-flash': { inputPer1M: 1.5, outputPer1M: 9.0, cachedInputPer1M: 0.38 },\n 'gemini-3.1-flash-lite': { inputPer1M: 0.25, outputPer1M: 1.5, cachedInputPer1M: 0.06 },\n 'gemini-2.0-flash': { inputPer1M: 0.1, outputPer1M: 0.4, cachedInputPer1M: 0.025 },\n 'gemini-1.5-pro': { inputPer1M: 1.25, outputPer1M: 5.0, cachedInputPer1M: 0.3125 },\n 'gemini-1.5-flash': { inputPer1M: 0.075, outputPer1M: 0.3, cachedInputPer1M: 0.01875 },\n\n // --- xAI Grok ---\n 'grok-4.6': { inputPer1M: 3.0, outputPer1M: 15.0 },\n 'grok-2': { inputPer1M: 2.0, outputPer1M: 10.0 },\n 'grok-2-vision': { inputPer1M: 2.0, outputPer1M: 10.0 },\n 'grok-beta': { inputPer1M: 5.0, outputPer1M: 15.0 },\n\n // --- Mistral ---\n 'mistral-large-3': { inputPer1M: 2.0, outputPer1M: 6.0 },\n 'mistral-large-latest': { inputPer1M: 2.0, outputPer1M: 6.0 },\n 'codestral-latest': { inputPer1M: 0.3, outputPer1M: 0.9 },\n 'mistral-small-latest': { inputPer1M: 0.2, outputPer1M: 0.6 },\n 'ministral-8b-latest': { inputPer1M: 0.1, outputPer1M: 0.1 },\n\n // --- Groq LPUs ---\n 'llama-3.3-70b-versatile': { inputPer1M: 0.59, outputPer1M: 0.79 },\n 'llama-3.1-8b-instant': { inputPer1M: 0.05, outputPer1M: 0.08 },\n 'deepseek-r1-distill-llama-70b': { inputPer1M: 0.75, outputPer1M: 0.99 },\n 'qwen-2.5-32b': { inputPer1M: 0.29, outputPer1M: 0.39 },\n\n // --- DeepSeek ---\n 'deepseek-v4-pro': { inputPer1M: 0.66, outputPer1M: 1.98, cachedInputPer1M: 0.15 },\n 'deepseek-v4-flash': { inputPer1M: 0.22, outputPer1M: 0.66, cachedInputPer1M: 0.05 },\n 'deepseek-chat': { inputPer1M: 0.22, outputPer1M: 0.66, cachedInputPer1M: 0.05 },\n 'deepseek-reasoner': { inputPer1M: 0.66, outputPer1M: 1.98, cachedInputPer1M: 0.15 },\n\n // --- Cohere ---\n 'command-r-plus': { inputPer1M: 2.5, outputPer1M: 10.0 },\n 'command-r': { inputPer1M: 0.15, outputPer1M: 0.6 },\n};\n\n/**\n * Dynamic in-memory registry allowing runtime custom price registration\n */\nconst customPricingRegistry: Record<string, ModelPricingRates> = {};\n\n/**\n * Normalize model identifier to match pricing table keys\n */\nexport function normalizeModelKey(rawModel: string): string {\n if (!rawModel) return 'unknown';\n\n let model = rawModel.toLowerCase().trim();\n\n // Strip provider prefix if present (e.g. 'openai/gpt-4o' -> 'gpt-4o')\n if (model.includes('/')) {\n model = model.split('/')[1] || model;\n }\n\n // Remove date suffixes like -20240307 or -20250219\n model = model.replace(/-\\d{8}$/, '');\n model = model.replace(/-\\d{4}-\\d{2}-\\d{2}$/, '');\n\n return model;\n}\n\n/**\n * Retrieve pricing rates for a given model\n */\nexport function getModelPricing(modelName: string): ModelPricingRates {\n const normalized = normalizeModelKey(modelName);\n\n // Check custom registry first\n if (customPricingRegistry[normalized]) {\n return customPricingRegistry[normalized];\n }\n if (customPricingRegistry[modelName]) {\n return customPricingRegistry[modelName];\n }\n\n // Check built-in table\n if (MODEL_PRICING_TABLE[normalized]) {\n return MODEL_PRICING_TABLE[normalized];\n }\n if (MODEL_PRICING_TABLE[modelName]) {\n return MODEL_PRICING_TABLE[modelName];\n }\n\n // Fallback defaults for unknown models (conservative estimates: $1.00 in, $3.00 out)\n return {\n inputPer1M: 1.0,\n outputPer1M: 3.0,\n cachedInputPer1M: 0.5,\n };\n}\n\n/**\n * Register or override pricing rates for a custom model\n */\nexport function registerModelPricing(modelName: string, rates: ModelPricingRates): void {\n const normalized = normalizeModelKey(modelName);\n customPricingRegistry[normalized] = rates;\n customPricingRegistry[modelName] = rates;\n}\n"],"mappings":"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","names":["readline","fs","path"]}
1
+ {"version":3,"sources":["../../src/cli/index.ts","../../src/pricing/table.ts","../../src/cli/audit.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport fs from 'fs';\nimport path from 'path';\nimport readline from 'readline';\nimport { MODEL_PRICING_TABLE } from '../pricing/table';\nimport { runAudit, displayAuditReport } from './audit';\n\nconst args = process.argv.slice(2);\nconst command = args[0] || 'init';\n\nfunction printBanner() {\n console.log(`\n\\x1b[38;2;212;255;50m✦\\x1b[0m \\x1b[1mvibezcheck CLI\\x1b[0m \\x1b[90mv0.4.1\\x1b[0m\n\\x1b[90mThe 1-line Stripe Billing & Token Metering Engine for LLMs\\x1b[0m\n`);\n}\n\nasync function prompt(question: string, defaultVal: string = ''): Promise<string> {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n return new Promise((resolve) => {\n const promptText = defaultVal\n ? `\\x1b[32m?\\x1b[0m \\x1b[1m${question}\\x1b[0m \\x1b[90m(${defaultVal})\\x1b[0m: `\n : `\\x1b[32m?\\x1b[0m \\x1b[1m${question}\\x1b[0m: `;\n\n rl.question(promptText, (answer) => {\n rl.close();\n resolve(answer.trim() || defaultVal);\n });\n });\n}\n\n/**\n * Command: npx vibezcheck init\n */\nasync function handleInit() {\n printBanner();\n console.log('\\x1b[35m🚀 Welcome to the VibezCheck Setup Wizard!\\x1b[0m\\n');\n\n const cwd = process.cwd();\n\n // Detect project structure\n const isNextAppRouter = fs.existsSync(path.join(cwd, 'app'));\n const isNextPagesRouter = fs.existsSync(path.join(cwd, 'pages'));\n const isSrcDir = fs.existsSync(path.join(cwd, 'src', 'app'));\n\n console.log(`\\x1b[90m📁 Project directory: ${cwd}\\x1b[0m`);\n if (isNextAppRouter || isSrcDir) {\n console.log('\\x1b[32m✓ Detected Next.js App Router project!\\x1b[0m\\n');\n }\n\n // 1. Prompt for API Keys\n const defaultGateway = 'vck_demo_key';\n const gatewayKey = await prompt('Enter your AI Gateway API Key (or OpenAI key)', defaultGateway);\n const gatewayUrl = await prompt('Enter your AI Gateway Base URL', 'https://ai-gateway.vercel.sh/v1');\n const stripeKey = await prompt('Enter your Stripe Secret Key (optional for test mode)', '');\n\n // 2. Create / Update .env.local\n const envPath = path.join(cwd, '.env.local');\n const envContent = `# VibezCheck AI Gateway & Stripe Configuration\nAI_GATEWAY_API_KEY=${gatewayKey}\nAI_GATEWAY_BASE_URL=${gatewayUrl}\nSTRIPE_SECRET_KEY=${stripeKey}\n`;\n\n fs.writeFileSync(envPath, envContent, { flag: 'w' });\n console.log(`\\x1b[32m✓ Created/updated .env.local\\x1b[0m`);\n\n // 3. Create Sample API Route (app/api/chat/route.ts)\n const apiDir = isSrcDir\n ? path.join(cwd, 'src', 'app', 'api', 'chat')\n : path.join(cwd, 'app', 'api', 'chat');\n\n fs.mkdirSync(apiDir, { recursive: true });\n const routePath = path.join(apiDir, 'route.ts');\n\n const routeContent = `import { streamText } from 'ai';\nimport { vibezcheck } from 'vibezcheck';\n\nexport const runtime = 'nodejs';\nexport const dynamic = 'force-dynamic';\n\nexport async function POST(req: Request) {\n const { messages, customer = 'demo@example.com' } = await req.json();\n\n // ⚡ 1-Line Declarative Model Metering\n const result = streamText({\n model: vibezcheck('openai/gpt-4o-mini', {\n customer,\n onUsage: (event) => {\n console.log(\\`⚡ [vibezcheck] Tokens: \\${event.usage.totalTokens} | Cost: $\\${event.cost.totalUSD.toFixed(6)}\\`);\n },\n }),\n messages,\n });\n\n return result.toTextStreamResponse();\n}\n`;\n\n fs.writeFileSync(routePath, routeContent, { flag: 'w' });\n console.log(`\\x1b[32m✓ Generated declarative API route: ${path.relative(cwd, routePath)}\\x1b[0m`);\n\n console.log(`\n\\x1b[32m\\x1b[1m🎉 Setup Complete!\\x1b[0m\n\n\\x1b[1mNext Steps:\\x1b[0m\n 1. Add \\x1b[36m<VibezSessionWidget />\\x1b[0m to your layout:\n \\x1b[90mimport { VibezSessionProvider, VibezSessionWidget } from 'vibezcheck/react';\\x1b[0m\n\n 2. Use \\x1b[36museVibezChat()\\x1b[0m in your client component:\n \\x1b[90mconst { messages, input, handleSubmit } = useVibezChat();\\x1b[0m\n\n 3. Run your dev server:\n \\x1b[33mnpm run dev\\x1b[0m or \\x1b[33mpnpm dev\\x1b[0m\n`);\n}\n\n/**\n * Command: npx vibezcheck prices\n */\nfunction handlePrices() {\n printBanner();\n console.log('\\x1b[1m📊 Official Model Pricing Registry (USD per 1M Tokens):\\x1b[0m\\n');\n\n console.log(\n 'Model'.padEnd(32) +\n 'Input / 1M'.padEnd(16) +\n 'Output / 1M'.padEnd(16) +\n 'Cached / 1M'\n );\n console.log('-'.repeat(78));\n\n Object.entries(MODEL_PRICING_TABLE).forEach(([model, rates]) => {\n const input = `$${rates.inputPer1M.toFixed(3)}`.padEnd(16);\n const output = `$${rates.outputPer1M.toFixed(3)}`.padEnd(16);\n const cached = rates.cachedInputPer1M\n ? `$${rates.cachedInputPer1M.toFixed(3)}`\n : '—';\n\n console.log(model.padEnd(32) + input + output + cached);\n });\n}\n\n/**\n * Command: npx vibezcheck doctor\n */\nfunction handleDoctor() {\n printBanner();\n console.log('\\x1b[1m🩺 Running VibezCheck System Health Check...\\x1b[0m\\n');\n\n const cwd = process.cwd();\n const envPath = path.join(cwd, '.env.local');\n const hasEnv = fs.existsSync(envPath);\n\n console.log(`Node.js Version: \\x1b[32m${process.version}\\x1b[0m`);\n console.log(`Working Directory: \\x1b[90m${cwd}\\x1b[0m`);\n console.log(`Environment File: ${hasEnv ? '\\x1b[32m✓ Found (.env.local)\\x1b[0m' : '\\x1b[33m⚠ Missing (.env.local)\\x1b[0m'}`);\n\n const hasStripe = Boolean(process.env.STRIPE_SECRET_KEY);\n console.log(`Stripe Key: ${hasStripe ? '\\x1b[32m✓ Active\\x1b[0m' : '\\x1b[90m○ Free Local Mode (No Stripe key)\\x1b[0m'}`);\n\n const hasGateway = Boolean(process.env.AI_GATEWAY_API_KEY || process.env.OPENAI_API_KEY);\n console.log(`AI Provider Key: ${hasGateway ? '\\x1b[32m✓ Configured\\x1b[0m' : '\\x1b[33m⚠ Missing (Set AI_GATEWAY_API_KEY)\\x1b[0m'}`);\n\n console.log(`\\n\\x1b[32m✓ VibezCheck engine is healthy and ready to meter!\\x1b[0m\\n`);\n}\n\n/**\n * Command: npx vibezcheck audit [--fix] [--ci] [--json] [--dir <path>]\n */\nasync function handleAudit() {\n const fix = args.includes('--fix') || args.includes('-f');\n const ci = args.includes('--ci') || args.includes('-s') || args.includes('--strict');\n const json = args.includes('--json') || args.includes('-j');\n\n let dir = process.cwd();\n const dirIndex = args.indexOf('--dir') !== -1 ? args.indexOf('--dir') : args.indexOf('-d');\n if (dirIndex !== -1 && args[dirIndex + 1]) {\n dir = args[dirIndex + 1];\n }\n\n const summary = await runAudit({ dir, fix, ci, json });\n await displayAuditReport(summary, { dir, fix, ci, json });\n}\n\n// Router\nswitch (command) {\n case 'audit':\n case 'check':\n case 'scan':\n handleAudit();\n break;\n case 'init':\n handleInit();\n break;\n case 'prices':\n case 'pricing':\n handlePrices();\n break;\n case 'doctor':\n case 'health':\n handleDoctor();\n break;\n default:\n console.log(`\nUnknown command: \\x1b[31m${command}\\x1b[0m\n\nAvailable commands:\n \\x1b[36mvibezcheck audit\\x1b[0m Scan project for unmetered AI routes and runaway loop risks\n \\x1b[36mvibezcheck init\\x1b[0m Interactive project setup wizard\n \\x1b[36mvibezcheck prices\\x1b[0m Display supported model pricing table\n \\x1b[36mvibezcheck doctor\\x1b[0m Diagnose environment and API configurations\n`);\n break;\n}\n","import type { ModelPricingRates } from '../types';\n\n/**\n * Built-in Registry of Model Pricing (USD per 1 Million Tokens)\n * Sourced from official 2026 provider pricing tables.\n */\nexport const MODEL_PRICING_TABLE: Record<string, ModelPricingRates> = {\n // --- OpenAI ---\n 'gpt-5.6-sol': { inputPer1M: 4.0, outputPer1M: 20.0, cachedInputPer1M: 0.4 },\n 'gpt-5.6-terra': { inputPer1M: 2.0, outputPer1M: 12.0, cachedInputPer1M: 0.2 },\n 'gpt-5.6-luna': { inputPer1M: 0.2, outputPer1M: 1.2, cachedInputPer1M: 0.02 },\n 'gpt-5': { inputPer1M: 4.0, outputPer1M: 20.0, cachedInputPer1M: 0.4 },\n 'gpt-5-mini': { inputPer1M: 0.2, outputPer1M: 1.2, cachedInputPer1M: 0.02 },\n 'o1': { inputPer1M: 15.0, outputPer1M: 60.0, cachedInputPer1M: 7.5 },\n 'o1-mini': { inputPer1M: 1.1, outputPer1M: 4.4, cachedInputPer1M: 0.55 },\n 'o3': { inputPer1M: 15.0, outputPer1M: 60.0, cachedInputPer1M: 7.5 },\n 'o3-mini': { inputPer1M: 1.1, outputPer1M: 4.4, cachedInputPer1M: 0.55 },\n 'gpt-4o': { inputPer1M: 2.5, outputPer1M: 10.0, cachedInputPer1M: 1.25 },\n 'gpt-4o-mini': { inputPer1M: 0.15, outputPer1M: 0.6, cachedInputPer1M: 0.075 },\n 'gpt-4.1': { inputPer1M: 2.0, outputPer1M: 8.0, cachedInputPer1M: 1.0 },\n 'gpt-4.1-nano': { inputPer1M: 0.1, outputPer1M: 0.4, cachedInputPer1M: 0.05 },\n 'text-embedding-3-small': { inputPer1M: 0.02, outputPer1M: 0.0 },\n 'text-embedding-3-large': { inputPer1M: 0.13, outputPer1M: 0.0 },\n\n // --- Anthropic ---\n 'claude-3-7-sonnet': { inputPer1M: 0.59, outputPer1M: 2.93, cachedInputPer1M: 0.3 },\n 'claude-sonnet-5': { inputPer1M: 2.0, outputPer1M: 10.0, cachedInputPer1M: 0.3 },\n 'claude-3-5-sonnet': { inputPer1M: 3.0, outputPer1M: 15.0, cachedInputPer1M: 0.3 },\n 'claude-3-5-haiku': { inputPer1M: 0.8, outputPer1M: 4.0, cachedInputPer1M: 0.08 },\n 'haiku-4.5': { inputPer1M: 1.0, outputPer1M: 5.0, cachedInputPer1M: 0.1 },\n 'claude-opus-5': { inputPer1M: 5.0, outputPer1M: 25.0, cachedInputPer1M: 1.5 },\n 'claude-3-opus': { inputPer1M: 15.0, outputPer1M: 75.0, cachedInputPer1M: 1.5 },\n\n // --- Google Gemini ---\n 'gemini-3.7-flash': { inputPer1M: 0.75, outputPer1M: 3.75, cachedInputPer1M: 0.18 },\n 'gemini-3.1-pro': { inputPer1M: 2.0, outputPer1M: 12.0, cachedInputPer1M: 0.5 },\n 'gemini-3.5-flash': { inputPer1M: 1.5, outputPer1M: 9.0, cachedInputPer1M: 0.38 },\n 'gemini-3.1-flash-lite': { inputPer1M: 0.25, outputPer1M: 1.5, cachedInputPer1M: 0.06 },\n 'gemini-2.0-flash': { inputPer1M: 0.1, outputPer1M: 0.4, cachedInputPer1M: 0.025 },\n 'gemini-1.5-pro': { inputPer1M: 1.25, outputPer1M: 5.0, cachedInputPer1M: 0.3125 },\n 'gemini-1.5-flash': { inputPer1M: 0.075, outputPer1M: 0.3, cachedInputPer1M: 0.01875 },\n\n // --- xAI Grok ---\n 'grok-4.6': { inputPer1M: 3.0, outputPer1M: 15.0 },\n 'grok-2': { inputPer1M: 2.0, outputPer1M: 10.0 },\n 'grok-2-vision': { inputPer1M: 2.0, outputPer1M: 10.0 },\n 'grok-beta': { inputPer1M: 5.0, outputPer1M: 15.0 },\n\n // --- Mistral ---\n 'mistral-large-3': { inputPer1M: 2.0, outputPer1M: 6.0 },\n 'mistral-large-latest': { inputPer1M: 2.0, outputPer1M: 6.0 },\n 'codestral-latest': { inputPer1M: 0.3, outputPer1M: 0.9 },\n 'mistral-small-latest': { inputPer1M: 0.2, outputPer1M: 0.6 },\n 'ministral-8b-latest': { inputPer1M: 0.1, outputPer1M: 0.1 },\n\n // --- Groq LPUs ---\n 'llama-3.3-70b-versatile': { inputPer1M: 0.59, outputPer1M: 0.79 },\n 'llama-3.1-8b-instant': { inputPer1M: 0.05, outputPer1M: 0.08 },\n 'deepseek-r1-distill-llama-70b': { inputPer1M: 0.75, outputPer1M: 0.99 },\n 'qwen-2.5-32b': { inputPer1M: 0.29, outputPer1M: 0.39 },\n\n // --- DeepSeek ---\n 'deepseek-v4-pro': { inputPer1M: 0.66, outputPer1M: 1.98, cachedInputPer1M: 0.15 },\n 'deepseek-v4-flash': { inputPer1M: 0.22, outputPer1M: 0.66, cachedInputPer1M: 0.05 },\n 'deepseek-chat': { inputPer1M: 0.22, outputPer1M: 0.66, cachedInputPer1M: 0.05 },\n 'deepseek-reasoner': { inputPer1M: 0.66, outputPer1M: 1.98, cachedInputPer1M: 0.15 },\n\n // --- Cohere ---\n 'command-r-plus': { inputPer1M: 2.5, outputPer1M: 10.0 },\n 'command-r': { inputPer1M: 0.15, outputPer1M: 0.6 },\n};\n\n/**\n * Dynamic in-memory registry allowing runtime custom price registration\n */\nconst customPricingRegistry: Record<string, ModelPricingRates> = {};\n\n/**\n * Normalize model identifier to match pricing table keys\n */\nexport function normalizeModelKey(rawModel: string): string {\n if (!rawModel) return 'unknown';\n\n let model = rawModel.toLowerCase().trim();\n\n // Strip provider prefix if present (e.g. 'openai/gpt-4o' -> 'gpt-4o')\n if (model.includes('/')) {\n model = model.split('/')[1] || model;\n }\n\n // Remove date suffixes like -20240307 or -20250219\n model = model.replace(/-\\d{8}$/, '');\n model = model.replace(/-\\d{4}-\\d{2}-\\d{2}$/, '');\n\n return model;\n}\n\n/**\n * Retrieve pricing rates for a given model\n */\nexport function getModelPricing(modelName: string): ModelPricingRates {\n const normalized = normalizeModelKey(modelName);\n\n // Check custom registry first\n if (customPricingRegistry[normalized]) {\n return customPricingRegistry[normalized];\n }\n if (customPricingRegistry[modelName]) {\n return customPricingRegistry[modelName];\n }\n\n // Check built-in table\n if (MODEL_PRICING_TABLE[normalized]) {\n return MODEL_PRICING_TABLE[normalized];\n }\n if (MODEL_PRICING_TABLE[modelName]) {\n return MODEL_PRICING_TABLE[modelName];\n }\n\n // Fallback defaults for unknown models (conservative estimates: $1.00 in, $3.00 out)\n return {\n inputPer1M: 1.0,\n outputPer1M: 3.0,\n cachedInputPer1M: 0.5,\n };\n}\n\n/**\n * Register or override pricing rates for a custom model\n */\nexport function registerModelPricing(modelName: string, rates: ModelPricingRates): void {\n const normalized = normalizeModelKey(modelName);\n customPricingRegistry[normalized] = rates;\n customPricingRegistry[modelName] = rates;\n}\n","import fs from 'fs';\nimport path from 'path';\nimport readline from 'readline';\n\nexport interface RouteAuditFinding {\n file: string;\n relativePath: string;\n status: 'protected' | 'unmetered' | 'unprotected_direct';\n line: number;\n rawSnippet: string;\n suggestedFix?: string;\n modelOrProvider?: string;\n details: string;\n}\n\nexport interface AuditOptions {\n dir?: string;\n fix?: boolean;\n ci?: boolean;\n json?: boolean;\n silent?: boolean;\n}\n\nexport interface AuditSummary {\n scannedFiles: number;\n aiRoutesCount: number;\n protectedCount: number;\n unmeteredCount: number;\n scanTimeMs: number;\n findings: RouteAuditFinding[];\n}\n\nconst IGNORED_DIRS = new Set([\n 'node_modules',\n '.next',\n '.git',\n 'dist',\n 'build',\n '.turbo',\n 'coverage',\n '.cache',\n '.vercel',\n 'tests',\n 'test',\n '__tests__',\n 'examples',\n 'fixtures',\n]);\n\nconst EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs']);\n\n/**\n * Discovers API and server route files in the project.\n */\nexport function findRouteFiles(rootDir: string): string[] {\n const targetSubdirs = [\n path.join('app', 'api'),\n path.join('src', 'app', 'api'),\n path.join('pages', 'api'),\n path.join('src', 'pages', 'api'),\n 'routes',\n path.join('src', 'routes'),\n path.join('server', 'api'),\n path.join('server', 'routes'),\n ];\n\n const foundFiles: string[] = [];\n\n // Check designated route directories first\n let searchedDesignated = false;\n for (const rel of targetSubdirs) {\n const full = path.join(rootDir, rel);\n if (fs.existsSync(full)) {\n searchedDesignated = true;\n scanDirRecursive(full, foundFiles);\n }\n }\n\n // If no designated route folders exist or very few files, scan app/ and src/app/\n if (foundFiles.length === 0) {\n const appDir = fs.existsSync(path.join(rootDir, 'app'))\n ? path.join(rootDir, 'app')\n : fs.existsSync(path.join(rootDir, 'src', 'app'))\n ? path.join(rootDir, 'src', 'app')\n : rootDir;\n\n scanDirRecursive(appDir, foundFiles);\n }\n\n return Array.from(new Set(foundFiles));\n}\n\nfunction scanDirRecursive(currentDir: string, results: string[]): void {\n try {\n const entries = fs.readdirSync(currentDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) {\n if (!IGNORED_DIRS.has(entry.name)) {\n scanDirRecursive(path.join(currentDir, entry.name), results);\n }\n } else if (entry.isFile()) {\n const ext = path.extname(entry.name);\n if (EXTENSIONS.has(ext)) {\n // Exclude spec/test files\n if (!entry.name.includes('.test.') && !entry.name.includes('.spec.')) {\n results.push(path.join(currentDir, entry.name));\n }\n }\n }\n }\n } catch {\n // Gracefully ignore inaccessible dirs\n }\n}\n\n/**\n * Audits a single file for AI SDK and LLM usage.\n */\nexport function auditFile(filePath: string, rootDir: string): RouteAuditFinding[] {\n const relativePath = path.relative(rootDir, filePath).replace(/\\\\/g, '/');\n let content = '';\n try {\n content = fs.readFileSync(filePath, 'utf-8');\n } catch {\n return [];\n }\n\n // Fast pre-filter: Does this file even reference AI libraries?\n const hasAiKeywords =\n content.includes('streamText') ||\n content.includes('generateText') ||\n content.includes('streamObject') ||\n content.includes('generateObject') ||\n content.includes('OpenAI') ||\n content.includes('Anthropic') ||\n content.includes('vibezcheck');\n\n if (!hasAiKeywords) {\n return [];\n }\n\n const lines = content.split(/\\r?\\n/);\n const findings: RouteAuditFinding[] = [];\n\n // Check 1: Vercel AI SDK usage (streamText, generateText, etc.)\n const aiSdkRegex = /\\b(streamText|generateText|streamObject|generateObject)\\s*\\(\\s*\\{/g;\n let match: RegExpExecArray | null;\n\n while ((match = aiSdkRegex.exec(content)) !== null) {\n const matchIndex = match.index;\n const lineNumber = content.substring(0, matchIndex).split(/\\r?\\n/).length;\n\n // Scan forward a few lines (up to 25 lines) to inspect the `model:` argument\n const snippetLines = lines.slice(lineNumber - 1, lineNumber + 25);\n const snippet = snippetLines.join('\\n');\n\n // Look for model:\n const modelLineMatch = snippet.match(/model\\s*:\\s*([^,\\n}]+)/);\n if (modelLineMatch) {\n const modelExpr = modelLineMatch[1].trim();\n const modelLineOffset = snippetLines.findIndex((l) => l.includes('model:'));\n const actualLine = lineNumber + (modelLineOffset >= 0 ? modelLineOffset : 0);\n const rawSnippet = lines[actualLine - 1] || modelLineMatch[0];\n\n const isProtected =\n modelExpr.includes('vibezcheck(') ||\n modelExpr.includes('session.model(') ||\n modelExpr.includes('vz.model(');\n\n if (isProtected) {\n findings.push({\n file: filePath,\n relativePath,\n status: 'protected',\n line: actualLine,\n rawSnippet,\n modelOrProvider: modelExpr,\n details: 'Metered with 0ms added latency and $0.50 safety fuse ceiling',\n });\n } else {\n // Raw unmetered AI call\n const suggestedFix = `model: vibezcheck(${modelExpr}),`;\n findings.push({\n file: filePath,\n relativePath,\n status: 'unmetered',\n line: actualLine,\n rawSnippet,\n suggestedFix,\n modelOrProvider: modelExpr,\n details: 'Direct provider call without cost limit or token metering',\n });\n }\n }\n }\n\n // Check 2: Direct raw client instantiation (new OpenAI(), new Anthropic())\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (\n (line.includes('new OpenAI(') || line.includes('new Anthropic(')) &&\n !content.includes('vibezcheck') &&\n !content.includes('wrapStream') &&\n !content.includes('trackUsage')\n ) {\n findings.push({\n file: filePath,\n relativePath,\n status: 'unprotected_direct',\n line: i + 1,\n rawSnippet: line.trim(),\n details: 'Direct client instance without circuit breaker fuse',\n });\n break; // One direct warning per file is sufficient\n }\n }\n\n return findings;\n}\n\n/**\n * Runs the audit across the project.\n */\nexport async function runAudit(options: AuditOptions = {}): Promise<AuditSummary> {\n const startTime = Date.now();\n const rootDir = options.dir ? path.resolve(options.dir) : process.cwd();\n\n const files = findRouteFiles(rootDir);\n const allFindings: RouteAuditFinding[] = [];\n\n for (const file of files) {\n const findings = auditFile(file, rootDir);\n allFindings.push(...findings);\n }\n\n const protectedCount = allFindings.filter((f) => f.status === 'protected').length;\n const unmeteredCount = allFindings.filter(\n (f) => f.status === 'unmetered' || f.status === 'unprotected_direct'\n ).length;\n\n const summary: AuditSummary = {\n scannedFiles: files.length,\n aiRoutesCount: allFindings.length,\n protectedCount,\n unmeteredCount,\n scanTimeMs: Date.now() - startTime,\n findings: allFindings,\n };\n\n return summary;\n}\n\n/**\n * Safely applies the 1-line vibezcheck wrap to an unmetered file.\n * Creates a `.bak` backup copy first.\n */\nexport function applyFixToFile(filePath: string): boolean {\n try {\n const content = fs.readFileSync(filePath, 'utf-8');\n\n // Create backup\n fs.writeFileSync(`${filePath}.bak`, content, 'utf-8');\n\n let updated = content;\n\n // 1. Ensure import exists\n if (!updated.includes(\"from 'vibezcheck'\") && !updated.includes('from \"vibezcheck\"')) {\n // Find the last import statement or place at top\n const importRegex = /^import\\s+.*?;\\s*$/gm;\n let lastImportMatch: RegExpExecArray | null = null;\n let match: RegExpExecArray | null;\n while ((match = importRegex.exec(updated)) !== null) {\n lastImportMatch = match;\n }\n\n const importStatement = \"import { vibezcheck } from 'vibezcheck';\\n\";\n if (lastImportMatch) {\n const insertPos = lastImportMatch.index + lastImportMatch[0].length;\n updated = updated.slice(0, insertPos) + '\\n' + importStatement + updated.slice(insertPos);\n } else {\n updated = importStatement + updated;\n }\n }\n\n // 2. Wrap model: <expr> with vibezcheck(<expr>)\n // Regex matches: model:\\s*([a-zA-Z0-9_$]+(?:\\([^)]*\\)|'[^']*'|\"[^\"]*\"))\n updated = updated.replace(\n /model\\s*:\\s*(?!vibezcheck\\()([a-zA-Z0-9_$]+(?:\\([^)]*\\)|'[^']*'|\"[^\"]*\"))/g,\n 'model: vibezcheck($1)'\n );\n\n fs.writeFileSync(filePath, updated, 'utf-8');\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Renders the kind, minimalist terminal output.\n */\nexport async function displayAuditReport(\n summary: AuditSummary,\n options: AuditOptions = {}\n): Promise<void> {\n // If JSON mode requested, output raw JSON and return\n if (options.json) {\n console.log(JSON.stringify(summary, null, 2));\n if (options.ci && summary.unmeteredCount > 0) {\n process.exit(1);\n }\n return;\n }\n\n console.log(`\\n\\x1b[38;2;212;255;50m✦\\x1b[0m \\x1b[1mvibezcheck audit\\x1b[0m \\x1b[90m(${summary.scanTimeMs}ms)\\x1b[0m\\n`);\n\n // Case 1: No AI routes detected\n if (summary.aiRoutesCount === 0) {\n console.log(` \\x1b[90mNo AI routes detected across ${summary.scannedFiles} files.\\x1b[0m`);\n console.log(` Ready to build your first metered route? Run: \\x1b[36mnpx vibezcheck init\\x1b[0m\\n`);\n return;\n }\n\n // Case 2: All routes protected (The Calm State)\n if (summary.unmeteredCount === 0) {\n const routeWord = summary.protectedCount === 1 ? 'route' : 'routes';\n console.log(` \\x1b[32m✓ All ${summary.protectedCount} AI ${routeWord} are metered with $0.50 safety fuses.\\x1b[0m`);\n console.log(` \\x1b[90mYour wallet is protected. You're good to ship.\\x1b[0m\\n`);\n return;\n }\n\n // Case 3: Unmetered routes found (The Kind Companion)\n const unmeteredFindings = summary.findings.filter((f) => f.status !== 'protected');\n const countWord = unmeteredFindings.length === 1 ? 'route' : 'routes';\n\n console.log(\n ` We noticed \\x1b[33m${unmeteredFindings.length} ${countWord}\\x1b[0m calling AI providers directly without a safety fuse:\\n`\n );\n\n for (const finding of unmeteredFindings) {\n console.log(` \\x1b[1m→ ${finding.relativePath}:${finding.line}\\x1b[0m`);\n console.log(` \\x1b[90mCurrent:\\x1b[0m \\x1b[31m${finding.rawSnippet.trim()}\\x1b[0m`);\n if (finding.suggestedFix) {\n console.log(` \\x1b[90m1-Line Fix:\\x1b[0m \\x1b[32m${finding.suggestedFix}\\x1b[0m`);\n }\n console.log('');\n }\n\n console.log(` \\x1b[1mWhy this matters:\\x1b[0m`);\n console.log(` \\x1b[90mUnmetered routes bill directly to your credit card without runaway limits.`);\n console.log(` Wrapping them adds 0ms token metering, $0.50 runaway fuses, and prompt cache discounts.\\x1b[0m\\n`);\n\n // Auto-fix handling\n if (options.fix) {\n let fixedCount = 0;\n const uniqueFiles = Array.from(new Set(unmeteredFindings.map((f) => f.file)));\n for (const file of uniqueFiles) {\n if (applyFixToFile(file)) {\n fixedCount++;\n const rel = path.relative(process.cwd(), file).replace(/\\\\/g, '/');\n console.log(` \\x1b[32m✓ Safely wrapped ${rel}\\x1b[0m \\x1b[90m(backup saved as .bak)\\x1b[0m`);\n }\n }\n console.log(`\\n \\x1b[32m\\x1b[1m🎉 All done!\\x1b[0m \\x1b[90mRun tests or build to verify.\\x1b[0m\\n`);\n return;\n }\n\n // Interactive prompt if TTY and not CI\n if (!options.ci && process.stdin.isTTY) {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n const answer = await new Promise<string>((resolve) => {\n rl.question(\n ` \\x1b[38;2;212;255;50m⚡ Would you like VibezCheck to safely wrap these routes for you? (y/N): \\x1b[0m`,\n (ans) => {\n rl.close();\n resolve(ans.trim().toLowerCase());\n }\n );\n });\n\n if (answer === 'y' || answer === 'yes') {\n const uniqueFiles = Array.from(new Set(unmeteredFindings.map((f) => f.file)));\n for (const file of uniqueFiles) {\n if (applyFixToFile(file)) {\n const rel = path.relative(process.cwd(), file).replace(/\\\\/g, '/');\n console.log(` \\x1b[32m✓ Safely wrapped ${rel}\\x1b[0m \\x1b[90m(backup saved as .bak)\\x1b[0m`);\n }\n }\n console.log(`\\n \\x1b[32m\\x1b[1m🎉 All done!\\x1b[0m \\x1b[90mRun tests or build to verify.\\x1b[0m\\n`);\n return;\n }\n }\n\n // If CI mode and unmetered found, exit with non-zero\n if (options.ci) {\n console.log(` \\x1b[31m✖ CI check failed: ${summary.unmeteredCount} unmetered route(s) found.\\x1b[0m\\n`);\n process.exit(1);\n }\n}\n"],"mappings":"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","names":["import_fs","import_path","import_readline","path","fs","readline","readline","fs","path"]}
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli/index.ts
4
- import fs from "fs";
5
- import path from "path";
6
- import readline from "readline";
4
+ import fs2 from "fs";
5
+ import path2 from "path";
6
+ import readline2 from "readline";
7
7
 
8
8
  // src/pricing/table.ts
9
9
  var MODEL_PRICING_TABLE = {
@@ -65,17 +65,298 @@ var MODEL_PRICING_TABLE = {
65
65
  "command-r": { inputPer1M: 0.15, outputPer1M: 0.6 }
66
66
  };
67
67
 
68
+ // src/cli/audit.ts
69
+ import fs from "fs";
70
+ import path from "path";
71
+ import readline from "readline";
72
+ var IGNORED_DIRS = /* @__PURE__ */ new Set([
73
+ "node_modules",
74
+ ".next",
75
+ ".git",
76
+ "dist",
77
+ "build",
78
+ ".turbo",
79
+ "coverage",
80
+ ".cache",
81
+ ".vercel",
82
+ "tests",
83
+ "test",
84
+ "__tests__",
85
+ "examples",
86
+ "fixtures"
87
+ ]);
88
+ var EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs"]);
89
+ function findRouteFiles(rootDir) {
90
+ const targetSubdirs = [
91
+ path.join("app", "api"),
92
+ path.join("src", "app", "api"),
93
+ path.join("pages", "api"),
94
+ path.join("src", "pages", "api"),
95
+ "routes",
96
+ path.join("src", "routes"),
97
+ path.join("server", "api"),
98
+ path.join("server", "routes")
99
+ ];
100
+ const foundFiles = [];
101
+ let searchedDesignated = false;
102
+ for (const rel of targetSubdirs) {
103
+ const full = path.join(rootDir, rel);
104
+ if (fs.existsSync(full)) {
105
+ searchedDesignated = true;
106
+ scanDirRecursive(full, foundFiles);
107
+ }
108
+ }
109
+ if (foundFiles.length === 0) {
110
+ const appDir = fs.existsSync(path.join(rootDir, "app")) ? path.join(rootDir, "app") : fs.existsSync(path.join(rootDir, "src", "app")) ? path.join(rootDir, "src", "app") : rootDir;
111
+ scanDirRecursive(appDir, foundFiles);
112
+ }
113
+ return Array.from(new Set(foundFiles));
114
+ }
115
+ function scanDirRecursive(currentDir, results) {
116
+ try {
117
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
118
+ for (const entry of entries) {
119
+ if (entry.isDirectory()) {
120
+ if (!IGNORED_DIRS.has(entry.name)) {
121
+ scanDirRecursive(path.join(currentDir, entry.name), results);
122
+ }
123
+ } else if (entry.isFile()) {
124
+ const ext = path.extname(entry.name);
125
+ if (EXTENSIONS.has(ext)) {
126
+ if (!entry.name.includes(".test.") && !entry.name.includes(".spec.")) {
127
+ results.push(path.join(currentDir, entry.name));
128
+ }
129
+ }
130
+ }
131
+ }
132
+ } catch {
133
+ }
134
+ }
135
+ function auditFile(filePath, rootDir) {
136
+ const relativePath = path.relative(rootDir, filePath).replace(/\\/g, "/");
137
+ let content = "";
138
+ try {
139
+ content = fs.readFileSync(filePath, "utf-8");
140
+ } catch {
141
+ return [];
142
+ }
143
+ const hasAiKeywords = content.includes("streamText") || content.includes("generateText") || content.includes("streamObject") || content.includes("generateObject") || content.includes("OpenAI") || content.includes("Anthropic") || content.includes("vibezcheck");
144
+ if (!hasAiKeywords) {
145
+ return [];
146
+ }
147
+ const lines = content.split(/\r?\n/);
148
+ const findings = [];
149
+ const aiSdkRegex = /\b(streamText|generateText|streamObject|generateObject)\s*\(\s*\{/g;
150
+ let match;
151
+ while ((match = aiSdkRegex.exec(content)) !== null) {
152
+ const matchIndex = match.index;
153
+ const lineNumber = content.substring(0, matchIndex).split(/\r?\n/).length;
154
+ const snippetLines = lines.slice(lineNumber - 1, lineNumber + 25);
155
+ const snippet = snippetLines.join("\n");
156
+ const modelLineMatch = snippet.match(/model\s*:\s*([^,\n}]+)/);
157
+ if (modelLineMatch) {
158
+ const modelExpr = modelLineMatch[1].trim();
159
+ const modelLineOffset = snippetLines.findIndex((l) => l.includes("model:"));
160
+ const actualLine = lineNumber + (modelLineOffset >= 0 ? modelLineOffset : 0);
161
+ const rawSnippet = lines[actualLine - 1] || modelLineMatch[0];
162
+ const isProtected = modelExpr.includes("vibezcheck(") || modelExpr.includes("session.model(") || modelExpr.includes("vz.model(");
163
+ if (isProtected) {
164
+ findings.push({
165
+ file: filePath,
166
+ relativePath,
167
+ status: "protected",
168
+ line: actualLine,
169
+ rawSnippet,
170
+ modelOrProvider: modelExpr,
171
+ details: "Metered with 0ms added latency and $0.50 safety fuse ceiling"
172
+ });
173
+ } else {
174
+ const suggestedFix = `model: vibezcheck(${modelExpr}),`;
175
+ findings.push({
176
+ file: filePath,
177
+ relativePath,
178
+ status: "unmetered",
179
+ line: actualLine,
180
+ rawSnippet,
181
+ suggestedFix,
182
+ modelOrProvider: modelExpr,
183
+ details: "Direct provider call without cost limit or token metering"
184
+ });
185
+ }
186
+ }
187
+ }
188
+ for (let i = 0; i < lines.length; i++) {
189
+ const line = lines[i];
190
+ if ((line.includes("new OpenAI(") || line.includes("new Anthropic(")) && !content.includes("vibezcheck") && !content.includes("wrapStream") && !content.includes("trackUsage")) {
191
+ findings.push({
192
+ file: filePath,
193
+ relativePath,
194
+ status: "unprotected_direct",
195
+ line: i + 1,
196
+ rawSnippet: line.trim(),
197
+ details: "Direct client instance without circuit breaker fuse"
198
+ });
199
+ break;
200
+ }
201
+ }
202
+ return findings;
203
+ }
204
+ async function runAudit(options = {}) {
205
+ const startTime = Date.now();
206
+ const rootDir = options.dir ? path.resolve(options.dir) : process.cwd();
207
+ const files = findRouteFiles(rootDir);
208
+ const allFindings = [];
209
+ for (const file of files) {
210
+ const findings = auditFile(file, rootDir);
211
+ allFindings.push(...findings);
212
+ }
213
+ const protectedCount = allFindings.filter((f) => f.status === "protected").length;
214
+ const unmeteredCount = allFindings.filter(
215
+ (f) => f.status === "unmetered" || f.status === "unprotected_direct"
216
+ ).length;
217
+ const summary = {
218
+ scannedFiles: files.length,
219
+ aiRoutesCount: allFindings.length,
220
+ protectedCount,
221
+ unmeteredCount,
222
+ scanTimeMs: Date.now() - startTime,
223
+ findings: allFindings
224
+ };
225
+ return summary;
226
+ }
227
+ function applyFixToFile(filePath) {
228
+ try {
229
+ const content = fs.readFileSync(filePath, "utf-8");
230
+ fs.writeFileSync(`${filePath}.bak`, content, "utf-8");
231
+ let updated = content;
232
+ if (!updated.includes("from 'vibezcheck'") && !updated.includes('from "vibezcheck"')) {
233
+ const importRegex = /^import\s+.*?;\s*$/gm;
234
+ let lastImportMatch = null;
235
+ let match;
236
+ while ((match = importRegex.exec(updated)) !== null) {
237
+ lastImportMatch = match;
238
+ }
239
+ const importStatement = "import { vibezcheck } from 'vibezcheck';\n";
240
+ if (lastImportMatch) {
241
+ const insertPos = lastImportMatch.index + lastImportMatch[0].length;
242
+ updated = updated.slice(0, insertPos) + "\n" + importStatement + updated.slice(insertPos);
243
+ } else {
244
+ updated = importStatement + updated;
245
+ }
246
+ }
247
+ updated = updated.replace(
248
+ /model\s*:\s*(?!vibezcheck\()([a-zA-Z0-9_$]+(?:\([^)]*\)|'[^']*'|"[^"]*"))/g,
249
+ "model: vibezcheck($1)"
250
+ );
251
+ fs.writeFileSync(filePath, updated, "utf-8");
252
+ return true;
253
+ } catch {
254
+ return false;
255
+ }
256
+ }
257
+ async function displayAuditReport(summary, options = {}) {
258
+ if (options.json) {
259
+ console.log(JSON.stringify(summary, null, 2));
260
+ if (options.ci && summary.unmeteredCount > 0) {
261
+ process.exit(1);
262
+ }
263
+ return;
264
+ }
265
+ console.log(`
266
+ \x1B[38;2;212;255;50m\u2726\x1B[0m \x1B[1mvibezcheck audit\x1B[0m \x1B[90m(${summary.scanTimeMs}ms)\x1B[0m
267
+ `);
268
+ if (summary.aiRoutesCount === 0) {
269
+ console.log(` \x1B[90mNo AI routes detected across ${summary.scannedFiles} files.\x1B[0m`);
270
+ console.log(` Ready to build your first metered route? Run: \x1B[36mnpx vibezcheck init\x1B[0m
271
+ `);
272
+ return;
273
+ }
274
+ if (summary.unmeteredCount === 0) {
275
+ const routeWord = summary.protectedCount === 1 ? "route" : "routes";
276
+ console.log(` \x1B[32m\u2713 All ${summary.protectedCount} AI ${routeWord} are metered with $0.50 safety fuses.\x1B[0m`);
277
+ console.log(` \x1B[90mYour wallet is protected. You're good to ship.\x1B[0m
278
+ `);
279
+ return;
280
+ }
281
+ const unmeteredFindings = summary.findings.filter((f) => f.status !== "protected");
282
+ const countWord = unmeteredFindings.length === 1 ? "route" : "routes";
283
+ console.log(
284
+ ` We noticed \x1B[33m${unmeteredFindings.length} ${countWord}\x1B[0m calling AI providers directly without a safety fuse:
285
+ `
286
+ );
287
+ for (const finding of unmeteredFindings) {
288
+ console.log(` \x1B[1m\u2192 ${finding.relativePath}:${finding.line}\x1B[0m`);
289
+ console.log(` \x1B[90mCurrent:\x1B[0m \x1B[31m${finding.rawSnippet.trim()}\x1B[0m`);
290
+ if (finding.suggestedFix) {
291
+ console.log(` \x1B[90m1-Line Fix:\x1B[0m \x1B[32m${finding.suggestedFix}\x1B[0m`);
292
+ }
293
+ console.log("");
294
+ }
295
+ console.log(` \x1B[1mWhy this matters:\x1B[0m`);
296
+ console.log(` \x1B[90mUnmetered routes bill directly to your credit card without runaway limits.`);
297
+ console.log(` Wrapping them adds 0ms token metering, $0.50 runaway fuses, and prompt cache discounts.\x1B[0m
298
+ `);
299
+ if (options.fix) {
300
+ let fixedCount = 0;
301
+ const uniqueFiles = Array.from(new Set(unmeteredFindings.map((f) => f.file)));
302
+ for (const file of uniqueFiles) {
303
+ if (applyFixToFile(file)) {
304
+ fixedCount++;
305
+ const rel = path.relative(process.cwd(), file).replace(/\\/g, "/");
306
+ console.log(` \x1B[32m\u2713 Safely wrapped ${rel}\x1B[0m \x1B[90m(backup saved as .bak)\x1B[0m`);
307
+ }
308
+ }
309
+ console.log(`
310
+ \x1B[32m\x1B[1m\u{1F389} All done!\x1B[0m \x1B[90mRun tests or build to verify.\x1B[0m
311
+ `);
312
+ return;
313
+ }
314
+ if (!options.ci && process.stdin.isTTY) {
315
+ const rl = readline.createInterface({
316
+ input: process.stdin,
317
+ output: process.stdout
318
+ });
319
+ const answer = await new Promise((resolve) => {
320
+ rl.question(
321
+ ` \x1B[38;2;212;255;50m\u26A1 Would you like VibezCheck to safely wrap these routes for you? (y/N): \x1B[0m`,
322
+ (ans) => {
323
+ rl.close();
324
+ resolve(ans.trim().toLowerCase());
325
+ }
326
+ );
327
+ });
328
+ if (answer === "y" || answer === "yes") {
329
+ const uniqueFiles = Array.from(new Set(unmeteredFindings.map((f) => f.file)));
330
+ for (const file of uniqueFiles) {
331
+ if (applyFixToFile(file)) {
332
+ const rel = path.relative(process.cwd(), file).replace(/\\/g, "/");
333
+ console.log(` \x1B[32m\u2713 Safely wrapped ${rel}\x1B[0m \x1B[90m(backup saved as .bak)\x1B[0m`);
334
+ }
335
+ }
336
+ console.log(`
337
+ \x1B[32m\x1B[1m\u{1F389} All done!\x1B[0m \x1B[90mRun tests or build to verify.\x1B[0m
338
+ `);
339
+ return;
340
+ }
341
+ }
342
+ if (options.ci) {
343
+ console.log(` \x1B[31m\u2716 CI check failed: ${summary.unmeteredCount} unmetered route(s) found.\x1B[0m
344
+ `);
345
+ process.exit(1);
346
+ }
347
+ }
348
+
68
349
  // src/cli/index.ts
69
350
  var args = process.argv.slice(2);
70
351
  var command = args[0] || "init";
71
352
  function printBanner() {
72
353
  console.log(`
73
- \x1B[36m\u26A1 \x1B[1mvibezcheck CLI\x1B[0m \x1B[90mv0.3.0\x1B[0m
354
+ \x1B[38;2;212;255;50m\u2726\x1B[0m \x1B[1mvibezcheck CLI\x1B[0m \x1B[90mv0.4.1\x1B[0m
74
355
  \x1B[90mThe 1-line Stripe Billing & Token Metering Engine for LLMs\x1B[0m
75
356
  `);
76
357
  }
77
358
  async function prompt(question, defaultVal = "") {
78
- const rl = readline.createInterface({
359
+ const rl = readline2.createInterface({
79
360
  input: process.stdin,
80
361
  output: process.stdout
81
362
  });
@@ -91,9 +372,9 @@ async function handleInit() {
91
372
  printBanner();
92
373
  console.log("\x1B[35m\u{1F680} Welcome to the VibezCheck Setup Wizard!\x1B[0m\n");
93
374
  const cwd = process.cwd();
94
- const isNextAppRouter = fs.existsSync(path.join(cwd, "app"));
95
- const isNextPagesRouter = fs.existsSync(path.join(cwd, "pages"));
96
- const isSrcDir = fs.existsSync(path.join(cwd, "src", "app"));
375
+ const isNextAppRouter = fs2.existsSync(path2.join(cwd, "app"));
376
+ const isNextPagesRouter = fs2.existsSync(path2.join(cwd, "pages"));
377
+ const isSrcDir = fs2.existsSync(path2.join(cwd, "src", "app"));
97
378
  console.log(`\x1B[90m\u{1F4C1} Project directory: ${cwd}\x1B[0m`);
98
379
  if (isNextAppRouter || isSrcDir) {
99
380
  console.log("\x1B[32m\u2713 Detected Next.js App Router project!\x1B[0m\n");
@@ -102,17 +383,17 @@ async function handleInit() {
102
383
  const gatewayKey = await prompt("Enter your AI Gateway API Key (or OpenAI key)", defaultGateway);
103
384
  const gatewayUrl = await prompt("Enter your AI Gateway Base URL", "https://ai-gateway.vercel.sh/v1");
104
385
  const stripeKey = await prompt("Enter your Stripe Secret Key (optional for test mode)", "");
105
- const envPath = path.join(cwd, ".env.local");
386
+ const envPath = path2.join(cwd, ".env.local");
106
387
  const envContent = `# VibezCheck AI Gateway & Stripe Configuration
107
388
  AI_GATEWAY_API_KEY=${gatewayKey}
108
389
  AI_GATEWAY_BASE_URL=${gatewayUrl}
109
390
  STRIPE_SECRET_KEY=${stripeKey}
110
391
  `;
111
- fs.writeFileSync(envPath, envContent, { flag: "w" });
392
+ fs2.writeFileSync(envPath, envContent, { flag: "w" });
112
393
  console.log(`\x1B[32m\u2713 Created/updated .env.local\x1B[0m`);
113
- const apiDir = isSrcDir ? path.join(cwd, "src", "app", "api", "chat") : path.join(cwd, "app", "api", "chat");
114
- fs.mkdirSync(apiDir, { recursive: true });
115
- const routePath = path.join(apiDir, "route.ts");
394
+ const apiDir = isSrcDir ? path2.join(cwd, "src", "app", "api", "chat") : path2.join(cwd, "app", "api", "chat");
395
+ fs2.mkdirSync(apiDir, { recursive: true });
396
+ const routePath = path2.join(apiDir, "route.ts");
116
397
  const routeContent = `import { streamText } from 'ai';
117
398
  import { vibezcheck } from 'vibezcheck';
118
399
 
@@ -136,8 +417,8 @@ export async function POST(req: Request) {
136
417
  return result.toTextStreamResponse();
137
418
  }
138
419
  `;
139
- fs.writeFileSync(routePath, routeContent, { flag: "w" });
140
- console.log(`\x1B[32m\u2713 Generated declarative API route: ${path.relative(cwd, routePath)}\x1B[0m`);
420
+ fs2.writeFileSync(routePath, routeContent, { flag: "w" });
421
+ console.log(`\x1B[32m\u2713 Generated declarative API route: ${path2.relative(cwd, routePath)}\x1B[0m`);
141
422
  console.log(`
142
423
  \x1B[32m\x1B[1m\u{1F389} Setup Complete!\x1B[0m
143
424
 
@@ -170,8 +451,8 @@ function handleDoctor() {
170
451
  printBanner();
171
452
  console.log("\x1B[1m\u{1FA7A} Running VibezCheck System Health Check...\x1B[0m\n");
172
453
  const cwd = process.cwd();
173
- const envPath = path.join(cwd, ".env.local");
174
- const hasEnv = fs.existsSync(envPath);
454
+ const envPath = path2.join(cwd, ".env.local");
455
+ const hasEnv = fs2.existsSync(envPath);
175
456
  console.log(`Node.js Version: \x1B[32m${process.version}\x1B[0m`);
176
457
  console.log(`Working Directory: \x1B[90m${cwd}\x1B[0m`);
177
458
  console.log(`Environment File: ${hasEnv ? "\x1B[32m\u2713 Found (.env.local)\x1B[0m" : "\x1B[33m\u26A0 Missing (.env.local)\x1B[0m"}`);
@@ -183,7 +464,24 @@ function handleDoctor() {
183
464
  \x1B[32m\u2713 VibezCheck engine is healthy and ready to meter!\x1B[0m
184
465
  `);
185
466
  }
467
+ async function handleAudit() {
468
+ const fix = args.includes("--fix") || args.includes("-f");
469
+ const ci = args.includes("--ci") || args.includes("-s") || args.includes("--strict");
470
+ const json = args.includes("--json") || args.includes("-j");
471
+ let dir = process.cwd();
472
+ const dirIndex = args.indexOf("--dir") !== -1 ? args.indexOf("--dir") : args.indexOf("-d");
473
+ if (dirIndex !== -1 && args[dirIndex + 1]) {
474
+ dir = args[dirIndex + 1];
475
+ }
476
+ const summary = await runAudit({ dir, fix, ci, json });
477
+ await displayAuditReport(summary, { dir, fix, ci, json });
478
+ }
186
479
  switch (command) {
480
+ case "audit":
481
+ case "check":
482
+ case "scan":
483
+ handleAudit();
484
+ break;
187
485
  case "init":
188
486
  handleInit();
189
487
  break;
@@ -200,6 +498,7 @@ switch (command) {
200
498
  Unknown command: \x1B[31m${command}\x1B[0m
201
499
 
202
500
  Available commands:
501
+ \x1B[36mvibezcheck audit\x1B[0m Scan project for unmetered AI routes and runaway loop risks
203
502
  \x1B[36mvibezcheck init\x1B[0m Interactive project setup wizard
204
503
  \x1B[36mvibezcheck prices\x1B[0m Display supported model pricing table
205
504
  \x1B[36mvibezcheck doctor\x1B[0m Diagnose environment and API configurations
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/index.ts","../../src/pricing/table.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport fs from 'fs';\nimport path from 'path';\nimport readline from 'readline';\nimport { MODEL_PRICING_TABLE } from '../pricing/table';\n\nconst args = process.argv.slice(2);\nconst command = args[0] || 'init';\n\nfunction printBanner() {\n console.log(`\n\\x1b[36m⚡ \\x1b[1mvibezcheck CLI\\x1b[0m \\x1b[90mv0.3.0\\x1b[0m\n\\x1b[90mThe 1-line Stripe Billing & Token Metering Engine for LLMs\\x1b[0m\n`);\n}\n\nasync function prompt(question: string, defaultVal: string = ''): Promise<string> {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n return new Promise((resolve) => {\n const promptText = defaultVal\n ? `\\x1b[32m?\\x1b[0m \\x1b[1m${question}\\x1b[0m \\x1b[90m(${defaultVal})\\x1b[0m: `\n : `\\x1b[32m?\\x1b[0m \\x1b[1m${question}\\x1b[0m: `;\n\n rl.question(promptText, (answer) => {\n rl.close();\n resolve(answer.trim() || defaultVal);\n });\n });\n}\n\n/**\n * Command: npx vibezcheck init\n */\nasync function handleInit() {\n printBanner();\n console.log('\\x1b[35m🚀 Welcome to the VibezCheck Setup Wizard!\\x1b[0m\\n');\n\n const cwd = process.cwd();\n\n // Detect project structure\n const isNextAppRouter = fs.existsSync(path.join(cwd, 'app'));\n const isNextPagesRouter = fs.existsSync(path.join(cwd, 'pages'));\n const isSrcDir = fs.existsSync(path.join(cwd, 'src', 'app'));\n\n console.log(`\\x1b[90m📁 Project directory: ${cwd}\\x1b[0m`);\n if (isNextAppRouter || isSrcDir) {\n console.log('\\x1b[32m✓ Detected Next.js App Router project!\\x1b[0m\\n');\n }\n\n // 1. Prompt for API Keys\n const defaultGateway = 'vck_demo_key';\n const gatewayKey = await prompt('Enter your AI Gateway API Key (or OpenAI key)', defaultGateway);\n const gatewayUrl = await prompt('Enter your AI Gateway Base URL', 'https://ai-gateway.vercel.sh/v1');\n const stripeKey = await prompt('Enter your Stripe Secret Key (optional for test mode)', '');\n\n // 2. Create / Update .env.local\n const envPath = path.join(cwd, '.env.local');\n const envContent = `# VibezCheck AI Gateway & Stripe Configuration\nAI_GATEWAY_API_KEY=${gatewayKey}\nAI_GATEWAY_BASE_URL=${gatewayUrl}\nSTRIPE_SECRET_KEY=${stripeKey}\n`;\n\n fs.writeFileSync(envPath, envContent, { flag: 'w' });\n console.log(`\\x1b[32m✓ Created/updated .env.local\\x1b[0m`);\n\n // 3. Create Sample API Route (app/api/chat/route.ts)\n const apiDir = isSrcDir\n ? path.join(cwd, 'src', 'app', 'api', 'chat')\n : path.join(cwd, 'app', 'api', 'chat');\n\n fs.mkdirSync(apiDir, { recursive: true });\n const routePath = path.join(apiDir, 'route.ts');\n\n const routeContent = `import { streamText } from 'ai';\nimport { vibezcheck } from 'vibezcheck';\n\nexport const runtime = 'nodejs';\nexport const dynamic = 'force-dynamic';\n\nexport async function POST(req: Request) {\n const { messages, customer = 'demo@example.com' } = await req.json();\n\n // ⚡ 1-Line Declarative Model Metering\n const result = streamText({\n model: vibezcheck('openai/gpt-4o-mini', {\n customer,\n onUsage: (event) => {\n console.log(\\`⚡ [vibezcheck] Tokens: \\${event.usage.totalTokens} | Cost: $\\${event.cost.totalUSD.toFixed(6)}\\`);\n },\n }),\n messages,\n });\n\n return result.toTextStreamResponse();\n}\n`;\n\n fs.writeFileSync(routePath, routeContent, { flag: 'w' });\n console.log(`\\x1b[32m✓ Generated declarative API route: ${path.relative(cwd, routePath)}\\x1b[0m`);\n\n console.log(`\n\\x1b[32m\\x1b[1m🎉 Setup Complete!\\x1b[0m\n\n\\x1b[1mNext Steps:\\x1b[0m\n 1. Add \\x1b[36m<VibezSessionWidget />\\x1b[0m to your layout:\n \\x1b[90mimport { VibezSessionProvider, VibezSessionWidget } from 'vibezcheck/react';\\x1b[0m\n\n 2. Use \\x1b[36museVibezChat()\\x1b[0m in your client component:\n \\x1b[90mconst { messages, input, handleSubmit } = useVibezChat();\\x1b[0m\n\n 3. Run your dev server:\n \\x1b[33mnpm run dev\\x1b[0m or \\x1b[33mpnpm dev\\x1b[0m\n`);\n}\n\n/**\n * Command: npx vibezcheck prices\n */\nfunction handlePrices() {\n printBanner();\n console.log('\\x1b[1m📊 Official Model Pricing Registry (USD per 1M Tokens):\\x1b[0m\\n');\n\n console.log(\n 'Model'.padEnd(32) +\n 'Input / 1M'.padEnd(16) +\n 'Output / 1M'.padEnd(16) +\n 'Cached / 1M'\n );\n console.log('-'.repeat(78));\n\n Object.entries(MODEL_PRICING_TABLE).forEach(([model, rates]) => {\n const input = `$${rates.inputPer1M.toFixed(3)}`.padEnd(16);\n const output = `$${rates.outputPer1M.toFixed(3)}`.padEnd(16);\n const cached = rates.cachedInputPer1M\n ? `$${rates.cachedInputPer1M.toFixed(3)}`\n : '—';\n\n console.log(model.padEnd(32) + input + output + cached);\n });\n}\n\n/**\n * Command: npx vibezcheck doctor\n */\nfunction handleDoctor() {\n printBanner();\n console.log('\\x1b[1m🩺 Running VibezCheck System Health Check...\\x1b[0m\\n');\n\n const cwd = process.cwd();\n const envPath = path.join(cwd, '.env.local');\n const hasEnv = fs.existsSync(envPath);\n\n console.log(`Node.js Version: \\x1b[32m${process.version}\\x1b[0m`);\n console.log(`Working Directory: \\x1b[90m${cwd}\\x1b[0m`);\n console.log(`Environment File: ${hasEnv ? '\\x1b[32m✓ Found (.env.local)\\x1b[0m' : '\\x1b[33m⚠ Missing (.env.local)\\x1b[0m'}`);\n\n const hasStripe = Boolean(process.env.STRIPE_SECRET_KEY);\n console.log(`Stripe Key: ${hasStripe ? '\\x1b[32m✓ Active\\x1b[0m' : '\\x1b[90m○ Free Local Mode (No Stripe key)\\x1b[0m'}`);\n\n const hasGateway = Boolean(process.env.AI_GATEWAY_API_KEY || process.env.OPENAI_API_KEY);\n console.log(`AI Provider Key: ${hasGateway ? '\\x1b[32m✓ Configured\\x1b[0m' : '\\x1b[33m⚠ Missing (Set AI_GATEWAY_API_KEY)\\x1b[0m'}`);\n\n console.log(`\\n\\x1b[32m✓ VibezCheck engine is healthy and ready to meter!\\x1b[0m\\n`);\n}\n\n// Router\nswitch (command) {\n case 'init':\n handleInit();\n break;\n case 'prices':\n case 'pricing':\n handlePrices();\n break;\n case 'doctor':\n case 'health':\n handleDoctor();\n break;\n default:\n console.log(`\nUnknown command: \\x1b[31m${command}\\x1b[0m\n\nAvailable commands:\n \\x1b[36mvibezcheck init\\x1b[0m Interactive project setup wizard\n \\x1b[36mvibezcheck prices\\x1b[0m Display supported model pricing table\n \\x1b[36mvibezcheck doctor\\x1b[0m Diagnose environment and API configurations\n`);\n break;\n}\n","import type { ModelPricingRates } from '../types';\n\n/**\n * Built-in Registry of Model Pricing (USD per 1 Million Tokens)\n * Sourced from official 2026 provider pricing tables.\n */\nexport const MODEL_PRICING_TABLE: Record<string, ModelPricingRates> = {\n // --- OpenAI ---\n 'gpt-5.6-sol': { inputPer1M: 4.0, outputPer1M: 20.0, cachedInputPer1M: 0.4 },\n 'gpt-5.6-terra': { inputPer1M: 2.0, outputPer1M: 12.0, cachedInputPer1M: 0.2 },\n 'gpt-5.6-luna': { inputPer1M: 0.2, outputPer1M: 1.2, cachedInputPer1M: 0.02 },\n 'gpt-5': { inputPer1M: 4.0, outputPer1M: 20.0, cachedInputPer1M: 0.4 },\n 'gpt-5-mini': { inputPer1M: 0.2, outputPer1M: 1.2, cachedInputPer1M: 0.02 },\n 'o1': { inputPer1M: 15.0, outputPer1M: 60.0, cachedInputPer1M: 7.5 },\n 'o1-mini': { inputPer1M: 1.1, outputPer1M: 4.4, cachedInputPer1M: 0.55 },\n 'o3': { inputPer1M: 15.0, outputPer1M: 60.0, cachedInputPer1M: 7.5 },\n 'o3-mini': { inputPer1M: 1.1, outputPer1M: 4.4, cachedInputPer1M: 0.55 },\n 'gpt-4o': { inputPer1M: 2.5, outputPer1M: 10.0, cachedInputPer1M: 1.25 },\n 'gpt-4o-mini': { inputPer1M: 0.15, outputPer1M: 0.6, cachedInputPer1M: 0.075 },\n 'gpt-4.1': { inputPer1M: 2.0, outputPer1M: 8.0, cachedInputPer1M: 1.0 },\n 'gpt-4.1-nano': { inputPer1M: 0.1, outputPer1M: 0.4, cachedInputPer1M: 0.05 },\n 'text-embedding-3-small': { inputPer1M: 0.02, outputPer1M: 0.0 },\n 'text-embedding-3-large': { inputPer1M: 0.13, outputPer1M: 0.0 },\n\n // --- Anthropic ---\n 'claude-3-7-sonnet': { inputPer1M: 0.59, outputPer1M: 2.93, cachedInputPer1M: 0.3 },\n 'claude-sonnet-5': { inputPer1M: 2.0, outputPer1M: 10.0, cachedInputPer1M: 0.3 },\n 'claude-3-5-sonnet': { inputPer1M: 3.0, outputPer1M: 15.0, cachedInputPer1M: 0.3 },\n 'claude-3-5-haiku': { inputPer1M: 0.8, outputPer1M: 4.0, cachedInputPer1M: 0.08 },\n 'haiku-4.5': { inputPer1M: 1.0, outputPer1M: 5.0, cachedInputPer1M: 0.1 },\n 'claude-opus-5': { inputPer1M: 5.0, outputPer1M: 25.0, cachedInputPer1M: 1.5 },\n 'claude-3-opus': { inputPer1M: 15.0, outputPer1M: 75.0, cachedInputPer1M: 1.5 },\n\n // --- Google Gemini ---\n 'gemini-3.7-flash': { inputPer1M: 0.75, outputPer1M: 3.75, cachedInputPer1M: 0.18 },\n 'gemini-3.1-pro': { inputPer1M: 2.0, outputPer1M: 12.0, cachedInputPer1M: 0.5 },\n 'gemini-3.5-flash': { inputPer1M: 1.5, outputPer1M: 9.0, cachedInputPer1M: 0.38 },\n 'gemini-3.1-flash-lite': { inputPer1M: 0.25, outputPer1M: 1.5, cachedInputPer1M: 0.06 },\n 'gemini-2.0-flash': { inputPer1M: 0.1, outputPer1M: 0.4, cachedInputPer1M: 0.025 },\n 'gemini-1.5-pro': { inputPer1M: 1.25, outputPer1M: 5.0, cachedInputPer1M: 0.3125 },\n 'gemini-1.5-flash': { inputPer1M: 0.075, outputPer1M: 0.3, cachedInputPer1M: 0.01875 },\n\n // --- xAI Grok ---\n 'grok-4.6': { inputPer1M: 3.0, outputPer1M: 15.0 },\n 'grok-2': { inputPer1M: 2.0, outputPer1M: 10.0 },\n 'grok-2-vision': { inputPer1M: 2.0, outputPer1M: 10.0 },\n 'grok-beta': { inputPer1M: 5.0, outputPer1M: 15.0 },\n\n // --- Mistral ---\n 'mistral-large-3': { inputPer1M: 2.0, outputPer1M: 6.0 },\n 'mistral-large-latest': { inputPer1M: 2.0, outputPer1M: 6.0 },\n 'codestral-latest': { inputPer1M: 0.3, outputPer1M: 0.9 },\n 'mistral-small-latest': { inputPer1M: 0.2, outputPer1M: 0.6 },\n 'ministral-8b-latest': { inputPer1M: 0.1, outputPer1M: 0.1 },\n\n // --- Groq LPUs ---\n 'llama-3.3-70b-versatile': { inputPer1M: 0.59, outputPer1M: 0.79 },\n 'llama-3.1-8b-instant': { inputPer1M: 0.05, outputPer1M: 0.08 },\n 'deepseek-r1-distill-llama-70b': { inputPer1M: 0.75, outputPer1M: 0.99 },\n 'qwen-2.5-32b': { inputPer1M: 0.29, outputPer1M: 0.39 },\n\n // --- DeepSeek ---\n 'deepseek-v4-pro': { inputPer1M: 0.66, outputPer1M: 1.98, cachedInputPer1M: 0.15 },\n 'deepseek-v4-flash': { inputPer1M: 0.22, outputPer1M: 0.66, cachedInputPer1M: 0.05 },\n 'deepseek-chat': { inputPer1M: 0.22, outputPer1M: 0.66, cachedInputPer1M: 0.05 },\n 'deepseek-reasoner': { inputPer1M: 0.66, outputPer1M: 1.98, cachedInputPer1M: 0.15 },\n\n // --- Cohere ---\n 'command-r-plus': { inputPer1M: 2.5, outputPer1M: 10.0 },\n 'command-r': { inputPer1M: 0.15, outputPer1M: 0.6 },\n};\n\n/**\n * Dynamic in-memory registry allowing runtime custom price registration\n */\nconst customPricingRegistry: Record<string, ModelPricingRates> = {};\n\n/**\n * Normalize model identifier to match pricing table keys\n */\nexport function normalizeModelKey(rawModel: string): string {\n if (!rawModel) return 'unknown';\n\n let model = rawModel.toLowerCase().trim();\n\n // Strip provider prefix if present (e.g. 'openai/gpt-4o' -> 'gpt-4o')\n if (model.includes('/')) {\n model = model.split('/')[1] || model;\n }\n\n // Remove date suffixes like -20240307 or -20250219\n model = model.replace(/-\\d{8}$/, '');\n model = model.replace(/-\\d{4}-\\d{2}-\\d{2}$/, '');\n\n return model;\n}\n\n/**\n * Retrieve pricing rates for a given model\n */\nexport function getModelPricing(modelName: string): ModelPricingRates {\n const normalized = normalizeModelKey(modelName);\n\n // Check custom registry first\n if (customPricingRegistry[normalized]) {\n return customPricingRegistry[normalized];\n }\n if (customPricingRegistry[modelName]) {\n return customPricingRegistry[modelName];\n }\n\n // Check built-in table\n if (MODEL_PRICING_TABLE[normalized]) {\n return MODEL_PRICING_TABLE[normalized];\n }\n if (MODEL_PRICING_TABLE[modelName]) {\n return MODEL_PRICING_TABLE[modelName];\n }\n\n // Fallback defaults for unknown models (conservative estimates: $1.00 in, $3.00 out)\n return {\n inputPer1M: 1.0,\n outputPer1M: 3.0,\n cachedInputPer1M: 0.5,\n };\n}\n\n/**\n * Register or override pricing rates for a custom model\n */\nexport function registerModelPricing(modelName: string, rates: ModelPricingRates): void {\n const normalized = normalizeModelKey(modelName);\n customPricingRegistry[normalized] = rates;\n customPricingRegistry[modelName] = rates;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/cli/index.ts","../../src/pricing/table.ts","../../src/cli/audit.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport fs from 'fs';\nimport path from 'path';\nimport readline from 'readline';\nimport { MODEL_PRICING_TABLE } from '../pricing/table';\nimport { runAudit, displayAuditReport } from './audit';\n\nconst args = process.argv.slice(2);\nconst command = args[0] || 'init';\n\nfunction printBanner() {\n console.log(`\n\\x1b[38;2;212;255;50m✦\\x1b[0m \\x1b[1mvibezcheck CLI\\x1b[0m \\x1b[90mv0.4.1\\x1b[0m\n\\x1b[90mThe 1-line Stripe Billing & Token Metering Engine for LLMs\\x1b[0m\n`);\n}\n\nasync function prompt(question: string, defaultVal: string = ''): Promise<string> {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n return new Promise((resolve) => {\n const promptText = defaultVal\n ? `\\x1b[32m?\\x1b[0m \\x1b[1m${question}\\x1b[0m \\x1b[90m(${defaultVal})\\x1b[0m: `\n : `\\x1b[32m?\\x1b[0m \\x1b[1m${question}\\x1b[0m: `;\n\n rl.question(promptText, (answer) => {\n rl.close();\n resolve(answer.trim() || defaultVal);\n });\n });\n}\n\n/**\n * Command: npx vibezcheck init\n */\nasync function handleInit() {\n printBanner();\n console.log('\\x1b[35m🚀 Welcome to the VibezCheck Setup Wizard!\\x1b[0m\\n');\n\n const cwd = process.cwd();\n\n // Detect project structure\n const isNextAppRouter = fs.existsSync(path.join(cwd, 'app'));\n const isNextPagesRouter = fs.existsSync(path.join(cwd, 'pages'));\n const isSrcDir = fs.existsSync(path.join(cwd, 'src', 'app'));\n\n console.log(`\\x1b[90m📁 Project directory: ${cwd}\\x1b[0m`);\n if (isNextAppRouter || isSrcDir) {\n console.log('\\x1b[32m✓ Detected Next.js App Router project!\\x1b[0m\\n');\n }\n\n // 1. Prompt for API Keys\n const defaultGateway = 'vck_demo_key';\n const gatewayKey = await prompt('Enter your AI Gateway API Key (or OpenAI key)', defaultGateway);\n const gatewayUrl = await prompt('Enter your AI Gateway Base URL', 'https://ai-gateway.vercel.sh/v1');\n const stripeKey = await prompt('Enter your Stripe Secret Key (optional for test mode)', '');\n\n // 2. Create / Update .env.local\n const envPath = path.join(cwd, '.env.local');\n const envContent = `# VibezCheck AI Gateway & Stripe Configuration\nAI_GATEWAY_API_KEY=${gatewayKey}\nAI_GATEWAY_BASE_URL=${gatewayUrl}\nSTRIPE_SECRET_KEY=${stripeKey}\n`;\n\n fs.writeFileSync(envPath, envContent, { flag: 'w' });\n console.log(`\\x1b[32m✓ Created/updated .env.local\\x1b[0m`);\n\n // 3. Create Sample API Route (app/api/chat/route.ts)\n const apiDir = isSrcDir\n ? path.join(cwd, 'src', 'app', 'api', 'chat')\n : path.join(cwd, 'app', 'api', 'chat');\n\n fs.mkdirSync(apiDir, { recursive: true });\n const routePath = path.join(apiDir, 'route.ts');\n\n const routeContent = `import { streamText } from 'ai';\nimport { vibezcheck } from 'vibezcheck';\n\nexport const runtime = 'nodejs';\nexport const dynamic = 'force-dynamic';\n\nexport async function POST(req: Request) {\n const { messages, customer = 'demo@example.com' } = await req.json();\n\n // ⚡ 1-Line Declarative Model Metering\n const result = streamText({\n model: vibezcheck('openai/gpt-4o-mini', {\n customer,\n onUsage: (event) => {\n console.log(\\`⚡ [vibezcheck] Tokens: \\${event.usage.totalTokens} | Cost: $\\${event.cost.totalUSD.toFixed(6)}\\`);\n },\n }),\n messages,\n });\n\n return result.toTextStreamResponse();\n}\n`;\n\n fs.writeFileSync(routePath, routeContent, { flag: 'w' });\n console.log(`\\x1b[32m✓ Generated declarative API route: ${path.relative(cwd, routePath)}\\x1b[0m`);\n\n console.log(`\n\\x1b[32m\\x1b[1m🎉 Setup Complete!\\x1b[0m\n\n\\x1b[1mNext Steps:\\x1b[0m\n 1. Add \\x1b[36m<VibezSessionWidget />\\x1b[0m to your layout:\n \\x1b[90mimport { VibezSessionProvider, VibezSessionWidget } from 'vibezcheck/react';\\x1b[0m\n\n 2. Use \\x1b[36museVibezChat()\\x1b[0m in your client component:\n \\x1b[90mconst { messages, input, handleSubmit } = useVibezChat();\\x1b[0m\n\n 3. Run your dev server:\n \\x1b[33mnpm run dev\\x1b[0m or \\x1b[33mpnpm dev\\x1b[0m\n`);\n}\n\n/**\n * Command: npx vibezcheck prices\n */\nfunction handlePrices() {\n printBanner();\n console.log('\\x1b[1m📊 Official Model Pricing Registry (USD per 1M Tokens):\\x1b[0m\\n');\n\n console.log(\n 'Model'.padEnd(32) +\n 'Input / 1M'.padEnd(16) +\n 'Output / 1M'.padEnd(16) +\n 'Cached / 1M'\n );\n console.log('-'.repeat(78));\n\n Object.entries(MODEL_PRICING_TABLE).forEach(([model, rates]) => {\n const input = `$${rates.inputPer1M.toFixed(3)}`.padEnd(16);\n const output = `$${rates.outputPer1M.toFixed(3)}`.padEnd(16);\n const cached = rates.cachedInputPer1M\n ? `$${rates.cachedInputPer1M.toFixed(3)}`\n : '—';\n\n console.log(model.padEnd(32) + input + output + cached);\n });\n}\n\n/**\n * Command: npx vibezcheck doctor\n */\nfunction handleDoctor() {\n printBanner();\n console.log('\\x1b[1m🩺 Running VibezCheck System Health Check...\\x1b[0m\\n');\n\n const cwd = process.cwd();\n const envPath = path.join(cwd, '.env.local');\n const hasEnv = fs.existsSync(envPath);\n\n console.log(`Node.js Version: \\x1b[32m${process.version}\\x1b[0m`);\n console.log(`Working Directory: \\x1b[90m${cwd}\\x1b[0m`);\n console.log(`Environment File: ${hasEnv ? '\\x1b[32m✓ Found (.env.local)\\x1b[0m' : '\\x1b[33m⚠ Missing (.env.local)\\x1b[0m'}`);\n\n const hasStripe = Boolean(process.env.STRIPE_SECRET_KEY);\n console.log(`Stripe Key: ${hasStripe ? '\\x1b[32m✓ Active\\x1b[0m' : '\\x1b[90m○ Free Local Mode (No Stripe key)\\x1b[0m'}`);\n\n const hasGateway = Boolean(process.env.AI_GATEWAY_API_KEY || process.env.OPENAI_API_KEY);\n console.log(`AI Provider Key: ${hasGateway ? '\\x1b[32m✓ Configured\\x1b[0m' : '\\x1b[33m⚠ Missing (Set AI_GATEWAY_API_KEY)\\x1b[0m'}`);\n\n console.log(`\\n\\x1b[32m✓ VibezCheck engine is healthy and ready to meter!\\x1b[0m\\n`);\n}\n\n/**\n * Command: npx vibezcheck audit [--fix] [--ci] [--json] [--dir <path>]\n */\nasync function handleAudit() {\n const fix = args.includes('--fix') || args.includes('-f');\n const ci = args.includes('--ci') || args.includes('-s') || args.includes('--strict');\n const json = args.includes('--json') || args.includes('-j');\n\n let dir = process.cwd();\n const dirIndex = args.indexOf('--dir') !== -1 ? args.indexOf('--dir') : args.indexOf('-d');\n if (dirIndex !== -1 && args[dirIndex + 1]) {\n dir = args[dirIndex + 1];\n }\n\n const summary = await runAudit({ dir, fix, ci, json });\n await displayAuditReport(summary, { dir, fix, ci, json });\n}\n\n// Router\nswitch (command) {\n case 'audit':\n case 'check':\n case 'scan':\n handleAudit();\n break;\n case 'init':\n handleInit();\n break;\n case 'prices':\n case 'pricing':\n handlePrices();\n break;\n case 'doctor':\n case 'health':\n handleDoctor();\n break;\n default:\n console.log(`\nUnknown command: \\x1b[31m${command}\\x1b[0m\n\nAvailable commands:\n \\x1b[36mvibezcheck audit\\x1b[0m Scan project for unmetered AI routes and runaway loop risks\n \\x1b[36mvibezcheck init\\x1b[0m Interactive project setup wizard\n \\x1b[36mvibezcheck prices\\x1b[0m Display supported model pricing table\n \\x1b[36mvibezcheck doctor\\x1b[0m Diagnose environment and API configurations\n`);\n break;\n}\n","import type { ModelPricingRates } from '../types';\n\n/**\n * Built-in Registry of Model Pricing (USD per 1 Million Tokens)\n * Sourced from official 2026 provider pricing tables.\n */\nexport const MODEL_PRICING_TABLE: Record<string, ModelPricingRates> = {\n // --- OpenAI ---\n 'gpt-5.6-sol': { inputPer1M: 4.0, outputPer1M: 20.0, cachedInputPer1M: 0.4 },\n 'gpt-5.6-terra': { inputPer1M: 2.0, outputPer1M: 12.0, cachedInputPer1M: 0.2 },\n 'gpt-5.6-luna': { inputPer1M: 0.2, outputPer1M: 1.2, cachedInputPer1M: 0.02 },\n 'gpt-5': { inputPer1M: 4.0, outputPer1M: 20.0, cachedInputPer1M: 0.4 },\n 'gpt-5-mini': { inputPer1M: 0.2, outputPer1M: 1.2, cachedInputPer1M: 0.02 },\n 'o1': { inputPer1M: 15.0, outputPer1M: 60.0, cachedInputPer1M: 7.5 },\n 'o1-mini': { inputPer1M: 1.1, outputPer1M: 4.4, cachedInputPer1M: 0.55 },\n 'o3': { inputPer1M: 15.0, outputPer1M: 60.0, cachedInputPer1M: 7.5 },\n 'o3-mini': { inputPer1M: 1.1, outputPer1M: 4.4, cachedInputPer1M: 0.55 },\n 'gpt-4o': { inputPer1M: 2.5, outputPer1M: 10.0, cachedInputPer1M: 1.25 },\n 'gpt-4o-mini': { inputPer1M: 0.15, outputPer1M: 0.6, cachedInputPer1M: 0.075 },\n 'gpt-4.1': { inputPer1M: 2.0, outputPer1M: 8.0, cachedInputPer1M: 1.0 },\n 'gpt-4.1-nano': { inputPer1M: 0.1, outputPer1M: 0.4, cachedInputPer1M: 0.05 },\n 'text-embedding-3-small': { inputPer1M: 0.02, outputPer1M: 0.0 },\n 'text-embedding-3-large': { inputPer1M: 0.13, outputPer1M: 0.0 },\n\n // --- Anthropic ---\n 'claude-3-7-sonnet': { inputPer1M: 0.59, outputPer1M: 2.93, cachedInputPer1M: 0.3 },\n 'claude-sonnet-5': { inputPer1M: 2.0, outputPer1M: 10.0, cachedInputPer1M: 0.3 },\n 'claude-3-5-sonnet': { inputPer1M: 3.0, outputPer1M: 15.0, cachedInputPer1M: 0.3 },\n 'claude-3-5-haiku': { inputPer1M: 0.8, outputPer1M: 4.0, cachedInputPer1M: 0.08 },\n 'haiku-4.5': { inputPer1M: 1.0, outputPer1M: 5.0, cachedInputPer1M: 0.1 },\n 'claude-opus-5': { inputPer1M: 5.0, outputPer1M: 25.0, cachedInputPer1M: 1.5 },\n 'claude-3-opus': { inputPer1M: 15.0, outputPer1M: 75.0, cachedInputPer1M: 1.5 },\n\n // --- Google Gemini ---\n 'gemini-3.7-flash': { inputPer1M: 0.75, outputPer1M: 3.75, cachedInputPer1M: 0.18 },\n 'gemini-3.1-pro': { inputPer1M: 2.0, outputPer1M: 12.0, cachedInputPer1M: 0.5 },\n 'gemini-3.5-flash': { inputPer1M: 1.5, outputPer1M: 9.0, cachedInputPer1M: 0.38 },\n 'gemini-3.1-flash-lite': { inputPer1M: 0.25, outputPer1M: 1.5, cachedInputPer1M: 0.06 },\n 'gemini-2.0-flash': { inputPer1M: 0.1, outputPer1M: 0.4, cachedInputPer1M: 0.025 },\n 'gemini-1.5-pro': { inputPer1M: 1.25, outputPer1M: 5.0, cachedInputPer1M: 0.3125 },\n 'gemini-1.5-flash': { inputPer1M: 0.075, outputPer1M: 0.3, cachedInputPer1M: 0.01875 },\n\n // --- xAI Grok ---\n 'grok-4.6': { inputPer1M: 3.0, outputPer1M: 15.0 },\n 'grok-2': { inputPer1M: 2.0, outputPer1M: 10.0 },\n 'grok-2-vision': { inputPer1M: 2.0, outputPer1M: 10.0 },\n 'grok-beta': { inputPer1M: 5.0, outputPer1M: 15.0 },\n\n // --- Mistral ---\n 'mistral-large-3': { inputPer1M: 2.0, outputPer1M: 6.0 },\n 'mistral-large-latest': { inputPer1M: 2.0, outputPer1M: 6.0 },\n 'codestral-latest': { inputPer1M: 0.3, outputPer1M: 0.9 },\n 'mistral-small-latest': { inputPer1M: 0.2, outputPer1M: 0.6 },\n 'ministral-8b-latest': { inputPer1M: 0.1, outputPer1M: 0.1 },\n\n // --- Groq LPUs ---\n 'llama-3.3-70b-versatile': { inputPer1M: 0.59, outputPer1M: 0.79 },\n 'llama-3.1-8b-instant': { inputPer1M: 0.05, outputPer1M: 0.08 },\n 'deepseek-r1-distill-llama-70b': { inputPer1M: 0.75, outputPer1M: 0.99 },\n 'qwen-2.5-32b': { inputPer1M: 0.29, outputPer1M: 0.39 },\n\n // --- DeepSeek ---\n 'deepseek-v4-pro': { inputPer1M: 0.66, outputPer1M: 1.98, cachedInputPer1M: 0.15 },\n 'deepseek-v4-flash': { inputPer1M: 0.22, outputPer1M: 0.66, cachedInputPer1M: 0.05 },\n 'deepseek-chat': { inputPer1M: 0.22, outputPer1M: 0.66, cachedInputPer1M: 0.05 },\n 'deepseek-reasoner': { inputPer1M: 0.66, outputPer1M: 1.98, cachedInputPer1M: 0.15 },\n\n // --- Cohere ---\n 'command-r-plus': { inputPer1M: 2.5, outputPer1M: 10.0 },\n 'command-r': { inputPer1M: 0.15, outputPer1M: 0.6 },\n};\n\n/**\n * Dynamic in-memory registry allowing runtime custom price registration\n */\nconst customPricingRegistry: Record<string, ModelPricingRates> = {};\n\n/**\n * Normalize model identifier to match pricing table keys\n */\nexport function normalizeModelKey(rawModel: string): string {\n if (!rawModel) return 'unknown';\n\n let model = rawModel.toLowerCase().trim();\n\n // Strip provider prefix if present (e.g. 'openai/gpt-4o' -> 'gpt-4o')\n if (model.includes('/')) {\n model = model.split('/')[1] || model;\n }\n\n // Remove date suffixes like -20240307 or -20250219\n model = model.replace(/-\\d{8}$/, '');\n model = model.replace(/-\\d{4}-\\d{2}-\\d{2}$/, '');\n\n return model;\n}\n\n/**\n * Retrieve pricing rates for a given model\n */\nexport function getModelPricing(modelName: string): ModelPricingRates {\n const normalized = normalizeModelKey(modelName);\n\n // Check custom registry first\n if (customPricingRegistry[normalized]) {\n return customPricingRegistry[normalized];\n }\n if (customPricingRegistry[modelName]) {\n return customPricingRegistry[modelName];\n }\n\n // Check built-in table\n if (MODEL_PRICING_TABLE[normalized]) {\n return MODEL_PRICING_TABLE[normalized];\n }\n if (MODEL_PRICING_TABLE[modelName]) {\n return MODEL_PRICING_TABLE[modelName];\n }\n\n // Fallback defaults for unknown models (conservative estimates: $1.00 in, $3.00 out)\n return {\n inputPer1M: 1.0,\n outputPer1M: 3.0,\n cachedInputPer1M: 0.5,\n };\n}\n\n/**\n * Register or override pricing rates for a custom model\n */\nexport function registerModelPricing(modelName: string, rates: ModelPricingRates): void {\n const normalized = normalizeModelKey(modelName);\n customPricingRegistry[normalized] = rates;\n customPricingRegistry[modelName] = rates;\n}\n","import fs from 'fs';\nimport path from 'path';\nimport readline from 'readline';\n\nexport interface RouteAuditFinding {\n file: string;\n relativePath: string;\n status: 'protected' | 'unmetered' | 'unprotected_direct';\n line: number;\n rawSnippet: string;\n suggestedFix?: string;\n modelOrProvider?: string;\n details: string;\n}\n\nexport interface AuditOptions {\n dir?: string;\n fix?: boolean;\n ci?: boolean;\n json?: boolean;\n silent?: boolean;\n}\n\nexport interface AuditSummary {\n scannedFiles: number;\n aiRoutesCount: number;\n protectedCount: number;\n unmeteredCount: number;\n scanTimeMs: number;\n findings: RouteAuditFinding[];\n}\n\nconst IGNORED_DIRS = new Set([\n 'node_modules',\n '.next',\n '.git',\n 'dist',\n 'build',\n '.turbo',\n 'coverage',\n '.cache',\n '.vercel',\n 'tests',\n 'test',\n '__tests__',\n 'examples',\n 'fixtures',\n]);\n\nconst EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs']);\n\n/**\n * Discovers API and server route files in the project.\n */\nexport function findRouteFiles(rootDir: string): string[] {\n const targetSubdirs = [\n path.join('app', 'api'),\n path.join('src', 'app', 'api'),\n path.join('pages', 'api'),\n path.join('src', 'pages', 'api'),\n 'routes',\n path.join('src', 'routes'),\n path.join('server', 'api'),\n path.join('server', 'routes'),\n ];\n\n const foundFiles: string[] = [];\n\n // Check designated route directories first\n let searchedDesignated = false;\n for (const rel of targetSubdirs) {\n const full = path.join(rootDir, rel);\n if (fs.existsSync(full)) {\n searchedDesignated = true;\n scanDirRecursive(full, foundFiles);\n }\n }\n\n // If no designated route folders exist or very few files, scan app/ and src/app/\n if (foundFiles.length === 0) {\n const appDir = fs.existsSync(path.join(rootDir, 'app'))\n ? path.join(rootDir, 'app')\n : fs.existsSync(path.join(rootDir, 'src', 'app'))\n ? path.join(rootDir, 'src', 'app')\n : rootDir;\n\n scanDirRecursive(appDir, foundFiles);\n }\n\n return Array.from(new Set(foundFiles));\n}\n\nfunction scanDirRecursive(currentDir: string, results: string[]): void {\n try {\n const entries = fs.readdirSync(currentDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) {\n if (!IGNORED_DIRS.has(entry.name)) {\n scanDirRecursive(path.join(currentDir, entry.name), results);\n }\n } else if (entry.isFile()) {\n const ext = path.extname(entry.name);\n if (EXTENSIONS.has(ext)) {\n // Exclude spec/test files\n if (!entry.name.includes('.test.') && !entry.name.includes('.spec.')) {\n results.push(path.join(currentDir, entry.name));\n }\n }\n }\n }\n } catch {\n // Gracefully ignore inaccessible dirs\n }\n}\n\n/**\n * Audits a single file for AI SDK and LLM usage.\n */\nexport function auditFile(filePath: string, rootDir: string): RouteAuditFinding[] {\n const relativePath = path.relative(rootDir, filePath).replace(/\\\\/g, '/');\n let content = '';\n try {\n content = fs.readFileSync(filePath, 'utf-8');\n } catch {\n return [];\n }\n\n // Fast pre-filter: Does this file even reference AI libraries?\n const hasAiKeywords =\n content.includes('streamText') ||\n content.includes('generateText') ||\n content.includes('streamObject') ||\n content.includes('generateObject') ||\n content.includes('OpenAI') ||\n content.includes('Anthropic') ||\n content.includes('vibezcheck');\n\n if (!hasAiKeywords) {\n return [];\n }\n\n const lines = content.split(/\\r?\\n/);\n const findings: RouteAuditFinding[] = [];\n\n // Check 1: Vercel AI SDK usage (streamText, generateText, etc.)\n const aiSdkRegex = /\\b(streamText|generateText|streamObject|generateObject)\\s*\\(\\s*\\{/g;\n let match: RegExpExecArray | null;\n\n while ((match = aiSdkRegex.exec(content)) !== null) {\n const matchIndex = match.index;\n const lineNumber = content.substring(0, matchIndex).split(/\\r?\\n/).length;\n\n // Scan forward a few lines (up to 25 lines) to inspect the `model:` argument\n const snippetLines = lines.slice(lineNumber - 1, lineNumber + 25);\n const snippet = snippetLines.join('\\n');\n\n // Look for model:\n const modelLineMatch = snippet.match(/model\\s*:\\s*([^,\\n}]+)/);\n if (modelLineMatch) {\n const modelExpr = modelLineMatch[1].trim();\n const modelLineOffset = snippetLines.findIndex((l) => l.includes('model:'));\n const actualLine = lineNumber + (modelLineOffset >= 0 ? modelLineOffset : 0);\n const rawSnippet = lines[actualLine - 1] || modelLineMatch[0];\n\n const isProtected =\n modelExpr.includes('vibezcheck(') ||\n modelExpr.includes('session.model(') ||\n modelExpr.includes('vz.model(');\n\n if (isProtected) {\n findings.push({\n file: filePath,\n relativePath,\n status: 'protected',\n line: actualLine,\n rawSnippet,\n modelOrProvider: modelExpr,\n details: 'Metered with 0ms added latency and $0.50 safety fuse ceiling',\n });\n } else {\n // Raw unmetered AI call\n const suggestedFix = `model: vibezcheck(${modelExpr}),`;\n findings.push({\n file: filePath,\n relativePath,\n status: 'unmetered',\n line: actualLine,\n rawSnippet,\n suggestedFix,\n modelOrProvider: modelExpr,\n details: 'Direct provider call without cost limit or token metering',\n });\n }\n }\n }\n\n // Check 2: Direct raw client instantiation (new OpenAI(), new Anthropic())\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (\n (line.includes('new OpenAI(') || line.includes('new Anthropic(')) &&\n !content.includes('vibezcheck') &&\n !content.includes('wrapStream') &&\n !content.includes('trackUsage')\n ) {\n findings.push({\n file: filePath,\n relativePath,\n status: 'unprotected_direct',\n line: i + 1,\n rawSnippet: line.trim(),\n details: 'Direct client instance without circuit breaker fuse',\n });\n break; // One direct warning per file is sufficient\n }\n }\n\n return findings;\n}\n\n/**\n * Runs the audit across the project.\n */\nexport async function runAudit(options: AuditOptions = {}): Promise<AuditSummary> {\n const startTime = Date.now();\n const rootDir = options.dir ? path.resolve(options.dir) : process.cwd();\n\n const files = findRouteFiles(rootDir);\n const allFindings: RouteAuditFinding[] = [];\n\n for (const file of files) {\n const findings = auditFile(file, rootDir);\n allFindings.push(...findings);\n }\n\n const protectedCount = allFindings.filter((f) => f.status === 'protected').length;\n const unmeteredCount = allFindings.filter(\n (f) => f.status === 'unmetered' || f.status === 'unprotected_direct'\n ).length;\n\n const summary: AuditSummary = {\n scannedFiles: files.length,\n aiRoutesCount: allFindings.length,\n protectedCount,\n unmeteredCount,\n scanTimeMs: Date.now() - startTime,\n findings: allFindings,\n };\n\n return summary;\n}\n\n/**\n * Safely applies the 1-line vibezcheck wrap to an unmetered file.\n * Creates a `.bak` backup copy first.\n */\nexport function applyFixToFile(filePath: string): boolean {\n try {\n const content = fs.readFileSync(filePath, 'utf-8');\n\n // Create backup\n fs.writeFileSync(`${filePath}.bak`, content, 'utf-8');\n\n let updated = content;\n\n // 1. Ensure import exists\n if (!updated.includes(\"from 'vibezcheck'\") && !updated.includes('from \"vibezcheck\"')) {\n // Find the last import statement or place at top\n const importRegex = /^import\\s+.*?;\\s*$/gm;\n let lastImportMatch: RegExpExecArray | null = null;\n let match: RegExpExecArray | null;\n while ((match = importRegex.exec(updated)) !== null) {\n lastImportMatch = match;\n }\n\n const importStatement = \"import { vibezcheck } from 'vibezcheck';\\n\";\n if (lastImportMatch) {\n const insertPos = lastImportMatch.index + lastImportMatch[0].length;\n updated = updated.slice(0, insertPos) + '\\n' + importStatement + updated.slice(insertPos);\n } else {\n updated = importStatement + updated;\n }\n }\n\n // 2. Wrap model: <expr> with vibezcheck(<expr>)\n // Regex matches: model:\\s*([a-zA-Z0-9_$]+(?:\\([^)]*\\)|'[^']*'|\"[^\"]*\"))\n updated = updated.replace(\n /model\\s*:\\s*(?!vibezcheck\\()([a-zA-Z0-9_$]+(?:\\([^)]*\\)|'[^']*'|\"[^\"]*\"))/g,\n 'model: vibezcheck($1)'\n );\n\n fs.writeFileSync(filePath, updated, 'utf-8');\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Renders the kind, minimalist terminal output.\n */\nexport async function displayAuditReport(\n summary: AuditSummary,\n options: AuditOptions = {}\n): Promise<void> {\n // If JSON mode requested, output raw JSON and return\n if (options.json) {\n console.log(JSON.stringify(summary, null, 2));\n if (options.ci && summary.unmeteredCount > 0) {\n process.exit(1);\n }\n return;\n }\n\n console.log(`\\n\\x1b[38;2;212;255;50m✦\\x1b[0m \\x1b[1mvibezcheck audit\\x1b[0m \\x1b[90m(${summary.scanTimeMs}ms)\\x1b[0m\\n`);\n\n // Case 1: No AI routes detected\n if (summary.aiRoutesCount === 0) {\n console.log(` \\x1b[90mNo AI routes detected across ${summary.scannedFiles} files.\\x1b[0m`);\n console.log(` Ready to build your first metered route? Run: \\x1b[36mnpx vibezcheck init\\x1b[0m\\n`);\n return;\n }\n\n // Case 2: All routes protected (The Calm State)\n if (summary.unmeteredCount === 0) {\n const routeWord = summary.protectedCount === 1 ? 'route' : 'routes';\n console.log(` \\x1b[32m✓ All ${summary.protectedCount} AI ${routeWord} are metered with $0.50 safety fuses.\\x1b[0m`);\n console.log(` \\x1b[90mYour wallet is protected. You're good to ship.\\x1b[0m\\n`);\n return;\n }\n\n // Case 3: Unmetered routes found (The Kind Companion)\n const unmeteredFindings = summary.findings.filter((f) => f.status !== 'protected');\n const countWord = unmeteredFindings.length === 1 ? 'route' : 'routes';\n\n console.log(\n ` We noticed \\x1b[33m${unmeteredFindings.length} ${countWord}\\x1b[0m calling AI providers directly without a safety fuse:\\n`\n );\n\n for (const finding of unmeteredFindings) {\n console.log(` \\x1b[1m→ ${finding.relativePath}:${finding.line}\\x1b[0m`);\n console.log(` \\x1b[90mCurrent:\\x1b[0m \\x1b[31m${finding.rawSnippet.trim()}\\x1b[0m`);\n if (finding.suggestedFix) {\n console.log(` \\x1b[90m1-Line Fix:\\x1b[0m \\x1b[32m${finding.suggestedFix}\\x1b[0m`);\n }\n console.log('');\n }\n\n console.log(` \\x1b[1mWhy this matters:\\x1b[0m`);\n console.log(` \\x1b[90mUnmetered routes bill directly to your credit card without runaway limits.`);\n console.log(` Wrapping them adds 0ms token metering, $0.50 runaway fuses, and prompt cache discounts.\\x1b[0m\\n`);\n\n // Auto-fix handling\n if (options.fix) {\n let fixedCount = 0;\n const uniqueFiles = Array.from(new Set(unmeteredFindings.map((f) => f.file)));\n for (const file of uniqueFiles) {\n if (applyFixToFile(file)) {\n fixedCount++;\n const rel = path.relative(process.cwd(), file).replace(/\\\\/g, '/');\n console.log(` \\x1b[32m✓ Safely wrapped ${rel}\\x1b[0m \\x1b[90m(backup saved as .bak)\\x1b[0m`);\n }\n }\n console.log(`\\n \\x1b[32m\\x1b[1m🎉 All done!\\x1b[0m \\x1b[90mRun tests or build to verify.\\x1b[0m\\n`);\n return;\n }\n\n // Interactive prompt if TTY and not CI\n if (!options.ci && process.stdin.isTTY) {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n const answer = await new Promise<string>((resolve) => {\n rl.question(\n ` \\x1b[38;2;212;255;50m⚡ Would you like VibezCheck to safely wrap these routes for you? (y/N): \\x1b[0m`,\n (ans) => {\n rl.close();\n resolve(ans.trim().toLowerCase());\n }\n );\n });\n\n if (answer === 'y' || answer === 'yes') {\n const uniqueFiles = Array.from(new Set(unmeteredFindings.map((f) => f.file)));\n for (const file of uniqueFiles) {\n if (applyFixToFile(file)) {\n const rel = path.relative(process.cwd(), file).replace(/\\\\/g, '/');\n console.log(` \\x1b[32m✓ Safely wrapped ${rel}\\x1b[0m \\x1b[90m(backup saved as .bak)\\x1b[0m`);\n }\n }\n console.log(`\\n \\x1b[32m\\x1b[1m🎉 All done!\\x1b[0m \\x1b[90mRun tests or build to verify.\\x1b[0m\\n`);\n return;\n }\n }\n\n // If CI mode and unmetered found, exit with non-zero\n if (options.ci) {\n console.log(` \\x1b[31m✖ CI check failed: ${summary.unmeteredCount} unmetered route(s) found.\\x1b[0m\\n`);\n process.exit(1);\n }\n}\n"],"mappings":"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","names":["fs","path","readline","readline","fs","path"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibezcheck",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "The 1-line Stripe Billing and Token Metering engine for LLMs. Track tokens, compute real-time dollar costs, and bill customers with 0ms added latency.",
5
5
  "author": "VibezCheck <dev@vibezcheck.xyz> (https://vibezcheck.xyz)",
6
6
  "license": "MIT",
@@ -72,7 +72,7 @@
72
72
  }
73
73
  },
74
74
  "bin": {
75
- "vibezcheck": "./bin/vibezcheck.js"
75
+ "vibezcheck": "bin/vibezcheck.js"
76
76
  },
77
77
  "files": [
78
78
  "dist",