Yogesh
initial deploy
cd8bd0a
Raw
History Blame Contribute Delete
4.31 kB
import { NextResponse } from "next/server";
import fs from "fs/promises";
import path from "path";
import { z } from "zod";
import { requireCliToolsAuth } from "@/lib/api/requireCliToolsAuth";
import { getCliPrimaryConfigPath } from "@/shared/services/cliRuntime";
import { validateBaseUrl } from "@/lib/cli-helper/config-generator";
import {
generateHermesAgentConfig,
getCurrentHermesAgentRoles,
} from "@/lib/cli-helper/config-generator/hermes-agent";
import { getHermesConfigPath } from "@/lib/cli-helper/config-generator/hermesHome";
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error.ts";
const hermesAgentSettingsSchema = z.object({
baseUrl: z.string().min(1, "baseUrl is required"),
keyId: z.string().optional().nullable(),
apiKey: z.string().optional().nullable(),
selections: z
.array(
z.object({
role: z.string(),
model: z.string(),
})
)
.min(1, "selections must be a non-empty array of { role, model }"),
preview: z.boolean().optional(),
});
/**
* Dedicated endpoint for Hermes Agent (the advanced Nous Research terminal agent).
* This is separate from the original simple "Hermes" guided tool.
*
* GET -> returns current per-role configuration (default, delegation, auxiliary.*)
* POST -> accepts { baseUrl, keyId?, apiKey?, selections: [{role, model}, ...] }
*/
// Resolved lazily so HERMES_HOME is always honoured (#3628).
const getConfigPath = () => getHermesConfigPath();
function getMetadataPath(configPath: string) {
return path.join(path.dirname(configPath), ".first-setup.json");
}
export async function GET(request: Request) {
// cli-tools routes touch host config files — guard every handler with the shared auth.
const authError = await requireCliToolsAuth(request);
if (authError) return authError;
try {
const roles = await getCurrentHermesAgentRoles();
const configPath = getCliPrimaryConfigPath("hermes-agent") || getConfigPath();
let firstSetupAt: string | null = null;
try {
const metaRaw = await fs.readFile(getMetadataPath(configPath), "utf8");
const meta = JSON.parse(metaRaw);
firstSetupAt = meta.firstSetupAt || null;
} catch {
// no metadata yet
}
return NextResponse.json({ success: true, roles, firstSetupAt });
} catch (error) {
return NextResponse.json(
{ success: false, error: sanitizeErrorMessage(error) },
{ status: 500 }
);
}
}
export async function POST(request: Request) {
const authError = await requireCliToolsAuth(request);
if (authError) return authError;
let rawBody;
try {
rawBody = await request.json();
} catch {
return NextResponse.json({ error: "Invalid JSON" }, { status: 400 });
}
const parsed = hermesAgentSettingsSchema.safeParse(rawBody);
if (!parsed.success) {
return NextResponse.json(
{ error: parsed.error.issues[0]?.message ?? "Invalid request" },
{ status: 400 }
);
}
const { baseUrl, keyId, apiKey, selections, preview } = parsed.data;
if (!validateBaseUrl(baseUrl)) {
return NextResponse.json({ error: "baseUrl must be a valid http(s) URL" }, { status: 400 });
}
const configPath = getCliPrimaryConfigPath("hermes-agent") || getConfigPath();
const configDir = path.dirname(configPath);
await fs.mkdir(configDir, { recursive: true });
const payload = {
baseUrl,
keyId,
apiKey,
selections,
};
const result = await generateHermesAgentConfig(payload);
if (result.error) {
return NextResponse.json({ error: result.error }, { status: 400 });
}
// Preview mode: return the would-be YAML without writing it (Phase 5 polish)
if (preview === true) {
return NextResponse.json({
success: true,
preview: true,
yaml: result.yaml,
configPath,
});
}
await fs.writeFile(configPath, result.yaml, "utf-8");
// Record first setup time if this is the first save via OmniRoute
const metaPath = getMetadataPath(configPath);
try {
await fs.access(metaPath);
} catch {
await fs.writeFile(
metaPath,
JSON.stringify({ firstSetupAt: new Date().toISOString() }),
"utf8"
);
}
return NextResponse.json({
success: true,
message: `Hermes Agent config saved to ${configPath}`,
configPath,
});
}