import { stringify } from "yaml"; import { buildInstallationContract } from "./contracts.js"; import { validateCanonicalWorkspace, type WorkspaceDescriptor } from "./schema.js"; import type { ResolvedBinding, RuntimeBindings } from "./bindings.js"; export type { RuntimeBindings } from "./bindings.js"; export interface RuntimePaths { sessions: string; artifacts: string; indexes: string; } function seconds(timeoutMs: number | undefined): number | undefined { return timeoutMs === undefined ? undefined : Math.max(1, Math.ceil(timeoutMs / 1_000)); } function bindingValue(binding: ResolvedBinding, name: string): string | undefined { return binding.values[name]; } function requireBinding(binding: ResolvedBinding, name: string): string { const value = bindingValue(binding, name); if (value === undefined) throw new Error(`runtime binding is missing ${name}`); return value; } function legacyDirectConnection( binding: ResolvedBinding, names: { host: string; port: string; user: string; passwordFile: string; tlsCaFile: string }, identity: { database: string; schema: string }, ): Record { const connection: Record = { host: requireBinding(binding, names.host), port: Number(requireBinding(binding, names.port)), database: identity.database, schema: identity.schema, user: requireBinding(binding, names.user), password_file: requireBinding(binding, names.passwordFile), }; const tlsCaFile = bindingValue(binding, names.tlsCaFile); if (tlsCaFile !== undefined) connection.ssl_ca_file = tlsCaFile; return connection; } function legacyRestEndpoint( binding: ResolvedBinding, names: { baseUrl: string; apiKeyFile: string; tlsCaFile: string }, requiresCredential: boolean, ): Record { const endpoint: Record = { base_url: requireBinding(binding, names.baseUrl), }; if (requiresCredential) endpoint.api_key_file = requireBinding(binding, names.apiKeyFile); const tlsCaFile = bindingValue(binding, names.tlsCaFile); if (tlsCaFile !== undefined) endpoint.ssl_ca_file = tlsCaFile; return endpoint; } function placeholderConnection(identity: { database: string; schema: string }): Record { return { host: "localhost", port: 5432, database: identity.database, schema: identity.schema, user: "rest", password: "", transport: "rest", }; } /** Render the compatibility fields consumed by the current Python harness. */ export function renderRuntimeConfig( workspace: WorkspaceDescriptor, bindings: RuntimeBindings, paths: RuntimePaths, ): string { const canonical = validateCanonicalWorkspace(workspace); if ([...bindings.dwh.missing, ...bindings.vector.missing, ...bindings.embedding.missing].length > 0) { throw new Error("runtime configuration requires complete bindings"); } const contract = buildInstallationContract(canonical); const name = (role: "DWH" | "VECTOR" | "EMBEDDING", suffix: string) => { const variable = contract.variables.find((entry) => entry.role === role && entry.suffix === suffix); if (!variable) throw new Error(`workspace contract is missing ${role}_${suffix}`); return variable.name; }; const dwhIdentity = { database: canonical.dwh.database, schema: canonical.dwh.schema }; const vectorIdentity = { database: canonical.semantic_index.vector_store.database, schema: canonical.semantic_index.vector_store.schema, }; const dwhDirect = bindings.dwh.transport === "postgres_direct"; const vectorDirect = bindings.vector.transport === "pgvector_direct"; const database = dwhDirect ? { ...legacyDirectConnection(bindings.dwh, { host: name("DWH", "HOST"), port: name("DWH", "PORT"), user: name("DWH", "USER"), passwordFile: name("DWH", "PASSWORD_FILE"), tlsCaFile: name("DWH", "TLS_CA_FILE"), }, dwhIdentity), transport: "direct" } : placeholderConnection(dwhIdentity); const vectorDb = vectorDirect ? legacyDirectConnection(bindings.vector, { host: name("VECTOR", "HOST"), port: name("VECTOR", "PORT"), user: name("VECTOR", "USER"), passwordFile: name("VECTOR", "PASSWORD_FILE"), tlsCaFile: name("VECTOR", "TLS_CA_FILE"), }, vectorIdentity) : placeholderConnection(vectorIdentity); const embedding: Record = { base_url: requireBinding(bindings.embedding, name("EMBEDDING", "BASE_URL")), model: canonical.semantic_index.embedding.model, dim: canonical.semantic_index.embedding.dimensions, }; const embeddingTimeout = seconds(canonical.semantic_index.embedding.timeout_ms); if (embeddingTimeout !== undefined) embedding.timeout = embeddingTimeout; const rendered: Record = { language: canonical.workspace.language, database, vector_db: vectorDb, embeddings: embedding, paths, }; if (dwhDirect) { rendered.dwh = { type: "postgres_direct", connection: database }; } else if (bindings.dwh.transport === "rest_api") { const rest = legacyRestEndpoint(bindings.dwh, { baseUrl: name("DWH", "BASE_URL"), apiKeyFile: name("DWH", "API_KEY_FILE"), tlsCaFile: name("DWH", "TLS_CA_FILE"), }, canonical.diagnostics?.dwh_rest?.auth !== "none"); rendered.rest = rest; rendered.dwh = { type: "thoth_rest", database: dwhIdentity, endpoint: rest }; } else { throw new Error("ssh_tunnel runtime configuration requires a diagnostic tunnel"); } if (vectorDirect) { rendered.vectors = { type: "pgvector_direct", connection: vectorDb }; } else if (bindings.vector.transport === "rest_api") { const vectorRest = legacyRestEndpoint(bindings.vector, { baseUrl: name("VECTOR", "BASE_URL"), apiKeyFile: name("VECTOR", "API_KEY_FILE"), tlsCaFile: name("VECTOR", "TLS_CA_FILE"), }, canonical.diagnostics?.vector_rest?.metadata.auth !== "none"); rendered.vector_rest = vectorRest; rendered.vectors = { type: "thoth_vector_http", reader: vectorRest }; } else { throw new Error("ssh_tunnel runtime configuration requires a diagnostic tunnel"); } return stringify(rendered, { lineWidth: 0, sortMapEntries: false }); }