import path from "node:path"; import { PostgresHarnessRLRegistry } from "./registry.ts"; import { createHarnessRLService } from "./service.ts"; import { createHarnessRLTemporalGateway } from "./temporal.ts"; export function harnessRLRuntime(env: Record = process.env) { const cwd = path.resolve(String(env.HWLAB_CASERUN_REPO_ROOT ?? process.cwd())); const caseRepo = path.resolve(cwd, String(env.HWLAB_CASERUN_CASE_REPO ?? env.HWLAB_CASE_REPO ?? ".")); const stateRoot = path.resolve(cwd, String(env.HARNESSRL_STATE_DIR ?? ".state/harnessrl")); const registry = new PostgresHarnessRLRegistry(String(env.HARNESSRL_DATABASE_URL ?? "")); const temporalAddress = String(env.HARNESSRL_TEMPORAL_ADDRESS ?? env.TEMPORAL_ADDRESS ?? ""); const temporalNamespace = String(env.HARNESSRL_TEMPORAL_NAMESPACE ?? "unidesk"); const taskQueue = String(env.HARNESSRL_TEMPORAL_TASK_QUEUE ?? "hwlab-v03-harnessrl"); const activity = { startToCloseTimeoutMs: positiveInteger(env.HARNESSRL_ACTIVITY_START_TO_CLOSE_TIMEOUT_MS, 1_800_000, "HARNESSRL_ACTIVITY_START_TO_CLOSE_TIMEOUT_MS"), heartbeatTimeoutMs: positiveInteger(env.HARNESSRL_ACTIVITY_HEARTBEAT_TIMEOUT_MS, 30_000, "HARNESSRL_ACTIVITY_HEARTBEAT_TIMEOUT_MS"), retryInitialIntervalMs: positiveInteger(env.HARNESSRL_ACTIVITY_RETRY_INITIAL_INTERVAL_MS, 1_000, "HARNESSRL_ACTIVITY_RETRY_INITIAL_INTERVAL_MS"), retryMaximumIntervalMs: positiveInteger(env.HARNESSRL_ACTIVITY_RETRY_MAXIMUM_INTERVAL_MS, 30_000, "HARNESSRL_ACTIVITY_RETRY_MAXIMUM_INTERVAL_MS"), retryMaximumAttempts: positiveInteger(env.HARNESSRL_ACTIVITY_RETRY_MAXIMUM_ATTEMPTS, 5, "HARNESSRL_ACTIVITY_RETRY_MAXIMUM_ATTEMPTS") }; const temporal = createHarnessRLTemporalGateway({ address: temporalAddress, namespace: temporalNamespace, taskQueue, activity }); return { cwd, caseRepo, stateRoot, registry, temporalAddress, temporalNamespace, taskQueue, activity, service: createHarnessRLService({ registry, temporal, cwd, caseRepo, stateRoot }) }; } function positiveInteger(value: string | undefined, fallback: number, name: string) { const parsed = Number.parseInt(String(value ?? fallback), 10); if (!Number.isSafeInteger(parsed) || parsed <= 0) throw Object.assign(new Error(`${name} must be a positive integer`), { code: "harnessrl_temporal_config_invalid", details: { name } }); return parsed; }