infrawise 0.13.0 → 0.13.2
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 -1
- package/dist/cli/commands/serve.js +4 -4
- package/dist/cli/commands/stdio.js +4 -4
- package/dist/core/cache.js +14 -0
- package/dist/core/index.js +1 -1
- package/dist/server/index.js +23 -2
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -149,7 +149,7 @@ Add to your editor's MCP config:
|
|
|
149
149
|
|
|
150
150
|
| Tool | What it provides |
|
|
151
151
|
| ---------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
152
|
-
| `get_infra_overview` | Complete snapshot —
|
|
152
|
+
| `get_infra_overview` | Complete snapshot — services, counts, high-severity findings, analysis `freshness` (age + stale flag), `configured` flag |
|
|
153
153
|
| `get_graph_summary` | Full infrastructure graph — all nodes, edges, and findings |
|
|
154
154
|
| `analyze_function` | Issues in a specific function — scans, missing indexes, N+1, trigger event shapes, missing IAM permissions |
|
|
155
155
|
| `suggest_gsi` | Exact GSI config for a DynamoDB table + attribute |
|
|
@@ -2,7 +2,7 @@ import * as fs from 'fs';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import ora from 'ora';
|
|
5
|
-
import { loadConfig, readCache, setCacheDir } from '../../core/index.js';
|
|
5
|
+
import { loadConfig, readCache, readCacheTimestamp, setCacheDir } from '../../core/index.js';
|
|
6
6
|
import { createServer, setGraphState, setConfigured } from '../../server/index.js';
|
|
7
7
|
import { log, printHeader } from '../utils.js';
|
|
8
8
|
import { runAnalyze, runCodeRefresh } from './analyze.js';
|
|
@@ -83,7 +83,7 @@ export async function runServe(options = {}) {
|
|
|
83
83
|
const cachedFindings = readCache('findings');
|
|
84
84
|
if (cachedGraph && cachedFindings) {
|
|
85
85
|
log.success('Cached analysis loaded', `${cachedGraph.nodes.length} nodes · ${cachedGraph.edges.length} edges · ${cachedFindings.length} finding(s)`);
|
|
86
|
-
setGraphState(cachedGraph, cachedFindings);
|
|
86
|
+
setGraphState(cachedGraph, cachedFindings, readCacheTimestamp('graph'));
|
|
87
87
|
}
|
|
88
88
|
else if (config) {
|
|
89
89
|
log.warn('No cache found — running analysis now...');
|
|
@@ -91,10 +91,10 @@ export async function runServe(options = {}) {
|
|
|
91
91
|
await runAnalyze({ repo: repoPath, config: options.config });
|
|
92
92
|
const freshGraph = readCache('graph') ?? { nodes: [], edges: [] };
|
|
93
93
|
const freshFindings = readCache('findings') ?? [];
|
|
94
|
-
setGraphState(freshGraph, freshFindings);
|
|
94
|
+
setGraphState(freshGraph, freshFindings, readCacheTimestamp('graph'));
|
|
95
95
|
}
|
|
96
96
|
else {
|
|
97
|
-
setGraphState({ nodes: [], edges: [] }, []);
|
|
97
|
+
setGraphState({ nodes: [], edges: [] }, [], null);
|
|
98
98
|
}
|
|
99
99
|
console.log('');
|
|
100
100
|
// Start server
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
-
import { loadConfig, readCache, setCacheDir } from '../../core/index.js';
|
|
4
|
+
import { loadConfig, readCache, readCacheTimestamp, setCacheDir } from '../../core/index.js';
|
|
5
5
|
import { createMcpServer, setGraphState, setConfigured } from '../../server/index.js';
|
|
6
6
|
import { runAnalyze, runCodeRefresh } from './analyze.js';
|
|
7
7
|
const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
@@ -22,16 +22,16 @@ export async function runStdio(configPath) {
|
|
|
22
22
|
const cachedGraph = readCache('graph', CACHE_TTL_MS);
|
|
23
23
|
const cachedFindings = readCache('findings', CACHE_TTL_MS);
|
|
24
24
|
if (cachedGraph && cachedFindings) {
|
|
25
|
-
setGraphState(cachedGraph, cachedFindings);
|
|
25
|
+
setGraphState(cachedGraph, cachedFindings, readCacheTimestamp('graph'));
|
|
26
26
|
}
|
|
27
27
|
else if (config) {
|
|
28
28
|
await runAnalyze({ config: configPath });
|
|
29
29
|
const graph = readCache('graph', CACHE_TTL_MS) ?? { nodes: [], edges: [] };
|
|
30
30
|
const findings = readCache('findings', CACHE_TTL_MS) ?? [];
|
|
31
|
-
setGraphState(graph, findings);
|
|
31
|
+
setGraphState(graph, findings, readCacheTimestamp('graph'));
|
|
32
32
|
}
|
|
33
33
|
else {
|
|
34
|
-
setGraphState({ nodes: [], edges: [] }, []);
|
|
34
|
+
setGraphState({ nodes: [], edges: [] }, [], null);
|
|
35
35
|
}
|
|
36
36
|
// File watching drives runCodeRefresh, which needs a config — skip it without one.
|
|
37
37
|
// stderr is safe in stdio transport; stdout is reserved for MCP JSON-RPC.
|
package/dist/core/cache.js
CHANGED
|
@@ -37,6 +37,20 @@ export function readCache(key, maxAgeMs = 3600000) {
|
|
|
37
37
|
return null;
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
|
+
// Returns when the entry was written (ms epoch), ignoring TTL — used to surface
|
|
41
|
+
// analysis freshness. null if the entry is missing or unreadable.
|
|
42
|
+
export function readCacheTimestamp(key) {
|
|
43
|
+
const filePath = path.join(cacheDir, `${key}.json`);
|
|
44
|
+
if (!fs.existsSync(filePath))
|
|
45
|
+
return null;
|
|
46
|
+
try {
|
|
47
|
+
const entry = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
48
|
+
return typeof entry.timestamp === 'number' ? entry.timestamp : null;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
40
54
|
export function clearCache(key) {
|
|
41
55
|
if (key) {
|
|
42
56
|
const filePath = path.join(cacheDir, `${key}.json`);
|
package/dist/core/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { loadConfig, generateDefaultConfig, InfrawiseConfigSchema, ConfigError } from './config.js';
|
|
2
2
|
export { logger } from './logger.js';
|
|
3
3
|
export { InfrawiseError, DynamoDBError, PostgresConnectionError, RepositoryScanError, formatError, } from './errors.js';
|
|
4
|
-
export { writeCache, readCache, clearCache, setCacheDir } from './cache.js';
|
|
4
|
+
export { writeCache, readCache, readCacheTimestamp, clearCache, setCacheDir } from './cache.js';
|
package/dist/server/index.js
CHANGED
|
@@ -12,12 +12,32 @@ import { getTableNodes, getFunctionNodes, getQueueNodes, getTopicNodes, getSecre
|
|
|
12
12
|
// ── State ────────────────────────────────────────────────────────────────────
|
|
13
13
|
let currentGraph = { nodes: [], edges: [] };
|
|
14
14
|
let currentFindings = [];
|
|
15
|
+
// When the loaded analysis was produced (ms epoch). null when serving an empty
|
|
16
|
+
// graph with no analysis. Surfaced via get_infra_overview so assistants can
|
|
17
|
+
// judge how stale the facts are and decide to refresh.
|
|
18
|
+
let analyzedAt = null;
|
|
19
|
+
// Analysis is considered stale past this age — matches the 24h cache TTL that
|
|
20
|
+
// drives auto-refresh in stdio/start.
|
|
21
|
+
const STALE_AGE_MS = 24 * 60 * 60 * 1000;
|
|
15
22
|
// False when the server booted without an infrawise.yaml (e.g. a hosted MCP
|
|
16
23
|
// runtime). Used to return a "run locally" hint instead of a bare empty graph.
|
|
17
24
|
let configured = true;
|
|
18
|
-
export function setGraphState(graph, findings) {
|
|
25
|
+
export function setGraphState(graph, findings, analyzedAtMs = Date.now()) {
|
|
19
26
|
currentGraph = graph;
|
|
20
27
|
currentFindings = findings;
|
|
28
|
+
analyzedAt = analyzedAtMs;
|
|
29
|
+
}
|
|
30
|
+
function freshness() {
|
|
31
|
+
if (analyzedAt === null)
|
|
32
|
+
return { analyzedAt: null, ageSeconds: null, stale: false };
|
|
33
|
+
const ageMs = Date.now() - analyzedAt;
|
|
34
|
+
const stale = ageMs > STALE_AGE_MS;
|
|
35
|
+
return {
|
|
36
|
+
analyzedAt: new Date(analyzedAt).toISOString(),
|
|
37
|
+
ageSeconds: Math.round(ageMs / 1000),
|
|
38
|
+
stale,
|
|
39
|
+
...(stale ? { hint: 'Analysis is stale — run `infrawise analyze` to refresh.' } : {}),
|
|
40
|
+
};
|
|
21
41
|
}
|
|
22
42
|
export function setConfigured(value) {
|
|
23
43
|
configured = value;
|
|
@@ -38,7 +58,7 @@ function logged(name, fn) {
|
|
|
38
58
|
export function createMcpServer() {
|
|
39
59
|
const mcp = new McpServer({ name: 'infrawise', version });
|
|
40
60
|
mcp.registerTool('get_infra_overview', {
|
|
41
|
-
description: 'Returns a compact infrastructure snapshot: service counts, all databases, queues, topics, secrets, lambdas, and high-severity findings. Call this first at the start of any database or infrastructure task to understand what services are in scope. Prefer this over get_graph_summary for quick orientation; use get_graph_summary only when you need every node, edge, and finding in full. Also returns a `configured` flag — when false, the server has no infrawise.yaml loaded (e.g. a remotely hosted instance) and all tools return empty results; a `setupHint` explains how to run infrawise locally.',
|
|
61
|
+
description: 'Returns a compact infrastructure snapshot: service counts, all databases, queues, topics, secrets, lambdas, and high-severity findings. Call this first at the start of any database or infrastructure task to understand what services are in scope. Prefer this over get_graph_summary for quick orientation; use get_graph_summary only when you need every node, edge, and finding in full. Also returns a `configured` flag — when false, the server has no infrawise.yaml loaded (e.g. a remotely hosted instance) and all tools return empty results; a `setupHint` explains how to run infrawise locally. The `freshness` field reports when the analysis ran (`analyzedAt`, `ageSeconds`) and a `stale` flag with a refresh hint once the data is older than 24h.',
|
|
42
62
|
inputSchema: z.object({}),
|
|
43
63
|
}, logged('get_infra_overview', async () => {
|
|
44
64
|
const tables = getTableNodes(currentGraph);
|
|
@@ -53,6 +73,7 @@ export function createMcpServer() {
|
|
|
53
73
|
return toText({
|
|
54
74
|
configured,
|
|
55
75
|
...(configured ? {} : { setupHint: NOT_CONFIGURED_HINT }),
|
|
76
|
+
freshness: freshness(),
|
|
56
77
|
summary: {
|
|
57
78
|
tables: tables.length,
|
|
58
79
|
functions: functions.length,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "infrawise",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.2",
|
|
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, API Gateway, CloudWatch Logs and exposes findings as an MCP server for Claude Code",
|
|
6
6
|
"keywords": [
|
|
@@ -67,6 +67,7 @@
|
|
|
67
67
|
"record": "bash docs/demo/record.sh",
|
|
68
68
|
"dev": "node dist/cli/index.js serve",
|
|
69
69
|
"release": "node scripts/release.js",
|
|
70
|
+
"publish-smithery": "node scripts/smithery-publish.mjs",
|
|
70
71
|
"prepare": "simple-git-hooks"
|
|
71
72
|
},
|
|
72
73
|
"simple-git-hooks": {
|