import { randomUUID } from "node:crypto"; import { ENVIRONMENT_DEV, ERROR_CODES, JSON_RPC_VERSION, validateRequest, validateResponse } from "../protocol/index.mjs"; import { AUDIT_FIELD_NAMES, CLOUD_API_SERVICE_ID, createAuditRecord, deriveActorFromMeta } from "../audit/index.mjs"; export const SUPPORTED_RPC_METHODS = Object.freeze([ "system.health", "cloud.adapter.describe", "hardware.operation.request" ]); const rpcHandlers = new Map([ ["system.health", handleSystemHealth], ["cloud.adapter.describe", handleAdapterDescribe], ["hardware.operation.request", handleHardwareOperationRequest] ]); export async function handleJsonRpcRequest(envelope, context = {}) { const requestMeta = extractMeta(envelope); const requestId = envelope?.id ?? "req_unassigned"; const method = typeof envelope?.method === "string" ? envelope.method : "method_unspecified"; try { validateRequest(envelope); } catch (error) { return createErrorEnvelope({ id: requestId, code: ERROR_CODES.invalidRequest, message: "Invalid JSON-RPC request", data: { reason: error.message }, requestMeta, context: { ...context, operation: "json_rpc.validate", target: { type: "rpc_method", id: method } } }); } const handler = rpcHandlers.get(envelope.method); if (!handler) { return createErrorEnvelope({ id: envelope.id, code: ERROR_CODES.methodNotFound, message: `Method ${envelope.method} is not implemented by hwlab-cloud-api`, data: { method: envelope.method }, requestMeta: envelope.meta, context: { ...context, operation: envelope.method, target: { type: "rpc_method", id: envelope.method }, result: "rejected" } }); } try { const result = await handler(envelope.params ?? {}, envelope, context); return createResultEnvelope({ id: envelope.id, result, requestMeta: envelope.meta }); } catch (error) { return createErrorEnvelope({ id: envelope.id, code: ERROR_CODES.internalError, message: "hwlab-cloud-api failed to process the request", data: { reason: error.message }, requestMeta: envelope.meta, context: { ...context, operation: envelope.method, target: { type: "rpc_method", id: envelope.method }, result: "failed" } }); } } export function createResultEnvelope({ id, result, requestMeta = {} }) { const envelope = { jsonrpc: JSON_RPC_VERSION, id, result, meta: createResponseMeta(requestMeta) }; validateResponse(envelope); return envelope; } export function createErrorEnvelope({ id = "req_unassigned", code = ERROR_CODES.hwlabUnknown, message = "HWLAB runtime error", data = {}, requestMeta = {}, context = {} } = {}) { const responseId = id ?? "req_unassigned"; const meta = createResponseMeta(requestMeta); const audit = createAuditRecord({ requestId: responseId, actor: deriveActorFromMeta(requestMeta), source: { serviceId: CLOUD_API_SERVICE_ID, environment: ENVIRONMENT_DEV, traceId: meta.traceId, adapter: context.adapter || "json-rpc" }, operation: context.operation || "json_rpc.error", target: context.target || { type: "cloud_api", id: CLOUD_API_SERVICE_ID }, result: context.result || "failed", timestamp: context.timestamp }); const envelope = { jsonrpc: JSON_RPC_VERSION, id: responseId, error: { code, message, data: { ...normalizeErrorData(data), audit } }, meta }; validateResponse(envelope); return envelope; } export function createResponseMeta(requestMeta = {}) { const meta = { traceId: requestMeta.traceId || `trc_${randomUUID()}`, serviceId: CLOUD_API_SERVICE_ID, environment: ENVIRONMENT_DEV }; if (requestMeta.actorId) { meta.actorId = requestMeta.actorId; } return meta; } function handleSystemHealth() { return { status: "ok", serviceId: CLOUD_API_SERVICE_ID, environment: ENVIRONMENT_DEV, db: { connected: false, mode: "not_configured" } }; } function handleAdapterDescribe() { return { serviceId: CLOUD_API_SERVICE_ID, rpcEndpoint: "POST /rpc", restEndpoint: "POST /v1/rpc/{method}", methods: SUPPORTED_RPC_METHODS, auditFields: AUDIT_FIELD_NAMES, db: { connected: false, mode: "migration_skeleton_only" } }; } function handleHardwareOperationRequest(params, envelope, context) { const projectId = params.projectId || "project_unspecified"; return { accepted: false, status: "skeleton", reason: "cloud core L1 does not dispatch hardware operations yet", audit: createAuditRecord({ requestId: envelope.id, actor: deriveActorFromMeta(envelope.meta), source: { serviceId: CLOUD_API_SERVICE_ID, environment: ENVIRONMENT_DEV, traceId: envelope.meta.traceId, adapter: context.adapter || "json-rpc" }, operation: "hardware.operation.request", target: { type: "project", id: projectId }, result: "rejected" }) }; } function extractMeta(envelope) { if (envelope && typeof envelope === "object" && !Array.isArray(envelope) && envelope.meta) { return envelope.meta; } return {}; } function normalizeErrorData(data) { if (!data || typeof data !== "object" || Array.isArray(data)) { return {}; } return data; }