import { randomUUID } from "node:crypto"; const CONTRACT_VERSION = "provider-profile-management-v1"; const DEFAULT_AGENTRUN_MGR_URL = "http://agentrun-mgr.agentrun-v01.svc.cluster.local:8080"; const AGENTRUN_MANAGER_HOST_PATTERN = /^agentrun-mgr\.[a-z0-9]([-a-z0-9]*[a-z0-9])?\.svc\.cluster\.local$/u; const DEFAULT_TIMEOUT_MS = 30000; const PROVIDER_PROFILE_ALIASES = Object.freeze({ codex: "codex-api" }); const PROVIDER_PROFILE_ID_PATTERN = /^[a-z0-9][a-z0-9-]{0,63}$/u; const RESERVED_PROVIDER_PROFILES = new Set(["runtime-default"]); export async function handleProviderProfileCatalogHttp(request, response, options = {}) { try { const auth = await authenticateActor(request, options); if (!auth.ok) return sendJson(response, numericStatus(auth.status) ?? numericStatus(auth.statusCode) ?? 401, auth); const requestId = requestIdFor(request); const env = options.env ?? process.env; const managerUrl = resolveAgentRunManagerUrl(env); const agentRunApiKey = resolveAgentRunApiKey(env); const fetchImpl = options.fetchImpl ?? fetch; const timeoutMs = positiveInteger(env.HWLAB_PROVIDER_PROFILE_AGENTRUN_HTTP_TIMEOUT_MS, positiveInteger(env.HWLAB_CODE_AGENT_AGENTRUN_HTTP_TIMEOUT_MS, DEFAULT_TIMEOUT_MS)); const delegated = await agentRunJson(fetchImpl, managerUrl, "/api/v1/provider-profiles", { method: "GET", requestId, timeoutMs, apiKey: agentRunApiKey }); const data = normalizeCatalogData(delegated.data); const status = delegated.ok ? delegated.httpStatus || 200 : delegated.httpStatus || 502; const base = { ok: delegated.ok, status: delegated.ok ? "ok" : "failed", contractVersion: CONTRACT_VERSION, actor: actorSummary(auth.actor), valuesPrinted: false }; if (delegated.ok) { return sendJson(response, status, { ...base, items: data.items, count: data.count, data }); } return sendJson(response, status, { ...base, error: delegatedError(delegated), data: null }); } catch (error) { return sendError(response, error.httpStatus ?? 500, error.code ?? "provider_profile_catalog_failed", error.message ?? "Provider profile catalog failed", error.details); } } export async function handleProviderProfilesHttp(request, response, url, options = {}) { try { const auth = await authenticateAdmin(request, options); if (!auth.ok) return sendJson(response, numericStatus(auth.status) ?? numericStatus(auth.statusCode) ?? 401, auth); const requestId = requestIdFor(request); const route = parseProviderProfileRoute(url.pathname, request.method); const env = options.env ?? process.env; const managerUrl = resolveAgentRunManagerUrl(env); const agentRunApiKey = resolveAgentRunApiKey(env); const fetchImpl = options.fetchImpl ?? fetch; const timeoutMs = positiveInteger(env.HWLAB_PROVIDER_PROFILE_AGENTRUN_HTTP_TIMEOUT_MS, positiveInteger(env.HWLAB_CODE_AGENT_AGENTRUN_HTTP_TIMEOUT_MS, DEFAULT_TIMEOUT_MS)); if (route.kind === "list") { const delegated = await agentRunJson(fetchImpl, managerUrl, "/api/v1/provider-profiles", { method: "GET", requestId, timeoutMs, apiKey: agentRunApiKey }); return sendDelegated(response, delegated, auth, route, managerUrl, requestId, normalizeListData(delegated.data)); } if (route.kind === "remove") { const delegated = await agentRunJson(fetchImpl, managerUrl, `/api/v1/provider-profiles/${encodeURIComponent(route.agentRunProfile)}`, { method: "DELETE", requestId, timeoutMs, apiKey: agentRunApiKey }); return sendDelegated(response, delegated, auth, route, managerUrl, requestId, normalizeProfileData(delegated.data)); } if (route.kind === "credential") { const body = await readJsonObject(request, options.bodyLimitBytes); const credentialApiKey = text(body.apiKey); const authJson = typeof body.authJson === "string" ? body.authJson : ""; if (credentialApiKey.length < 8 && !authJson.trim()) return sendError(response, 400, "credential_required", "apiKey or authJson is required and must not be empty"); const delegatedBody = { ...(authJson.trim() ? { authJson } : { apiKey: credentialApiKey }), ...(body.config && typeof body.config === "object" ? { config: body.config } : {}), delegatedBy: delegatedBy(auth, requestId), reason: text(body.reason) || "hwlab-provider-management" }; const delegated = await agentRunJson(fetchImpl, managerUrl, `/api/v1/provider-profiles/${encodeURIComponent(route.agentRunProfile)}/credential`, { method: "PUT", body: delegatedBody, requestId, timeoutMs, apiKey: agentRunApiKey }); return sendDelegated(response, delegated, auth, route, managerUrl, requestId, normalizeProfileData(delegated.data)); } if (route.kind === "config") { if (route.method === "GET") { const delegated = await agentRunJson(fetchImpl, managerUrl, `/api/v1/provider-profiles/${encodeURIComponent(route.agentRunProfile)}/config`, { method: "GET", requestId, timeoutMs, apiKey: agentRunApiKey }); return sendDelegated(response, delegated, auth, route, managerUrl, requestId, normalizeConfigData(delegated.data), { allowConfigToml: true }); } const body = await readJsonObject(request, options.bodyLimitBytes); const configToml = typeof body.configToml === "string" ? body.configToml : ""; if (!configToml.trim()) return sendError(response, 400, "config_toml_required", "configToml is required and must not be empty"); const delegatedBody = { configToml, delegatedBy: delegatedBy(auth, requestId), reason: text(body.reason) || "hwlab-provider-management" }; const delegated = await agentRunJson(fetchImpl, managerUrl, `/api/v1/provider-profiles/${encodeURIComponent(route.agentRunProfile)}/config`, { method: "PUT", body: delegatedBody, requestId, timeoutMs, apiKey: agentRunApiKey }); return sendDelegated(response, delegated, auth, route, managerUrl, requestId, normalizeConfigData(delegated.data)); } if (route.kind === "validate") { const body = await readOptionalJsonObject(request, options.bodyLimitBytes); const delegatedBody = { ...(text(body.prompt) ? { prompt: text(body.prompt) } : {}), delegatedBy: delegatedBy(auth, requestId), reason: text(body.reason) || "hwlab-provider-management" }; const delegated = await agentRunJson(fetchImpl, managerUrl, `/api/v1/provider-profiles/${encodeURIComponent(route.agentRunProfile)}/validate`, { method: "POST", body: delegatedBody, requestId, timeoutMs, apiKey: agentRunApiKey }); return sendDelegated(response, delegated, auth, route, managerUrl, requestId, normalizeValidationData(delegated.data, route.publicProfile)); } if (route.kind === "validation") { const delegated = await agentRunJson(fetchImpl, managerUrl, `/api/v1/provider-profiles/${encodeURIComponent(route.agentRunProfile)}/validations/${encodeURIComponent(route.validationId)}`, { method: "GET", requestId, timeoutMs, apiKey: agentRunApiKey }); return sendDelegated(response, delegated, auth, route, managerUrl, requestId, normalizeValidationData(delegated.data, route.publicProfile)); } return sendError(response, 404, "not_found", "Provider profile route is not implemented"); } catch (error) { return sendError(response, error.httpStatus ?? 500, error.code ?? "provider_profile_management_failed", error.message ?? "Provider profile management failed", error.details); } } async function authenticateAdmin(request, options) { const auth = await authenticateActor(request, options); if (!auth.ok) return auth; if (auth.actor?.role !== "admin") return errorPayload("admin_required", "Only admin users can manage provider profiles", 403); return auth; } async function authenticateActor(request, options) { const controller = options.accessController; if (!controller || typeof controller.authenticate !== "function") { return errorPayload("access_controller_unavailable", "HWLAB access controller is not available", 503); } const auth = await controller.authenticate(request, { required: true }); if (!auth.ok) return auth; return auth; } function parseProviderProfileRoute(pathname, method) { if (pathname === "/v1/admin/provider-profiles") { if (method !== "GET") throw routeError(405, "method_not_allowed", "Provider profiles collection only supports GET"); return { kind: "list", publicProfile: null, agentRunProfile: null }; } const match = pathname.match(/^\/v1\/admin\/provider-profiles\/([^/]+)(?:\/(credential|config|validate|validations\/([^/]+)))?$/u); if (!match) throw routeError(404, "not_found", "Provider profile route is not implemented"); const profile = normalizeProfile(decodeURIComponent(match[1])); const suffix = match[2] ?? ""; if (suffix === "credential") { if (method !== "PUT") throw routeError(405, "method_not_allowed", "Provider profile credential only supports PUT"); return { kind: "credential", ...profile }; } if (!suffix) { if (method !== "DELETE") throw routeError(405, "method_not_allowed", "Provider profile root only supports DELETE"); return { kind: "remove", ...profile }; } if (suffix === "config") { if (method !== "GET" && method !== "PUT") throw routeError(405, "method_not_allowed", "Provider profile config only supports GET and PUT"); return { kind: "config", method, ...profile }; } if (suffix === "validate") { if (method !== "POST") throw routeError(405, "method_not_allowed", "Provider profile validation only supports POST"); return { kind: "validate", ...profile }; } if (suffix.startsWith("validations/")) { if (method !== "GET") throw routeError(405, "method_not_allowed", "Provider profile validation lookup only supports GET"); return { kind: "validation", ...profile, validationId: decodeURIComponent(match[3] ?? "") }; } throw routeError(404, "not_found", "Provider profile route is not implemented"); } function normalizeProfile(value) { const input = text(value).toLowerCase(); const publicProfile = PROVIDER_PROFILE_ALIASES[input] ?? input; if (!publicProfile || RESERVED_PROVIDER_PROFILES.has(publicProfile) || !PROVIDER_PROFILE_ID_PATTERN.test(publicProfile)) { throw routeError(400, "invalid_provider_profile", "Provider profile must be a lowercase slug such as deepseek, minimax-m3, dsflash-go, or codex-api", { profile: input, alias: "codex -> codex-api", pattern: String(PROVIDER_PROFILE_ID_PATTERN) }); } return { publicProfile, agentRunProfile: publicProfile === "codex-api" ? "codex" : publicProfile }; } async function agentRunJson(fetchImpl, managerUrl, path, { method = "GET", body, requestId, timeoutMs, apiKey }) { const startedAt = Date.now(); const targetUrl = `${managerUrl}${path}`; const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), timeoutMs); try { const headers: Record = { accept: "application/json", "content-type": "application/json", "x-request-id": requestId, "x-source-service-id": "hwlab-cloud-api", "x-hwlab-delegation": "provider-profile-management-v1" }; if (apiKey) headers.authorization = `Bearer ${apiKey}`; const response = await fetchImpl(targetUrl, { method, headers, body: body === undefined ? undefined : JSON.stringify(body), signal: controller.signal }); const textBody = await response.text(); const payload = parseJson(textBody); const ok = response.ok && payload?.ok !== false; return { ok, httpStatus: response.status, data: payload?.data ?? payload, payload, traceId: text(payload?.traceId), elapsedMs: Date.now() - startedAt, method, path }; } catch (error) { const timedOut = controller.signal.aborted || error?.name === "AbortError"; return { ok: false, httpStatus: 0, data: null, payload: { ok: false, error: { code: timedOut ? "agentrun_timeout" : "agentrun_request_failed", message: error?.message ?? String(error) } }, traceId: "", elapsedMs: Date.now() - startedAt, method, path, transportError: { code: timedOut ? "agentrun_timeout" : "agentrun_request_failed", message: error?.message ?? String(error), valuesPrinted: false } }; } finally { clearTimeout(timer); } } function sendDelegated(response, delegated, auth, route, managerUrl, requestId, data, options = {}) { const mutation = route.kind === "remove" || route.kind === "credential" || (route.kind === "config" && route.method === "PUT"); const status = delegated.ok ? (mutation ? 200 : delegated.httpStatus || 200) : delegated.httpStatus || 502; const base = { ok: delegated.ok, status: delegated.ok ? (route.kind === "remove" ? "removed" : mutation ? "updated" : "ok") : "failed", contractVersion: CONTRACT_VERSION, actor: actorSummary(auth.actor), profile: route.publicProfile ?? undefined, delegation: delegationSummary(managerUrl, delegated, requestId), valuesPrinted: false }; if (delegated.ok) return sendJson(response, status, { ...base, ...listAliases(data), data }, options); return sendJson(response, status, { ...base, error: delegatedError(delegated), data: null }); } function normalizeListData(data) { const sourceItems = Array.isArray(data?.items) ? data.items : []; const items = sourceItems.map(normalizeProfileData); return { ...redactObject(data), items, count: items.length, valuesPrinted: false }; } function normalizeCatalogData(data) { const sourceItems = Array.isArray(data?.items) ? data.items : []; const items = sourceItems.map(normalizeCatalogProfileData); return { items, count: items.length, valuesPrinted: false }; } function normalizeProfileData(item) { if (!item || typeof item !== "object") return item; const copy = redactObject(item); if (copy.profile === "codex") { copy.backendProfile = "codex"; copy.profile = "codex-api"; } if (copy.profile === "deepseek") copy.bridge = deepseekBridgeSummary(); if (text(copy.configSummary?.model ?? copy.model) === "deepseek-v4-flash") copy.bridge = opencodeZenGoBridgeSummary(); copy.valuesPrinted = false; return copy; } function normalizeCatalogProfileData(item) { const normalized = normalizeProfileData(item); if (!normalized || typeof normalized !== "object") return normalized; return { profile: normalized.profile, backendProfile: normalized.backendProfile, backendKind: normalized.backendKind, configured: normalized.configured, failureKind: normalized.failureKind ?? null, bridge: normalized.bridge, valuesPrinted: false }; } function normalizeConfigData(data) { if (!data || typeof data !== "object") return data; const configToml = typeof data.configToml === "string" ? data.configToml : typeof data["config.toml"] === "string" ? data["config.toml"] : undefined; const copy = normalizeProfileData(data); if (typeof configToml === "string") copy.configToml = configToml; copy.configTomlPrinted = typeof configToml === "string"; copy.credentialValuesPrinted = false; copy.valuesPrinted = false; return copy; } function normalizeValidationData(data, fallbackProfile) { if (!data || typeof data !== "object") return data; const copy = normalizeProfileData(data); if (!copy.profile) copy.profile = fallbackProfile; if (copy.result && typeof copy.result === "object") copy.result = redactObject(copy.result); if (copy.runnerJobs && Array.isArray(copy.runnerJobs)) copy.runnerJobs = copy.runnerJobs.map(redactObject); return copy; } function listAliases(data) { return Array.isArray(data?.items) ? { items: data.items, count: data.count ?? data.items.length } : {}; } function delegatedError(delegated) { const body = delegated.payload && typeof delegated.payload === "object" ? delegated.payload : {}; const nested = body.error && typeof body.error === "object" ? body.error : {}; return { code: text(nested.code ?? body.code) || "agentrun_delegation_failed", message: text(nested.message ?? body.message) || `AgentRun provider profile delegation failed with HTTP ${delegated.httpStatus}`, failureKind: text(nested.failureKind ?? body.failureKind) || undefined, agentRun: { httpStatus: delegated.httpStatus, traceId: delegated.traceId || undefined, route: { method: delegated.method, path: delegated.path }, elapsedMs: delegated.elapsedMs, transportError: delegated.transportError ? redactObject(delegated.transportError) : undefined, valuesPrinted: false }, valuesPrinted: false }; } function delegatedBy(auth, requestId) { return { system: "hwlab-v02", userId: text(auth.actor?.id), username: text(auth.actor?.username), authMethod: text(auth.authMethod) || (auth.apiKey ? "api-key" : "web-session"), requestId, valuesPrinted: false }; } function actorSummary(actor) { return actor ? { id: text(actor.id), username: text(actor.username), displayName: text(actor.displayName), role: text(actor.role), status: text(actor.status), valuesPrinted: false } : null; } function delegationSummary(managerUrl, delegated, requestId) { const url = new URL(managerUrl); return { provider: "agentrun-v01", requestId, agentRunBaseUrl: redactUrl(managerUrl), agentRunHost: url.hostname, traceId: delegated.traceId || undefined, httpStatus: delegated.httpStatus, elapsedMs: delegated.elapsedMs, valuesPrinted: false }; } function deepseekBridgeSummary() { return { kind: "moon-bridge", route: "HWLAB Moon Bridge -> DeepSeek official upstream", serviceUrl: "http://hwlab-deepseek-proxy.hwlab-v02.svc.cluster.local:4000/v1", responsesPath: "/v1/responses", forbiddenHosts: ["hyueapi.com"], valuesPrinted: false }; } function opencodeZenGoBridgeSummary() { return { kind: "moon-bridge", route: "HWLAB Moon Bridge -> OpenCode Zen Go Chat Completions", serviceUrl: "http://hwlab-deepseek-proxy.hwlab-v02.svc.cluster.local:4000/v1", responsesPath: "/v1/responses", model: "deepseek-v4-flash", upstreamProvider: "opencode", upstreamProtocol: "openai-chat", forbiddenHosts: ["hyueapi.com"], valuesPrinted: false }; } function resolveAgentRunManagerUrl(env) { const raw = text(env.AGENTRUN_MGR_URL) || text(env.HWLAB_CODE_AGENT_AGENTRUN_MGR_URL) || text(env.AGENTRUN_MANAGER_URL) || DEFAULT_AGENTRUN_MGR_URL; let parsed; try { parsed = new URL(raw); } catch { throw routeError(503, "agentrun_manager_url_invalid", "AgentRun manager URL is invalid", { managerUrl: redactUrl(raw) }); } const allowLocal = truthy(env.HWLAB_PROVIDER_PROFILE_AGENTRUN_ALLOW_NON_K3S_URL) || truthy(env.HWLAB_CODE_AGENT_AGENTRUN_ALLOW_NON_K3S_URL); const localAllowed = allowLocal && ["127.0.0.1", "localhost"].includes(parsed.hostname); if (!isAgentRunManagerInternalServiceHost(parsed.hostname) && !localAllowed) { throw routeError(503, "agentrun_manager_url_invalid", "AgentRun manager URL must use internal k3s Service DNS agentrun-mgr..svc.cluster.local", { managerUrl: redactUrl(raw) }); } return raw.replace(/\/+$/u, ""); } function resolveAgentRunApiKey(env) { return text(env.AGENTRUN_API_KEY) || text(env.HWLAB_CODE_AGENT_AGENTRUN_API_KEY) || text(env.HWLAB_PROVIDER_PROFILE_AGENTRUN_API_KEY) || text(env.HWLAB_API_KEY); } function isAgentRunManagerInternalServiceHost(host) { return AGENTRUN_MANAGER_HOST_PATTERN.test(String(host ?? "").trim().toLowerCase()); } async function readOptionalJsonObject(request, limitBytes) { const body = await readBody(request, limitBytes); if (!body.trim()) return {}; return parseJsonObject(body); } async function readJsonObject(request, limitBytes) { return parseJsonObject(await readBody(request, limitBytes)); } async function readBody(request, limitBytes = 1024 * 1024) { const chunks = []; let total = 0; for await (const chunk of request) { const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk); total += buffer.length; if (total > limitBytes) throw routeError(413, "body_too_large", "Request body is too large"); chunks.push(buffer); } return Buffer.concat(chunks).toString("utf8"); } function parseJsonObject(body) { const parsed = parseJson(body); if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw routeError(400, "invalid_json_body", "Request body must be a JSON object"); return parsed; } function parseJson(value) { try { return value ? JSON.parse(value) : null; } catch (error) { throw routeError(400, "parse_error", "Invalid JSON body", { reason: error?.message ?? String(error) }); } } function redactObject(value, key = "") { if (Array.isArray(value)) return value.map((item) => redactObject(item, key)); if (value && typeof value === "object") { return Object.fromEntries(Object.entries(value).map(([entryKey, entryValue]) => [entryKey, redactObject(entryValue, entryKey)])); } if (typeof value === "string" && (secretField(key) || looksSecret(value))) return "[redacted]"; return value; } function secretField(key) { const lower = key.toLowerCase(); if (["apikey", "api_key", "authorization", "authjson", "auth.json", "configtoml", "config.toml", "stringdata"].includes(lower)) return true; if (lower === "secretref" || lower === "secretname" || lower === "secretkey" || lower === "keys") return false; return lower.includes("password") || lower.endsWith("token"); } function looksSecret(value) { return /^sk-[A-Za-z0-9_-]{10,}/u.test(value) || /^Bearer\s+\S+/u.test(value); } function requestIdFor(request) { return getHeader(request, "x-request-id") || `req_provider_${randomUUID().replace(/-/gu, "")}`; } function getHeader(request, name) { const value = request.headers?.[name.toLowerCase()]; if (Array.isArray(value)) return text(value[0]); return text(value); } function positiveInteger(value, fallback) { const parsed = Number(value); return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : fallback; } function numericStatus(value) { const parsed = Number(value); return Number.isInteger(parsed) && parsed >= 100 && parsed <= 599 ? parsed : null; } function truthy(value) { return ["1", "true", "yes", "on"].includes(text(value).toLowerCase()); } function redactUrl(value) { try { const url = new URL(value); if (url.username) url.username = "redacted"; if (url.password) url.password = "redacted"; return url.toString().replace(/\/$/u, ""); } catch { return "[invalid-url]"; } } function routeError(httpStatus, code, message, details = {}) { return Object.assign(new Error(message), { httpStatus, code, details }); } function errorPayload(code, message, status, details = {}) { return { ok: false, status: "failed", contractVersion: CONTRACT_VERSION, error: { code, message, ...details, valuesPrinted: false }, valuesPrinted: false, statusCode: status }; } function sendError(response, statusCode, code, message, details = {}) { return sendJson(response, statusCode, { ok: false, status: "failed", contractVersion: CONTRACT_VERSION, error: { code, message, ...redactObject(details), valuesPrinted: false }, valuesPrinted: false }); } function sendJson(response, statusCode, payload, options = {}) { response.writeHead(statusCode, { "content-type": "application/json" }); const body = options.allowConfigToml ? redactObjectAllowingConfigToml(payload) : redactObject(payload); response.end(`${JSON.stringify(body)}\n`); } function redactObjectAllowingConfigToml(value, key = "") { if ((key === "configToml" || key === "config.toml") && typeof value === "string") return value; if (Array.isArray(value)) return value.map((item) => redactObjectAllowingConfigToml(item, key)); if (value && typeof value === "object") { return Object.fromEntries(Object.entries(value).map(([entryKey, entryValue]) => [entryKey, redactObjectAllowingConfigToml(entryValue, entryKey)])); } if (typeof value === "string" && (secretField(key) || looksSecret(value))) return "[redacted]"; return value; } function text(value) { return String(value ?? "").trim(); }