infrawise 0.2.2 → 0.3.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/dist/adapters/aws.js +11 -3
- package/dist/adapters/dynamodb.js +14 -2
- package/dist/adapters/logs.js +11 -3
- package/dist/cli/commands/analyze.js +1 -1
- package/dist/cli/commands/doctor.js +13 -9
- package/dist/cli/commands/init.js +38 -20
- package/dist/core/config.js +2 -0
- package/package.json +1 -1
package/dist/adapters/aws.js
CHANGED
|
@@ -22,9 +22,17 @@ const credential_providers_1 = require("@aws-sdk/credential-providers");
|
|
|
22
22
|
const core_1 = require("../core");
|
|
23
23
|
function clientConfig(cfg) {
|
|
24
24
|
const region = cfg.region ?? 'us-east-1';
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
const base = { region };
|
|
26
|
+
if (cfg.endpoint)
|
|
27
|
+
base.endpoint = cfg.endpoint;
|
|
28
|
+
if (cfg.profile)
|
|
29
|
+
base.credentials = (0, credential_providers_1.fromIni)({ profile: cfg.profile });
|
|
30
|
+
else if (cfg.endpoint)
|
|
31
|
+
base.credentials = {
|
|
32
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID ?? 'test',
|
|
33
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY ?? 'test',
|
|
34
|
+
};
|
|
35
|
+
return base;
|
|
28
36
|
}
|
|
29
37
|
// ─── SQS ─────────────────────────────────────────────────────────────────────
|
|
30
38
|
async function extractSQSMetadata(cfg = {}) {
|
|
@@ -2,16 +2,24 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.extractDynamoMetadata = extractDynamoMetadata;
|
|
4
4
|
exports.validateDynamoAccess = validateDynamoAccess;
|
|
5
|
+
exports.probeDynamoAccess = probeDynamoAccess;
|
|
5
6
|
const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
|
|
6
7
|
const credential_providers_1 = require("@aws-sdk/credential-providers");
|
|
7
8
|
const core_1 = require("../core");
|
|
8
9
|
function createDynamoClient(config) {
|
|
9
10
|
const region = config.aws?.region ?? 'us-east-1';
|
|
10
11
|
const profile = config.aws?.profile;
|
|
12
|
+
const endpoint = config.aws?.endpoint;
|
|
11
13
|
const clientConfig = { region };
|
|
12
|
-
if (
|
|
14
|
+
if (endpoint)
|
|
15
|
+
clientConfig.endpoint = endpoint;
|
|
16
|
+
if (profile)
|
|
13
17
|
clientConfig.credentials = (0, credential_providers_1.fromIni)({ profile });
|
|
14
|
-
|
|
18
|
+
else if (endpoint)
|
|
19
|
+
clientConfig.credentials = {
|
|
20
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID ?? 'test',
|
|
21
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY ?? 'test',
|
|
22
|
+
};
|
|
15
23
|
return new client_dynamodb_1.DynamoDBClient(clientConfig);
|
|
16
24
|
}
|
|
17
25
|
function parseTableDescription(desc) {
|
|
@@ -95,3 +103,7 @@ async function validateDynamoAccess(config) {
|
|
|
95
103
|
return false;
|
|
96
104
|
}
|
|
97
105
|
}
|
|
106
|
+
async function probeDynamoAccess(config) {
|
|
107
|
+
const client = createDynamoClient(config);
|
|
108
|
+
await client.send(new client_dynamodb_1.ListTablesCommand({ Limit: 1 }));
|
|
109
|
+
}
|
package/dist/adapters/logs.js
CHANGED
|
@@ -10,9 +10,17 @@ const MAX_LOG_GROUPS = 50;
|
|
|
10
10
|
const MAX_EVENTS_PER_GROUP = 50;
|
|
11
11
|
function clientConfig(cfg) {
|
|
12
12
|
const region = cfg.region ?? 'us-east-1';
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
const base = { region };
|
|
14
|
+
if (cfg.endpoint)
|
|
15
|
+
base.endpoint = cfg.endpoint;
|
|
16
|
+
if (cfg.profile)
|
|
17
|
+
base.credentials = (0, credential_providers_1.fromIni)({ profile: cfg.profile });
|
|
18
|
+
else if (cfg.endpoint)
|
|
19
|
+
base.credentials = {
|
|
20
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID ?? 'test',
|
|
21
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY ?? 'test',
|
|
22
|
+
};
|
|
23
|
+
return base;
|
|
16
24
|
}
|
|
17
25
|
// Strip UUIDs, timestamps, numbers → group similar messages by pattern
|
|
18
26
|
function toPattern(message) {
|
|
@@ -64,7 +64,7 @@ async function runAnalyze(options = {}) {
|
|
|
64
64
|
process.exit(1);
|
|
65
65
|
}
|
|
66
66
|
const repoPath = options.repo ?? process.cwd();
|
|
67
|
-
const awsCfg = { region: config.aws?.region, profile: config.aws?.profile };
|
|
67
|
+
const awsCfg = { region: config.aws?.region, profile: config.aws?.profile, endpoint: config.aws?.endpoint };
|
|
68
68
|
const dynamoMeta = [];
|
|
69
69
|
const postgresMeta = [];
|
|
70
70
|
const mysqlMeta = [];
|
|
@@ -113,7 +113,7 @@ async function runDoctor(options = {}) {
|
|
|
113
113
|
detail: hasCreds ? undefined : 'Run: aws configure',
|
|
114
114
|
};
|
|
115
115
|
}));
|
|
116
|
-
const awsCfg = { region: config?.aws?.region, profile: config?.aws?.profile };
|
|
116
|
+
const awsCfg = { region: config?.aws?.region, profile: config?.aws?.profile, endpoint: config?.aws?.endpoint };
|
|
117
117
|
// DynamoDB
|
|
118
118
|
results.push(await runCheck('Testing DynamoDB access...', async () => {
|
|
119
119
|
if (!config)
|
|
@@ -121,15 +121,15 @@ async function runDoctor(options = {}) {
|
|
|
121
121
|
if (config.dynamodb?.enabled !== true)
|
|
122
122
|
return { name: 'DynamoDB', status: 'skip', message: 'Disabled in config' };
|
|
123
123
|
try {
|
|
124
|
-
|
|
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
|
-
};
|
|
124
|
+
await (0, dynamodb_1.probeDynamoAccess)(config);
|
|
125
|
+
return { name: 'DynamoDB', status: 'pass', message: `Connected (profile: ${config.aws?.profile ?? 'default'})` };
|
|
130
126
|
}
|
|
131
127
|
catch (err) {
|
|
132
|
-
return {
|
|
128
|
+
return {
|
|
129
|
+
name: 'DynamoDB', status: 'warn',
|
|
130
|
+
message: err instanceof Error ? err.message : String(err),
|
|
131
|
+
detail: 'Check IAM: dynamodb:ListTables, dynamodb:DescribeTable',
|
|
132
|
+
};
|
|
133
133
|
}
|
|
134
134
|
}));
|
|
135
135
|
// SQS
|
|
@@ -293,7 +293,11 @@ async function runDoctor(options = {}) {
|
|
|
293
293
|
// IaC detection
|
|
294
294
|
results.push(await runCheck('Detecting IaC files...', async () => {
|
|
295
295
|
const cwd = process.cwd();
|
|
296
|
-
const
|
|
296
|
+
const hasTfFile = (dir) => fs.existsSync(dir) && fs.readdirSync(dir).some((f) => f.endsWith('.tf'));
|
|
297
|
+
const hasTF = hasTfFile(cwd) || fs.readdirSync(cwd).some((entry) => {
|
|
298
|
+
const full = path.join(cwd, entry);
|
|
299
|
+
return fs.statSync(full).isDirectory() && hasTfFile(full);
|
|
300
|
+
});
|
|
297
301
|
const hasCFN = fs.existsSync(path.join(cwd, 'template.yaml')) || fs.existsSync(path.join(cwd, 'template.json'));
|
|
298
302
|
const hasCDK = fs.existsSync(path.join(cwd, 'cdk.json'));
|
|
299
303
|
const hasCDKOut = fs.existsSync(path.join(cwd, 'cdk.out'));
|
|
@@ -71,7 +71,13 @@ async function runInit(options = {}) {
|
|
|
71
71
|
type: 'list',
|
|
72
72
|
name: 'awsProfile',
|
|
73
73
|
message: 'AWS profile:',
|
|
74
|
-
choices:
|
|
74
|
+
choices: [
|
|
75
|
+
new inquirer_1.default.Separator('── no profile ──'),
|
|
76
|
+
{ name: 'Environment variables (CI/CD, real AWS)', value: '__env__' },
|
|
77
|
+
{ name: 'LocalStack (local development)', value: '__localstack__' },
|
|
78
|
+
new inquirer_1.default.Separator('── named profiles ──'),
|
|
79
|
+
...profiles,
|
|
80
|
+
],
|
|
75
81
|
default: profiles[0],
|
|
76
82
|
},
|
|
77
83
|
{
|
|
@@ -80,24 +86,18 @@ async function runInit(options = {}) {
|
|
|
80
86
|
message: 'AWS region:',
|
|
81
87
|
default: detectedRegion,
|
|
82
88
|
},
|
|
89
|
+
{
|
|
90
|
+
type: 'input',
|
|
91
|
+
name: 'endpoint',
|
|
92
|
+
message: 'LocalStack endpoint:',
|
|
93
|
+
default: 'http://localhost:4566',
|
|
94
|
+
when: (a) => a.awsProfile === '__localstack__',
|
|
95
|
+
},
|
|
83
96
|
]);
|
|
84
97
|
// ── Databases ──────────────────────────────────────────────────────────────
|
|
85
98
|
console.log('\n ' + chalk_1.default.bold('Databases'));
|
|
99
|
+
console.log(chalk_1.default.dim(' Self-hosted databases (PostgreSQL, MySQL, MongoDB).'));
|
|
86
100
|
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
101
|
{
|
|
102
102
|
type: 'confirm',
|
|
103
103
|
name: 'pgEnabled',
|
|
@@ -140,8 +140,22 @@ async function runInit(options = {}) {
|
|
|
140
140
|
]);
|
|
141
141
|
// ── AWS services ───────────────────────────────────────────────────────────
|
|
142
142
|
console.log('\n ' + chalk_1.default.bold('AWS Services'));
|
|
143
|
-
console.log(chalk_1.default.dim(' Infrawise will introspect these services
|
|
143
|
+
console.log(chalk_1.default.dim(' Infrawise will introspect these services using the credentials configured above.'));
|
|
144
144
|
const services = await inquirer_1.default.prompt([
|
|
145
|
+
{
|
|
146
|
+
type: 'confirm',
|
|
147
|
+
name: 'dynamoEnabled',
|
|
148
|
+
message: 'Introspect DynamoDB?',
|
|
149
|
+
default: true,
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
type: 'input',
|
|
153
|
+
name: 'dynamoTables',
|
|
154
|
+
message: 'DynamoDB tables to include:',
|
|
155
|
+
default: '',
|
|
156
|
+
suffix: chalk_1.default.dim(' (comma-separated, blank = all)'),
|
|
157
|
+
when: (a) => a.dynamoEnabled,
|
|
158
|
+
},
|
|
145
159
|
{
|
|
146
160
|
type: 'confirm',
|
|
147
161
|
name: 'sqsEnabled',
|
|
@@ -196,8 +210,8 @@ async function runInit(options = {}) {
|
|
|
196
210
|
},
|
|
197
211
|
]);
|
|
198
212
|
// ── Build config ───────────────────────────────────────────────────────────
|
|
199
|
-
const includeTables =
|
|
200
|
-
?
|
|
213
|
+
const includeTables = services.dynamoTables
|
|
214
|
+
? services.dynamoTables.split(',').map((t) => t.trim()).filter(Boolean)
|
|
201
215
|
: [];
|
|
202
216
|
const ssmPaths = services.ssmPaths
|
|
203
217
|
? services.ssmPaths.split(',').map((p) => p.trim()).filter(Boolean)
|
|
@@ -205,9 +219,13 @@ async function runInit(options = {}) {
|
|
|
205
219
|
const logGroupPrefixes = services.logGroupPrefixes
|
|
206
220
|
? services.logGroupPrefixes.split(',').map((p) => p.trim()).filter(Boolean)
|
|
207
221
|
: [];
|
|
222
|
+
const isLocalStack = core.awsProfile === '__localstack__';
|
|
223
|
+
const isEnvVars = core.awsProfile === '__env__';
|
|
224
|
+
const resolvedProfile = isLocalStack || isEnvVars ? '' : core.awsProfile;
|
|
225
|
+
const resolvedEndpoint = isLocalStack ? (core.endpoint || 'http://localhost:4566') : undefined;
|
|
208
226
|
const configContent = (0, core_1.generateDefaultConfig)(core.project, {
|
|
209
|
-
aws: { profile:
|
|
210
|
-
dynamodb: { enabled:
|
|
227
|
+
aws: { profile: resolvedProfile, region: core.region, endpoint: resolvedEndpoint },
|
|
228
|
+
dynamodb: { enabled: services.dynamoEnabled, includeTables },
|
|
211
229
|
postgres: { enabled: databases.pgEnabled, connectionString: databases.pgConnectionString ?? '' },
|
|
212
230
|
mysql: { enabled: databases.mysqlEnabled, connectionString: databases.mysqlConnectionString ?? '' },
|
|
213
231
|
mongodb: { enabled: databases.mongoEnabled, connectionString: databases.mongoConnectionString ?? '' },
|
package/dist/core/config.js
CHANGED
|
@@ -46,6 +46,7 @@ exports.InfrawiseConfigSchema = zod_1.z.object({
|
|
|
46
46
|
.object({
|
|
47
47
|
profile: zod_1.z.string().optional().default('default'),
|
|
48
48
|
region: zod_1.z.string().optional().default('us-east-1'),
|
|
49
|
+
endpoint: zod_1.z.string().optional(),
|
|
49
50
|
})
|
|
50
51
|
.optional()
|
|
51
52
|
.default({}),
|
|
@@ -128,6 +129,7 @@ function generateDefaultConfig(projectName, options) {
|
|
|
128
129
|
aws: {
|
|
129
130
|
profile: options?.aws?.profile ?? 'default',
|
|
130
131
|
region: options?.aws?.region ?? 'us-east-1',
|
|
132
|
+
...(options?.aws?.endpoint ? { endpoint: options.aws.endpoint } : {}),
|
|
131
133
|
},
|
|
132
134
|
dynamodb: { enabled: options?.dynamodb?.enabled ?? true, includeTables: options?.dynamodb?.includeTables ?? [] },
|
|
133
135
|
postgres: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "infrawise",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "CLI-first infrastructure intelligence platform — analyzes DynamoDB, PostgreSQL, MySQL, MongoDB, SQS, SNS, SSM, Secrets Manager, Lambda, CloudWatch Logs and exposes findings as an MCP server for Claude Code",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"infrastructure",
|