infrawise 0.5.0 → 0.7.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 +26 -14
- package/dist/adapters/aws.js +152 -72
- package/dist/adapters/dynamodb.js +17 -27
- package/dist/adapters/logs.js +12 -21
- 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 +34 -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 +131 -158
- package/dist/cli/commands/auth.js +24 -30
- package/dist/cli/commands/dev.js +46 -84
- package/dist/cli/commands/doctor.js +51 -74
- package/dist/cli/commands/init.js +48 -73
- package/dist/cli/index.js +21 -23
- 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 +50 -83
- 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 +73 -33
- package/dist/server/index.js +116 -93
- package/dist/types.js +1 -2
- package/package.json +32 -30
|
@@ -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',
|
|
@@ -68,14 +29,14 @@ async function runInit(options = {}) {
|
|
|
68
29
|
default: repoName,
|
|
69
30
|
},
|
|
70
31
|
{
|
|
71
|
-
type: '
|
|
32
|
+
type: 'select',
|
|
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
|
{
|
|
@@ -194,6 +155,18 @@ async function runInit(options = {}) {
|
|
|
194
155
|
message: 'Introspect Lambda functions?',
|
|
195
156
|
default: true,
|
|
196
157
|
},
|
|
158
|
+
{
|
|
159
|
+
type: 'confirm',
|
|
160
|
+
name: 'eventbridgeEnabled',
|
|
161
|
+
message: 'Introspect EventBridge rules?',
|
|
162
|
+
default: true,
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
type: 'confirm',
|
|
166
|
+
name: 'rdsEnabled',
|
|
167
|
+
message: 'Introspect RDS instances?',
|
|
168
|
+
default: true,
|
|
169
|
+
},
|
|
197
170
|
{
|
|
198
171
|
type: 'confirm',
|
|
199
172
|
name: 'logsEnabled',
|
|
@@ -205,7 +178,7 @@ async function runInit(options = {}) {
|
|
|
205
178
|
name: 'logGroupPrefixes',
|
|
206
179
|
message: 'CloudWatch log group prefixes:',
|
|
207
180
|
default: '',
|
|
208
|
-
suffix:
|
|
181
|
+
suffix: chalk.dim(' (comma-separated, blank = all)'),
|
|
209
182
|
when: (a) => a.logsEnabled,
|
|
210
183
|
},
|
|
211
184
|
]);
|
|
@@ -223,7 +196,7 @@ async function runInit(options = {}) {
|
|
|
223
196
|
const isEnvVars = core.awsProfile === '__env__';
|
|
224
197
|
const resolvedProfile = isLocalStack || isEnvVars ? '' : core.awsProfile;
|
|
225
198
|
const resolvedEndpoint = isLocalStack ? (core.endpoint || 'http://localhost:4566') : undefined;
|
|
226
|
-
const configContent =
|
|
199
|
+
const configContent = generateDefaultConfig(core.project, {
|
|
227
200
|
aws: { profile: resolvedProfile, region: core.region, endpoint: resolvedEndpoint },
|
|
228
201
|
dynamodb: { enabled: services.dynamoEnabled, includeTables },
|
|
229
202
|
postgres: { enabled: databases.pgEnabled, connectionString: databases.pgConnectionString ?? '' },
|
|
@@ -234,6 +207,8 @@ async function runInit(options = {}) {
|
|
|
234
207
|
ssm: { enabled: services.ssmEnabled, paths: ssmPaths },
|
|
235
208
|
secretsManager: { enabled: services.secretsEnabled },
|
|
236
209
|
lambda: { enabled: services.lambdaEnabled },
|
|
210
|
+
eventbridge: { enabled: services.eventbridgeEnabled },
|
|
211
|
+
rds: { enabled: services.rdsEnabled },
|
|
237
212
|
cloudwatchLogs: {
|
|
238
213
|
enabled: services.logsEnabled,
|
|
239
214
|
logGroupPrefixes,
|
|
@@ -242,11 +217,11 @@ async function runInit(options = {}) {
|
|
|
242
217
|
});
|
|
243
218
|
fs.writeFileSync(configPath, configContent, 'utf-8');
|
|
244
219
|
console.log('');
|
|
245
|
-
|
|
220
|
+
log.success(`Created ${chalk.bold('infrawise.yaml')}`);
|
|
246
221
|
console.log('');
|
|
247
|
-
console.log(
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
222
|
+
console.log(chalk.bold(' Next steps:'));
|
|
223
|
+
log.info(`Run ${chalk.cyan('infrawise doctor')} to validate your setup`);
|
|
224
|
+
log.info(`Run ${chalk.cyan('infrawise analyze')} to scan your infrastructure`);
|
|
225
|
+
log.info(`Run ${chalk.cyan('infrawise dev')} to start the MCP server`);
|
|
251
226
|
console.log('');
|
|
252
227
|
}
|
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')
|
|
@@ -38,8 +36,8 @@ program
|
|
|
38
36
|
.option('-r, --repo <path>', 'Path to repository to scan', process.cwd())
|
|
39
37
|
.option('--no-cache', 'Skip reading/writing the cache')
|
|
40
38
|
.action(async (options) => {
|
|
41
|
-
|
|
42
|
-
await
|
|
39
|
+
printBanner();
|
|
40
|
+
await runAnalyze({
|
|
43
41
|
config: options.config !== 'infrawise.yaml' ? options.config : undefined,
|
|
44
42
|
repo: options.repo,
|
|
45
43
|
noCache: !options.cache,
|
|
@@ -51,8 +49,8 @@ program
|
|
|
51
49
|
.option('-c, --config <path>', 'Path to infrawise.yaml', 'infrawise.yaml')
|
|
52
50
|
.option('-p, --port <number>', 'Port to listen on', '3000')
|
|
53
51
|
.action(async (options) => {
|
|
54
|
-
|
|
55
|
-
await
|
|
52
|
+
printBanner();
|
|
53
|
+
await runDev({
|
|
56
54
|
config: options.config !== 'infrawise.yaml' ? options.config : undefined,
|
|
57
55
|
port: parseInt(options.port, 10),
|
|
58
56
|
});
|
|
@@ -62,8 +60,8 @@ program
|
|
|
62
60
|
.description('Validate AWS access, postgres connectivity, config, and repo scan')
|
|
63
61
|
.option('-c, --config <path>', 'Path to infrawise.yaml', 'infrawise.yaml')
|
|
64
62
|
.action(async (options) => {
|
|
65
|
-
|
|
66
|
-
await
|
|
63
|
+
printBanner();
|
|
64
|
+
await runDoctor({
|
|
67
65
|
config: options.config !== 'infrawise.yaml' ? options.config : undefined,
|
|
68
66
|
});
|
|
69
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
|
}
|