infrawise 0.1.2 → 0.2.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/LICENSE +21 -0
- package/README.md +146 -74
- package/dist/adapters/aws.js +255 -0
- package/dist/adapters/dynamodb.js +97 -0
- package/dist/adapters/logs.js +119 -0
- package/dist/adapters/mongodb.js +109 -0
- package/dist/adapters/mysql.js +135 -0
- package/dist/adapters/postgres.js +131 -0
- package/dist/adapters/terraform.js +510 -0
- package/dist/analyzers/aws-services.js +168 -0
- package/dist/analyzers/dynamodb.js +144 -0
- package/dist/analyzers/index.js +59 -0
- package/dist/analyzers/mongodb.js +82 -0
- package/dist/analyzers/mysql.js +82 -0
- package/dist/analyzers/postgres.js +148 -0
- package/dist/analyzers/rds.js +109 -0
- package/dist/analyzers/terraform.js +95 -0
- package/dist/cli/commands/analyze.js +319 -0
- package/dist/cli/commands/auth.js +57 -0
- package/dist/cli/commands/dev.js +127 -0
- package/dist/cli/commands/doctor.js +338 -0
- package/dist/cli/commands/init.js +234 -0
- package/dist/cli/index.js +82 -0
- package/dist/cli/utils.js +165 -0
- package/dist/context/index.js +597 -0
- package/dist/core/cache.js +89 -0
- package/dist/core/config.js +163 -0
- package/dist/core/errors.js +97 -0
- package/dist/core/index.js +22 -0
- package/dist/core/logger.js +28 -0
- package/dist/graph/index.js +268 -0
- package/dist/server/index.js +347 -0
- package/dist/types.js +2 -0
- package/package.json +20 -30
- package/dist/index.js +0 -4800
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
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.runDoctor = runDoctor;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const os = __importStar(require("os"));
|
|
43
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
44
|
+
const ora_1 = __importDefault(require("ora"));
|
|
45
|
+
const core_1 = require("../../core");
|
|
46
|
+
const dynamodb_1 = require("../../adapters/dynamodb");
|
|
47
|
+
const postgres_1 = require("../../adapters/postgres");
|
|
48
|
+
const mysql_1 = require("../../adapters/mysql");
|
|
49
|
+
const mongodb_1 = require("../../adapters/mongodb");
|
|
50
|
+
const aws_1 = require("../../adapters/aws");
|
|
51
|
+
const logs_1 = require("../../adapters/logs");
|
|
52
|
+
const utils_1 = require("../utils");
|
|
53
|
+
async function runCheck(label, fn) {
|
|
54
|
+
const spin = (0, ora_1.default)({ text: chalk_1.default.dim(label), color: 'cyan' }).start();
|
|
55
|
+
const result = await fn();
|
|
56
|
+
switch (result.status) {
|
|
57
|
+
case 'pass':
|
|
58
|
+
spin.succeed(chalk_1.default.green(result.name) + chalk_1.default.dim(` ${result.message}`));
|
|
59
|
+
break;
|
|
60
|
+
case 'fail':
|
|
61
|
+
spin.fail(chalk_1.default.red(result.name) + chalk_1.default.dim(` ${result.message}`));
|
|
62
|
+
break;
|
|
63
|
+
case 'warn':
|
|
64
|
+
spin.warn(chalk_1.default.yellow(result.name) + chalk_1.default.dim(` ${result.message}`));
|
|
65
|
+
break;
|
|
66
|
+
case 'skip':
|
|
67
|
+
spin.info(chalk_1.default.dim(`${result.name} ${result.message}`));
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
if (result.detail)
|
|
71
|
+
console.log(chalk_1.default.dim(` ${result.detail}`));
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
async function runDoctor(options = {}) {
|
|
75
|
+
(0, utils_1.printHeader)('Infrawise Doctor');
|
|
76
|
+
const configPath = path.resolve(options.config ?? 'infrawise.yaml');
|
|
77
|
+
const results = [];
|
|
78
|
+
// Config file
|
|
79
|
+
results.push(await runCheck('Checking config file...', async () => {
|
|
80
|
+
const exists = fs.existsSync(configPath);
|
|
81
|
+
return {
|
|
82
|
+
name: 'Config file',
|
|
83
|
+
status: exists ? 'pass' : 'fail',
|
|
84
|
+
message: exists ? configPath : `Not found at ${configPath}`,
|
|
85
|
+
detail: exists ? undefined : 'Run: infrawise init',
|
|
86
|
+
};
|
|
87
|
+
}));
|
|
88
|
+
// Config valid
|
|
89
|
+
let config;
|
|
90
|
+
results.push(await runCheck('Validating config...', async () => {
|
|
91
|
+
if (!fs.existsSync(configPath))
|
|
92
|
+
return { name: 'Config validation', status: 'skip', message: 'No config file' };
|
|
93
|
+
try {
|
|
94
|
+
config = (0, core_1.loadConfig)(options.config);
|
|
95
|
+
return { name: 'Config validation', status: 'pass', message: `project: ${config.project}` };
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
return {
|
|
99
|
+
name: 'Config validation', status: 'fail', message: 'Invalid config',
|
|
100
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}));
|
|
104
|
+
// AWS credentials
|
|
105
|
+
results.push(await runCheck('Checking AWS credentials...', async () => {
|
|
106
|
+
const home = process.env.HOME ?? os.homedir();
|
|
107
|
+
const hasCreds = fs.existsSync(path.join(home, '.aws', 'credentials')) ||
|
|
108
|
+
fs.existsSync(path.join(home, '.aws', 'config'));
|
|
109
|
+
return {
|
|
110
|
+
name: 'AWS credentials',
|
|
111
|
+
status: hasCreds ? 'pass' : 'warn',
|
|
112
|
+
message: hasCreds ? 'Found' : 'Not found — may use env vars or IAM role',
|
|
113
|
+
detail: hasCreds ? undefined : 'Run: aws configure',
|
|
114
|
+
};
|
|
115
|
+
}));
|
|
116
|
+
const awsCfg = { region: config?.aws?.region, profile: config?.aws?.profile };
|
|
117
|
+
// DynamoDB
|
|
118
|
+
results.push(await runCheck('Testing DynamoDB access...', async () => {
|
|
119
|
+
if (!config)
|
|
120
|
+
return { name: 'DynamoDB', status: 'skip', message: 'No valid config' };
|
|
121
|
+
if (config.dynamodb?.enabled !== true)
|
|
122
|
+
return { name: 'DynamoDB', status: 'skip', message: 'Disabled in config' };
|
|
123
|
+
try {
|
|
124
|
+
const ok = await (0, dynamodb_1.validateDynamoAccess)(config);
|
|
125
|
+
return {
|
|
126
|
+
name: 'DynamoDB', status: ok ? 'pass' : 'warn',
|
|
127
|
+
message: ok ? `Connected (profile: ${config.aws?.profile ?? 'default'})` : 'Cannot connect',
|
|
128
|
+
detail: ok ? undefined : 'Check IAM: dynamodb:ListTables, dynamodb:DescribeTable',
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
return { name: 'DynamoDB', status: 'warn', message: err instanceof Error ? err.message : String(err) };
|
|
133
|
+
}
|
|
134
|
+
}));
|
|
135
|
+
// SQS
|
|
136
|
+
results.push(await runCheck('Testing SQS access...', async () => {
|
|
137
|
+
if (config?.sqs?.enabled !== true)
|
|
138
|
+
return { name: 'SQS', status: 'skip', message: 'Disabled in config' };
|
|
139
|
+
try {
|
|
140
|
+
await (0, aws_1.validateSQSAccess)(awsCfg);
|
|
141
|
+
return { name: 'SQS', status: 'pass', message: 'Connected' };
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
return {
|
|
145
|
+
name: 'SQS', status: 'warn',
|
|
146
|
+
message: err instanceof Error ? err.message : String(err),
|
|
147
|
+
detail: 'Check IAM: sqs:ListQueues, sqs:GetQueueAttributes',
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
}));
|
|
151
|
+
// SNS
|
|
152
|
+
results.push(await runCheck('Testing SNS access...', async () => {
|
|
153
|
+
if (config?.sns?.enabled !== true)
|
|
154
|
+
return { name: 'SNS', status: 'skip', message: 'Disabled in config' };
|
|
155
|
+
try {
|
|
156
|
+
await (0, aws_1.validateSNSAccess)(awsCfg);
|
|
157
|
+
return { name: 'SNS', status: 'pass', message: 'Connected' };
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
return {
|
|
161
|
+
name: 'SNS', status: 'warn',
|
|
162
|
+
message: err instanceof Error ? err.message : String(err),
|
|
163
|
+
detail: 'Check IAM: sns:ListTopics, sns:GetTopicAttributes, sns:ListSubscriptionsByTopic',
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}));
|
|
167
|
+
// SSM
|
|
168
|
+
results.push(await runCheck('Testing SSM Parameter Store access...', async () => {
|
|
169
|
+
if (config?.ssm?.enabled !== true)
|
|
170
|
+
return { name: 'SSM', status: 'skip', message: 'Disabled in config' };
|
|
171
|
+
try {
|
|
172
|
+
await (0, aws_1.validateSSMAccess)(awsCfg);
|
|
173
|
+
return { name: 'SSM', status: 'pass', message: 'Connected (metadata only)' };
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
return {
|
|
177
|
+
name: 'SSM', status: 'warn',
|
|
178
|
+
message: err instanceof Error ? err.message : String(err),
|
|
179
|
+
detail: 'Check IAM: ssm:DescribeParameters',
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
}));
|
|
183
|
+
// Secrets Manager
|
|
184
|
+
results.push(await runCheck('Testing Secrets Manager access...', async () => {
|
|
185
|
+
if (config?.secretsManager?.enabled !== true)
|
|
186
|
+
return { name: 'Secrets Manager', status: 'skip', message: 'Disabled in config' };
|
|
187
|
+
try {
|
|
188
|
+
await (0, aws_1.validateSecretsAccess)(awsCfg);
|
|
189
|
+
return { name: 'Secrets Manager', status: 'pass', message: 'Connected (names/rotation only)' };
|
|
190
|
+
}
|
|
191
|
+
catch (err) {
|
|
192
|
+
return {
|
|
193
|
+
name: 'Secrets Manager', status: 'warn',
|
|
194
|
+
message: err instanceof Error ? err.message : String(err),
|
|
195
|
+
detail: 'Check IAM: secretsmanager:ListSecrets',
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
}));
|
|
199
|
+
// Lambda
|
|
200
|
+
results.push(await runCheck('Testing Lambda access...', async () => {
|
|
201
|
+
if (config?.lambda?.enabled !== true)
|
|
202
|
+
return { name: 'Lambda', status: 'skip', message: 'Disabled in config' };
|
|
203
|
+
try {
|
|
204
|
+
await (0, aws_1.validateLambdaAccess)(awsCfg);
|
|
205
|
+
return { name: 'Lambda', status: 'pass', message: 'Connected' };
|
|
206
|
+
}
|
|
207
|
+
catch (err) {
|
|
208
|
+
return {
|
|
209
|
+
name: 'Lambda', status: 'warn',
|
|
210
|
+
message: err instanceof Error ? err.message : String(err),
|
|
211
|
+
detail: 'Check IAM: lambda:ListFunctions',
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
}));
|
|
215
|
+
// CloudWatch Logs
|
|
216
|
+
results.push(await runCheck('Testing CloudWatch Logs access...', async () => {
|
|
217
|
+
if (!config?.cloudwatchLogs?.enabled)
|
|
218
|
+
return { name: 'CloudWatch Logs', status: 'skip', message: 'Not enabled in config' };
|
|
219
|
+
try {
|
|
220
|
+
await (0, logs_1.validateLogsAccess)(awsCfg);
|
|
221
|
+
return { name: 'CloudWatch Logs', status: 'pass', message: 'Connected' };
|
|
222
|
+
}
|
|
223
|
+
catch (err) {
|
|
224
|
+
return {
|
|
225
|
+
name: 'CloudWatch Logs', status: 'warn',
|
|
226
|
+
message: err instanceof Error ? err.message : String(err),
|
|
227
|
+
detail: 'Check IAM: logs:DescribeLogGroups, logs:FilterLogEvents',
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
}));
|
|
231
|
+
// PostgreSQL
|
|
232
|
+
results.push(await runCheck('Testing PostgreSQL...', async () => {
|
|
233
|
+
if (!config?.postgres?.enabled || !config.postgres.connectionString) {
|
|
234
|
+
return { name: 'PostgreSQL', status: 'skip', message: 'Not configured' };
|
|
235
|
+
}
|
|
236
|
+
try {
|
|
237
|
+
const ok = await (0, postgres_1.validatePostgresAccess)(config.postgres.connectionString);
|
|
238
|
+
return {
|
|
239
|
+
name: 'PostgreSQL', status: ok ? 'pass' : 'fail',
|
|
240
|
+
message: ok ? 'Connected' : 'Cannot connect',
|
|
241
|
+
detail: ok ? undefined : 'Check connection string and security group',
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
catch (err) {
|
|
245
|
+
return { name: 'PostgreSQL', status: 'fail', message: err instanceof Error ? err.message : String(err) };
|
|
246
|
+
}
|
|
247
|
+
}));
|
|
248
|
+
// MySQL
|
|
249
|
+
results.push(await runCheck('Testing MySQL...', async () => {
|
|
250
|
+
if (!config?.mysql?.enabled || !config.mysql.connectionString) {
|
|
251
|
+
return { name: 'MySQL', status: 'skip', message: 'Not configured' };
|
|
252
|
+
}
|
|
253
|
+
try {
|
|
254
|
+
const ok = await (0, mysql_1.validateMySQLAccess)(config.mysql.connectionString);
|
|
255
|
+
return {
|
|
256
|
+
name: 'MySQL', status: ok ? 'pass' : 'fail',
|
|
257
|
+
message: ok ? 'Connected' : 'Cannot connect',
|
|
258
|
+
detail: ok ? undefined : 'Check connection string, host, port 3306',
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
catch (err) {
|
|
262
|
+
return { name: 'MySQL', status: 'fail', message: err instanceof Error ? err.message : String(err) };
|
|
263
|
+
}
|
|
264
|
+
}));
|
|
265
|
+
// MongoDB
|
|
266
|
+
results.push(await runCheck('Testing MongoDB...', async () => {
|
|
267
|
+
if (!config?.mongodb?.enabled || !config.mongodb.connectionString) {
|
|
268
|
+
return { name: 'MongoDB', status: 'skip', message: 'Not configured' };
|
|
269
|
+
}
|
|
270
|
+
try {
|
|
271
|
+
const ok = await (0, mongodb_1.validateMongoAccess)(config.mongodb.connectionString);
|
|
272
|
+
return {
|
|
273
|
+
name: 'MongoDB', status: ok ? 'pass' : 'fail',
|
|
274
|
+
message: ok ? 'Connected' : 'Cannot connect',
|
|
275
|
+
detail: ok ? undefined : 'Check connection string and port 27017',
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
catch (err) {
|
|
279
|
+
return { name: 'MongoDB', status: 'fail', message: err instanceof Error ? err.message : String(err) };
|
|
280
|
+
}
|
|
281
|
+
}));
|
|
282
|
+
// Project type
|
|
283
|
+
results.push(await runCheck('Detecting project type...', async () => {
|
|
284
|
+
const hasTsConfig = fs.existsSync(path.join(process.cwd(), 'tsconfig.json'));
|
|
285
|
+
const hasPkg = fs.existsSync(path.join(process.cwd(), 'package.json'));
|
|
286
|
+
return {
|
|
287
|
+
name: 'Project type',
|
|
288
|
+
status: hasTsConfig ? 'pass' : 'warn',
|
|
289
|
+
message: hasTsConfig ? 'TypeScript' : hasPkg ? 'JavaScript (no tsconfig.json)' : 'Unknown',
|
|
290
|
+
detail: hasTsConfig ? undefined : 'Scanner works best with TypeScript projects',
|
|
291
|
+
};
|
|
292
|
+
}));
|
|
293
|
+
// IaC detection
|
|
294
|
+
results.push(await runCheck('Detecting IaC files...', async () => {
|
|
295
|
+
const cwd = process.cwd();
|
|
296
|
+
const hasTF = fs.existsSync(path.join(cwd, 'main.tf')) || fs.readdirSync(cwd).some((f) => f.endsWith('.tf'));
|
|
297
|
+
const hasCFN = fs.existsSync(path.join(cwd, 'template.yaml')) || fs.existsSync(path.join(cwd, 'template.json'));
|
|
298
|
+
const hasCDK = fs.existsSync(path.join(cwd, 'cdk.json'));
|
|
299
|
+
const hasCDKOut = fs.existsSync(path.join(cwd, 'cdk.out'));
|
|
300
|
+
const found = [];
|
|
301
|
+
if (hasTF)
|
|
302
|
+
found.push('Terraform');
|
|
303
|
+
if (hasCFN)
|
|
304
|
+
found.push('CloudFormation');
|
|
305
|
+
if (hasCDK)
|
|
306
|
+
found.push(`CDK${hasCDKOut ? ' (synthesized)' : ' (run cdk synth)'}`);
|
|
307
|
+
return {
|
|
308
|
+
name: 'IaC files',
|
|
309
|
+
status: found.length > 0 ? 'pass' : 'warn',
|
|
310
|
+
message: found.length > 0 ? found.join(', ') : 'No Terraform/CFN/CDK files detected',
|
|
311
|
+
detail: found.length === 0 ? 'IaC analysis will be skipped without TF/CFN/CDK files' : undefined,
|
|
312
|
+
};
|
|
313
|
+
}));
|
|
314
|
+
// Cache
|
|
315
|
+
results.push(await runCheck('Checking analysis cache...', async () => {
|
|
316
|
+
const cached = fs.existsSync(path.join(process.cwd(), '.infrawise', 'cache', 'graph.json'));
|
|
317
|
+
return {
|
|
318
|
+
name: 'Analysis cache',
|
|
319
|
+
status: cached ? 'pass' : 'warn',
|
|
320
|
+
message: cached ? 'Cached results found' : 'No cache — run infrawise analyze first',
|
|
321
|
+
};
|
|
322
|
+
}));
|
|
323
|
+
// Summary
|
|
324
|
+
const passed = results.filter((r) => r.status === 'pass').length;
|
|
325
|
+
const failed = results.filter((r) => r.status === 'fail').length;
|
|
326
|
+
const warned = results.filter((r) => r.status === 'warn').length;
|
|
327
|
+
console.log('');
|
|
328
|
+
console.log(chalk_1.default.dim(' ' + '─'.repeat(40)));
|
|
329
|
+
if (failed === 0) {
|
|
330
|
+
console.log(` ${chalk_1.default.green.bold('All checks passed')} ${chalk_1.default.dim(`${passed} passed, ${warned} warning(s)`)}`);
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
console.log(` ${chalk_1.default.red.bold(`${failed} check(s) failed`)} ${chalk_1.default.dim(`${passed} passed, ${warned} warning(s)`)}`);
|
|
334
|
+
}
|
|
335
|
+
console.log('');
|
|
336
|
+
if (failed > 0)
|
|
337
|
+
process.exit(1);
|
|
338
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
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.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 = {}) {
|
|
47
|
+
const cwd = process.cwd();
|
|
48
|
+
const configPath = path.join(cwd, 'infrawise.yaml');
|
|
49
|
+
if (fs.existsSync(configPath) && !options.force) {
|
|
50
|
+
console.log(`\n ${chalk_1.default.yellow('⚠')} ${chalk_1.default.yellow('infrawise.yaml already exists.')} ${chalk_1.default.dim('Use --force to overwrite.')}\n`);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
(0, utils_1.printHeader)('Initialize Infrawise');
|
|
54
|
+
const repoType = (0, utils_1.detectRepoType)(cwd);
|
|
55
|
+
const repoName = path.basename(cwd);
|
|
56
|
+
const profiles = (0, utils_1.readAWSProfiles)();
|
|
57
|
+
const detectedRegion = (0, utils_1.detectAWSRegion)();
|
|
58
|
+
utils_1.log.success(`Repository detected`, repoName);
|
|
59
|
+
utils_1.log.success(`Type`, repoType);
|
|
60
|
+
utils_1.log.success(`AWS profiles found`, String(profiles.length));
|
|
61
|
+
console.log('');
|
|
62
|
+
// ── Core settings ──────────────────────────────────────────────────────────
|
|
63
|
+
const core = await inquirer_1.default.prompt([
|
|
64
|
+
{
|
|
65
|
+
type: 'input',
|
|
66
|
+
name: 'project',
|
|
67
|
+
message: 'Project name:',
|
|
68
|
+
default: repoName,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
type: 'list',
|
|
72
|
+
name: 'awsProfile',
|
|
73
|
+
message: 'AWS profile:',
|
|
74
|
+
choices: profiles,
|
|
75
|
+
default: profiles[0],
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
type: 'input',
|
|
79
|
+
name: 'region',
|
|
80
|
+
message: 'AWS region:',
|
|
81
|
+
default: detectedRegion,
|
|
82
|
+
},
|
|
83
|
+
]);
|
|
84
|
+
// ── Databases ──────────────────────────────────────────────────────────────
|
|
85
|
+
console.log('\n ' + chalk_1.default.bold('Databases'));
|
|
86
|
+
const databases = await inquirer_1.default.prompt([
|
|
87
|
+
{
|
|
88
|
+
type: 'confirm',
|
|
89
|
+
name: 'dynamoEnabled',
|
|
90
|
+
message: 'Enable DynamoDB analysis?',
|
|
91
|
+
default: true,
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
type: 'input',
|
|
95
|
+
name: 'dynamoTables',
|
|
96
|
+
message: 'DynamoDB tables to include:',
|
|
97
|
+
default: '',
|
|
98
|
+
suffix: chalk_1.default.dim(' (comma-separated, blank = all)'),
|
|
99
|
+
when: (a) => a.dynamoEnabled,
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
type: 'confirm',
|
|
103
|
+
name: 'pgEnabled',
|
|
104
|
+
message: 'Enable PostgreSQL analysis?',
|
|
105
|
+
default: false,
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
type: 'input',
|
|
109
|
+
name: 'pgConnectionString',
|
|
110
|
+
message: 'PostgreSQL connection string:',
|
|
111
|
+
default: 'postgresql://localhost:5432/mydb',
|
|
112
|
+
when: (a) => a.pgEnabled,
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
type: 'confirm',
|
|
116
|
+
name: 'mysqlEnabled',
|
|
117
|
+
message: 'Enable MySQL analysis?',
|
|
118
|
+
default: false,
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
type: 'input',
|
|
122
|
+
name: 'mysqlConnectionString',
|
|
123
|
+
message: 'MySQL connection string:',
|
|
124
|
+
default: 'mysql://localhost:3306/mydb',
|
|
125
|
+
when: (a) => a.mysqlEnabled,
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
type: 'confirm',
|
|
129
|
+
name: 'mongoEnabled',
|
|
130
|
+
message: 'Enable MongoDB analysis?',
|
|
131
|
+
default: false,
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
type: 'input',
|
|
135
|
+
name: 'mongoConnectionString',
|
|
136
|
+
message: 'MongoDB connection string:',
|
|
137
|
+
default: 'mongodb://localhost:27017',
|
|
138
|
+
when: (a) => a.mongoEnabled,
|
|
139
|
+
},
|
|
140
|
+
]);
|
|
141
|
+
// ── AWS services ───────────────────────────────────────────────────────────
|
|
142
|
+
console.log('\n ' + chalk_1.default.bold('AWS Services'));
|
|
143
|
+
console.log(chalk_1.default.dim(' Infrawise will introspect these services — credentials from the AWS profile above.'));
|
|
144
|
+
const services = await inquirer_1.default.prompt([
|
|
145
|
+
{
|
|
146
|
+
type: 'confirm',
|
|
147
|
+
name: 'sqsEnabled',
|
|
148
|
+
message: 'Introspect SQS queues?',
|
|
149
|
+
default: true,
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
type: 'confirm',
|
|
153
|
+
name: 'snsEnabled',
|
|
154
|
+
message: 'Introspect SNS topics?',
|
|
155
|
+
default: true,
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
type: 'confirm',
|
|
159
|
+
name: 'ssmEnabled',
|
|
160
|
+
message: 'Introspect SSM Parameter Store? (metadata only, no values)',
|
|
161
|
+
default: true,
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
type: 'input',
|
|
165
|
+
name: 'ssmPaths',
|
|
166
|
+
message: 'SSM path prefixes to filter:',
|
|
167
|
+
default: '',
|
|
168
|
+
suffix: chalk_1.default.dim(' (comma-separated, blank = all e.g. /myapp/prod)'),
|
|
169
|
+
when: (a) => a.ssmEnabled,
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
type: 'confirm',
|
|
173
|
+
name: 'secretsEnabled',
|
|
174
|
+
message: 'Introspect Secrets Manager? (names & rotation only, no values)',
|
|
175
|
+
default: true,
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
type: 'confirm',
|
|
179
|
+
name: 'lambdaEnabled',
|
|
180
|
+
message: 'Introspect Lambda functions?',
|
|
181
|
+
default: true,
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
type: 'confirm',
|
|
185
|
+
name: 'logsEnabled',
|
|
186
|
+
message: 'Sample CloudWatch Logs? (error patterns only, no raw logs)',
|
|
187
|
+
default: false,
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
type: 'input',
|
|
191
|
+
name: 'logGroupPrefixes',
|
|
192
|
+
message: 'CloudWatch log group prefixes:',
|
|
193
|
+
default: '',
|
|
194
|
+
suffix: chalk_1.default.dim(' (comma-separated, blank = all)'),
|
|
195
|
+
when: (a) => a.logsEnabled,
|
|
196
|
+
},
|
|
197
|
+
]);
|
|
198
|
+
// ── Build config ───────────────────────────────────────────────────────────
|
|
199
|
+
const includeTables = databases.dynamoTables
|
|
200
|
+
? databases.dynamoTables.split(',').map((t) => t.trim()).filter(Boolean)
|
|
201
|
+
: [];
|
|
202
|
+
const ssmPaths = services.ssmPaths
|
|
203
|
+
? services.ssmPaths.split(',').map((p) => p.trim()).filter(Boolean)
|
|
204
|
+
: [];
|
|
205
|
+
const logGroupPrefixes = services.logGroupPrefixes
|
|
206
|
+
? services.logGroupPrefixes.split(',').map((p) => p.trim()).filter(Boolean)
|
|
207
|
+
: [];
|
|
208
|
+
const configContent = (0, core_1.generateDefaultConfig)(core.project, {
|
|
209
|
+
aws: { profile: core.awsProfile, region: core.region },
|
|
210
|
+
dynamodb: { enabled: databases.dynamoEnabled, includeTables },
|
|
211
|
+
postgres: { enabled: databases.pgEnabled, connectionString: databases.pgConnectionString ?? '' },
|
|
212
|
+
mysql: { enabled: databases.mysqlEnabled, connectionString: databases.mysqlConnectionString ?? '' },
|
|
213
|
+
mongodb: { enabled: databases.mongoEnabled, connectionString: databases.mongoConnectionString ?? '' },
|
|
214
|
+
sqs: { enabled: services.sqsEnabled },
|
|
215
|
+
sns: { enabled: services.snsEnabled },
|
|
216
|
+
ssm: { enabled: services.ssmEnabled, paths: ssmPaths },
|
|
217
|
+
secretsManager: { enabled: services.secretsEnabled },
|
|
218
|
+
lambda: { enabled: services.lambdaEnabled },
|
|
219
|
+
cloudwatchLogs: {
|
|
220
|
+
enabled: services.logsEnabled,
|
|
221
|
+
logGroupPrefixes,
|
|
222
|
+
windowHours: 24,
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
fs.writeFileSync(configPath, configContent, 'utf-8');
|
|
226
|
+
console.log('');
|
|
227
|
+
utils_1.log.success(`Created ${chalk_1.default.bold('infrawise.yaml')}`);
|
|
228
|
+
console.log('');
|
|
229
|
+
console.log(chalk_1.default.bold(' Next steps:'));
|
|
230
|
+
utils_1.log.info(`Run ${chalk_1.default.cyan('infrawise doctor')} to validate your setup`);
|
|
231
|
+
utils_1.log.info(`Run ${chalk_1.default.cyan('infrawise analyze')} to scan your infrastructure`);
|
|
232
|
+
utils_1.log.info(`Run ${chalk_1.default.cyan('infrawise dev')} to start the MCP server`);
|
|
233
|
+
console.log('');
|
|
234
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const commander_1 = require("commander");
|
|
7
|
+
const utils_1 = require("./utils");
|
|
8
|
+
const init_1 = require("./commands/init");
|
|
9
|
+
const auth_1 = require("./commands/auth");
|
|
10
|
+
const analyze_1 = require("./commands/analyze");
|
|
11
|
+
const dev_1 = require("./commands/dev");
|
|
12
|
+
const doctor_1 = require("./commands/doctor");
|
|
13
|
+
const { version } = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(__dirname, '../../package.json'), 'utf8'));
|
|
14
|
+
const program = new commander_1.Command();
|
|
15
|
+
program
|
|
16
|
+
.name('infrawise')
|
|
17
|
+
.description('CLI-first infrastructure intelligence platform — analyze your databases, AWS services, and IaC')
|
|
18
|
+
.version(version);
|
|
19
|
+
program
|
|
20
|
+
.command('init')
|
|
21
|
+
.description('Detect AWS profile/region, discover DynamoDB tables, and generate infrawise.yaml')
|
|
22
|
+
.option('--force', 'Overwrite existing infrawise.yaml')
|
|
23
|
+
.action(async (options) => {
|
|
24
|
+
(0, utils_1.printBanner)();
|
|
25
|
+
await (0, init_1.runInit)({ force: options.force });
|
|
26
|
+
});
|
|
27
|
+
program
|
|
28
|
+
.command('auth')
|
|
29
|
+
.description('Validate and select AWS profile from ~/.aws/credentials')
|
|
30
|
+
.action(async () => {
|
|
31
|
+
(0, utils_1.printBanner)();
|
|
32
|
+
await (0, auth_1.runAuth)();
|
|
33
|
+
});
|
|
34
|
+
program
|
|
35
|
+
.command('analyze')
|
|
36
|
+
.description('Load config, run extractors, build graph, and run all analyzers')
|
|
37
|
+
.option('-c, --config <path>', 'Path to infrawise.yaml', 'infrawise.yaml')
|
|
38
|
+
.option('-r, --repo <path>', 'Path to repository to scan', process.cwd())
|
|
39
|
+
.option('--no-cache', 'Skip reading/writing the cache')
|
|
40
|
+
.action(async (options) => {
|
|
41
|
+
(0, utils_1.printBanner)();
|
|
42
|
+
await (0, analyze_1.runAnalyze)({
|
|
43
|
+
config: options.config !== 'infrawise.yaml' ? options.config : undefined,
|
|
44
|
+
repo: options.repo,
|
|
45
|
+
noCache: !options.cache,
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
program
|
|
49
|
+
.command('dev')
|
|
50
|
+
.description('Start Fastify MCP server on localhost:3000')
|
|
51
|
+
.option('-c, --config <path>', 'Path to infrawise.yaml', 'infrawise.yaml')
|
|
52
|
+
.option('-p, --port <number>', 'Port to listen on', '3000')
|
|
53
|
+
.action(async (options) => {
|
|
54
|
+
(0, utils_1.printBanner)();
|
|
55
|
+
await (0, dev_1.runDev)({
|
|
56
|
+
config: options.config !== 'infrawise.yaml' ? options.config : undefined,
|
|
57
|
+
port: parseInt(options.port, 10),
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
program
|
|
61
|
+
.command('doctor')
|
|
62
|
+
.description('Validate AWS access, postgres connectivity, config, and repo scan')
|
|
63
|
+
.option('-c, --config <path>', 'Path to infrawise.yaml', 'infrawise.yaml')
|
|
64
|
+
.action(async (options) => {
|
|
65
|
+
(0, utils_1.printBanner)();
|
|
66
|
+
await (0, doctor_1.runDoctor)({
|
|
67
|
+
config: options.config !== 'infrawise.yaml' ? options.config : undefined,
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
// Global error handling
|
|
71
|
+
program.exitOverride((err) => {
|
|
72
|
+
if (err.code === 'commander.helpDisplayed' || err.code === 'commander.version') {
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
console.error(`\nError: ${err.message}`);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
});
|
|
78
|
+
process.on('unhandledRejection', (reason) => {
|
|
79
|
+
console.error('\nUnhandled error:', reason instanceof Error ? reason.message : String(reason));
|
|
80
|
+
process.exit(1);
|
|
81
|
+
});
|
|
82
|
+
program.parse(process.argv);
|