@oculum/cli 1.0.7 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +147 -16
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -46906,8 +46906,7 @@ function recordScan() {
46906
46906
  }
46907
46907
 
46908
46908
  // src/ui/onboarding.ts
46909
- async function showWelcomeScreen() {
46910
- console.clear();
46909
+ function showLogo() {
46911
46910
  console.log(source_default.cyan(`
46912
46911
  \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557
46913
46912
  \u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551
@@ -46916,6 +46915,10 @@ async function showWelcomeScreen() {
46916
46915
  \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551
46917
46916
  \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D
46918
46917
  `));
46918
+ }
46919
+ async function showWelcomeScreen() {
46920
+ console.clear();
46921
+ showLogo();
46919
46922
  console.log(source_default.bold.white(" AI-Native Security Scanner for Modern Codebases\n"));
46920
46923
  console.log(source_default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n"));
46921
46924
  console.log(source_default.white(" Oculum detects security vulnerabilities in AI-generated"));
@@ -47972,11 +47975,136 @@ async function runHistoryFlow() {
47972
47975
  }
47973
47976
  }
47974
47977
  }
47978
+ function formatNumber(num) {
47979
+ if (num === -1) return "unlimited";
47980
+ return num.toLocaleString();
47981
+ }
47982
+ function createProgressBar(percentage, width = 20) {
47983
+ const filled = Math.round(percentage / 100 * width);
47984
+ const empty = width - filled;
47985
+ const bar = "\u2588".repeat(filled) + "\u2591".repeat(empty);
47986
+ if (percentage >= 90) return source_default.red(bar);
47987
+ if (percentage >= 70) return source_default.yellow(bar);
47988
+ return source_default.green(bar);
47989
+ }
47990
+ function formatDate(dateStr) {
47991
+ const date = new Date(dateStr);
47992
+ return date.toLocaleDateString("en-US", {
47993
+ month: "short",
47994
+ day: "numeric",
47995
+ year: "numeric"
47996
+ });
47997
+ }
47998
+ function getTimeAgo(date) {
47999
+ const now = /* @__PURE__ */ new Date();
48000
+ const diffMs = now.getTime() - date.getTime();
48001
+ const diffMins = Math.floor(diffMs / 6e4);
48002
+ const diffHours = Math.floor(diffMs / 36e5);
48003
+ const diffDays = Math.floor(diffMs / 864e5);
48004
+ if (diffMins < 1) return "just now";
48005
+ if (diffMins < 60) return `${diffMins}m ago`;
48006
+ if (diffHours < 24) return `${diffHours}h ago`;
48007
+ if (diffDays < 7) return `${diffDays}d ago`;
48008
+ return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });
48009
+ }
48010
+ async function runUsageFlow() {
48011
+ const config = getConfig();
48012
+ if (!isAuthenticated()) {
48013
+ console.log("");
48014
+ M2.warn("Not logged in");
48015
+ console.log("");
48016
+ console.log(source_default.dim(" Login to view your usage and quota:"));
48017
+ console.log(source_default.cyan(" oculum login"));
48018
+ console.log("");
48019
+ await ve({
48020
+ message: "Press Enter to continue",
48021
+ options: [{ value: "back", label: "\u2190 Back to menu" }]
48022
+ });
48023
+ return;
48024
+ }
48025
+ const spinner = Y2();
48026
+ spinner.start("Fetching usage data...");
48027
+ try {
48028
+ const result = await getUsage(config.apiKey);
48029
+ if (!result.success || !result.usage || !result.plan) {
48030
+ spinner.stop("Failed to fetch usage data");
48031
+ console.log("");
48032
+ M2.error(result.error || "Unknown error");
48033
+ console.log("");
48034
+ await ve({
48035
+ message: "Press Enter to continue",
48036
+ options: [{ value: "back", label: "\u2190 Back to menu" }]
48037
+ });
48038
+ return;
48039
+ }
48040
+ spinner.stop("Usage data loaded");
48041
+ const { plan, usage: usageData } = result;
48042
+ console.clear();
48043
+ console.log("");
48044
+ console.log(source_default.bold(" \u{1F4CA} Oculum Usage"));
48045
+ console.log(source_default.dim(" " + "\u2500".repeat(38)));
48046
+ console.log("");
48047
+ const planBadge = plan.name === "pro" ? source_default.bgBlue.white(" PRO ") : plan.name === "enterprise" || plan.name === "max" ? source_default.bgMagenta.white(" MAX ") : plan.name === "starter" ? source_default.bgCyan.white(" STARTER ") : source_default.bgGray.white(" FREE ");
48048
+ console.log(source_default.dim(" Plan: ") + planBadge + source_default.white(` ${plan.displayName}`));
48049
+ console.log(source_default.dim(" Month: ") + source_default.white(usageData.month));
48050
+ console.log("");
48051
+ console.log(source_default.bold(" Credits Usage"));
48052
+ console.log("");
48053
+ const creditsDisplay = usageData.creditsLimit === -1 ? `${formatNumber(usageData.creditsUsed)} / unlimited` : `${formatNumber(usageData.creditsUsed)} / ${formatNumber(usageData.creditsLimit)}`;
48054
+ console.log(source_default.dim(" Used: ") + source_default.white(creditsDisplay));
48055
+ if (usageData.creditsLimit !== -1) {
48056
+ console.log(source_default.dim(" Remaining: ") + source_default.white(formatNumber(usageData.creditsRemaining)));
48057
+ console.log("");
48058
+ console.log(source_default.dim(" ") + createProgressBar(usageData.usagePercentage) + source_default.dim(` ${usageData.usagePercentage.toFixed(1)}%`));
48059
+ }
48060
+ console.log("");
48061
+ console.log(source_default.bold(" This Month"));
48062
+ console.log("");
48063
+ console.log(source_default.dim(" Scans: ") + source_default.white(formatNumber(usageData.totalScans)));
48064
+ console.log(source_default.dim(" Files: ") + source_default.white(formatNumber(usageData.totalFiles)));
48065
+ console.log("");
48066
+ console.log(source_default.dim(" Resets on: ") + source_default.white(formatDate(usageData.resetDate)));
48067
+ console.log("");
48068
+ if (result.recentScans && result.recentScans.length > 0) {
48069
+ console.log(source_default.bold(" Recent Scans"));
48070
+ console.log("");
48071
+ const recentToShow = result.recentScans.slice(0, 5);
48072
+ for (const scan of recentToShow) {
48073
+ const date = new Date(scan.createdAt);
48074
+ const timeAgo = getTimeAgo(date);
48075
+ console.log(source_default.dim(" \u2022 ") + source_default.white(scan.repoName || "unknown") + source_default.dim(` (${timeAgo})`));
48076
+ console.log(source_default.dim(" ") + source_default.dim(`${scan.filesScanned} files, ${scan.findingsCount} findings, ${scan.creditsUsed} credits`));
48077
+ }
48078
+ console.log("");
48079
+ }
48080
+ if (plan.name === "free" || plan.name === "starter") {
48081
+ console.log(source_default.dim(" " + "\u2500".repeat(38)));
48082
+ console.log("");
48083
+ console.log(source_default.dim(" Need more credits? Upgrade your plan"));
48084
+ console.log("");
48085
+ }
48086
+ await ve({
48087
+ message: "Press Enter to continue",
48088
+ options: [{ value: "back", label: "\u2190 Back to menu" }]
48089
+ });
48090
+ } catch (error) {
48091
+ spinner.stop("Failed to fetch usage data");
48092
+ console.log("");
48093
+ M2.error(String(error));
48094
+ console.log("");
48095
+ await ve({
48096
+ message: "Press Enter to continue",
48097
+ options: [{ value: "back", label: "\u2190 Back to menu" }]
48098
+ });
48099
+ }
48100
+ }
47975
48101
  async function runUI() {
48102
+ console.clear();
48103
+ showLogo();
48104
+ console.log();
47976
48105
  const onboardingResult = await handleOnboarding();
47977
48106
  if (onboardingResult.quickStartResult) {
47978
48107
  const { path: path2, depth } = onboardingResult.quickStartResult;
47979
- Ie(source_default.bold("Oculum"));
47980
48108
  try {
47981
48109
  const { output, exitCode, result } = await runScanOnce(path2, {
47982
48110
  depth,
@@ -48004,8 +48132,6 @@ async function runUI() {
48004
48132
  } catch (err) {
48005
48133
  M2.error(`Scan failed: ${String(err)}`);
48006
48134
  }
48007
- } else {
48008
- Ie(source_default.bold("Oculum"));
48009
48135
  }
48010
48136
  let lastScanEntry;
48011
48137
  const userState = getUserState();
@@ -48021,6 +48147,7 @@ async function runUI() {
48021
48147
  { value: "scan", label: "\u{1F50D} Custom Scan", hint: "Configure scan options" },
48022
48148
  { value: "history", label: "\u{1F4DC} Scan History", hint: `${listScanHistory().length} past scans` },
48023
48149
  { value: "auth", label: "\u{1F510} Auth", hint: isAuthenticated() ? `Logged in (${getConfig().tier || "free"})` : "Not logged in" },
48150
+ { value: "usage", label: "\u{1F4CA} Usage", hint: isAuthenticated() ? "View credits and quota" : "Requires login" },
48024
48151
  { value: "help", label: "\u2753 Help", hint: "Commands and tips" },
48025
48152
  { value: "exit", label: "\u{1F44B} Exit" }
48026
48153
  ];
@@ -48102,6 +48229,10 @@ async function runUI() {
48102
48229
  await runHistoryFlow();
48103
48230
  continue;
48104
48231
  }
48232
+ if (action === "usage") {
48233
+ await runUsageFlow();
48234
+ continue;
48235
+ }
48105
48236
  if (action === "help") {
48106
48237
  await showHelpScreen();
48107
48238
  continue;
@@ -48154,11 +48285,11 @@ var uiCommand = new Command("ui").description("Interactive terminal UI").action(
48154
48285
  });
48155
48286
 
48156
48287
  // src/commands/usage.ts
48157
- function formatNumber(num) {
48288
+ function formatNumber2(num) {
48158
48289
  if (num === -1) return "unlimited";
48159
48290
  return num.toLocaleString();
48160
48291
  }
48161
- function createProgressBar(percentage, width = 20) {
48292
+ function createProgressBar2(percentage, width = 20) {
48162
48293
  const filled = Math.round(percentage / 100 * width);
48163
48294
  const empty = width - filled;
48164
48295
  const bar = "\u2588".repeat(filled) + "\u2591".repeat(empty);
@@ -48166,7 +48297,7 @@ function createProgressBar(percentage, width = 20) {
48166
48297
  if (percentage >= 70) return source_default.yellow(bar);
48167
48298
  return source_default.green(bar);
48168
48299
  }
48169
- function formatDate(dateStr) {
48300
+ function formatDate2(dateStr) {
48170
48301
  const date = new Date(dateStr);
48171
48302
  return date.toLocaleDateString("en-US", {
48172
48303
  month: "short",
@@ -48211,20 +48342,20 @@ async function usage(options) {
48211
48342
  console.log("");
48212
48343
  console.log(source_default.bold(" Credits Usage"));
48213
48344
  console.log("");
48214
- const creditsDisplay = usageData.creditsLimit === -1 ? `${formatNumber(usageData.creditsUsed)} / unlimited` : `${formatNumber(usageData.creditsUsed)} / ${formatNumber(usageData.creditsLimit)}`;
48345
+ const creditsDisplay = usageData.creditsLimit === -1 ? `${formatNumber2(usageData.creditsUsed)} / unlimited` : `${formatNumber2(usageData.creditsUsed)} / ${formatNumber2(usageData.creditsLimit)}`;
48215
48346
  console.log(source_default.dim(" Used: ") + source_default.white(creditsDisplay));
48216
48347
  if (usageData.creditsLimit !== -1) {
48217
- console.log(source_default.dim(" Remaining: ") + source_default.white(formatNumber(usageData.creditsRemaining)));
48348
+ console.log(source_default.dim(" Remaining: ") + source_default.white(formatNumber2(usageData.creditsRemaining)));
48218
48349
  console.log("");
48219
- console.log(source_default.dim(" ") + createProgressBar(usageData.usagePercentage) + source_default.dim(` ${usageData.usagePercentage.toFixed(1)}%`));
48350
+ console.log(source_default.dim(" ") + createProgressBar2(usageData.usagePercentage) + source_default.dim(` ${usageData.usagePercentage.toFixed(1)}%`));
48220
48351
  }
48221
48352
  console.log("");
48222
48353
  console.log(source_default.bold(" This Month"));
48223
48354
  console.log("");
48224
- console.log(source_default.dim(" Scans: ") + source_default.white(formatNumber(usageData.totalScans)));
48225
- console.log(source_default.dim(" Files: ") + source_default.white(formatNumber(usageData.totalFiles)));
48355
+ console.log(source_default.dim(" Scans: ") + source_default.white(formatNumber2(usageData.totalScans)));
48356
+ console.log(source_default.dim(" Files: ") + source_default.white(formatNumber2(usageData.totalFiles)));
48226
48357
  console.log("");
48227
- console.log(source_default.dim(" Resets on: ") + source_default.white(formatDate(usageData.resetDate)));
48358
+ console.log(source_default.dim(" Resets on: ") + source_default.white(formatDate2(usageData.resetDate)));
48228
48359
  console.log("");
48229
48360
  if (result.recentScans && result.recentScans.length > 0) {
48230
48361
  console.log(source_default.bold(" Recent Scans"));
@@ -48232,7 +48363,7 @@ async function usage(options) {
48232
48363
  const recentToShow = result.recentScans.slice(0, 5);
48233
48364
  for (const scan of recentToShow) {
48234
48365
  const date = new Date(scan.createdAt);
48235
- const timeAgo = getTimeAgo(date);
48366
+ const timeAgo = getTimeAgo2(date);
48236
48367
  console.log(source_default.dim(" \u2022 ") + source_default.white(scan.repoName || "unknown") + source_default.dim(` (${timeAgo})`));
48237
48368
  console.log(source_default.dim(" ") + source_default.dim(`${scan.filesScanned} files, ${scan.findingsCount} findings, ${scan.creditsUsed} credits`));
48238
48369
  }
@@ -48252,7 +48383,7 @@ async function usage(options) {
48252
48383
  process.exit(1);
48253
48384
  }
48254
48385
  }
48255
- function getTimeAgo(date) {
48386
+ function getTimeAgo2(date) {
48256
48387
  const now = /* @__PURE__ */ new Date();
48257
48388
  const diffMs = now.getTime() - date.getTime();
48258
48389
  const diffMins = Math.floor(diffMs / 6e4);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oculum/cli",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "AI-native security scanner CLI for detecting vulnerabilities in AI-generated code, BYOK patterns, and modern web applications",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -18,14 +18,14 @@
18
18
  "url": "https://github.com/flexipie/oculum/issues"
19
19
  },
20
20
  "scripts": {
21
- "build": "esbuild src/index.ts --bundle --platform=node --target=node18 --outfile=dist/index.js --banner:js=\"#!/usr/bin/env node\" --define:process.env.OCULUM_API_URL='undefined' --define:VERSION='\"1.0.7\"'",
21
+ "build": "esbuild src/index.ts --bundle --platform=node --target=node18 --outfile=dist/index.js --banner:js=\"#!/usr/bin/env node\" --define:process.env.OCULUM_API_URL='undefined' --define:VERSION='\"1.0.8\"'",
22
22
  "dev": "npm run build -- --watch",
23
23
  "test": "echo \"No tests configured yet\"",
24
24
  "lint": "eslint src/"
25
25
  },
26
26
  "dependencies": {
27
- "@oculum/scanner": "^1.0.2",
28
- "@oculum/shared": "^1.0.0",
27
+ "@oculum/scanner": "^1.0.3",
28
+ "@oculum/shared": "^1.0.2",
29
29
  "commander": "^12.1.0",
30
30
  "chalk": "^5.3.0",
31
31
  "ora": "^8.1.1",