Spaces:
Runtime error
Runtime error
File size: 4,309 Bytes
cd8bd0a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | 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,
});
}
|