@oculum/cli 1.0.0 → 1.0.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/index.js +146 -57
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -42892,14 +42892,18 @@ function enhanceAPIError(error) {
|
|
|
42892
42892
|
]
|
|
42893
42893
|
};
|
|
42894
42894
|
case 429:
|
|
42895
|
+
const rateLimitInfo = error.reason === "quota_exceeded" ? "You've reached your monthly scan quota." : "Too many requests in a short period.";
|
|
42896
|
+
const rateLimitSuggestion = error.reason === "quota_exceeded" ? "Your quota resets at the start of next month. Upgrade to Pro for higher limits." : "Wait a moment and try again. Consider using --depth cheap for unlimited local scans.";
|
|
42895
42897
|
return {
|
|
42896
|
-
message:
|
|
42897
|
-
suggestion:
|
|
42898
|
+
message: rateLimitInfo,
|
|
42899
|
+
suggestion: rateLimitSuggestion,
|
|
42898
42900
|
category: "server",
|
|
42899
42901
|
recoveryActions: [
|
|
42900
|
-
{ label: "
|
|
42901
|
-
{ label: "View
|
|
42902
|
-
|
|
42902
|
+
{ label: "Use free local scan", command: "oculum scan . --depth cheap", action: "fallback" },
|
|
42903
|
+
{ label: "View usage & upgrade", action: "upgrade" },
|
|
42904
|
+
{ label: "Retry in 30 seconds", action: "retry" }
|
|
42905
|
+
],
|
|
42906
|
+
details: error.reason === "quota_exceeded" ? "Check your usage at https://oculum.dev/dashboard/usage" : void 0
|
|
42903
42907
|
};
|
|
42904
42908
|
case 500:
|
|
42905
42909
|
case 502:
|
|
@@ -43443,11 +43447,17 @@ async function runScanOnce(targetPath, options) {
|
|
|
43443
43447
|
const noColor = options.color === false;
|
|
43444
43448
|
if ((options.depth === "validated" || options.depth === "deep") && !isAuthenticated()) {
|
|
43445
43449
|
if (!options.quiet) {
|
|
43446
|
-
console.log(
|
|
43447
|
-
console.log(
|
|
43448
|
-
|
|
43449
|
-
);
|
|
43450
|
-
console.log(source_default.dim("
|
|
43450
|
+
console.log("");
|
|
43451
|
+
console.log(source_default.yellow("\u26A0\uFE0F AI-powered scans require authentication"));
|
|
43452
|
+
console.log("");
|
|
43453
|
+
console.log(source_default.dim(" You requested: ") + source_default.white(options.depth) + source_default.dim(" scan"));
|
|
43454
|
+
console.log(source_default.dim(" This requires: ") + source_default.white("Pro subscription"));
|
|
43455
|
+
console.log("");
|
|
43456
|
+
console.log(source_default.dim(" Options:"));
|
|
43457
|
+
console.log(source_default.cyan(" \u2022 oculum login") + source_default.dim(" - Authenticate to unlock Pro features"));
|
|
43458
|
+
console.log(source_default.cyan(" \u2022 --depth cheap") + source_default.dim(" - Use free local pattern matching"));
|
|
43459
|
+
console.log("");
|
|
43460
|
+
console.log(source_default.dim(" Continuing with ") + source_default.green("cheap") + source_default.dim(" scan (free)...\n"));
|
|
43451
43461
|
}
|
|
43452
43462
|
options.depth = "cheap";
|
|
43453
43463
|
}
|
|
@@ -43469,19 +43479,24 @@ async function runScanOnce(targetPath, options) {
|
|
|
43469
43479
|
const onProgress = (progress) => {
|
|
43470
43480
|
switch (progress.status) {
|
|
43471
43481
|
case "layer1":
|
|
43472
|
-
spinner.text = `
|
|
43482
|
+
spinner.text = `Scanning patterns... ${source_default.dim(`(${progress.vulnerabilitiesFound} potential issues)`)}`;
|
|
43473
43483
|
break;
|
|
43474
43484
|
case "layer2":
|
|
43475
|
-
spinner.text = `
|
|
43485
|
+
spinner.text = `Analyzing code structure... ${source_default.dim(`(${progress.vulnerabilitiesFound} findings)`)}`;
|
|
43476
43486
|
break;
|
|
43477
43487
|
case "validating":
|
|
43478
|
-
spinner.text = `AI
|
|
43488
|
+
spinner.text = `AI validating findings... ${source_default.dim(`(${progress.vulnerabilitiesFound} candidates)`)}`;
|
|
43479
43489
|
break;
|
|
43480
43490
|
case "layer3":
|
|
43481
|
-
spinner.text = `
|
|
43491
|
+
spinner.text = `Deep AI analysis in progress...`;
|
|
43482
43492
|
break;
|
|
43483
43493
|
case "complete":
|
|
43484
|
-
|
|
43494
|
+
const issueText = progress.vulnerabilitiesFound === 1 ? "issue" : "issues";
|
|
43495
|
+
if (progress.vulnerabilitiesFound === 0) {
|
|
43496
|
+
spinner.succeed(source_default.green("Scan complete: No security issues found! \u2713"));
|
|
43497
|
+
} else {
|
|
43498
|
+
spinner.succeed(`Scan complete: ${progress.vulnerabilitiesFound} ${issueText} found`);
|
|
43499
|
+
}
|
|
43485
43500
|
break;
|
|
43486
43501
|
case "failed":
|
|
43487
43502
|
spinner.fail(progress.message);
|
|
@@ -43614,23 +43629,41 @@ async function login(options) {
|
|
|
43614
43629
|
const result = await verifyApiKey(options.apiKey);
|
|
43615
43630
|
if (!result.valid) {
|
|
43616
43631
|
spinner.fail("Invalid API key");
|
|
43632
|
+
console.log("");
|
|
43633
|
+
console.log(source_default.dim(" The API key could not be verified. Please check:"));
|
|
43634
|
+
console.log(source_default.dim(" \u2022 The key is copied correctly (no extra spaces)"));
|
|
43635
|
+
console.log(source_default.dim(" \u2022 The key has not expired"));
|
|
43636
|
+
console.log(source_default.dim(" \u2022 Generate a new key at: ") + source_default.cyan("https://oculum.dev/dashboard/api-keys"));
|
|
43637
|
+
console.log("");
|
|
43617
43638
|
process.exit(1);
|
|
43618
43639
|
}
|
|
43619
43640
|
setAuthCredentials(options.apiKey, result.email, result.tier);
|
|
43620
43641
|
spinner.succeed("Logged in successfully!");
|
|
43621
|
-
console.log(
|
|
43622
|
-
console.log(source_default.dim(
|
|
43642
|
+
console.log("");
|
|
43643
|
+
console.log(source_default.dim(" Email: ") + source_default.white(result.email));
|
|
43644
|
+
console.log(source_default.dim(" Plan: ") + source_default.white(result.tier));
|
|
43645
|
+
console.log("");
|
|
43646
|
+
console.log(source_default.green(" You can now use AI-powered scans:"));
|
|
43647
|
+
console.log(source_default.cyan(" oculum scan . --depth validated"));
|
|
43648
|
+
console.log("");
|
|
43623
43649
|
return;
|
|
43624
43650
|
}
|
|
43625
43651
|
try {
|
|
43652
|
+
console.log("");
|
|
43653
|
+
console.log(source_default.bold(" \u{1F510} Oculum Login"));
|
|
43654
|
+
console.log(source_default.dim(" " + "\u2500".repeat(38)));
|
|
43655
|
+
console.log("");
|
|
43626
43656
|
spinner.start("Initiating login...");
|
|
43627
43657
|
const { authUrl, deviceCode } = await initiateLogin();
|
|
43628
43658
|
spinner.stop();
|
|
43629
|
-
console.log("
|
|
43630
|
-
console.log(
|
|
43631
|
-
|
|
43632
|
-
console.log(
|
|
43633
|
-
|
|
43659
|
+
console.log(source_default.white(" Open this URL in your browser to login:"));
|
|
43660
|
+
console.log("");
|
|
43661
|
+
console.log(source_default.cyan.bold(` ${authUrl}`));
|
|
43662
|
+
console.log("");
|
|
43663
|
+
console.log(source_default.dim(" Waiting for browser authorization..."));
|
|
43664
|
+
console.log(source_default.dim(" (This will timeout after 5 minutes)"));
|
|
43665
|
+
console.log("");
|
|
43666
|
+
spinner.start("Waiting for authorization...");
|
|
43634
43667
|
const maxAttempts = 60;
|
|
43635
43668
|
for (let i = 0; i < maxAttempts; i++) {
|
|
43636
43669
|
await new Promise((resolve7) => setTimeout(resolve7, 5e3));
|
|
@@ -43638,15 +43671,35 @@ async function login(options) {
|
|
|
43638
43671
|
if (result.complete) {
|
|
43639
43672
|
setAuthCredentials(result.apiKey, result.email, result.tier);
|
|
43640
43673
|
spinner.succeed("Logged in successfully!");
|
|
43641
|
-
console.log(
|
|
43642
|
-
console.log(source_default.dim(
|
|
43674
|
+
console.log("");
|
|
43675
|
+
console.log(source_default.dim(" Email: ") + source_default.white(result.email));
|
|
43676
|
+
console.log(source_default.dim(" Plan: ") + source_default.white(result.tier));
|
|
43677
|
+
console.log("");
|
|
43678
|
+
console.log(source_default.green(" You can now use AI-powered scans:"));
|
|
43679
|
+
console.log(source_default.cyan(" oculum scan . --depth validated"));
|
|
43680
|
+
console.log("");
|
|
43643
43681
|
return;
|
|
43644
43682
|
}
|
|
43683
|
+
if (result.expired) {
|
|
43684
|
+
spinner.fail("Login session expired");
|
|
43685
|
+
console.log("");
|
|
43686
|
+
console.log(source_default.dim(" Please try again: ") + source_default.cyan("oculum login"));
|
|
43687
|
+
console.log("");
|
|
43688
|
+
process.exit(1);
|
|
43689
|
+
}
|
|
43645
43690
|
}
|
|
43646
|
-
spinner.fail("Login timed out
|
|
43691
|
+
spinner.fail("Login timed out");
|
|
43692
|
+
console.log("");
|
|
43693
|
+
console.log(source_default.dim(" The login session expired. Please try again:"));
|
|
43694
|
+
console.log(source_default.cyan(" oculum login"));
|
|
43695
|
+
console.log("");
|
|
43647
43696
|
process.exit(1);
|
|
43648
43697
|
} catch (err) {
|
|
43649
|
-
spinner.fail(
|
|
43698
|
+
spinner.fail("Login failed");
|
|
43699
|
+
console.log("");
|
|
43700
|
+
console.log(source_default.dim(" Error: ") + source_default.red(String(err)));
|
|
43701
|
+
console.log(source_default.dim(" Please check your internet connection and try again."));
|
|
43702
|
+
console.log("");
|
|
43650
43703
|
process.exit(1);
|
|
43651
43704
|
}
|
|
43652
43705
|
}
|
|
@@ -43660,40 +43713,67 @@ function logout() {
|
|
|
43660
43713
|
}
|
|
43661
43714
|
async function status() {
|
|
43662
43715
|
const config = getConfig();
|
|
43663
|
-
console.log("
|
|
43664
|
-
console.log(source_default.
|
|
43716
|
+
console.log("");
|
|
43717
|
+
console.log(source_default.bold(" \u{1F510} Oculum Authentication Status"));
|
|
43718
|
+
console.log(source_default.dim(" " + "\u2500".repeat(38)));
|
|
43665
43719
|
if (!isAuthenticated()) {
|
|
43666
|
-
console.log(
|
|
43667
|
-
console.log(source_default.
|
|
43668
|
-
console.log(
|
|
43669
|
-
console.log(source_default.dim("
|
|
43720
|
+
console.log("");
|
|
43721
|
+
console.log(source_default.yellow(" Status: ") + source_default.white("Not logged in"));
|
|
43722
|
+
console.log("");
|
|
43723
|
+
console.log(source_default.dim(" You can use Oculum without logging in for free local scans."));
|
|
43724
|
+
console.log(source_default.dim(" Login to unlock AI-powered validation and deep analysis."));
|
|
43725
|
+
console.log("");
|
|
43726
|
+
console.log(source_default.bold(" Quick Start:"));
|
|
43727
|
+
console.log(source_default.cyan(" oculum scan .") + source_default.dim(" Free pattern-based scan"));
|
|
43728
|
+
console.log(source_default.cyan(" oculum login") + source_default.dim(" Authenticate for Pro features"));
|
|
43729
|
+
console.log("");
|
|
43670
43730
|
return;
|
|
43671
43731
|
}
|
|
43672
|
-
const spinner = ora("Verifying credentials...").start();
|
|
43732
|
+
const spinner = ora(" Verifying credentials...").start();
|
|
43673
43733
|
const result = await verifyApiKey(config.apiKey);
|
|
43674
43734
|
if (!result.valid) {
|
|
43675
|
-
spinner.fail("Stored credentials are invalid or expired
|
|
43676
|
-
console.log(
|
|
43735
|
+
spinner.fail(" Stored credentials are invalid or expired");
|
|
43736
|
+
console.log("");
|
|
43737
|
+
console.log(source_default.dim(" Your session may have expired. Please login again:"));
|
|
43738
|
+
console.log(source_default.cyan(" oculum login"));
|
|
43739
|
+
console.log("");
|
|
43677
43740
|
return;
|
|
43678
43741
|
}
|
|
43679
|
-
spinner.succeed("Authenticated");
|
|
43680
|
-
console.log(
|
|
43681
|
-
|
|
43682
|
-
console.log("\n" + source_default.bold("Available Scan Depths:"));
|
|
43742
|
+
spinner.succeed(" Authenticated");
|
|
43743
|
+
console.log("");
|
|
43744
|
+
const email = result.email || config.email || "unknown";
|
|
43683
43745
|
const tier = result.tier || config.tier || "free";
|
|
43684
|
-
|
|
43746
|
+
const tierBadge = tier === "pro" ? source_default.bgBlue.white(" PRO ") : tier === "enterprise" ? source_default.bgMagenta.white(" ENTERPRISE ") : source_default.bgGray.white(" FREE ");
|
|
43747
|
+
console.log(source_default.dim(" Email: ") + source_default.white(email));
|
|
43748
|
+
console.log(source_default.dim(" Plan: ") + tierBadge);
|
|
43749
|
+
console.log("");
|
|
43750
|
+
console.log(source_default.bold(" Available Scan Depths:"));
|
|
43751
|
+
console.log("");
|
|
43752
|
+
console.log(source_default.green(" \u2713 ") + source_default.white("cheap") + source_default.dim(" Fast pattern matching (always free)"));
|
|
43685
43753
|
if (tier === "pro" || tier === "enterprise") {
|
|
43686
|
-
console.log(source_default.green(" validated
|
|
43687
|
-
console.log(source_default.
|
|
43754
|
+
console.log(source_default.green(" \u2713 ") + source_default.white("validated") + source_default.dim(" AI validation (~70% fewer false positives)"));
|
|
43755
|
+
console.log(source_default.dim(" \u{1F512} ") + source_default.white("deep") + source_default.dim(" Multi-agent analysis (coming soon)"));
|
|
43688
43756
|
} else {
|
|
43689
|
-
console.log(source_default.dim(" validated
|
|
43690
|
-
console.log(source_default.dim(" deep
|
|
43757
|
+
console.log(source_default.dim(" \u{1F512} ") + source_default.white("validated") + source_default.dim(" AI validation (requires Pro)"));
|
|
43758
|
+
console.log(source_default.dim(" \u{1F512} ") + source_default.white("deep") + source_default.dim(" Multi-agent analysis (requires Pro)"));
|
|
43691
43759
|
}
|
|
43692
43760
|
console.log("");
|
|
43761
|
+
console.log(source_default.dim(" Manage subscription: ") + source_default.cyan("https://oculum.dev/billing"));
|
|
43762
|
+
console.log("");
|
|
43693
43763
|
}
|
|
43694
43764
|
function upgrade() {
|
|
43695
|
-
console.log(
|
|
43696
|
-
console.log(source_default.bold("
|
|
43765
|
+
console.log("");
|
|
43766
|
+
console.log(source_default.bold(" \u{1F680} Upgrade to Oculum Pro"));
|
|
43767
|
+
console.log(source_default.dim(" " + "\u2500".repeat(38)));
|
|
43768
|
+
console.log("");
|
|
43769
|
+
console.log(source_default.white(" Pro features include:"));
|
|
43770
|
+
console.log(source_default.green(" \u2713 ") + source_default.white("AI-validated scans") + source_default.dim(" - ~70% fewer false positives"));
|
|
43771
|
+
console.log(source_default.green(" \u2713 ") + source_default.white("Higher scan limits") + source_default.dim(" - More scans per month"));
|
|
43772
|
+
console.log(source_default.green(" \u2713 ") + source_default.white("Priority support") + source_default.dim(" - Get help when you need it"));
|
|
43773
|
+
console.log(source_default.green(" \u2713 ") + source_default.white("GitHub Action") + source_default.dim(" - Automated PR scanning"));
|
|
43774
|
+
console.log("");
|
|
43775
|
+
console.log(source_default.dim(" Visit: ") + source_default.cyan.underline("https://oculum.dev/pricing"));
|
|
43776
|
+
console.log("");
|
|
43697
43777
|
}
|
|
43698
43778
|
var authCommand = new Command("auth").description("Manage authentication");
|
|
43699
43779
|
authCommand.command("login").description("Log in to Oculum").option("-k, --api-key <key>", "API key (skip browser auth)").action(login);
|
|
@@ -45454,18 +45534,23 @@ async function watch2(targetPath, options) {
|
|
|
45454
45534
|
const config = getConfig();
|
|
45455
45535
|
if ((options.depth === "validated" || options.depth === "deep") && !isAuthenticated()) {
|
|
45456
45536
|
if (!options.quiet) {
|
|
45457
|
-
console.log(
|
|
45458
|
-
console.log(source_default.
|
|
45537
|
+
console.log("");
|
|
45538
|
+
console.log(source_default.yellow("\u26A0\uFE0F AI-powered scans require authentication"));
|
|
45539
|
+
console.log(source_default.dim(" Using free local scan instead. Run `oculum login` to unlock Pro features."));
|
|
45459
45540
|
}
|
|
45460
45541
|
options.depth = "cheap";
|
|
45461
45542
|
}
|
|
45462
45543
|
if (!options.quiet) {
|
|
45463
|
-
console.log(
|
|
45464
|
-
console.log(source_default.
|
|
45465
|
-
console.log(source_default.dim(
|
|
45466
|
-
console.log(
|
|
45467
|
-
console.log(source_default.dim(
|
|
45468
|
-
console.log(source_default.dim("
|
|
45544
|
+
console.log("");
|
|
45545
|
+
console.log(source_default.bold(" \u{1F441}\uFE0F Oculum Watch Mode"));
|
|
45546
|
+
console.log(source_default.dim(" " + "\u2500".repeat(38)));
|
|
45547
|
+
console.log("");
|
|
45548
|
+
console.log(source_default.dim(" Watching: ") + source_default.white(absolutePath));
|
|
45549
|
+
console.log(source_default.dim(" Depth: ") + source_default.white(options.depth === "cheap" ? "Quick (pattern matching)" : options.depth));
|
|
45550
|
+
console.log(source_default.dim(" Debounce: ") + source_default.white(`${options.debounce}ms`));
|
|
45551
|
+
console.log("");
|
|
45552
|
+
console.log(source_default.dim(" Press ") + source_default.white("Ctrl+C") + source_default.dim(" to stop watching"));
|
|
45553
|
+
console.log("");
|
|
45469
45554
|
}
|
|
45470
45555
|
const changedFiles = /* @__PURE__ */ new Set();
|
|
45471
45556
|
let isScanning = false;
|
|
@@ -45478,8 +45563,9 @@ async function watch2(targetPath, options) {
|
|
|
45478
45563
|
console.clear();
|
|
45479
45564
|
}
|
|
45480
45565
|
if (!options.quiet) {
|
|
45481
|
-
|
|
45482
|
-
|
|
45566
|
+
const fileText = filesToScan.length === 1 ? "file" : "files";
|
|
45567
|
+
console.log("");
|
|
45568
|
+
console.log(source_default.cyan(` \u27F3 [${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] Scanning ${filesToScan.length} changed ${fileText}...`));
|
|
45483
45569
|
}
|
|
45484
45570
|
const scanFiles = [];
|
|
45485
45571
|
for (const filePath of filesToScan) {
|
|
@@ -45509,9 +45595,12 @@ async function watch2(targetPath, options) {
|
|
|
45509
45595
|
);
|
|
45510
45596
|
if (result.vulnerabilities.length === 0) {
|
|
45511
45597
|
if (!options.quiet) {
|
|
45512
|
-
console.log(source_default.green("No issues found
|
|
45598
|
+
console.log(source_default.green(" \u2713 No issues found"));
|
|
45513
45599
|
}
|
|
45514
45600
|
} else {
|
|
45601
|
+
const issueCount = result.vulnerabilities.length;
|
|
45602
|
+
const issueText = issueCount === 1 ? "issue" : "issues";
|
|
45603
|
+
console.log(source_default.yellow(` \u26A0 Found ${issueCount} ${issueText}:`));
|
|
45515
45604
|
console.log((0, import_formatters2.formatTerminalOutput)(result, {
|
|
45516
45605
|
maxFindingsPerGroup: 5
|
|
45517
45606
|
}));
|
package/package.json
CHANGED