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 +316 -17
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/index.mjs +316 -17
- package/dist/cli/index.mjs.map +1 -1
- package/package.json +2 -2
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
|
|
28
|
-
var
|
|
29
|
-
var
|
|
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[
|
|
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 =
|
|
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 =
|
|
118
|
-
const isNextPagesRouter =
|
|
119
|
-
const isSrcDir =
|
|
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 =
|
|
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
|
-
|
|
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 ?
|
|
137
|
-
|
|
138
|
-
const routePath =
|
|
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
|
-
|
|
163
|
-
console.log(`\x1B[32m\u2713 Generated declarative API route: ${
|
|
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 =
|
|
197
|
-
const hasEnv =
|
|
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
|
package/dist/cli/index.js.map
CHANGED
|
@@ -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"]}
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli/index.ts
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
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[
|
|
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 =
|
|
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 =
|
|
95
|
-
const isNextPagesRouter =
|
|
96
|
-
const isSrcDir =
|
|
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 =
|
|
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
|
-
|
|
392
|
+
fs2.writeFileSync(envPath, envContent, { flag: "w" });
|
|
112
393
|
console.log(`\x1B[32m\u2713 Created/updated .env.local\x1B[0m`);
|
|
113
|
-
const apiDir = isSrcDir ?
|
|
114
|
-
|
|
115
|
-
const routePath =
|
|
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
|
-
|
|
140
|
-
console.log(`\x1B[32m\u2713 Generated declarative API route: ${
|
|
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 =
|
|
174
|
-
const hasEnv =
|
|
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
|
package/dist/cli/index.mjs.map
CHANGED
|
@@ -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.
|
|
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": "
|
|
75
|
+
"vibezcheck": "bin/vibezcheck.js"
|
|
76
76
|
},
|
|
77
77
|
"files": [
|
|
78
78
|
"dist",
|