infrawise 0.4.2 → 0.6.0
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/README.md +21 -48
- package/dist/adapters/aws.js +50 -64
- package/dist/adapters/dynamodb.js +17 -22
- package/dist/adapters/logs.js +12 -16
- package/dist/adapters/mongodb.js +10 -16
- package/dist/adapters/mysql.js +8 -17
- package/dist/adapters/postgres.js +9 -13
- package/dist/adapters/terraform.js +14 -56
- package/dist/analyzers/aws-services.js +7 -17
- package/dist/analyzers/dynamodb.js +6 -12
- package/dist/analyzers/index.js +13 -41
- package/dist/analyzers/mongodb.js +4 -9
- package/dist/analyzers/mysql.js +4 -9
- package/dist/analyzers/postgres.js +3 -9
- package/dist/analyzers/rds.js +5 -13
- package/dist/analyzers/terraform.js +1 -5
- package/dist/cli/commands/analyze.js +178 -215
- package/dist/cli/commands/auth.js +24 -30
- package/dist/cli/commands/dev.js +84 -43
- package/dist/cli/commands/doctor.js +35 -74
- package/dist/cli/commands/init.js +33 -72
- package/dist/cli/index.js +21 -28
- package/dist/cli/utils.js +40 -86
- package/dist/context/index.js +49 -85
- package/dist/core/cache.js +5 -43
- package/dist/core/config.js +43 -81
- package/dist/core/errors.js +7 -17
- package/dist/core/index.js +4 -22
- package/dist/core/logger.js +4 -10
- package/dist/graph/index.js +15 -32
- package/dist/server/index.js +236 -302
- package/dist/types.js +1 -2
- package/package.json +34 -31
|
@@ -1,66 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.runInit = runInit;
|
|
40
|
-
const fs = __importStar(require("fs"));
|
|
41
|
-
const path = __importStar(require("path"));
|
|
42
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
43
|
-
const inquirer_1 = __importDefault(require("inquirer"));
|
|
44
|
-
const core_1 = require("../../core");
|
|
45
|
-
const utils_1 = require("../utils");
|
|
46
|
-
async function runInit(options = {}) {
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import inquirer from 'inquirer';
|
|
5
|
+
import { generateDefaultConfig } from '../../core/index.js';
|
|
6
|
+
import { readAWSProfiles, detectAWSRegion, detectRepoType, log, printHeader } from '../utils.js';
|
|
7
|
+
export async function runInit(options = {}) {
|
|
47
8
|
const cwd = process.cwd();
|
|
48
9
|
const configPath = path.join(cwd, 'infrawise.yaml');
|
|
49
10
|
if (fs.existsSync(configPath) && !options.force) {
|
|
50
|
-
console.log(`\n ${
|
|
11
|
+
console.log(`\n ${chalk.yellow('⚠')} ${chalk.yellow('infrawise.yaml already exists.')} ${chalk.dim('Use --force to overwrite.')}\n`);
|
|
51
12
|
return;
|
|
52
13
|
}
|
|
53
|
-
|
|
54
|
-
const repoType =
|
|
14
|
+
printHeader('Initialize Infrawise');
|
|
15
|
+
const repoType = detectRepoType(cwd);
|
|
55
16
|
const repoName = path.basename(cwd);
|
|
56
|
-
const profiles =
|
|
57
|
-
const detectedRegion =
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
17
|
+
const profiles = readAWSProfiles();
|
|
18
|
+
const detectedRegion = detectAWSRegion();
|
|
19
|
+
log.success(`Repository detected`, repoName);
|
|
20
|
+
log.success(`Type`, repoType);
|
|
21
|
+
log.success(`AWS profiles found`, String(profiles.length));
|
|
61
22
|
console.log('');
|
|
62
23
|
// ── Core settings ──────────────────────────────────────────────────────────
|
|
63
|
-
const core = await
|
|
24
|
+
const core = await inquirer.prompt([
|
|
64
25
|
{
|
|
65
26
|
type: 'input',
|
|
66
27
|
name: 'project',
|
|
@@ -72,10 +33,10 @@ async function runInit(options = {}) {
|
|
|
72
33
|
name: 'awsProfile',
|
|
73
34
|
message: 'AWS profile:',
|
|
74
35
|
choices: [
|
|
75
|
-
new
|
|
36
|
+
new inquirer.Separator('── no profile ──'),
|
|
76
37
|
{ name: 'Environment variables (CI/CD, real AWS)', value: '__env__' },
|
|
77
38
|
{ name: 'LocalStack (local development)', value: '__localstack__' },
|
|
78
|
-
new
|
|
39
|
+
new inquirer.Separator('── named profiles ──'),
|
|
79
40
|
...profiles,
|
|
80
41
|
],
|
|
81
42
|
default: profiles[0],
|
|
@@ -95,9 +56,9 @@ async function runInit(options = {}) {
|
|
|
95
56
|
},
|
|
96
57
|
]);
|
|
97
58
|
// ── Databases ──────────────────────────────────────────────────────────────
|
|
98
|
-
console.log('\n ' +
|
|
99
|
-
console.log(
|
|
100
|
-
const databases = await
|
|
59
|
+
console.log('\n ' + chalk.bold('Databases'));
|
|
60
|
+
console.log(chalk.dim(' Self-hosted databases (PostgreSQL, MySQL, MongoDB).'));
|
|
61
|
+
const databases = await inquirer.prompt([
|
|
101
62
|
{
|
|
102
63
|
type: 'confirm',
|
|
103
64
|
name: 'pgEnabled',
|
|
@@ -139,9 +100,9 @@ async function runInit(options = {}) {
|
|
|
139
100
|
},
|
|
140
101
|
]);
|
|
141
102
|
// ── AWS services ───────────────────────────────────────────────────────────
|
|
142
|
-
console.log('\n ' +
|
|
143
|
-
console.log(
|
|
144
|
-
const services = await
|
|
103
|
+
console.log('\n ' + chalk.bold('AWS Services'));
|
|
104
|
+
console.log(chalk.dim(' Infrawise will introspect these services using the credentials configured above.'));
|
|
105
|
+
const services = await inquirer.prompt([
|
|
145
106
|
{
|
|
146
107
|
type: 'confirm',
|
|
147
108
|
name: 'dynamoEnabled',
|
|
@@ -153,7 +114,7 @@ async function runInit(options = {}) {
|
|
|
153
114
|
name: 'dynamoTables',
|
|
154
115
|
message: 'DynamoDB tables to include:',
|
|
155
116
|
default: '',
|
|
156
|
-
suffix:
|
|
117
|
+
suffix: chalk.dim(' (comma-separated, blank = all)'),
|
|
157
118
|
when: (a) => a.dynamoEnabled,
|
|
158
119
|
},
|
|
159
120
|
{
|
|
@@ -179,7 +140,7 @@ async function runInit(options = {}) {
|
|
|
179
140
|
name: 'ssmPaths',
|
|
180
141
|
message: 'SSM path prefixes to filter:',
|
|
181
142
|
default: '',
|
|
182
|
-
suffix:
|
|
143
|
+
suffix: chalk.dim(' (comma-separated, blank = all e.g. /myapp/prod)'),
|
|
183
144
|
when: (a) => a.ssmEnabled,
|
|
184
145
|
},
|
|
185
146
|
{
|
|
@@ -205,7 +166,7 @@ async function runInit(options = {}) {
|
|
|
205
166
|
name: 'logGroupPrefixes',
|
|
206
167
|
message: 'CloudWatch log group prefixes:',
|
|
207
168
|
default: '',
|
|
208
|
-
suffix:
|
|
169
|
+
suffix: chalk.dim(' (comma-separated, blank = all)'),
|
|
209
170
|
when: (a) => a.logsEnabled,
|
|
210
171
|
},
|
|
211
172
|
]);
|
|
@@ -223,7 +184,7 @@ async function runInit(options = {}) {
|
|
|
223
184
|
const isEnvVars = core.awsProfile === '__env__';
|
|
224
185
|
const resolvedProfile = isLocalStack || isEnvVars ? '' : core.awsProfile;
|
|
225
186
|
const resolvedEndpoint = isLocalStack ? (core.endpoint || 'http://localhost:4566') : undefined;
|
|
226
|
-
const configContent =
|
|
187
|
+
const configContent = generateDefaultConfig(core.project, {
|
|
227
188
|
aws: { profile: resolvedProfile, region: core.region, endpoint: resolvedEndpoint },
|
|
228
189
|
dynamodb: { enabled: services.dynamoEnabled, includeTables },
|
|
229
190
|
postgres: { enabled: databases.pgEnabled, connectionString: databases.pgConnectionString ?? '' },
|
|
@@ -242,11 +203,11 @@ async function runInit(options = {}) {
|
|
|
242
203
|
});
|
|
243
204
|
fs.writeFileSync(configPath, configContent, 'utf-8');
|
|
244
205
|
console.log('');
|
|
245
|
-
|
|
206
|
+
log.success(`Created ${chalk.bold('infrawise.yaml')}`);
|
|
246
207
|
console.log('');
|
|
247
|
-
console.log(
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
208
|
+
console.log(chalk.bold(' Next steps:'));
|
|
209
|
+
log.info(`Run ${chalk.cyan('infrawise doctor')} to validate your setup`);
|
|
210
|
+
log.info(`Run ${chalk.cyan('infrawise analyze')} to scan your infrastructure`);
|
|
211
|
+
log.info(`Run ${chalk.cyan('infrawise dev')} to start the MCP server`);
|
|
251
212
|
console.log('');
|
|
252
213
|
}
|
package/dist/cli/index.js
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const { version } = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(__dirname, '../../package.json'), 'utf8'));
|
|
14
|
-
const program = new commander_1.Command();
|
|
2
|
+
import { readFileSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { Command } from 'commander';
|
|
5
|
+
import { printBanner } from './utils.js';
|
|
6
|
+
import { runInit } from './commands/init.js';
|
|
7
|
+
import { runAuth } from './commands/auth.js';
|
|
8
|
+
import { runAnalyze } from './commands/analyze.js';
|
|
9
|
+
import { runDev } from './commands/dev.js';
|
|
10
|
+
import { runDoctor } from './commands/doctor.js';
|
|
11
|
+
const { version } = JSON.parse(readFileSync(join(import.meta.dirname, '../../package.json'), 'utf8'));
|
|
12
|
+
const program = new Command();
|
|
15
13
|
program
|
|
16
14
|
.name('infrawise')
|
|
17
15
|
.description('CLI-first infrastructure intelligence platform — analyze your databases, AWS services, and IaC')
|
|
@@ -21,15 +19,15 @@ program
|
|
|
21
19
|
.description('Detect AWS profile/region, discover DynamoDB tables, and generate infrawise.yaml')
|
|
22
20
|
.option('--force', 'Overwrite existing infrawise.yaml')
|
|
23
21
|
.action(async (options) => {
|
|
24
|
-
|
|
25
|
-
await
|
|
22
|
+
printBanner();
|
|
23
|
+
await runInit({ force: options.force });
|
|
26
24
|
});
|
|
27
25
|
program
|
|
28
26
|
.command('auth')
|
|
29
27
|
.description('Validate and select AWS profile from ~/.aws/credentials')
|
|
30
28
|
.action(async () => {
|
|
31
|
-
|
|
32
|
-
await
|
|
29
|
+
printBanner();
|
|
30
|
+
await runAuth();
|
|
33
31
|
});
|
|
34
32
|
program
|
|
35
33
|
.command('analyze')
|
|
@@ -37,17 +35,12 @@ program
|
|
|
37
35
|
.option('-c, --config <path>', 'Path to infrawise.yaml', 'infrawise.yaml')
|
|
38
36
|
.option('-r, --repo <path>', 'Path to repository to scan', process.cwd())
|
|
39
37
|
.option('--no-cache', 'Skip reading/writing the cache')
|
|
40
|
-
.option('--ci', 'CI mode: GitHub annotations output, job summary, exit code on findings')
|
|
41
|
-
.option('--fail-on <severity>', 'Exit 1 if findings at or above this severity (high|medium|low|never)', 'high')
|
|
42
38
|
.action(async (options) => {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
await (0, analyze_1.runAnalyze)({
|
|
39
|
+
printBanner();
|
|
40
|
+
await runAnalyze({
|
|
46
41
|
config: options.config !== 'infrawise.yaml' ? options.config : undefined,
|
|
47
42
|
repo: options.repo,
|
|
48
43
|
noCache: !options.cache,
|
|
49
|
-
ci: options.ci ?? false,
|
|
50
|
-
failOn: options.failOn ?? 'high',
|
|
51
44
|
});
|
|
52
45
|
});
|
|
53
46
|
program
|
|
@@ -56,8 +49,8 @@ program
|
|
|
56
49
|
.option('-c, --config <path>', 'Path to infrawise.yaml', 'infrawise.yaml')
|
|
57
50
|
.option('-p, --port <number>', 'Port to listen on', '3000')
|
|
58
51
|
.action(async (options) => {
|
|
59
|
-
|
|
60
|
-
await
|
|
52
|
+
printBanner();
|
|
53
|
+
await runDev({
|
|
61
54
|
config: options.config !== 'infrawise.yaml' ? options.config : undefined,
|
|
62
55
|
port: parseInt(options.port, 10),
|
|
63
56
|
});
|
|
@@ -67,8 +60,8 @@ program
|
|
|
67
60
|
.description('Validate AWS access, postgres connectivity, config, and repo scan')
|
|
68
61
|
.option('-c, --config <path>', 'Path to infrawise.yaml', 'infrawise.yaml')
|
|
69
62
|
.action(async (options) => {
|
|
70
|
-
|
|
71
|
-
await
|
|
63
|
+
printBanner();
|
|
64
|
+
await runDoctor({
|
|
72
65
|
config: options.config !== 'infrawise.yaml' ? options.config : undefined,
|
|
73
66
|
});
|
|
74
67
|
});
|
package/dist/cli/utils.js
CHANGED
|
@@ -1,55 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.log = void 0;
|
|
40
|
-
exports.readAWSProfiles = readAWSProfiles;
|
|
41
|
-
exports.detectAWSRegion = detectAWSRegion;
|
|
42
|
-
exports.detectRepoType = detectRepoType;
|
|
43
|
-
exports.printBanner = printBanner;
|
|
44
|
-
exports.printHeader = printHeader;
|
|
45
|
-
exports.printFinding = printFinding;
|
|
46
|
-
exports.printSummaryBox = printSummaryBox;
|
|
47
|
-
const fs = __importStar(require("fs"));
|
|
48
|
-
const path = __importStar(require("path"));
|
|
49
|
-
const os = __importStar(require("os"));
|
|
50
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
import chalk from 'chalk';
|
|
51
5
|
// ─── AWS helpers ─────────────────────────────────────────────────────────────
|
|
52
|
-
function readAWSProfiles() {
|
|
6
|
+
export function readAWSProfiles() {
|
|
53
7
|
const credentialsPath = path.join(os.homedir(), '.aws', 'credentials');
|
|
54
8
|
const configPath = path.join(os.homedir(), '.aws', 'config');
|
|
55
9
|
const profiles = new Set();
|
|
@@ -70,7 +24,7 @@ function readAWSProfiles() {
|
|
|
70
24
|
parseFile(configPath);
|
|
71
25
|
return profiles.size > 0 ? [...profiles] : ['default'];
|
|
72
26
|
}
|
|
73
|
-
function detectAWSRegion() {
|
|
27
|
+
export function detectAWSRegion() {
|
|
74
28
|
if (process.env.AWS_DEFAULT_REGION)
|
|
75
29
|
return process.env.AWS_DEFAULT_REGION;
|
|
76
30
|
if (process.env.AWS_REGION)
|
|
@@ -83,7 +37,7 @@ function detectAWSRegion() {
|
|
|
83
37
|
}
|
|
84
38
|
return 'us-east-1';
|
|
85
39
|
}
|
|
86
|
-
function detectRepoType(repoPath) {
|
|
40
|
+
export function detectRepoType(repoPath) {
|
|
87
41
|
if (fs.existsSync(path.join(repoPath, 'tsconfig.json')))
|
|
88
42
|
return 'typescript';
|
|
89
43
|
if (fs.existsSync(path.join(repoPath, 'package.json')))
|
|
@@ -93,73 +47,73 @@ function detectRepoType(repoPath) {
|
|
|
93
47
|
// ─── Banner ──────────────────────────────────────────────────────────────────
|
|
94
48
|
function readVersion() {
|
|
95
49
|
try {
|
|
96
|
-
const pkg = JSON.parse(fs.readFileSync(path.join(
|
|
50
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(import.meta.dirname, '../../package.json'), 'utf-8'));
|
|
97
51
|
return pkg.version;
|
|
98
52
|
}
|
|
99
53
|
catch {
|
|
100
54
|
return 'unknown';
|
|
101
55
|
}
|
|
102
56
|
}
|
|
103
|
-
function printBanner() {
|
|
104
|
-
const line1 =
|
|
105
|
-
const version =
|
|
106
|
-
const tagline =
|
|
57
|
+
export function printBanner() {
|
|
58
|
+
const line1 = chalk.bold.hex('#6366f1')(' infrawise');
|
|
59
|
+
const version = chalk.dim(` v${readVersion()}`);
|
|
60
|
+
const tagline = chalk.dim(' Infrastructure Intelligence Platform\n');
|
|
107
61
|
console.log(`\n${line1}${version}`);
|
|
108
62
|
console.log(tagline);
|
|
109
63
|
}
|
|
110
64
|
// ─── Section header ──────────────────────────────────────────────────────────
|
|
111
|
-
function printHeader(title) {
|
|
112
|
-
console.log(
|
|
113
|
-
console.log(
|
|
65
|
+
export function printHeader(title) {
|
|
66
|
+
console.log(chalk.bold(`\n${title}`));
|
|
67
|
+
console.log(chalk.dim('─'.repeat(title.length + 2)));
|
|
114
68
|
}
|
|
115
69
|
// ─── Status lines ────────────────────────────────────────────────────────────
|
|
116
|
-
|
|
70
|
+
export const log = {
|
|
117
71
|
success: (msg, detail) => {
|
|
118
|
-
console.log(` ${
|
|
72
|
+
console.log(` ${chalk.green('✓')} ${msg}${detail ? chalk.dim(` ${detail}`) : ''}`);
|
|
119
73
|
},
|
|
120
74
|
fail: (msg, detail) => {
|
|
121
|
-
console.log(` ${
|
|
75
|
+
console.log(` ${chalk.red('✗')} ${chalk.red(msg)}${detail ? chalk.dim(`\n ${detail}`) : ''}`);
|
|
122
76
|
},
|
|
123
77
|
warn: (msg, detail) => {
|
|
124
|
-
console.log(` ${
|
|
78
|
+
console.log(` ${chalk.yellow('⚠')} ${chalk.yellow(msg)}${detail ? chalk.dim(`\n ${detail}`) : ''}`);
|
|
125
79
|
},
|
|
126
80
|
skip: (msg, detail) => {
|
|
127
|
-
console.log(` ${
|
|
81
|
+
console.log(` ${chalk.dim('−')} ${chalk.dim(msg)}${detail ? chalk.dim(` ${detail}`) : ''}`);
|
|
128
82
|
},
|
|
129
83
|
info: (msg) => {
|
|
130
|
-
console.log(` ${
|
|
84
|
+
console.log(` ${chalk.cyan('›')} ${msg}`);
|
|
131
85
|
},
|
|
132
86
|
dim: (msg) => {
|
|
133
|
-
console.log(
|
|
87
|
+
console.log(chalk.dim(` ${msg}`));
|
|
134
88
|
},
|
|
135
89
|
};
|
|
136
90
|
// ─── Findings ────────────────────────────────────────────────────────────────
|
|
137
91
|
function severityBadge(severity) {
|
|
138
92
|
switch (severity) {
|
|
139
|
-
case 'high': return
|
|
140
|
-
case 'medium': return
|
|
141
|
-
case 'low': return
|
|
93
|
+
case 'high': return chalk.bgRed.white.bold(` HIGH `);
|
|
94
|
+
case 'medium': return chalk.bgYellow.black.bold(` MED `);
|
|
95
|
+
case 'low': return chalk.bgCyan.black.bold(` LOW `);
|
|
142
96
|
}
|
|
143
97
|
}
|
|
144
|
-
function printFinding(finding, index) {
|
|
98
|
+
export function printFinding(finding, index) {
|
|
145
99
|
const badge = severityBadge(finding.severity);
|
|
146
|
-
const num =
|
|
147
|
-
console.log(`\n ${num} ${badge} ${
|
|
148
|
-
console.log(
|
|
149
|
-
console.log(` ${
|
|
100
|
+
const num = chalk.dim(`${index + 1}.`);
|
|
101
|
+
console.log(`\n ${num} ${badge} ${chalk.bold(finding.issue)}`);
|
|
102
|
+
console.log(chalk.dim(` ${finding.description}`));
|
|
103
|
+
console.log(` ${chalk.green('→')} ${finding.recommendation}`);
|
|
150
104
|
}
|
|
151
|
-
function printSummaryBox(findings) {
|
|
105
|
+
export function printSummaryBox(findings) {
|
|
152
106
|
const high = findings.filter((f) => f.severity === 'high').length;
|
|
153
107
|
const medium = findings.filter((f) => f.severity === 'medium').length;
|
|
154
108
|
const low = findings.filter((f) => f.severity === 'low').length;
|
|
155
109
|
console.log('');
|
|
156
|
-
console.log(
|
|
157
|
-
console.log(
|
|
158
|
-
console.log(
|
|
159
|
-
console.log(
|
|
160
|
-
console.log(
|
|
161
|
-
console.log(
|
|
162
|
-
console.log(
|
|
163
|
-
console.log(
|
|
164
|
-
console.log(
|
|
110
|
+
console.log(chalk.dim(' ┌─────────────────────────────┐'));
|
|
111
|
+
console.log(chalk.dim(' │') + chalk.bold(' Analysis Summary ') + chalk.dim('│'));
|
|
112
|
+
console.log(chalk.dim(' ├─────────────────────────────┤'));
|
|
113
|
+
console.log(chalk.dim(' │') + ` ${chalk.red('●')} High ${chalk.red.bold(String(high).padStart(3))} ` + chalk.dim('│'));
|
|
114
|
+
console.log(chalk.dim(' │') + ` ${chalk.yellow('●')} Medium ${chalk.yellow.bold(String(medium).padStart(3))} ` + chalk.dim('│'));
|
|
115
|
+
console.log(chalk.dim(' │') + ` ${chalk.cyan('●')} Low ${chalk.cyan.bold(String(low).padStart(3))} ` + chalk.dim('│'));
|
|
116
|
+
console.log(chalk.dim(' ├─────────────────────────────┤'));
|
|
117
|
+
console.log(chalk.dim(' │') + ` Total ${chalk.bold(String(findings.length).padStart(3))} ` + chalk.dim('│'));
|
|
118
|
+
console.log(chalk.dim(' └─────────────────────────────┘'));
|
|
165
119
|
}
|