infrawise 0.8.2 → 0.8.3
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 +1 -47
- package/dist/adapters/aws/logs.js +3 -1
- package/dist/adapters/aws/s3.js +14 -5
- package/dist/adapters/aws/services.js +12 -7
- package/dist/adapters/db/mongodb.js +1 -3
- package/dist/adapters/iac/terraform.js +52 -25
- package/dist/analyzers/aws-services.js +5 -1
- package/dist/analyzers/rds.js +10 -2
- package/dist/analyzers/terraform.js +18 -3
- package/dist/cli/commands/analyze.js +149 -102
- package/dist/cli/commands/auth.js +4 -1
- package/dist/cli/commands/dev.js +4 -2
- package/dist/cli/commands/doctor.js +64 -24
- package/dist/cli/commands/init.js +25 -7
- package/dist/cli/utils.js +23 -9
- package/dist/context/index.js +36 -9
- package/dist/core/config.js +38 -16
- package/dist/core/index.js +1 -1
- package/dist/graph/index.js +63 -9
- package/dist/server/index.js +170 -51
- package/package.json +18 -5
package/dist/server/index.js
CHANGED
|
@@ -45,23 +45,43 @@ export function createMcpServer() {
|
|
|
45
45
|
const buckets = getBucketNodes(currentGraph);
|
|
46
46
|
return toText({
|
|
47
47
|
summary: {
|
|
48
|
-
tables: tables.length,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
tables: tables.length,
|
|
49
|
+
functions: functions.length,
|
|
50
|
+
queues: queues.length,
|
|
51
|
+
topics: topics.length,
|
|
52
|
+
secrets: secrets.length,
|
|
53
|
+
parameters: parameters.length,
|
|
54
|
+
logGroups: logGroups.length,
|
|
55
|
+
lambdas: lambdas.length,
|
|
52
56
|
buckets: buckets.length,
|
|
53
|
-
totalNodes: currentGraph.nodes.length,
|
|
57
|
+
totalNodes: currentGraph.nodes.length,
|
|
58
|
+
totalEdges: currentGraph.edges.length,
|
|
54
59
|
findings: summarizeFindings(currentFindings),
|
|
55
60
|
},
|
|
56
61
|
databases: tables.map((t) => ({ name: t.name, type: t.databaseType })),
|
|
57
|
-
queues: queues.map((q) => ({
|
|
62
|
+
queues: queues.map((q) => ({
|
|
63
|
+
name: q.name,
|
|
64
|
+
hasDLQ: q.hasDLQ,
|
|
65
|
+
encrypted: q.encrypted,
|
|
66
|
+
approximateMessages: q.approximateMessages,
|
|
67
|
+
})),
|
|
58
68
|
topics: topics.map((t) => ({ name: t.name, subscriptions: t.subscriptionCount })),
|
|
59
69
|
secrets: secrets.map((s) => ({ name: s.name, rotationEnabled: s.rotationEnabled })),
|
|
60
70
|
parameters: parameters.map((p) => ({ name: p.name, type: p.paramType, tier: p.tier })),
|
|
61
71
|
lambdas: lambdas.map((l) => ({ name: l.name, runtime: l.runtime, memoryMB: l.memoryMB })),
|
|
62
|
-
logGroups: logGroups.map((lg) => ({
|
|
63
|
-
|
|
64
|
-
|
|
72
|
+
logGroups: logGroups.map((lg) => ({
|
|
73
|
+
name: lg.name,
|
|
74
|
+
retentionDays: lg.retentionDays ?? 'never',
|
|
75
|
+
errorCount: lg.errorCount,
|
|
76
|
+
})),
|
|
77
|
+
buckets: buckets.map((b) => ({
|
|
78
|
+
name: b.name,
|
|
79
|
+
versioned: b.versioned,
|
|
80
|
+
publicAccessBlocked: b.publicAccessBlocked,
|
|
81
|
+
})),
|
|
82
|
+
highFindings: currentFindings
|
|
83
|
+
.filter((f) => f.severity === 'high')
|
|
84
|
+
.map((f) => ({ issue: f.issue, recommendation: f.recommendation })),
|
|
65
85
|
});
|
|
66
86
|
}));
|
|
67
87
|
mcp.registerTool('get_graph_summary', {
|
|
@@ -72,9 +92,12 @@ export function createMcpServer() {
|
|
|
72
92
|
edges: currentGraph.edges,
|
|
73
93
|
findings: currentFindings,
|
|
74
94
|
summary: {
|
|
75
|
-
totalNodes: currentGraph.nodes.length,
|
|
76
|
-
|
|
77
|
-
|
|
95
|
+
totalNodes: currentGraph.nodes.length,
|
|
96
|
+
totalEdges: currentGraph.edges.length,
|
|
97
|
+
tables: getTableNodes(currentGraph).length,
|
|
98
|
+
functions: getFunctionNodes(currentGraph).length,
|
|
99
|
+
queues: getQueueNodes(currentGraph).length,
|
|
100
|
+
scans: getScanEdges(currentGraph).length,
|
|
78
101
|
...summarizeFindings(currentFindings),
|
|
79
102
|
},
|
|
80
103
|
})));
|
|
@@ -86,26 +109,44 @@ export function createMcpServer() {
|
|
|
86
109
|
// Also check if there's a Lambda node with this name (for AWS-deployed functions)
|
|
87
110
|
const lambdaNode = currentGraph.nodes.find((n) => n.type === 'lambda' && n.name === functionName);
|
|
88
111
|
if (!funcNode && !lambdaNode) {
|
|
89
|
-
return toText({
|
|
112
|
+
return toText({
|
|
113
|
+
function: functionName,
|
|
114
|
+
found: false,
|
|
115
|
+
issues: [],
|
|
116
|
+
recommendations: [`Function "${functionName}" not found in the analyzed codebase.`],
|
|
117
|
+
});
|
|
90
118
|
}
|
|
91
119
|
const outEdges = funcNode ? getOutgoingEdges(currentGraph, funcNode.id) : [];
|
|
92
120
|
const relatedFindings = currentFindings.filter((f) => {
|
|
93
121
|
const meta = f.metadata;
|
|
94
|
-
return meta?.functionName === functionName ||
|
|
122
|
+
return (meta?.functionName === functionName ||
|
|
123
|
+
String(meta?.callerFunctions ?? '').includes(functionName));
|
|
95
124
|
});
|
|
96
125
|
const allTriggers = lambdaNode?.type === 'lambda' ? (lambdaNode.triggers ?? []) : [];
|
|
97
126
|
return toText({
|
|
98
|
-
function: functionName,
|
|
127
|
+
function: functionName,
|
|
128
|
+
found: true,
|
|
99
129
|
file: funcNode?.type === 'function' ? funcNode.file : undefined,
|
|
100
130
|
triggers: allTriggers.map((t) => ({
|
|
101
|
-
type: t.type,
|
|
131
|
+
type: t.type,
|
|
132
|
+
source: t.sourceName,
|
|
133
|
+
eventShape: t.eventShape,
|
|
102
134
|
...(t.ruleName ? { ruleName: t.ruleName, eventPattern: t.eventPattern } : {}),
|
|
103
135
|
})),
|
|
104
136
|
accesses: outEdges.map((e) => {
|
|
105
137
|
const target = currentGraph.nodes.find((n) => n.id === e.to);
|
|
106
|
-
return {
|
|
138
|
+
return {
|
|
139
|
+
targetId: e.to,
|
|
140
|
+
edgeType: e.type,
|
|
141
|
+
targetName: target && 'name' in target ? target.name : e.to,
|
|
142
|
+
targetType: target?.type,
|
|
143
|
+
};
|
|
107
144
|
}),
|
|
108
|
-
issues: relatedFindings.map((f) => ({
|
|
145
|
+
issues: relatedFindings.map((f) => ({
|
|
146
|
+
severity: f.severity,
|
|
147
|
+
issue: f.issue,
|
|
148
|
+
description: f.description,
|
|
149
|
+
})),
|
|
109
150
|
recommendations: [...new Set(relatedFindings.map((f) => f.recommendation))],
|
|
110
151
|
});
|
|
111
152
|
}));
|
|
@@ -119,10 +160,20 @@ export function createMcpServer() {
|
|
|
119
160
|
const sanitizedAttr = attribute.replace(/[^a-zA-Z0-9_]/g, '_');
|
|
120
161
|
const sanitizedTable = tableName.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
121
162
|
const indexName = `${sanitizedTable}-${sanitizedAttr}-index`;
|
|
122
|
-
const tableNode = currentGraph.nodes.find((n) => n.type === 'table' &&
|
|
163
|
+
const tableNode = currentGraph.nodes.find((n) => n.type === 'table' &&
|
|
164
|
+
n.databaseType === 'dynamodb' &&
|
|
165
|
+
'name' in n &&
|
|
166
|
+
n.name === tableName);
|
|
123
167
|
return toText({
|
|
124
|
-
table: tableName,
|
|
125
|
-
|
|
168
|
+
table: tableName,
|
|
169
|
+
attribute,
|
|
170
|
+
found: !!tableNode,
|
|
171
|
+
index: {
|
|
172
|
+
name: indexName,
|
|
173
|
+
partitionKey: attribute,
|
|
174
|
+
projectionType: 'ALL',
|
|
175
|
+
billingMode: 'PAY_PER_REQUEST',
|
|
176
|
+
},
|
|
126
177
|
rationale: `A GSI on "${attribute}" allows Query instead of Scan when filtering by this attribute.`,
|
|
127
178
|
recommendation: `Add GSI "${indexName}" with partition key "${attribute}" to your IaC definition.`,
|
|
128
179
|
});
|
|
@@ -138,11 +189,15 @@ export function createMcpServer() {
|
|
|
138
189
|
const sanitizedTable = tableName.replace(/[^a-zA-Z0-9_]/g, '_');
|
|
139
190
|
const indexName = `idx_${sanitizedTable}_${sanitizedCol}`;
|
|
140
191
|
return toText({
|
|
141
|
-
table: tableName,
|
|
192
|
+
table: tableName,
|
|
193
|
+
column,
|
|
142
194
|
recommendation: `CREATE INDEX CONCURRENTLY ${indexName} ON ${sanitizedTable} (${sanitizedCol});`,
|
|
143
195
|
rationale: `An index on "${column}" eliminates sequential scans when filtering on this column.`,
|
|
144
|
-
notes: [
|
|
145
|
-
|
|
196
|
+
notes: [
|
|
197
|
+
'Use CONCURRENTLY to avoid locking the table',
|
|
198
|
+
'Run ANALYZE after creation',
|
|
199
|
+
`Partial index: CREATE INDEX CONCURRENTLY ${indexName}_partial ON ${sanitizedTable} (${sanitizedCol}) WHERE ${sanitizedCol} IS NOT NULL;`,
|
|
200
|
+
],
|
|
146
201
|
});
|
|
147
202
|
}));
|
|
148
203
|
mcp.registerTool('suggest_mongo_index', {
|
|
@@ -155,7 +210,8 @@ export function createMcpServer() {
|
|
|
155
210
|
const sanitizedCollection = collection.replace(/[^a-zA-Z0-9_]/g, '_');
|
|
156
211
|
const sanitizedField = field.replace(/[^a-zA-Z0-9_.]/g, '_');
|
|
157
212
|
return toText({
|
|
158
|
-
collection,
|
|
213
|
+
collection,
|
|
214
|
+
field,
|
|
159
215
|
recommendation: `db.${sanitizedCollection}.createIndex({ ${sanitizedField}: 1 })`,
|
|
160
216
|
rationale: `An index on "${field}" eliminates full collection scans when filtering on this field.`,
|
|
161
217
|
notes: [
|
|
@@ -176,11 +232,15 @@ export function createMcpServer() {
|
|
|
176
232
|
const sanitizedTable = tableName.replace(/[^a-zA-Z0-9_]/g, '_');
|
|
177
233
|
const indexName = `idx_${sanitizedTable}_${sanitizedCol}`;
|
|
178
234
|
return toText({
|
|
179
|
-
table: tableName,
|
|
235
|
+
table: tableName,
|
|
236
|
+
column,
|
|
180
237
|
recommendation: `ALTER TABLE ${sanitizedTable} ADD INDEX ${indexName} (${sanitizedCol});`,
|
|
181
238
|
rationale: `An index on "${column}" eliminates full table scans when filtering on this column.`,
|
|
182
|
-
notes: [
|
|
183
|
-
|
|
239
|
+
notes: [
|
|
240
|
+
'MySQL InnoDB adds indexes online (no full lock for 5.6+)',
|
|
241
|
+
'EXPLAIN SELECT ... to verify after adding',
|
|
242
|
+
`Composite: ALTER TABLE ${sanitizedTable} ADD INDEX idx_composite (${sanitizedCol}, other_column);`,
|
|
243
|
+
],
|
|
184
244
|
});
|
|
185
245
|
}));
|
|
186
246
|
mcp.registerTool('get_queue_details', {
|
|
@@ -192,9 +252,15 @@ export function createMcpServer() {
|
|
|
192
252
|
return toText({
|
|
193
253
|
total: queues.length,
|
|
194
254
|
queues: queues.map((q) => ({
|
|
195
|
-
name: q.name,
|
|
196
|
-
|
|
197
|
-
|
|
255
|
+
name: q.name,
|
|
256
|
+
provider: q.provider,
|
|
257
|
+
hasDLQ: q.hasDLQ,
|
|
258
|
+
encrypted: q.encrypted,
|
|
259
|
+
approximateMessages: q.approximateMessages,
|
|
260
|
+
retentionDays: q.retentionDays,
|
|
261
|
+
findings: queueFindings
|
|
262
|
+
.filter((f) => f.metadata.queueName === q.name)
|
|
263
|
+
.map((f) => ({ severity: f.severity, issue: f.issue })),
|
|
198
264
|
})),
|
|
199
265
|
});
|
|
200
266
|
}));
|
|
@@ -203,7 +269,15 @@ export function createMcpServer() {
|
|
|
203
269
|
inputSchema: z.object({}),
|
|
204
270
|
}, logged('get_topic_details', async () => {
|
|
205
271
|
const topics = getTopicNodes(currentGraph);
|
|
206
|
-
return toText({
|
|
272
|
+
return toText({
|
|
273
|
+
total: topics.length,
|
|
274
|
+
topics: topics.map((t) => ({
|
|
275
|
+
name: t.name,
|
|
276
|
+
provider: t.provider,
|
|
277
|
+
subscriptionCount: t.subscriptionCount,
|
|
278
|
+
encrypted: t.encrypted,
|
|
279
|
+
})),
|
|
280
|
+
});
|
|
207
281
|
}));
|
|
208
282
|
mcp.registerTool('get_secrets_overview', {
|
|
209
283
|
description: 'Returns all Secrets Manager secrets with rotation status and rotation interval. Secret values are never returned. Call this when checking which secrets exist, confirming rotation is enabled before a security review, or identifying secrets that lack rotation.',
|
|
@@ -212,10 +286,16 @@ export function createMcpServer() {
|
|
|
212
286
|
const secrets = getSecretNodes(currentGraph);
|
|
213
287
|
const secretFindings = currentFindings.filter((f) => f.metadata?.secretName);
|
|
214
288
|
return toText({
|
|
215
|
-
total: secrets.length,
|
|
289
|
+
total: secrets.length,
|
|
290
|
+
note: 'Secret values are never included in this response.',
|
|
216
291
|
secrets: secrets.map((s) => ({
|
|
217
|
-
name: s.name,
|
|
218
|
-
|
|
292
|
+
name: s.name,
|
|
293
|
+
provider: s.provider,
|
|
294
|
+
rotationEnabled: s.rotationEnabled,
|
|
295
|
+
rotationDays: s.rotationDays,
|
|
296
|
+
findings: secretFindings
|
|
297
|
+
.filter((f) => f.metadata.secretName === s.name)
|
|
298
|
+
.map((f) => ({ severity: f.severity, issue: f.issue })),
|
|
219
299
|
})),
|
|
220
300
|
});
|
|
221
301
|
}));
|
|
@@ -225,8 +305,14 @@ export function createMcpServer() {
|
|
|
225
305
|
}, logged('get_parameter_overview', async () => {
|
|
226
306
|
const parameters = getParameterNodes(currentGraph);
|
|
227
307
|
return toText({
|
|
228
|
-
total: parameters.length,
|
|
229
|
-
|
|
308
|
+
total: parameters.length,
|
|
309
|
+
note: 'Parameter values are never included in this response.',
|
|
310
|
+
parameters: parameters.map((p) => ({
|
|
311
|
+
name: p.name,
|
|
312
|
+
provider: p.provider,
|
|
313
|
+
type: p.paramType,
|
|
314
|
+
tier: p.tier,
|
|
315
|
+
})),
|
|
230
316
|
});
|
|
231
317
|
}));
|
|
232
318
|
mcp.registerTool('get_lambda_overview', {
|
|
@@ -236,12 +322,24 @@ export function createMcpServer() {
|
|
|
236
322
|
const lambdas = getLambdaNodes(currentGraph);
|
|
237
323
|
const lambdaFindings = currentFindings.filter((f) => f.metadata?.functionName);
|
|
238
324
|
return toText({
|
|
239
|
-
total: lambdas.length,
|
|
325
|
+
total: lambdas.length,
|
|
326
|
+
note: 'Environment variable values are never included.',
|
|
240
327
|
lambdas: lambdas.map((l) => ({
|
|
241
|
-
name: l.name,
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
328
|
+
name: l.name,
|
|
329
|
+
runtime: l.runtime,
|
|
330
|
+
memoryMB: l.memoryMB,
|
|
331
|
+
timeoutSec: l.timeoutSec,
|
|
332
|
+
envVarCount: l.envVarKeys?.length ?? 0,
|
|
333
|
+
envVarKeys: l.envVarKeys,
|
|
334
|
+
triggers: (l.triggers ?? []).map((t) => ({
|
|
335
|
+
type: t.type,
|
|
336
|
+
source: t.sourceName,
|
|
337
|
+
eventShape: t.eventShape,
|
|
338
|
+
state: t.state,
|
|
339
|
+
})),
|
|
340
|
+
findings: lambdaFindings
|
|
341
|
+
.filter((f) => f.metadata.functionName === l.name)
|
|
342
|
+
.map((f) => ({ severity: f.severity, issue: f.issue })),
|
|
245
343
|
})),
|
|
246
344
|
});
|
|
247
345
|
}));
|
|
@@ -261,7 +359,7 @@ export function createMcpServer() {
|
|
|
261
359
|
.filter((e) => e.from === r.id && e.type === 'triggers')
|
|
262
360
|
.map((e) => currentGraph.nodes.find((n) => n.id === e.to))
|
|
263
361
|
.filter(Boolean)
|
|
264
|
-
.map((n) => n && 'name' in n ? n.name : ''),
|
|
362
|
+
.map((n) => (n && 'name' in n ? n.name : '')),
|
|
265
363
|
})),
|
|
266
364
|
});
|
|
267
365
|
}));
|
|
@@ -288,13 +386,20 @@ export function createMcpServer() {
|
|
|
288
386
|
}));
|
|
289
387
|
mcp.registerTool('get_log_errors', {
|
|
290
388
|
description: 'Returns recent error pattern summaries from CloudWatch log groups: pattern counts and frequencies grouped by log group. Raw log messages are never returned. Use the optional logGroup filter to scope to one group by name substring. Call this when investigating errors or identifying log groups with no retention policy.',
|
|
291
|
-
inputSchema: z.object({
|
|
389
|
+
inputSchema: z.object({
|
|
390
|
+
logGroup: z.string().describe('Filter to a specific log group name (optional)').optional(),
|
|
391
|
+
}),
|
|
292
392
|
}, logged('get_log_errors', async ({ logGroup: filterName }) => {
|
|
293
393
|
const logGroups = getLogGroupNodes(currentGraph).filter((lg) => !filterName || lg.name.includes(filterName));
|
|
294
394
|
return toText({
|
|
295
395
|
note: 'Only error patterns and counts are returned — no raw log messages.',
|
|
296
396
|
windowHours: 24,
|
|
297
|
-
logGroups: logGroups.map((lg) => ({
|
|
397
|
+
logGroups: logGroups.map((lg) => ({
|
|
398
|
+
name: lg.name,
|
|
399
|
+
retentionDays: lg.retentionDays ?? 'never-expires',
|
|
400
|
+
errorCount: lg.errorCount,
|
|
401
|
+
topErrorPatterns: lg.topErrorPatterns,
|
|
402
|
+
})),
|
|
298
403
|
});
|
|
299
404
|
}));
|
|
300
405
|
return mcp;
|
|
@@ -303,9 +408,9 @@ export function createMcpServer() {
|
|
|
303
408
|
export function createServer(port = 3000) {
|
|
304
409
|
const fastify = Fastify({ logger: false });
|
|
305
410
|
fastify.register(cors, { origin: true });
|
|
306
|
-
const mcp = createMcpServer();
|
|
307
411
|
fastify.get('/health', async () => ({
|
|
308
|
-
status: 'ok',
|
|
412
|
+
status: 'ok',
|
|
413
|
+
version,
|
|
309
414
|
graphNodes: currentGraph.nodes.length,
|
|
310
415
|
graphEdges: currentGraph.edges.length,
|
|
311
416
|
findings: currentFindings.length,
|
|
@@ -320,13 +425,27 @@ export function createServer(port = 3000) {
|
|
|
320
425
|
repository: 'https://github.com/Sidd27/infrawise',
|
|
321
426
|
transports: [{ type: 'streamable-http', url: `http://localhost:${port}/mcp` }],
|
|
322
427
|
tools: [
|
|
323
|
-
'get_infra_overview',
|
|
324
|
-
'
|
|
325
|
-
'
|
|
326
|
-
'
|
|
428
|
+
'get_infra_overview',
|
|
429
|
+
'get_graph_summary',
|
|
430
|
+
'analyze_function',
|
|
431
|
+
'suggest_gsi',
|
|
432
|
+
'postgres_index_suggestions',
|
|
433
|
+
'suggest_mongo_index',
|
|
434
|
+
'mysql_index_suggestions',
|
|
435
|
+
'get_queue_details',
|
|
436
|
+
'get_topic_details',
|
|
437
|
+
'get_secrets_overview',
|
|
438
|
+
'get_parameter_overview',
|
|
439
|
+
'get_lambda_overview',
|
|
440
|
+
'get_eventbridge_details',
|
|
441
|
+
'get_s3_overview',
|
|
442
|
+
'get_log_errors',
|
|
327
443
|
],
|
|
328
444
|
}));
|
|
329
445
|
fastify.post('/mcp', async (request, reply) => {
|
|
446
|
+
// Fresh McpServer per request: connect() is one-shot per instance and throws if called
|
|
447
|
+
// on a live server, so a shared instance breaks under concurrent requests.
|
|
448
|
+
const mcp = createMcpServer();
|
|
330
449
|
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
|
|
331
450
|
reply.raw.on('close', () => transport.close());
|
|
332
451
|
await mcp.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "infrawise",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.3",
|
|
4
4
|
"mcpName": "io.github.Sidd27/infrawise",
|
|
5
5
|
"description": "CLI-first infrastructure intelligence platform — analyzes DynamoDB, PostgreSQL, MySQL, MongoDB, SQS, SNS, SSM, Secrets Manager, Lambda, S3, CloudWatch Logs and exposes findings as an MCP server for Claude Code",
|
|
6
6
|
"keywords": [
|
|
@@ -59,24 +59,30 @@
|
|
|
59
59
|
"coverage": "vitest run --coverage",
|
|
60
60
|
"lint": "eslint src",
|
|
61
61
|
"typecheck": "tsc",
|
|
62
|
+
"format": "prettier --write src",
|
|
63
|
+
"generate-diagrams": "mmdc -i docs/architecture.mmd -o docs/architecture.svg --backgroundColor transparent",
|
|
62
64
|
"dev": "node dist/cli/index.js dev",
|
|
63
|
-
"release": "node scripts/release.js"
|
|
65
|
+
"release": "node scripts/release.js",
|
|
66
|
+
"prepare": "simple-git-hooks"
|
|
67
|
+
},
|
|
68
|
+
"simple-git-hooks": {
|
|
69
|
+
"pre-commit": "pnpm format && git add src/ && pnpm lint && pnpm typecheck && pnpm test"
|
|
64
70
|
},
|
|
65
71
|
"dependencies": {
|
|
66
72
|
"@aws-sdk/client-cloudwatch-logs": "^3.1048.0",
|
|
67
|
-
"@aws-sdk/client-s3": "^3.1048.0",
|
|
68
73
|
"@aws-sdk/client-dynamodb": "^3.1048.0",
|
|
69
74
|
"@aws-sdk/client-eventbridge": "^3.1051.0",
|
|
70
75
|
"@aws-sdk/client-lambda": "^3.1048.0",
|
|
71
76
|
"@aws-sdk/client-rds": "^3.1048.0",
|
|
77
|
+
"@aws-sdk/client-s3": "^3.1048.0",
|
|
72
78
|
"@aws-sdk/client-secrets-manager": "^3.1048.0",
|
|
73
79
|
"@aws-sdk/client-sns": "^3.1048.0",
|
|
74
80
|
"@aws-sdk/client-sqs": "^3.1048.0",
|
|
75
81
|
"@aws-sdk/client-ssm": "^3.1048.0",
|
|
76
82
|
"@aws-sdk/credential-providers": "^3.1048.0",
|
|
83
|
+
"@fastify/cors": "^11.2.0",
|
|
77
84
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
78
85
|
"chalk": "^5.6.2",
|
|
79
|
-
"@fastify/cors": "^11.2.0",
|
|
80
86
|
"commander": "^15.0.0",
|
|
81
87
|
"fastify": "^5.8.5",
|
|
82
88
|
"inquirer": "^14.0.2",
|
|
@@ -91,6 +97,7 @@
|
|
|
91
97
|
"zod": "^4.4.3"
|
|
92
98
|
},
|
|
93
99
|
"devDependencies": {
|
|
100
|
+
"@mermaid-js/mermaid-cli": "^11.15.0",
|
|
94
101
|
"@types/inquirer": "^9.0.9",
|
|
95
102
|
"@types/js-yaml": "^4.0.9",
|
|
96
103
|
"@types/node": "^25.8.0",
|
|
@@ -99,6 +106,8 @@
|
|
|
99
106
|
"@typescript-eslint/parser": "^8.59.3",
|
|
100
107
|
"@vitest/coverage-v8": "^4.1.6",
|
|
101
108
|
"eslint": "^10.4.0",
|
|
109
|
+
"prettier": "^3.8.3",
|
|
110
|
+
"simple-git-hooks": "^2.13.1",
|
|
102
111
|
"typescript": "^6.0.3",
|
|
103
112
|
"vite": "^8.0.13",
|
|
104
113
|
"vitest": "^4.1.6"
|
|
@@ -113,6 +122,10 @@
|
|
|
113
122
|
"pnpm": {
|
|
114
123
|
"overrides": {
|
|
115
124
|
"qs": ">=6.15.2"
|
|
116
|
-
}
|
|
125
|
+
},
|
|
126
|
+
"allowedBuildScripts": [
|
|
127
|
+
"puppeteer",
|
|
128
|
+
"simple-git-hooks"
|
|
129
|
+
]
|
|
117
130
|
}
|
|
118
131
|
}
|