Remove mock data from harness and add agent credential healthchecks
All checks were successful
CI / lint-and-test (push) Successful in 25s
Deploy Production / deploy (push) Successful in 59s
CI / build (push) Successful in 1m11s

Strip all seed/mock data (fake tasks, models, usage entries, agent configs)
so the dashboard starts clean and populates from real API state. Add
/api/agents/health endpoint that validates each agent's provider credentials
and CLI availability.
This commit is contained in:
Julia McGhee
2026-03-21 19:42:53 +00:00
parent 9a40240bd2
commit df1111da15
4 changed files with 140 additions and 176 deletions

View File

@@ -0,0 +1,131 @@
import { NextResponse } from "next/server";
import { getAllAgentConfigs, AGENT_RUNTIMES, AgentConfig } from "@/lib/agents";
import { getRawCredentialsByProvider, Provider } from "@/lib/credentials";
const PROVIDER_ENV_VARS: Record<string, string> = {
anthropic: "ANTHROPIC_API_KEY",
openai: "OPENAI_API_KEY",
google: "GOOGLE_API_KEY",
openrouter: "OPENROUTER_API_KEY",
"opencode-zen": "OPENCODE_ZEN_API_KEY",
};
const PROVIDER_VALIDATION: Record<string, (token: string, baseUrl?: string) => Promise<boolean>> = {
async anthropic(token) {
const res = await fetch("https://api.anthropic.com/v1/models", {
headers: { "x-api-key": token, "anthropic-version": "2023-06-01" },
});
return res.ok;
},
async openai(token, baseUrl) {
const res = await fetch(`${baseUrl || "https://api.openai.com"}/v1/models`, {
headers: { Authorization: `Bearer ${token}` },
});
return res.ok;
},
async google(token) {
const res = await fetch(
`https://generativelanguage.googleapis.com/v1beta/models?key=${token}`
);
return res.ok;
},
async openrouter(token) {
const res = await fetch("https://openrouter.ai/api/v1/models", {
headers: { Authorization: `Bearer ${token}` },
});
return res.ok;
},
};
export interface AgentHealthStatus {
agentId: string;
agentName: string;
runtime: string;
provider: string;
modelId: string;
credentialConfigured: boolean;
credentialValid: boolean | null; // null = not checked (no credential)
cliInstalled: boolean | null; // null = not checked
error?: string;
}
async function checkCliInstalled(command: string): Promise<boolean> {
try {
const { execSync } = require("node:child_process");
execSync(`which ${command}`, { stdio: "ignore" });
return true;
} catch {
return false;
}
}
async function checkAgent(config: AgentConfig): Promise<AgentHealthStatus> {
const runtime = AGENT_RUNTIMES[config.runtime];
const status: AgentHealthStatus = {
agentId: config.id,
agentName: config.name,
runtime: config.runtime,
provider: config.provider,
modelId: config.modelId,
credentialConfigured: false,
credentialValid: null,
cliInstalled: null,
};
// Check CLI
try {
status.cliInstalled = await checkCliInstalled(runtime.cliCommand);
} catch {
status.cliInstalled = false;
}
// Check credential exists
const creds = getRawCredentialsByProvider(config.provider as Provider);
status.credentialConfigured = creds.length > 0;
if (!status.credentialConfigured) {
return status;
}
// Validate credential against provider API
const validator = PROVIDER_VALIDATION[config.provider];
if (validator) {
try {
status.credentialValid = await validator(creds[0].token, creds[0].baseUrl);
} catch (err) {
status.credentialValid = false;
status.error = err instanceof Error ? err.message : "Validation failed";
}
} else {
// No validator for this provider (e.g. opencode-zen) — just confirm credential exists
status.credentialValid = null;
}
return status;
}
export async function GET() {
const configs = getAllAgentConfigs();
if (configs.length === 0) {
return NextResponse.json({
agents: [],
summary: { total: 0, healthy: 0, misconfigured: 0, unchecked: 0 },
});
}
const agents = await Promise.all(configs.map(checkAgent));
const healthy = agents.filter(
a => a.credentialConfigured && a.credentialValid === true && a.cliInstalled === true
).length;
const misconfigured = agents.filter(
a => !a.credentialConfigured || a.credentialValid === false || a.cliInstalled === false
).length;
const unchecked = agents.length - healthy - misconfigured;
return NextResponse.json({
agents,
summary: { total: agents.length, healthy, misconfigured, unchecked },
});
}