Fix runtime inventory MCP JSON parsing (#131)
* add gated codex goals support * sync README SDK versions * bump claude agent sdk * add codex goals settings toggle * reuse status dashboard message and simplify settings actions * refine settings page UX * fix settings nav hash routing * add dashboard UX verification * refine dashboard settings and inbox UX * remove inbox top-level navigation * remove dashboard health top-level navigation * fix: allow runtime image attachment paths * feat: configure attachment allowlist dirs * fix: clean duplicate dashboard status messages * fix: poll dashboard duplicate cleanup between status updates * fix: delete dashboard duplicates on create * Refine settings IA with tabbed sections * Add read-only runtime inventory settings * Fix runtime inventory MCP JSON parsing
This commit is contained in:
@@ -34,7 +34,19 @@ describe('runtime inventory', () => {
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(claudeDir, 'settings.json'),
|
||||
'{"fastMode":true,"secret":"should-not-leak"}\n',
|
||||
JSON.stringify({
|
||||
fastMode: true,
|
||||
secret: 'should-not-leak',
|
||||
mcpServers: {
|
||||
ejclaw: {
|
||||
command: 'node',
|
||||
args: ['secret-arg-should-not-leak'],
|
||||
},
|
||||
filesystem: {
|
||||
command: 'node',
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(codexDir, 'auth.json'),
|
||||
@@ -75,6 +87,10 @@ describe('runtime inventory', () => {
|
||||
ejclawConfigured: true,
|
||||
serverCount: 2,
|
||||
});
|
||||
expect(snapshot.claude.mcp).toMatchObject({
|
||||
ejclawConfigured: true,
|
||||
serverCount: 2,
|
||||
});
|
||||
expect(snapshot.codex.skillDirs[0]).toMatchObject({
|
||||
count: 1,
|
||||
skills: [{ name: 'browser', description: 'Browser automation' }],
|
||||
@@ -87,5 +103,6 @@ describe('runtime inventory', () => {
|
||||
const serialized = JSON.stringify(snapshot);
|
||||
expect(serialized).not.toContain('sk-secret');
|
||||
expect(serialized).not.toContain('should-not-leak');
|
||||
expect(serialized).not.toContain('secret-arg-should-not-leak');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -61,6 +61,11 @@ interface RuntimeInventoryOptions {
|
||||
generatedAt?: string;
|
||||
}
|
||||
|
||||
interface McpConfigSummary {
|
||||
ejclawConfigured: boolean;
|
||||
serverCount: number;
|
||||
}
|
||||
|
||||
function pathSnapshot(label: string, filePath: string): RuntimePathSnapshot {
|
||||
return {
|
||||
label,
|
||||
@@ -119,6 +124,42 @@ function readSkillDir(label: string, dirPath: string): RuntimeSkillDirSnapshot {
|
||||
};
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
||||
}
|
||||
|
||||
function summarizeTomlMcpConfig(content: string): McpConfigSummary {
|
||||
const serverMatches = content.match(/^\s*\[mcp_servers\.[^\]]+\]/gm) ?? [];
|
||||
return {
|
||||
ejclawConfigured: /^\s*\[mcp_servers\.ejclaw(?:\.[^\]]+)?\]/m.test(content),
|
||||
serverCount: serverMatches.length,
|
||||
};
|
||||
}
|
||||
|
||||
function summarizeJsonMcpConfig(content: string): McpConfigSummary | null {
|
||||
let parsed: unknown;
|
||||
try {
|
||||
parsed = JSON.parse(content);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
if (!isRecord(parsed)) return null;
|
||||
|
||||
const servers = parsed.mcpServers ?? parsed.mcp_servers;
|
||||
if (!isRecord(servers)) {
|
||||
return {
|
||||
ejclawConfigured: false,
|
||||
serverCount: 0,
|
||||
};
|
||||
}
|
||||
|
||||
const names = Object.keys(servers);
|
||||
return {
|
||||
ejclawConfigured: names.includes('ejclaw'),
|
||||
serverCount: names.length,
|
||||
};
|
||||
}
|
||||
|
||||
function readMcpSnapshot(
|
||||
label: string,
|
||||
configPath: string,
|
||||
@@ -132,11 +173,14 @@ function readMcpSnapshot(
|
||||
}
|
||||
}
|
||||
|
||||
const serverMatches = content.match(/^\s*\[mcp_servers\.[^\]]+\]/gm) ?? [];
|
||||
const summary =
|
||||
path.extname(configPath) === '.json'
|
||||
? (summarizeJsonMcpConfig(content) ?? summarizeTomlMcpConfig(content))
|
||||
: summarizeTomlMcpConfig(content);
|
||||
|
||||
return {
|
||||
configPath: pathSnapshot(label, configPath),
|
||||
ejclawConfigured: /^\s*\[mcp_servers\.ejclaw(?:\.[^\]]+)?\]/m.test(content),
|
||||
serverCount: serverMatches.length,
|
||||
...summary,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user