import { useState } from "react"; import type { AnswerBody, AskResponse, Citation } from "./types"; // Same-origin, always, unless a build explicitly overrides it. Caddy routes // /ask under ai.yokogawa.tech to ai-api (caddy/ai-routes.caddy), so the page // and the API share an origin and the browser attaches the Authelia session // cookie with no cross-origin handling at all. // // It is same-origin because an operator on cicore1 CANNOT resolve // api.yokogawa.tech: LAN hosts resolve through the DC, which holds a pinpoint // record for ai.yokogawa.tech and none for api. A cross-origin build loads and // then fails every question on DNS. // // VITE_API_BASE remains for a tunnelled build, which points it at // http://localhost:8001 - that has NO AUTHENTICATION in front of it, is // reachable only through an SSH tunnel from one machine, and is not a // deployment. The empty-string check is deliberate: the build arg defaults to // "" (web/Dockerfile), and `??` does not catch an empty string - it read as a // valid base and sent /ask to the origin with no route for it. const API = import.meta.env.DEV ? "/api" : (import.meta.env.VITE_API_BASE || ""); export default function App() { const [question, setQuestion] = useState(""); const [result, setResult] = useState(null); const [error, setError] = useState(null); const [busy, setBusy] = useState(false); const [showWorking, setShowWorking] = useState(true); // Stub mode only - the API ignores it otherwise. See api/stub.py. const [forcedClass, setForcedClass] = useState(""); async function ask(event: React.FormEvent) { event.preventDefault(); setBusy(true); setError(null); setResult(null); try { const response = await fetch(`${API}/ask`, { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify( forcedClass ? { question, question_class: forcedClass } : { question }, ), }); const body = await response.json(); if (!response.ok) { setError(body?.detail?.message ?? "The request failed."); return; } setResult(body as AskResponse); } catch { setError("Could not reach the assistant."); } finally { setBusy(false); } } return (

Waterloo Road Pump Station — Plant Assistant

Answers grounded in plant history and controlled documents. Not a control system, and not a substitute for a competent person.