(function () { if (window.AGENT_PLATFORM_LOADER_V2_RUNNING) { console.warn("Banty Widget Loader V2: Already running or loaded."); return; } window.AGENT_PLATFORM_LOADER_V2_RUNNING = true; const scriptEl = document.currentScript; if (!scriptEl) { console.error("Banty Widget Loader V2: currentScript not found."); return; } // Params from script tag const widgetVersionId = scriptEl.dataset.widgetId || null; const templateId = scriptEl.dataset.templateId || null; const ownerId = scriptEl.dataset.ownerId || null; // optional, unused here const mountId = scriptEl.dataset.mountId || null; const assetsBaseOverride = scriptEl.dataset.assetsBase || null; // optional escape hatch const platformRootUrl = String( scriptEl.dataset.platformApiBase || window.BANTY_PLATFORM_API_BASE || "https://staging.dpmg.xyz" ).replace(/\/+$/, ""); // trim trailing slash if (!widgetVersionId && !templateId) { console.error("Banty Widget Loader V2: data-widget-id or data-template-id is required."); return; } if (widgetVersionId && templateId) { console.warn("Banty Widget Loader V2: Both widgetId and templateId provided. widgetId takes precedence."); } const idForLog = widgetVersionId ? `widgetId: ${widgetVersionId}` : `templateId: ${templateId}`; console.log("Banty Loader: init for", idForLog, "platformRootUrl:", platformRootUrl); const initSessionUrl = `${platformRootUrl}/api/v1/widgets/init-session`; const generateWelcomeUrl = `${platformRootUrl}/api/v1/widgets/generate-welcome`; const initPayload = {}; if (widgetVersionId) initPayload.widget_id = widgetVersionId; else initPayload.template_id = templateId; let baseWidgetConfig = null; console.log("Loader: 1. init-session ->", initSessionUrl, "payload:", initPayload); fetch(initSessionUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify(initPayload), }) .then(resp => { if (!resp.ok) { return resp.text().then(t => { let detail = t; try { detail = JSON.parse(t).detail || t; } catch {} throw new Error(`init-session failed: ${resp.status} ${resp.statusText} - ${detail}`); }); } return resp.json(); }) .then(cfg => { // Validate response if (!cfg || !cfg.token || !cfg.versionId || !cfg.apiBaseUrl || !cfg.agentId || !cfg.config) { throw new Error("Loader: Incomplete init-session config."); } baseWidgetConfig = cfg; // 1) Align apiBaseUrl to the init origin if it drifted const initOrigin = new URL(platformRootUrl).origin; let apiBaseUrl = String(cfg.apiBaseUrl || '').replace(/\/+$/, ''); try { const apiOrigin = new URL(apiBaseUrl).origin; if (apiOrigin !== initOrigin) { console.warn("Loader: apiBaseUrl origin", apiOrigin, "mismatches init origin", initOrigin, "-> overriding to init origin."); apiBaseUrl = initOrigin; } } catch { console.warn("Loader: invalid apiBaseUrl from init-session; using init origin."); apiBaseUrl = initOrigin; } baseWidgetConfig.apiBaseUrl = apiBaseUrl; // 2) Decide public base for assets (serve.js, widget.js, chunks) const publicBase = String(assetsBaseOverride || apiBaseUrl).replace(/\/+$/, ''); // 3) Pre-expose globals for the runtime window.__BANTY_API_BASE__ = apiBaseUrl; window.__BANTY_PUBLIC_BASE__ = publicBase; // 4) Seed runtime config window.AGENT_PLATFORM_WIDGET_CONFIG = { ...cfg.config, token: cfg.token, ttl: cfg.ttl, expUtc: cfg.expUtc, agentId: cfg.agentId, apiBaseUrl: apiBaseUrl, versionId: cfg.versionId, ...(mountId ? { mountId } : {}), }; console.log("Loader: runtime config prepared:", { ...window.AGENT_PLATFORM_WIDGET_CONFIG, token: "[redacted]", }); // 5) Fetch serve.js from the aligned apiBaseUrl const serveJsUrl = `${apiBaseUrl}/api/v1/widgets/versions/${encodeURIComponent(cfg.versionId)}/serve.js`; console.log("Loader: 2. fetching serve.js ->", serveJsUrl); return fetch(serveJsUrl, { method: 'GET', headers: { 'Authorization': `Bearer ${cfg.token}`, 'Accept': 'application/javascript, text/javascript', }, cache: 'no-store', }); }) .then(res => { // WAT refresh const newWatServe = res.headers.get('X-New-WAT'); if (newWatServe) { window.AGENT_PLATFORM_WIDGET_CONFIG.token = newWatServe; baseWidgetConfig.token = newWatServe; const ttl = parseInt(res.headers.get('X-New-WAT-TTL') || '0', 10); if (ttl > 0) { const expUtc = Math.floor(Date.now() / 1000) + ttl; window.AGENT_PLATFORM_WIDGET_CONFIG.ttl = ttl; window.AGENT_PLATFORM_WIDGET_CONFIG.expUtc = expUtc; baseWidgetConfig.ttl = ttl; baseWidgetConfig.expUtc = expUtc; } console.log("Loader: refreshed WAT for serve.js."); } if (!res.ok) { return res.text().then(t => { throw new Error(`serve.js fetch failed: ${res.status} ${res.statusText} - ${t}`); }); } return res.text(); }) .then(code => { // Execute serve.js const s = document.createElement('script'); s.type = 'text/javascript'; s.text = code; document.head.appendChild(s); console.log("Loader: 3. serve.js executed."); // 4) Async generate-welcome on init origin setTimeout(() => { const payload = { widget_id: baseWidgetConfig.versionId, current_url: window.location.href, origin_hostname: window.location.hostname, }; console.log("Loader: 4. generate-welcome ->", generateWelcomeUrl, payload); fetch(generateWelcomeUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify(payload), }) .then(r => r.ok ? r.json() : r.text().then(t => { throw new Error(`welcome failed: ${r.status} ${t}`); })) .then(data => { if (data && data.welcome_message) { if (window.banty && typeof window.banty.updateConfig === 'function') { window.banty.updateConfig({ welcomeMessage: data.welcome_message }); } else if (window.AGENT_PLATFORM_WIDGET_CONFIG) { window.AGENT_PLATFORM_WIDGET_CONFIG.welcomeMessage = data.welcome_message; } console.log("Loader: welcome message updated."); } }) .catch(err => { console.warn("Loader: generate-welcome error:", err.message || err); }); }, 50); }) .catch(err => { console.error("Loader: critical error:", err.message || err); }); })();