@veestack-tools/cli 3.0.3 → 3.0.4
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 +148 -35
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5,11 +5,75 @@ import { Command } from "commander";
|
|
|
5
5
|
|
|
6
6
|
// src/commands/scan.ts
|
|
7
7
|
import { glob } from "glob";
|
|
8
|
-
import { readFileSync, statSync } from "fs";
|
|
9
|
-
import { join, resolve } from "path";
|
|
8
|
+
import { readFileSync as readFileSync2, statSync } from "fs";
|
|
9
|
+
import { join as join2, resolve } from "path";
|
|
10
10
|
import crypto from "crypto";
|
|
11
11
|
import ora from "ora";
|
|
12
|
+
import chalk2 from "chalk";
|
|
13
|
+
|
|
14
|
+
// src/utils/auth.ts
|
|
15
|
+
import { readFileSync, existsSync } from "fs";
|
|
16
|
+
import { join } from "path";
|
|
17
|
+
import { homedir } from "os";
|
|
18
|
+
import chalk from "chalk";
|
|
19
|
+
var CONFIG_DIR = join(homedir(), ".veestack");
|
|
20
|
+
var CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
21
|
+
function getAuthConfig() {
|
|
22
|
+
try {
|
|
23
|
+
if (!existsSync(CONFIG_FILE)) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const config = JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
|
|
27
|
+
return config;
|
|
28
|
+
} catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function isAuthenticated() {
|
|
33
|
+
const config = getAuthConfig();
|
|
34
|
+
return config !== null && config.apiKey !== void 0 && config.apiKey.length > 0;
|
|
35
|
+
}
|
|
36
|
+
function getSubscriptionTier() {
|
|
37
|
+
const config = getAuthConfig();
|
|
38
|
+
if (!config || !config.subscription) {
|
|
39
|
+
return "free";
|
|
40
|
+
}
|
|
41
|
+
return config.subscription.tier;
|
|
42
|
+
}
|
|
43
|
+
function isSubscriptionActive() {
|
|
44
|
+
const config = getAuthConfig();
|
|
45
|
+
if (!config || !config.subscription) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
if (config.subscription.tier === "free") {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
if (config.subscription.expiresAt) {
|
|
52
|
+
const expiresAt = new Date(config.subscription.expiresAt);
|
|
53
|
+
const now = /* @__PURE__ */ new Date();
|
|
54
|
+
return expiresAt > now && config.subscription.isActive;
|
|
55
|
+
}
|
|
56
|
+
return config.subscription.isActive;
|
|
57
|
+
}
|
|
58
|
+
function requireAuth() {
|
|
59
|
+
const config = getAuthConfig();
|
|
60
|
+
if (!config || !config.apiKey) {
|
|
61
|
+
console.log(chalk.red("\u274C Authentication required"));
|
|
62
|
+
console.log(chalk.yellow("\nPlease login first:"));
|
|
63
|
+
console.log(chalk.cyan(" veestack login"));
|
|
64
|
+
console.log(chalk.gray("\nOr get an API key from: https://veestack.tools/dashboard"));
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
return config;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// src/commands/scan.ts
|
|
12
71
|
async function scanCommand(options) {
|
|
72
|
+
const authConfig = requireAuth();
|
|
73
|
+
const tier = getSubscriptionTier();
|
|
74
|
+
console.log(chalk2.blue("\n\u{1F510} Authentication verified"));
|
|
75
|
+
console.log(chalk2.gray(`Subscription tier: ${tier === "pro" ? "Pro" : "Free"}`));
|
|
76
|
+
console.log(chalk2.gray("\u2500".repeat(40) + "\n"));
|
|
13
77
|
const spinner = ora({
|
|
14
78
|
text: "Scanning project...",
|
|
15
79
|
spinner: "dots",
|
|
@@ -69,7 +133,7 @@ async function scanFiles(projectPath) {
|
|
|
69
133
|
absolute: false
|
|
70
134
|
});
|
|
71
135
|
for (const filePath of filePaths) {
|
|
72
|
-
const fullPath =
|
|
136
|
+
const fullPath = join2(projectPath, filePath);
|
|
73
137
|
const stats = statSync(fullPath);
|
|
74
138
|
if (stats.isFile()) {
|
|
75
139
|
const fileNode = {
|
|
@@ -92,8 +156,8 @@ async function scanFiles(projectPath) {
|
|
|
92
156
|
async function scanDependencies(projectPath) {
|
|
93
157
|
const dependencies = [];
|
|
94
158
|
try {
|
|
95
|
-
const packageJsonPath =
|
|
96
|
-
const packageJson = JSON.parse(
|
|
159
|
+
const packageJsonPath = join2(projectPath, "package.json");
|
|
160
|
+
const packageJson = JSON.parse(readFileSync2(packageJsonPath, "utf-8"));
|
|
97
161
|
const processDeps = (deps, category) => {
|
|
98
162
|
for (const [name, version] of Object.entries(deps)) {
|
|
99
163
|
const versionMatch = version.match(/^(\d+)\.(\d+)/);
|
|
@@ -189,7 +253,7 @@ function hashString(str) {
|
|
|
189
253
|
|
|
190
254
|
// src/commands/upload.ts
|
|
191
255
|
import ora2 from "ora";
|
|
192
|
-
import
|
|
256
|
+
import chalk3 from "chalk";
|
|
193
257
|
import readline from "readline";
|
|
194
258
|
var supabaseUrl = process.env.SUPABASE_URL || "https://qhonrrojtqklvlkvfswb.supabase.co";
|
|
195
259
|
var supabaseAnonKey = process.env.SUPABASE_ANON_KEY || "";
|
|
@@ -245,17 +309,17 @@ function askQuestion(question) {
|
|
|
245
309
|
});
|
|
246
310
|
}
|
|
247
311
|
async function selectOrCreateProject() {
|
|
248
|
-
console.log(
|
|
312
|
+
console.log(chalk3.blue("\n\u{1F4C1} Loading projects...\n"));
|
|
249
313
|
const projects = await fetchProjects();
|
|
250
314
|
if (projects.length === 0) {
|
|
251
|
-
console.log(
|
|
315
|
+
console.log(chalk3.yellow("No projects found.\n"));
|
|
252
316
|
const answer2 = await askQuestion("Do you want to create a new project? (y/n): ");
|
|
253
317
|
if (answer2.toLowerCase() === "y") {
|
|
254
318
|
const name = await askQuestion("Enter project name: ");
|
|
255
319
|
if (name.trim()) {
|
|
256
320
|
const projectId = await createProject(name.trim());
|
|
257
321
|
if (projectId) {
|
|
258
|
-
console.log(
|
|
322
|
+
console.log(chalk3.green(`
|
|
259
323
|
\u2705 Project "${name}" created successfully!`));
|
|
260
324
|
return projectId;
|
|
261
325
|
}
|
|
@@ -263,12 +327,12 @@ async function selectOrCreateProject() {
|
|
|
263
327
|
}
|
|
264
328
|
return null;
|
|
265
329
|
}
|
|
266
|
-
console.log(
|
|
267
|
-
console.log(
|
|
330
|
+
console.log(chalk3.white("Select a project:\n"));
|
|
331
|
+
console.log(chalk3.gray("0. [+] Create new project\n"));
|
|
268
332
|
projects.forEach((p, i) => {
|
|
269
|
-
console.log(
|
|
270
|
-
console.log(
|
|
271
|
-
console.log(
|
|
333
|
+
console.log(chalk3.white(`${i + 1}. ${p.name}`));
|
|
334
|
+
console.log(chalk3.gray(` ID: ${p.id}`));
|
|
335
|
+
console.log(chalk3.gray(` Created: ${new Date(p.created_at).toLocaleDateString()}
|
|
272
336
|
`));
|
|
273
337
|
});
|
|
274
338
|
const answer = await askQuestion("Enter number (0-" + projects.length + "): ");
|
|
@@ -278,7 +342,7 @@ async function selectOrCreateProject() {
|
|
|
278
342
|
if (name.trim()) {
|
|
279
343
|
const projectId = await createProject(name.trim());
|
|
280
344
|
if (projectId) {
|
|
281
|
-
console.log(
|
|
345
|
+
console.log(chalk3.green(`
|
|
282
346
|
\u2705 Project "${name}" created successfully!`));
|
|
283
347
|
return projectId;
|
|
284
348
|
}
|
|
@@ -286,15 +350,20 @@ async function selectOrCreateProject() {
|
|
|
286
350
|
return null;
|
|
287
351
|
} else if (choice > 0 && choice <= projects.length) {
|
|
288
352
|
const selected = projects[choice - 1];
|
|
289
|
-
console.log(
|
|
353
|
+
console.log(chalk3.green(`
|
|
290
354
|
\u2705 Selected project: "${selected.name}"`));
|
|
291
355
|
return selected.id;
|
|
292
356
|
} else {
|
|
293
|
-
console.log(
|
|
357
|
+
console.log(chalk3.red("\n\u274C Invalid selection"));
|
|
294
358
|
return null;
|
|
295
359
|
}
|
|
296
360
|
}
|
|
297
361
|
async function uploadCommand(options) {
|
|
362
|
+
const authConfig = requireAuth();
|
|
363
|
+
const tier = getSubscriptionTier();
|
|
364
|
+
console.log(chalk3.blue("\n\u{1F510} Authentication verified"));
|
|
365
|
+
console.log(chalk3.gray(`Subscription tier: ${tier === "pro" ? "Pro" : "Free"}`));
|
|
366
|
+
console.log(chalk3.gray("\u2500".repeat(40) + "\n"));
|
|
298
367
|
const spinner = ora2("Preparing upload...").start();
|
|
299
368
|
try {
|
|
300
369
|
const fs = await import("fs/promises");
|
|
@@ -305,7 +374,7 @@ async function uploadCommand(options) {
|
|
|
305
374
|
spinner.stop();
|
|
306
375
|
const selectedProjectId = await selectOrCreateProject();
|
|
307
376
|
if (!selectedProjectId) {
|
|
308
|
-
console.log(
|
|
377
|
+
console.log(chalk3.red("\n\u274C No project selected. Exiting."));
|
|
309
378
|
process.exit(1);
|
|
310
379
|
}
|
|
311
380
|
projectId = selectedProjectId;
|
|
@@ -330,7 +399,7 @@ async function uploadCommand(options) {
|
|
|
330
399
|
if (!snapshotRes.ok) {
|
|
331
400
|
const error = await snapshotRes.json();
|
|
332
401
|
spinner.fail("Failed to create snapshot");
|
|
333
|
-
console.error(
|
|
402
|
+
console.error(chalk3.red(`Error: ${error.message || error.details || "Unknown error"}`));
|
|
334
403
|
process.exit(1);
|
|
335
404
|
}
|
|
336
405
|
const snapshotData2 = await snapshotRes.json();
|
|
@@ -343,7 +412,7 @@ async function uploadCommand(options) {
|
|
|
343
412
|
const result = await engine.analyze(snapshot);
|
|
344
413
|
if (!result.success) {
|
|
345
414
|
spinner.fail("Analysis failed");
|
|
346
|
-
console.error(
|
|
415
|
+
console.error(chalk3.red("Error:", result.error));
|
|
347
416
|
process.exit(1);
|
|
348
417
|
}
|
|
349
418
|
spinner.text = "Creating report...";
|
|
@@ -370,21 +439,21 @@ async function uploadCommand(options) {
|
|
|
370
439
|
if (!reportRes.ok) {
|
|
371
440
|
const error = await reportRes.json();
|
|
372
441
|
spinner.fail("Failed to create report");
|
|
373
|
-
console.error(
|
|
442
|
+
console.error(chalk3.red(`Error: ${error.message || error.details || "Unknown error"}`));
|
|
374
443
|
process.exit(1);
|
|
375
444
|
}
|
|
376
445
|
const reportData = await reportRes.json();
|
|
377
446
|
const reportId = reportData[0]?.id || reportData[0]?.id;
|
|
378
447
|
spinner.succeed("Analysis complete");
|
|
379
|
-
console.log(
|
|
380
|
-
console.log(
|
|
381
|
-
console.log(
|
|
382
|
-
console.log(
|
|
383
|
-
console.log(
|
|
384
|
-
console.log(
|
|
385
|
-
console.log(
|
|
386
|
-
console.log(
|
|
387
|
-
console.log(
|
|
448
|
+
console.log(chalk3.green("\n\u2705 Report generated and saved to VeeStack"));
|
|
449
|
+
console.log(chalk3.gray("Project ID:"), projectId);
|
|
450
|
+
console.log(chalk3.gray("Snapshot ID:"), snapshotId);
|
|
451
|
+
console.log(chalk3.gray("Report ID:"), reportId);
|
|
452
|
+
console.log(chalk3.bold("\n\u{1F4CA} Score:"), result.report.total_score);
|
|
453
|
+
console.log(chalk3.gray("Severity:"), result.report.severity_band);
|
|
454
|
+
console.log(chalk3.gray("Findings:"), result.report.summary.total_findings);
|
|
455
|
+
console.log(chalk3.blue("\n\u{1F517} View your report at:"));
|
|
456
|
+
console.log(chalk3.underline(`http://localhost:3001/reports/${reportId}?project=${projectId}`));
|
|
388
457
|
} catch (error) {
|
|
389
458
|
spinner.fail("Upload failed");
|
|
390
459
|
console.error(error);
|
|
@@ -394,8 +463,8 @@ async function uploadCommand(options) {
|
|
|
394
463
|
|
|
395
464
|
// src/commands/login.ts
|
|
396
465
|
import { writeFileSync } from "fs";
|
|
397
|
-
import { join as
|
|
398
|
-
import { homedir } from "os";
|
|
466
|
+
import { join as join3 } from "path";
|
|
467
|
+
import { homedir as homedir2 } from "os";
|
|
399
468
|
import ora3 from "ora";
|
|
400
469
|
import prompts from "prompts";
|
|
401
470
|
async function loginCommand(options) {
|
|
@@ -421,8 +490,8 @@ async function loginCommand(options) {
|
|
|
421
490
|
}
|
|
422
491
|
spinner.start("Validating API key...");
|
|
423
492
|
await new Promise((resolve2) => setTimeout(resolve2, 1e3));
|
|
424
|
-
const configDir =
|
|
425
|
-
const configFile =
|
|
493
|
+
const configDir = join3(homedir2(), ".veestack");
|
|
494
|
+
const configFile = join3(configDir, "config.json");
|
|
426
495
|
try {
|
|
427
496
|
const fs = await import("fs/promises");
|
|
428
497
|
await fs.mkdir(configDir, { recursive: true });
|
|
@@ -441,10 +510,54 @@ async function loginCommand(options) {
|
|
|
441
510
|
}
|
|
442
511
|
}
|
|
443
512
|
|
|
513
|
+
// src/commands/status.ts
|
|
514
|
+
import chalk4 from "chalk";
|
|
515
|
+
async function statusCommand() {
|
|
516
|
+
console.log(chalk4.blue("\n\u{1F4CA} VeeStack CLI Status\n"));
|
|
517
|
+
console.log(chalk4.gray("\u2500".repeat(40)));
|
|
518
|
+
const authenticated = isAuthenticated();
|
|
519
|
+
const tier = getSubscriptionTier();
|
|
520
|
+
const active = isSubscriptionActive();
|
|
521
|
+
if (!authenticated) {
|
|
522
|
+
console.log(chalk4.red("\n\u274C Not authenticated"));
|
|
523
|
+
console.log(chalk4.yellow("\nTo use VeeStack CLI, you need to login:"));
|
|
524
|
+
console.log(chalk4.cyan(" veestack login"));
|
|
525
|
+
console.log(chalk4.gray("\nGet your API key from: https://veestack.tools/dashboard"));
|
|
526
|
+
console.log(chalk4.gray("\u2500".repeat(40) + "\n"));
|
|
527
|
+
process.exit(1);
|
|
528
|
+
}
|
|
529
|
+
console.log(chalk4.green("\n\u2705 Authenticated"));
|
|
530
|
+
if (tier === "pro") {
|
|
531
|
+
console.log(chalk4.cyan("\u{1F48E} Subscription: Pro"));
|
|
532
|
+
if (!active) {
|
|
533
|
+
console.log(chalk4.red("\u26A0\uFE0F Status: Expired"));
|
|
534
|
+
console.log(chalk4.yellow("\nPlease renew your subscription:"));
|
|
535
|
+
console.log(chalk4.cyan(" https://veestack.tools/pricing"));
|
|
536
|
+
} else {
|
|
537
|
+
console.log(chalk4.green("\u2713 Status: Active"));
|
|
538
|
+
}
|
|
539
|
+
} else {
|
|
540
|
+
console.log(chalk4.blue("\u{1F4E6} Subscription: Free"));
|
|
541
|
+
console.log(chalk4.green("\u2713 Status: Active"));
|
|
542
|
+
console.log(chalk4.gray("\nUpgrade to Pro for advanced features:"));
|
|
543
|
+
console.log(chalk4.cyan(" https://veestack.tools/pricing"));
|
|
544
|
+
}
|
|
545
|
+
console.log(chalk4.gray("\u2500".repeat(40) + "\n"));
|
|
546
|
+
console.log(chalk4.white("Available commands:"));
|
|
547
|
+
console.log(chalk4.cyan(" veestack scan ") + chalk4.gray(" - Scan project (Free)"));
|
|
548
|
+
console.log(chalk4.cyan(" veestack upload ") + chalk4.gray(" - Upload to cloud (Free)"));
|
|
549
|
+
console.log(chalk4.cyan(" veestack status ") + chalk4.gray(" - Check status (Free)"));
|
|
550
|
+
if (tier === "pro") {
|
|
551
|
+
console.log(chalk4.cyan(" veestack analyze ") + chalk4.gray(" - Advanced analysis (Pro)"));
|
|
552
|
+
}
|
|
553
|
+
console.log(chalk4.gray("\n\u2500".repeat(40) + "\n"));
|
|
554
|
+
}
|
|
555
|
+
|
|
444
556
|
// src/index.ts
|
|
445
557
|
var program = new Command();
|
|
446
|
-
program.name("veestack").description("VeeStack CLI - Technical Stack Visibility Tool").version("3.0.
|
|
558
|
+
program.name("veestack").description("VeeStack CLI - Technical Stack Visibility Tool").version("3.0.3");
|
|
447
559
|
program.command("scan").description("Scan a project directory and generate analysis").option("-p, --path <path>", "Path to project directory", ".").option("-o, --output <path>", "Output file path", "snapshot.json").option("--ci", "CI mode (no interactive prompts)").action(scanCommand);
|
|
448
560
|
program.command("upload").description("Upload a snapshot to VeeStack server").option("-f, --file <path>", "Snapshot file path", "snapshot.json").option("-p, --project-id <id>", "Project ID").action(uploadCommand);
|
|
449
561
|
program.command("login").description("Authenticate with VeeStack server").option("-k, --key <apiKey>", "API key").action(loginCommand);
|
|
562
|
+
program.command("status").description("Check authentication and subscription status").action(statusCommand);
|
|
450
563
|
program.parse();
|