feat: add BCBS MA eligibility check with OTP flow

- New Selenium worker (fresh Chrome per run, no persistent session)
  login → OTP modal → eTools → ConnectCenter → Verification →
  New Eligibility Request → fill form (NPI, member ID, DOB) →
  Expand All → CDP PDF back to app
- Backend route fetches BCBS_MA credentials + provider NPI from settings
- Frontend OTP modal with 6-digit code entry
- BCBS MA added to insurance credentials dropdown in settings

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ff
2026-06-01 00:36:11 -04:00
co-authored by Claude Sonnet 4.6
parent 535619c286
commit e644d21cee
12 changed files with 1468 additions and 9 deletions
@@ -0,0 +1,67 @@
import axios from "axios";
import http from "http";
import https from "https";
import dotenv from "dotenv";
dotenv.config();
const SELENIUM_AGENT_BASE = process.env.SELENIUM_AGENT_BASE_URL;
const httpAgent = new http.Agent({ keepAlive: true, keepAliveMsecs: 60_000 });
const httpsAgent = new https.Agent({ keepAlive: true, keepAliveMsecs: 60_000 });
const client = axios.create({
baseURL: SELENIUM_AGENT_BASE,
timeout: 5 * 60 * 1000,
httpAgent,
httpsAgent,
validateStatus: (s) => s >= 200 && s < 600,
});
async function requestWithRetries(config: any, retries = 4, baseBackoffMs = 300) {
for (let attempt = 1; attempt <= retries; attempt++) {
try {
const r = await client.request(config);
if (![502, 503, 504].includes(r.status)) return r;
} catch (err: any) {
const code = err?.code;
const isTransient =
code === "ECONNRESET" || code === "ECONNREFUSED" || code === "EPIPE" || code === "ETIMEDOUT";
if (!isTransient) throw err;
}
await new Promise((r) => setTimeout(r, baseBackoffMs * attempt));
}
return client.request(config);
}
function log(tag: string, msg: string, ctx?: any) {
console.log(`${new Date().toISOString()} [${tag}] ${msg}`, ctx ?? "");
}
export async function forwardToSeleniumBcbsMaEligibilityAgent(data: any): Promise<any> {
const payload = { data };
log("bcbs-ma-client", "POST bcbs-ma-eligibility", { keys: Object.keys(payload) });
const r = await requestWithRetries({ url: "/bcbs-ma-eligibility", method: "POST", data: payload }, 4);
log("bcbs-ma-client", "agent response", { status: r.status });
if (r.status >= 500) throw new Error(`Selenium agent server error: ${r.status}`);
return r.data;
}
export async function forwardOtpToSeleniumBcbsMaAgent(sessionId: string, otp: string): Promise<any> {
log("bcbs-ma-client", "POST submit-otp", { sessionId });
const r = await requestWithRetries(
{ url: "/submit-otp", method: "POST", data: { session_id: sessionId, otp } },
4
);
if (r.status >= 500) throw new Error(`Selenium agent server error on submit-otp: ${r.status}`);
return r.data;
}
export async function getSeleniumBcbsMaSessionStatus(sessionId: string): Promise<any> {
const r = await requestWithRetries({ url: `/session/${sessionId}/status`, method: "GET" }, 4);
if (r.status === 404) {
const e: any = new Error("not_found");
e.response = { status: 404, data: r.data };
throw e;
}
return r.data;
}