import { createHead } from "@unhead/vue/server"; import { defineComponent, ref, onMounted, createSSRApp, computed, watch, reactive, nextTick, unref, mergeProps, useSSRContext, onUnmounted, resolveComponent, withCtx, createVNode, resolveDynamicComponent, openBlock, createBlock, toDisplayString, createCommentVNode, defineAsyncComponent } from "vue"; import { createRouter, createMemoryHistory, useRouter, useRoute } from "vue-router"; import { defineStore, createPinia } from "pinia"; import { ssrRenderAttrs, ssrRenderList, ssrRenderClass, ssrRenderStyle, ssrRenderComponent, ssrInterpolate, ssrRenderAttr, ssrRenderVNode, ssrIncludeBooleanAttr } from "vue/server-renderer"; import { CubeTransparentIcon, SunIcon, CommandLineIcon } from "@heroicons/vue/24/outline"; import { ChevronDownIcon, ChevronRightIcon, DocumentIcon, ChevronLeftIcon, ChatBubbleLeftRightIcon, FolderIcon, FolderOpenIcon, WifiIcon, SignalIcon, CodeBracketIcon, UserCircleIcon, ArrowRightEndOnRectangleIcon } from "@heroicons/vue/20/solid"; import { OverlayScrollbarsComponent } from "overlayscrollbars-vue"; import { OverlayScrollbars, ClickScrollPlugin } from "overlayscrollbars"; if (typeof window === "undefined") { const noop = () => { }; const storage = { getItem: () => null, setItem: noop, removeItem: noop, length: 0, clear: noop, key: () => null }; globalThis.window = { location: { protocol: "https:", hostname: "", port: "", hash: "", pathname: "/", origin: "", search: "", href: "" }, addEventListener: noop, removeEventListener: noop, __hermes: {}, matchMedia: () => ({ matches: false, addEventListener: noop, removeEventListener: noop }), innerWidth: 1280, innerHeight: 800, open: noop, getComputedStyle: () => ({}) }; globalThis.localStorage = storage; globalThis.sessionStorage = storage; globalThis.document = { querySelector: () => null, querySelectorAll: () => [], createElement: () => ({ style: {}, getContext: () => null, addEventListener: noop }), title: "", head: { appendChild: noop }, body: { appendChild: noop }, documentElement: { setAttribute: noop, removeAttribute: noop, style: {} } }; try { globalThis.navigator = { mediaDevices: {}, clipboard: {}, userAgent: "", platform: "" }; } catch { } globalThis.HTMLElement = class { }; globalThis.MediaStream = class { }; globalThis.WebSocket = class { send() { } close() { } }; globalThis.AudioContext = class { }; globalThis.MediaRecorder = class { }; globalThis.MutationObserver = class { observe() { } disconnect() { } }; globalThis.ResizeObserver = class { observe() { } disconnect() { } }; globalThis.IntersectionObserver = class { observe() { } disconnect() { } }; } const ClientOnly = defineComponent({ setup(props, { slots }) { const mounted = ref(false); onMounted(() => mounted.value = true); return () => { if (!mounted.value) return slots.placeholder && slots.placeholder({}); return slots.default && slots.default({}); }; } }); function ViteSSG(App, routerOptions, fn, options) { const { transformState, registerComponents = true, useHead = true, rootContainer = "#app" } = {}; async function createApp$1(routePath) { const app = createSSRApp(App); let head; if (useHead) { app.use(head = createHead()); } const router = createRouter({ history: createMemoryHistory(routerOptions.base), ...routerOptions }); const { routes: routes2 } = routerOptions; if (registerComponents) app.component("ClientOnly", ClientOnly); const appRenderCallbacks = []; const onSSRAppRendered = (cb) => appRenderCallbacks.push(cb); const triggerOnSSRAppRendered = () => { return Promise.all(appRenderCallbacks.map((cb) => cb())); }; const context = { app, head, isClient: false, router, routes: routes2, onSSRAppRendered, triggerOnSSRAppRendered, initialState: {}, transformState, routePath }; await fn?.(context); app.use(router); let entryRoutePath; let isFirstRoute = true; router.beforeEach((to, from, next) => { if (isFirstRoute || entryRoutePath && entryRoutePath === to.path) { isFirstRoute = false; entryRoutePath = to.path; to.meta.state = context.initialState; } next(); }); { const route = context.routePath ?? "/"; router.push(route); await router.isReady(); context.initialState = router.currentRoute.value.meta.state || {}; } const initialState = context.initialState; return { ...context, initialState }; } return createApp$1; } const version = "0.1.0"; const pkg = { version }; function useUI(status2) { const version2 = `${pkg.version}-${"mnf5vvcg"}`; const statusClass = computed(() => { if (status2.value.includes("Connected")) return "connected"; if (status2.value.includes("Connecting")) return "connecting"; if (status2.value.includes("Error")) return "error"; return ""; }); return { version: version2, statusClass }; } const _ssrFallback = {}; function useHermes() { if (typeof window === "undefined") return _ssrFallback; const w = window; if (!w.__hermes) w.__hermes = {}; return w.__hermes; } const H$1 = useHermes(); function getStream() { return H$1.captureStream || null; } function setStream(s) { H$1.captureStream = s; } const isActive = ref(false); syncState(); function syncState() { const stream = getStream(); isActive.value = !!(stream && stream.active && stream.getVideoTracks().some((t) => t.readyState === "live")); if (!isActive.value && stream) { stream.getTracks().forEach((t) => t.stop()); setStream(null); } } function useCapture() { async function enable() { const stream = getStream(); if (stream && stream.active) { syncState(); if (isActive.value) return { enabled: true }; } try { const newStream = await navigator.mediaDevices.getDisplayMedia({ video: { displaySurface: "browser" }, preferCurrentTab: true }); newStream.getVideoTracks()[0].onended = () => { setStream(null); syncState(); }; setStream(newStream); syncState(); return { enabled: true }; } catch (e) { setStream(null); syncState(); return { enabled: false, error: `Capture denied: ${e.message}` }; } } function disable() { const stream = getStream(); if (stream) { stream.getTracks().forEach((t) => t.stop()); setStream(null); } syncState(); } async function capture(quality = 0.7) { syncState(); const stream = getStream(); if (!stream) return { error: "Capture not enabled -- send enableCapture first" }; const track = stream.getVideoTracks()[0]; if (!track || track.readyState !== "live") { setStream(null); syncState(); return { error: "Capture stream ended -- re-enable with enableCapture" }; } const video = document.createElement("video"); video.srcObject = stream; video.muted = true; await video.play(); const canvas = document.createElement("canvas"); canvas.width = video.videoWidth; canvas.height = video.videoHeight; canvas.getContext("2d").drawImage(video, 0, 0); video.pause(); video.srcObject = null; return new Promise((resolve) => { canvas.toBlob((blob) => { if (!blob) { resolve({ error: "Canvas toBlob failed" }); return; } const reader = new FileReader(); reader.onloadend = () => { const dataUrl = reader.result; resolve({ dataUrl, length: dataUrl.length }); }; reader.readAsDataURL(blob); }, "image/jpeg", quality); }); } function healthCheck() { const stream = getStream(); if (!stream) return { active: false, tracks: 0, reason: "no stream" }; const tracks = stream.getVideoTracks(); const live = tracks.filter((t) => t.readyState === "live"); if (!stream.active || live.length === 0) { setStream(null); syncState(); return { active: false, tracks: 0, reason: "stream ended" }; } return { active: true, tracks: live.length }; } return { isActive, enable, disable, capture, healthCheck }; } const windows = /* @__PURE__ */ new Map(); const PRESETS = { mobile: [375, 812], tablet: [768, 1024], "tablet-landscape": [1024, 768], desktop: [1280, 800] }; function deriveToken(parentToken, name) { return `${parentToken}-breakout-${name}`; } function useBreakout() { const pendingRequest = ref(null); async function open(args) { const parentToken = sessionStorage.getItem("hermes_takeover_token"); if (!parentToken) return { error: "No takeover token active" }; const name = args.name || "mobile"; const preset = args.preset || "desktop"; const existing = windows.get(name); if (existing && !existing.closed) existing.close(); const nonce = Math.random().toString(36).slice(2, 6); const [pw, ph] = args.w && args.h ? [args.w, args.h] : PRESETS[preset] || PRESETS.desktop; const presetLabel = `${pw}x${ph}`; return new Promise((resolve) => { pendingRequest.value = { name, preset: presetLabel, nonce, resolve: (confirmed) => { pendingRequest.value = null; if (!confirmed) { resolve({ error: "rejected by user" }); return; } const token = deriveToken(parentToken, name); const url = `${window.location.origin}${window.location.pathname}?breakout_token=${token}/agents`; const popup = window.open(url, `hermes_breakout_${name}`, `width=${pw},height=${ph},resizable=yes,scrollbars=yes`); if (!popup) { resolve({ error: "popup blocked" }); return; } windows.set(name, popup); resolve({ opened: name, token, size: presetLabel }); } }; }); } function openDirect(name, presetStr, parentToken) { const [w, h] = presetStr.split("x").map(Number); const token = deriveToken(parentToken, name); const url = `${window.location.origin}${window.location.pathname}?breakout_token=${token}/agents`; const popup = window.open(url, `hermes_breakout_${name}`, `width=${w},height=${h},resizable=yes,scrollbars=yes`); if (!popup) { alert("Popup blocked -- allow popups for this site"); return; } windows.set(name, popup); } function list() { const result = {}; for (const [name, win] of windows) { result[name] = { alive: !win.closed }; if (win.closed) windows.delete(name); } return result; } function close(args) { const win = windows.get(args.name); if (!win) return { error: `No breakout: ${args.name}` }; if (!win.closed) win.close(); windows.delete(args.name); return { closed: args.name }; } return { windows, pendingRequest, open, openDirect, list, close }; } const TAKEOVER_KEY = "hermes_takeover_token"; let currentToken = typeof sessionStorage !== "undefined" ? sessionStorage.getItem(TAKEOVER_KEY) || "" : ""; function useTakeover(wsSend) { const token = ref(currentToken); const capture = useCapture(); const breakout = useBreakout(); function init() { const t = crypto.randomUUID(); token.value = t; currentToken = t; sessionStorage.setItem(TAKEOVER_KEY, t); wsSend({ type: "dev_takeover", token: t }); return t; } function revoke() { token.value = ""; currentToken = ""; sessionStorage.removeItem(TAKEOVER_KEY); } function reregister() { const urlParams = new URLSearchParams(window.location.search); const breakoutToken = urlParams.get("breakout_token"); if (breakoutToken) { sessionStorage.setItem(TAKEOVER_KEY, breakoutToken); urlParams.delete("breakout_token"); const clean = urlParams.toString(); const newUrl = window.location.pathname + (clean ? "?" + clean : "") + window.location.hash; window.history.replaceState(null, "", newUrl); } const t = sessionStorage.getItem(TAKEOVER_KEY); if (t) { token.value = t; currentToken = t; wsSend({ type: "dev_takeover", token: t }); } } function boxChain(args) { const el = document.querySelector(args.selector); if (!el) return { error: `No element: ${args.selector}` }; const chain = []; let node = el; while (node && node !== document.documentElement) { const cs = getComputedStyle(node); const rect = node.getBoundingClientRect(); chain.push({ tag: node.tagName.toLowerCase(), cls: (node.className?.toString() || "").split(" ").filter(Boolean).slice(0, 5).join(" "), w: Math.round(rect.width), h: Math.round(rect.height), l: Math.round(rect.left), r: Math.round(rect.right), pl: cs.paddingLeft, pr: cs.paddingRight, ml: cs.marginLeft, mr: cs.marginRight }); node = node.parentElement; } return chain; } function getStyles(args) { const el = document.querySelector(args.selector); if (!el) return { error: `No element: ${args.selector}` }; const cs = getComputedStyle(el); const rect = el.getBoundingClientRect(); const props = args.props || ["padding", "margin", "width", "height", "display", "flexDirection", "overflow", "gap"]; const styles = {}; for (const p of props) styles[p] = cs.getPropertyValue(p.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase())); return { ...styles, boundingRect: { w: Math.round(rect.width), h: Math.round(rect.height), l: Math.round(rect.left), r: Math.round(rect.right), t: Math.round(rect.top), b: Math.round(rect.bottom) } }; } function viewport() { return { w: window.innerWidth, h: window.innerHeight, dpr: window.devicePixelRatio, hash: window.location.hash }; } function navigate(args) { window.location.hash = args.hash; return { navigated: args.hash }; } function reload() { setTimeout(() => window.location.reload(), 100); return { reloading: true }; } function resize(args) { window.resizeTo(args.w, args.h); return { resized: `${args.w}x${args.h}` }; } function querySelector(args) { const els = document.querySelectorAll(args.selector); const limit = args.limit || 10; return Array.from(els).slice(0, limit).map((el, i) => { const rect = el.getBoundingClientRect(); return { i, tag: el.tagName.toLowerCase(), cls: (el.className?.toString() || "").split(" ").filter(Boolean).slice(0, 5).join(" "), text: (el.textContent || "").slice(0, 60), w: Math.round(rect.width), h: Math.round(rect.height), l: Math.round(rect.left), t: Math.round(rect.top) }; }); } function click(args) { const els = document.querySelectorAll(args.selector); const idx = args.index || 0; const el = els[idx]; if (!el) return { error: `No element: ${args.selector}[${idx}]` }; el.click(); return { clicked: args.selector, index: idx }; } function getValue(args) { const el = document.querySelector(args.selector); if (!el) return { error: `No element: ${args.selector}` }; return { value: el.value, tag: el.tagName.toLowerCase() }; } function setValue(args) { const el = document.querySelector(args.selector); if (!el) return { error: `No element: ${args.selector}` }; const nativeInputValueSetter = Object.getOwnPropertyDescriptor( el.tagName === "SELECT" ? HTMLSelectElement.prototype : HTMLInputElement.prototype, "value" )?.set; nativeInputValueSetter?.call(el, args.value); el.dispatchEvent(new Event("input", { bubbles: true })); el.dispatchEvent(new Event("change", { bubbles: true })); return { set: args.value, selector: args.selector }; } function typeText(args) { const el = document.querySelector(args.selector); if (!el) return { error: `No element: ${args.selector}` }; el.focus(); el.value = args.text; el.dispatchEvent(new Event("input", { bubbles: true })); return { typed: args.text, selector: args.selector }; } function screenshot() { return { url: window.location.hash, title: document.title, viewport: { w: window.innerWidth, h: window.innerHeight }, body: document.body.innerText.slice(0, 2e3) }; } function scroll(args) { const el = document.querySelector(args.selector); if (!el) return { error: `No element: ${args.selector}` }; const before = el.scrollTop; if (args.to !== void 0) { if (args.to === "top") el.scrollTop = 0; else if (args.to === "bottom") el.scrollTop = el.scrollHeight; else if (args.to === "middle") el.scrollTop = (el.scrollHeight - el.clientHeight) / 2; else el.scrollTop = args.to; } return { scrollTop: el.scrollTop, scrollHeight: el.scrollHeight, clientHeight: el.clientHeight, before }; } function getConsole(args) { const h = useHermes(); if (!h.console?.length && !h._origConsole) return { error: "Console hook not initialized" }; let entries = h.console; if (args.level) entries = entries.filter((e) => e.l === args.level); if (args.pattern) { const re = new RegExp(args.pattern, "i"); entries = entries.filter((e) => re.test(e.m)); } const result = entries.slice(-(args.last || 50)); if (args.clear) h.console.length = 0; return result; } const autoCommands = { boxChain, getStyles, viewport, navigate, reload, resize, querySelector, click, screenshot, getValue, setValue, typeText, scroll, getConsole, listBreakouts: () => breakout.list(), closeBreakout: (args) => breakout.close(args) }; const asyncCommands = { captureScreen: (args) => capture.capture(args.quality), enableCapture: () => capture.enable(), openBreakout: (args) => breakout.open(args) }; function dispatch(cmdId, cmd, args, sendResult) { const fn = autoCommands[cmd]; if (fn) { try { const result = fn(args); sendResult({ type: "dev_cmd_result", cmdId, result: JSON.parse(JSON.stringify(result ?? null)) }); } catch (err) { sendResult({ type: "dev_cmd_result", cmdId, error: err.message }); } return; } const asyncFn = asyncCommands[cmd]; if (asyncFn) { asyncFn(args).then((result) => { sendResult({ type: "dev_cmd_result", cmdId, result: JSON.parse(JSON.stringify(result ?? null)) }); }).catch((err) => { sendResult({ type: "dev_cmd_result", cmdId, error: err.message }); }); return; } if (cmd === "eval") { const js = args.js; if (!js) { sendResult({ type: "dev_cmd_result", cmdId, error: "js required" }); return; } const ok = window.confirm(`Dev takeover eval request: ${js.slice(0, 500)} Allow?`); if (!ok) { sendResult({ type: "dev_cmd_result", cmdId, error: "rejected by user" }); return; } try { const result = new Function("return (" + js + ")")(); const serialized = result instanceof Element ? result.outerHTML.slice(0, 500) : JSON.parse(JSON.stringify(result ?? null)); sendResult({ type: "dev_cmd_result", cmdId, result: serialized }); } catch (err) { sendResult({ type: "dev_cmd_result", cmdId, error: err.message }); } return; } sendResult({ type: "dev_cmd_result", cmdId, error: `Unknown command: ${cmd}` }); } return { token, capture, breakout, init, revoke, reregister, dispatch }; } const H = useHermes(); let _ws = H.ws ?? null; let _pingInterval = H.wsPing ?? null; const _onMessageCallbacks = H.wsCbs ?? []; const _messageBuffer = H.wsBuf ?? []; let _reconnectTimer = null; let _reconnectDelay = 1e3; let _selectedAgentRef = null; let _selectedModeRef = null; let _isLoggedInRef = null; let _loginErrorRef = null; let _takeover = null; const connected = H.wsConnected ?? ref(false); const status = H.wsStatus ?? ref("Disconnected"); const currentUser = H.wsUser ?? ref(""); const sessionId = H.wsSid ?? ref(null); const isInitialLoad = H.wsInit ?? ref(true); H.wsConnected = connected; H.wsStatus = status; H.wsUser = currentUser; H.wsSid = sessionId; H.wsInit = isInitialLoad; H.wsCbs = _onMessageCallbacks; H.wsBuf = _messageBuffer; function send(payload) { if (_ws && _ws.readyState === WebSocket.OPEN) _ws.send(JSON.stringify(payload)); } function getTakeover() { if (!_takeover) _takeover = useTakeover(send); return _takeover; } const DEV_TOKEN$1 = "7Oorb9S3OpwFyWgm4zi_Tq7GeamefbjjTgooPVPWAwPDOf6B4TvgvQlLbhmT4DjsqBS_D1g"; function getWsUrl() { let base; { base = "wss://assay.loop42.de/ws"; } const token = localStorage.getItem("nyx_session") || localStorage.getItem("titan_token") || DEV_TOKEN$1; const session = sessionStorage.getItem("nyx_ws_session") || ""; const qs = new URLSearchParams(); qs.set("token", token); if (session) qs.set("session", session); return qs.toString() ? `${base}?${qs}` : base; } function scheduleReconnect() { if (_reconnectTimer) return; if (!_isLoggedInRef?.value) return; status.value = `Reconnecting in ${Math.round(_reconnectDelay / 1e3)}sโฆ`; _reconnectTimer = setTimeout(() => { _reconnectTimer = null; if (_isLoggedInRef?.value) connect(_selectedAgentRef, _isLoggedInRef, _loginErrorRef); }, _reconnectDelay); _reconnectDelay = Math.min(_reconnectDelay * 2, 16e3); } function connect(selectedAgent, isLoggedInRef, loginErrorRef, selectedMode) { if (_ws && _ws.readyState <= WebSocket.OPEN) return; _selectedAgentRef = selectedAgent; _selectedModeRef = selectedMode ?? null; _isLoggedInRef = isLoggedInRef; _loginErrorRef = loginErrorRef; _reconnectDelay = 1e3; console.log("WS CONNECT attempt, ws state:", _ws?.readyState, "url:", getWsUrl()); const wsUrl = getWsUrl(); if (isInitialLoad.value) { status.value = "Connecting..."; isInitialLoad.value = false; } _ws = new WebSocket(wsUrl); H.ws = _ws; _ws.onopen = () => { _reconnectDelay = 1e3; if (_pingInterval) clearInterval(_pingInterval); _pingInterval = setInterval(() => { if (_ws?.readyState === WebSocket.OPEN) _ws.send(JSON.stringify({ type: "ping" })); }, 3e4); H.wsPing = _pingInterval; }; _ws.onmessage = (event) => { try { const data = JSON.parse(event.data); if (data.type === "dev_cmd" && data.cmdId && data.cmd) { getTakeover().dispatch(data.cmdId, data.cmd, data.args || {}, send); return; } if (data.type === "session_info") { sessionId.value = data.session_id; sessionStorage.setItem("nyx_ws_session", data.session_id); } if (data.type === "ready") { connected.value = true; status.value = "Connected"; } if (data.type === "error" && data.code === "SESSION_TERMINATED") { console.warn("Message bounced: Session terminated."); } _messageBuffer.push(data); _onMessageCallbacks.forEach((fn) => fn(data)); } catch (e) { console.error("Parse error:", e); } }; _ws.onclose = (e) => { if (_pingInterval) { clearInterval(_pingInterval); _pingInterval = null; } connected.value = false; _messageBuffer.length = 0; if (e.code === 4001) { isLoggedInRef.value = false; loginErrorRef.value = "Session expired. Please log in again."; localStorage.removeItem("nyx_session"); localStorage.removeItem("titan_token"); sessionStorage.removeItem("agent"); status.value = "Logged out"; if (window.location.pathname !== "/login") { window.location.pathname = "/login"; } } else { scheduleReconnect(); } }; _ws.onerror = () => { }; } function disconnect() { if (_pingInterval) { clearInterval(_pingInterval); _pingInterval = null; H.wsPing = null; } if (_ws) { _ws.onclose = null; _ws.close(); _ws = null; H.ws = null; } connected.value = false; status.value = "Disconnected"; currentUser.value = ""; sessionId.value = null; isInitialLoad.value = true; } function sendDeferredAuth() { } function switchAgent(agentId, mode) { _messageBuffer.length = 0; send({ type: "switch_agent", agent: agentId, mode: mode ?? _selectedModeRef?.value ?? "private" }); } function clearBuffer() { _messageBuffer.length = 0; } function onMessage(fn) { _onMessageCallbacks.push(fn); return () => { const i = _onMessageCallbacks.indexOf(fn); if (i !== -1) _onMessageCallbacks.splice(i, 1); }; } function replayBuffer(fn) { _messageBuffer.forEach((data) => fn(data)); } function useWebSocket() { return { connected, status, currentUser, sessionId, connect, disconnect, send, switchAgent, sendDeferredAuth, clearBuffer, onMessage, replayBuffer, getTakeover }; } let _cached = null; function getApiBase() { if (_cached !== null) return _cached; const wsUrl = "wss://assay.loop42.de/ws"; try { const url = new URL(wsUrl); const proto = url.protocol === "wss:" ? "https:" : "http:"; _cached = `${proto}//${url.host}`; } catch { _cached = ""; } return _cached; } const _allAgents = ref([]); const _initAgent = (() => { if (typeof window === "undefined") return ""; const url = new URLSearchParams(window.location.hash.split("?")[1] || "").get("agent"); if (url) return url; const saved = sessionStorage.getItem("agent"); if (saved) return saved; return ""; })(); const _selectedAgent = ref(_initAgent); const _initMode = (() => { if (typeof window === "undefined") return "private"; const url = new URLSearchParams(window.location.hash.split("?")[1] || "").get("mode"); if (url === "public" || url === "private") return url; const saved = sessionStorage.getItem("agent_mode"); if (saved === "public" || saved === "private") return saved; return "private"; })(); const _selectedMode = ref(_initMode); const _defaultAgent = ref("titan"); const _allowedAgentIds = ref([]); function useAgents(connected2) { const allAgents = _allAgents; const selectedAgent = _selectedAgent; const selectedMode = _selectedMode; const defaultAgent = _defaultAgent; const allowedAgentIds = _allowedAgentIds; const agentModels = ref([]); const filteredAgents = computed(() => { if (allowedAgentIds.value.length === 0) return allAgents.value; return allAgents.value.filter((a) => allowedAgentIds.value.includes(a.id)); }); function updateFromServer(data) { if (data.agents) { allAgents.value = data.agents; } if (data.defaultAgent) { _defaultAgent.value = data.defaultAgent; } if (data.allowedAgents) { _allowedAgentIds.value = data.allowedAgents; } if (allAgents.value.length === 0) { allAgents.value = [{ id: "assay", name: "assay", promptPrice: null, completionPrice: null }]; _allowedAgentIds.value = ["assay"]; _defaultAgent.value = "assay"; } const urlParams = new URLSearchParams(window.location.hash.split("?")[1] || ""); const urlMode = urlParams.get("mode"); const savedMode = sessionStorage.getItem("agent_mode"); if (urlMode === "public" || urlMode === "private") { _selectedMode.value = urlMode; } else if (savedMode === "public" || savedMode === "private") { _selectedMode.value = savedMode; } const urlAgent = urlParams.get("agent"); const savedAgent = sessionStorage.getItem("agent"); const isUrlAgentAllowed = urlAgent && _allowedAgentIds.value.includes(urlAgent); const isSavedAgentAllowed = savedAgent && _allowedAgentIds.value.includes(savedAgent); if (isUrlAgentAllowed) { selectedAgent.value = urlAgent; } else if (isSavedAgentAllowed) { selectedAgent.value = savedAgent; } else if (!selectedAgent.value && _defaultAgent.value) { selectedAgent.value = _defaultAgent.value; } } async function fetchAgentModels() { try { const protocol = window.location.protocol === "https:" ? "https:" : "http:"; const host = window.location.hostname; const sessionToken = localStorage.getItem("nyx_session") ?? ""; const res = await fetch(`${protocol}//${host}/agents`, { headers: { Authorization: `Bearer ${sessionToken}` } }); agentModels.value = await res.json(); } catch (e) { console.error("Failed to fetch agent models:", e); } } watch([connected2, _allowedAgentIds], () => { if (connected2.value && !_selectedAgent.value) { updateFromServer({}); } }); const channelStates = ref({}); let _pollTimer = null; let _pollIdx = 0; async function fetchOneAgent(agentId) { const sessionToken = localStorage.getItem("nyx_session") ?? ""; if (!sessionToken) return; try { const apiBase = getApiBase(); const res = await fetch(`${apiBase}/api/channels/${agentId}`, { headers: { Authorization: `Bearer ${sessionToken}` } }); if (!res.ok) return; const data = await res.json(); channelStates.value = { ...channelStates.value, [agentId]: { private: data.private ?? null, public: data.public ?? null } }; } catch { } } function pollNextAgent() { const agents2 = allAgents.value; if (!agents2.length) return; _pollIdx = _pollIdx % agents2.length; fetchOneAgent(agents2[_pollIdx].id); _pollIdx++; } function startChannelPolling() { if (_pollTimer) return; _pollIdx = 0; pollNextAgent(); _pollTimer = setInterval(pollNextAgent, 1e3); } function stopChannelPolling() { if (_pollTimer) { clearInterval(_pollTimer); _pollTimer = null; } } function getChannelState(agentId, mode) { const entry = channelStates.value[agentId]; if (!entry) return null; return mode === "private" ? entry.private : entry.public; } let currentUserRef = null; function setCurrentUser(userRef) { currentUserRef = userRef; } function syncStorage() { if (_selectedAgent.value) sessionStorage.setItem("agent", _selectedAgent.value); sessionStorage.setItem("agent_mode", _selectedMode.value); } watch([_selectedAgent, _selectedMode], syncStorage); return { allAgents, selectedAgent, selectedMode, defaultAgent, allowedAgentIds, agentModels, filteredAgents, channelStates, fetchAgentModels, updateFromServer, getChannelState, setCurrentUser, startChannelPolling, stopChannelPolling }; } const SESSION_TOKEN_KEY = "nyx_session"; const DEV_TOKEN = "7Oorb9S3OpwFyWgm4zi_Tq7GeamefbjjTgooPVPWAwPDOf6B4TvgvQlLbhmT4DjsqBS_D1g"; function useAuth(connectFn) { const router = useRouter(); if (typeof localStorage !== "undefined") { if (!localStorage.getItem(SESSION_TOKEN_KEY) && !localStorage.getItem("titan_token")) { localStorage.setItem(SESSION_TOKEN_KEY, DEV_TOKEN); } } const isLoggedIn = ref(typeof localStorage !== "undefined" ? !!localStorage.getItem(SESSION_TOKEN_KEY) : false); const loginToken = ref(""); const loginError = ref(""); const loggingIn = ref(false); async function doLogin() { const token = loginToken.value.trim(); if (!token) return; loggingIn.value = true; loginError.value = ""; try { const nonceRes = await fetch(`${getApiBase()}/api/auth/nonce`); if (!nonceRes.ok) { loginError.value = "Auth unavailable"; loggingIn.value = false; return; } const { nonce } = await nonceRes.json(); const res = await fetch(`${getApiBase()}/api/auth`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ token, nonce }) }); if (!res.ok) { const err = await res.json().catch(() => ({ error: "Login failed" })); loginError.value = err.error || "Invalid token"; loggingIn.value = false; return; } const { sessionToken } = await res.json(); localStorage.removeItem("titan_token"); localStorage.removeItem("nyx_token"); localStorage.setItem(SESSION_TOKEN_KEY, sessionToken); sessionStorage.removeItem("agent"); isLoggedIn.value = true; connectFn(); router.push("/chat"); setTimeout(() => { loggingIn.value = false; }, 500); } catch { loginError.value = "Network error"; loggingIn.value = false; } } async function doLogout(disconnectFn) { const sessionToken = localStorage.getItem(SESSION_TOKEN_KEY); if (sessionToken) { fetch(`${getApiBase()}/api/auth/logout`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ sessionToken }) }).catch(() => { }); } if (disconnectFn) disconnectFn(); localStorage.removeItem(SESSION_TOKEN_KEY); localStorage.removeItem("titan_token"); localStorage.removeItem("nyx_token"); sessionStorage.removeItem("agent"); sessionStorage.removeItem("viewer_auth"); isLoggedIn.value = false; loginToken.value = ""; loggingIn.value = false; router.push("/"); } return { isLoggedIn, loginToken, loginError, loggingIn, doLogin, doLogout }; } const STORAGE_KEY$2 = "viewer_auth"; const REFRESH_BEFORE_EXPIRY_MS = 5 * 60 * 1e3; function loadStored() { try { const raw = sessionStorage.getItem(STORAGE_KEY$2); if (!raw) return null; return JSON.parse(raw); } catch { return null; } } function saveStored(auth2) { try { sessionStorage.setItem(STORAGE_KEY$2, JSON.stringify(auth2)); } catch { } } function clearStored() { sessionStorage.removeItem(STORAGE_KEY$2); } const useViewerStore = defineStore("viewer", () => { const stored2 = loadStored(); const fstoken = ref(stored2?.fstoken ?? ""); const roots = ref(stored2?.roots ?? []); const expiresAt = ref(stored2?.expiresAt ?? 0); const ready = computed(() => !!fstoken.value && Date.now() < expiresAt.value); let acquiring = null; async function acquire(force = false) { if (!force && fstoken.value && Date.now() < expiresAt.value - REFRESH_BEFORE_EXPIRY_MS) return; if (acquiring) return acquiring; acquiring = _acquire().finally(() => { acquiring = null; }); return acquiring; } async function _acquire() { const sessionToken = localStorage.getItem("nyx_session") || localStorage.getItem("titan_token") || ""; if (!sessionToken) return; try { const res = await fetch(`${getApiBase()}/api/viewer/token`, { method: "POST", headers: { Authorization: `Bearer ${sessionToken}` } }); if (!res.ok) { fstoken.value = ""; roots.value = []; expiresAt.value = 0; clearStored(); return; } const data = await res.json(); const newToken = data.fstoken; let newRoots = roots.value.length ? roots.value : ["shared", "workspace-titan"]; try { const tr = await fetch(`${getApiBase()}/api/viewer/tree?root=&token=${encodeURIComponent(newToken)}`); if (tr.ok) { const td = await tr.json(); if (Array.isArray(td.dirs) && td.dirs.length) newRoots = td.dirs; } } catch { } const newExpiresAt = Date.now() + 60 * 60 * 1e3; fstoken.value = newToken; roots.value = newRoots; expiresAt.value = newExpiresAt; saveStored({ fstoken: newToken, roots: newRoots, expiresAt: newExpiresAt }); } catch { } } function invalidate() { fstoken.value = ""; roots.value = []; expiresAt.value = 0; clearStored(); } return { fstoken, roots, ready, acquire, invalidate }; }); const lastViewerPath = ref(typeof localStorage !== "undefined" ? localStorage.getItem("viewer_last_path") || "" : ""); const viewerOpenDirs = reactive(/* @__PURE__ */ new Set()); const ws = useWebSocket(); const takeover = ws.getTakeover(); const agents = useAgents(ws.connected); agents.setCurrentUser(ws.currentUser); const auth = useAuth(() => { ws.connect(agents.selectedAgent, auth.isLoggedIn, auth.loginError, agents.selectedMode); useViewerStore().acquire(); }); const STORAGE_KEY$1 = "hermes_theme"; const THEME_ICONS = { titan: CommandLineIcon, eras: SunIcon, loop42: CubeTransparentIcon }; const THEME_NAMES = { titan: "Titan", eras: "ERAS", loop42: "loop42" }; const THEME_LOGOS = { titan: null, eras: null, loop42: null }; const AGENT_THEME_MAP = { eras: "eras" }; function agentLogo(agentId) { const t = AGENT_THEME_MAP[agentId] ?? "titan"; return THEME_LOGOS[t]; } const stored = localStorage.getItem(STORAGE_KEY$1); const theme = ref( stored === "workhorse" ? "loop42" : ( // migrate legacy name stored || "loop42" ) ); const THEME_FAVICONS = { titan: "/favicon-titan.svg", eras: "/favicon-eras.svg", loop42: "/favicon-loop42.svg" }; function applyTheme(t) { if (t === "titan") { document.documentElement.removeAttribute("data-theme"); } else { document.documentElement.setAttribute("data-theme", t); } const link = document.querySelector('link[rel="icon"]'); if (link) link.href = THEME_FAVICONS[t] || "/favicon.svg"; } applyTheme(theme.value); watch(theme, (t) => { applyTheme(t); localStorage.setItem(STORAGE_KEY$1, t); }); function useTheme() { function setTheme(t) { theme.value = t; } return { theme, setTheme }; } const useChatStore = defineStore("chat", () => { const messages = ref([]); const channelState = ref("NO_SESSION"); const connectionState = ref("CONNECTING"); const smState = computed(() => { if (connectionState.value !== "SYNCED") return connectionState.value; return _stopPending.value ? "STOP_PENDING" : channelState.value; }); const _stopPending = ref(false); const truncatedWarning = ref(false); const localSessionId = ref(Math.random().toString(36).slice(2, 10)); const sessionTotalTokens = ref(null); const sessionUsage = ref(null); const beVersion = ref(""); const finance = ref(null); const sessionKey = ref(""); const sessionCost = computed(() => { const p = finance.value?.pricing; const u = sessionTotalTokens.value; if (!p || !u) return 0; return ((u.input_tokens || 0) * p.prompt + (u.output_tokens || 0) * p.completion) / 1e6; }); const queuedThought = ref(null); const activeTurnCorrId = ref(null); const handoverPending = computed( () => messages.value.some((m) => m.confirmNew && !m.confirmed) ); const isRunning = computed( () => channelState.value === "AGENT_RUNNING" || _stopPending.value || channelState.value === "HANDOVER_PENDING" ); let _wsSend = null; function setWsSend(fn) { _wsSend = fn; } function newSession() { stashMessages(); resetLocalSession(); _wsSend?.({ type: "new" }); } function handover() { _wsSend?.({ type: "handover_request" }); } function stop() { if (channelState.value !== "AGENT_RUNNING" && !_stopPending.value) return; queuedThought.value = null; _stopPending.value = true; pushSystem("Stopping after current turn..."); _wsSend?.({ type: "stop" }); } function confirmNew() { const bubble = [...messages.value].reverse().find((m) => m.confirmNew && !m.confirmed); if (bubble) bubble.confirmed = true; stashMessages(); resetLocalSession(); _wsSend?.({ type: "new" }); } function stay() { const bubble = [...messages.value].reverse().find((m) => m.confirmNew && !m.confirmed); if (bubble) { bubble.confirmed = true; setTimeout(() => { const idx = messages.value.findIndex((m) => m === bubble); if (idx !== -1) messages.value.splice(idx, 1); }, 1e3); } _wsSend?.({ type: "cancel_handover" }); } function applyChannelState(state) { channelState.value = state; if (state === "AGENT_RUNNING") sessionContextHint.value = ""; if (state === "READY" || state === "FRESH") _stopPending.value = false; } function applyConnectionState(state) { connectionState.value = state; } function applySessionState(state) { if (["FRESH", "READY", "AGENT_RUNNING", "HANDOVER_PENDING", "HANDOVER_DONE", "NO_SESSION"].includes(state)) { applyChannelState(state); } else if (["CONNECTING", "LOADING_HISTORY", "SYNCED", "SWITCHING"].includes(state)) { applyConnectionState(state); } } function setConnecting() { connectionState.value = "CONNECTING"; } let currentAssistantMessageIndex = -1; const streamingMessageVisibleContent = ref(""); let streamingMessageFullContent = ""; let streamingInterval = null; let charIndex = 0; let pendingVisibleClear = false; let thinkingMessageIndex = -1; const thinkingContent = ref(""); const sessionContextHint = ref(""); const TYPING_TICK_MS = 10; const smLabel = computed(() => { if (connectionState.value !== "SYNCED") { switch (connectionState.value) { case "CONNECTING": return "โณ Connecting..."; case "LOADING_HISTORY": return "โณ Loading..."; case "SWITCHING": return "๐ Switching..."; } } if (_stopPending.value) return "โ Stopping..."; switch (channelState.value) { case "AGENT_RUNNING": return "โ๏ธ Working..."; case "HANDOVER_PENDING": return "๐ Handover..."; case "HANDOVER_DONE": return "โ Handover ready"; case "READY": return sessionContextHint.value ? `โ Ready - ${sessionContextHint.value}` : "โ โ Ready"; case "FRESH": return "โจ New session"; case "RESETTING": return "๐ Resetting..."; case "NO_SESSION": return "โ No session"; default: return "โ โ Ready"; } }); const visibleMsgs = (visibleCount) => { return messages.value.slice(-visibleCount); }; function resetLocalSession() { localSessionId.value = Math.random().toString(36).slice(2, 10); console.log("[ChatStore] Local Session Reset:", localSessionId.value); resetStreamingState(); } function stashMessages() { const saveable = messages.value.filter((m) => m.role === "user" || m.role === "assistant").map((m) => ({ role: m.role, content: m.content, agentId: m.agentId })); if (saveable.length > 0) { try { sessionStorage.setItem("hermes_prev_session", JSON.stringify(saveable)); } catch (_) { } } } function getPreviousSession() { try { const raw = sessionStorage.getItem("hermes_prev_session"); return raw ? JSON.parse(raw) : []; } catch { return []; } } function clearMessages() { stashMessages(); messages.value = []; sessionTotalTokens.value = null; finance.value = null; resetStreamingState(); } function pushMessage(msg) { hasActiveStreamingMessage() ? currentAssistantMessageIndex : messages.value.length; const msgWithId = { ...msg, sessionId: localSessionId.value }; if (hasActiveStreamingMessage() && msg.role === "user") { messages.value.splice(currentAssistantMessageIndex, 0, msgWithId); currentAssistantMessageIndex++; } else { messages.value.push(msgWithId); } } function patchMessage(msgId, patch) { const msg = messages.value.find((m) => m.msgId === msgId); if (!msg) return false; Object.assign(msg, patch); return true; } function pushSystem(text, agentId) { const msg = { role: "system", content: text, agentId: agentId || null, sessionId: localSessionId.value }; if (hasActiveStreamingMessage()) { messages.value.splice(currentAssistantMessageIndex, 0, msg); currentAssistantMessageIndex++; } else { messages.value.push(msg); } } function resetStreamingState() { if (streamingInterval !== null) clearInterval(streamingInterval); streamingInterval = null; currentAssistantMessageIndex = -1; streamingMessageFullContent = ""; charIndex = 0; pendingVisibleClear = true; nextTick(() => { if (pendingVisibleClear) { streamingMessageVisibleContent.value = ""; pendingVisibleClear = false; } }); } function startNewAssistantMessage(agentId) { if (currentAssistantMessageIndex === -1 || !messages.value[currentAssistantMessageIndex]?.streaming) { pendingVisibleClear = false; resetStreamingState(); messages.value.push({ role: "assistant", content: "", fullContent: "", usage: null, streaming: true, agentId: agentId ?? null, sessionId: localSessionId.value, turnCorrId: activeTurnCorrId.value ?? null }); currentAssistantMessageIndex = messages.value.length - 1; } } function appendAssistantDelta(delta, agentId) { if (currentAssistantMessageIndex !== -1 && messages.value[currentAssistantMessageIndex]?.streaming) { streamingMessageFullContent += delta; messages.value[currentAssistantMessageIndex].fullContent = streamingMessageFullContent; if (!streamingInterval) startTypewriter(); } else { startNewAssistantMessage(agentId); appendAssistantDelta(delta, agentId); } } function startTypewriter() { if (streamingInterval !== null) clearInterval(streamingInterval); streamingInterval = setInterval(() => { const backlog = streamingMessageFullContent.length - charIndex; if (backlog <= 0) { console.log( "[hermes] typewriter done: charIndex=%d fullLen=%d visibleLen=%d streaming=%s", charIndex, streamingMessageFullContent.length, streamingMessageVisibleContent.value.length, messages.value[currentAssistantMessageIndex]?.streaming ); if (streamingInterval !== null) clearInterval(streamingInterval); streamingInterval = null; return; } const charsThisTick = backlog >= 200 ? 10 : backlog >= 50 ? 4 : 1; const end = Math.min(charIndex + charsThisTick, streamingMessageFullContent.length); streamingMessageVisibleContent.value += streamingMessageFullContent.slice(charIndex, end); charIndex = end; }, TYPING_TICK_MS); } function finalizeAssistantMessage(content, usage, truncated = false) { if (currentAssistantMessageIndex !== -1 && messages.value[currentAssistantMessageIndex]?.streaming) { const msg = messages.value[currentAssistantMessageIndex]; const finalContent = (content !== null && content !== void 0 ? content : streamingMessageFullContent).replace(/\s*NO_REPLY\s*$/g, "").trim(); console.log( "[hermes] finalizeAssistantMessage: charIndex=%d fullLen=%d visibleLen=%d finalLen=%d", charIndex, streamingMessageFullContent.length, streamingMessageVisibleContent.value.length, finalContent.length ); if (streamingInterval !== null) { clearInterval(streamingInterval); streamingInterval = null; } streamingMessageVisibleContent.value = finalContent; charIndex = finalContent.length; msg.content = finalContent; msg.fullContent = finalContent; if (usage) msg.usage = usage; if (truncated) msg.truncated = true; msg.streaming = false; console.log( "[hermes] post-finalize: msg.content.length=%d msg.streaming=%s idx=%d", msg.content.length, msg.streaming, currentAssistantMessageIndex ); resetStreamingState(); } } function appendThinking(content) { if (!content) return; thinkingContent.value += content; if (thinkingMessageIndex === -1) { messages.value.push({ role: "thinking", content: thinkingContent, collapsed: false }); thinkingMessageIndex = messages.value.length - 1; } } function collapseThinking() { if (thinkingMessageIndex !== -1 && messages.value[thinkingMessageIndex]) { messages.value[thinkingMessageIndex].collapsed = true; } thinkingMessageIndex = -1; thinkingContent.value = ""; } function createCompleteAssistantMessage(content, agentId, usage) { if (hasActiveStreamingMessage()) { finalizeAssistantMessage(content, usage); return; } const msg = { role: "assistant", content: content.replace(/\s*NO_REPLY\s*$/g, "").trim(), fullContent: content.replace(/\s*NO_REPLY\s*$/g, "").trim(), usage: usage || null, streaming: false, agentId: agentId ?? null, sessionId: localSessionId.value, turnCorrId: activeTurnCorrId.value ?? null }; messages.value.push(msg); } function hasActiveStreamingMessage() { return currentAssistantMessageIndex !== -1 && messages.value[currentAssistantMessageIndex]?.streaming; } function streamingMessageLength() { return streamingMessageFullContent.length; } function suppressAssistantMessage() { if (currentAssistantMessageIndex !== -1 && messages.value[currentAssistantMessageIndex]?.streaming) { if (streamingInterval !== null) { clearInterval(streamingInterval); streamingInterval = null; } messages.value.splice(currentAssistantMessageIndex, 1); } resetStreamingState(); } return { messages, activeTurnCorrId, sessionKey, smState, // legacy computed: connection priority, then channel channelState, connectionState, applySessionState, // legacy compat applyChannelState, applyConnectionState, setConnecting, truncatedWarning, localSessionId, sessionTotalTokens, sessionUsage, beVersion, sessionCost, finance, smLabel, sessionContextHint, streamingMessageVisibleContent, visibleMsgs, resetLocalSession, clearMessages, stashMessages, getPreviousSession, pushMessage, patchMessage, pushSystem, startNewAssistantMessage, appendAssistantDelta, finalizeAssistantMessage, createCompleteAssistantMessage, appendThinking, collapseThinking, hasActiveStreamingMessage, streamingMessageLength, suppressAssistantMessage, queuedThought, handoverPending, isRunning, setWsSend, newSession, handover, stop, confirmNew, stay }; }); const STORAGE_KEY = "hermes_dev_flags"; const defaults = { showGrid: false, showDebugInfo: false, showHud: false }; function load() { try { const raw = sessionStorage.getItem(STORAGE_KEY); if (!raw) return { ...defaults }; return { ...defaults, ...JSON.parse(raw) }; } catch { return { ...defaults }; } } const flags = reactive(load()); watch(flags, (v) => { sessionStorage.setItem(STORAGE_KEY, JSON.stringify(v)); }, { deep: true }); function useDevFlags() { return flags; } const columns = 12; const _sfc_main$8 = /* @__PURE__ */ defineComponent({ __name: "GridOverlay", __ssrInlineRender: true, setup(__props) { const devFlags = useDevFlags(); return (_ctx, _push, _parent, _attrs) => { if (unref(devFlags).showGrid) { _push(`
You're connected. Head back to chat or sign out below.
`); } else { _push(`