#include "olliedbusclient.h" #include #include #include static const char *SERVICE = "org.ollie.SessionManager"; static const char *OBJ_PATH = "/org/ollie/SessionManager"; static const char *IFACE = "org.ollie.SessionManager"; OllieDBusClient::OllieDBusClient(QObject *parent) : QObject(parent) { m_iface = new QDBusInterface(SERVICE, OBJ_PATH, IFACE, QDBusConnection::sessionBus(), this); // Connect D-Bus signals QDBusConnection bus = QDBusConnection::sessionBus(); bus.connect(SERVICE, OBJ_PATH, IFACE, "SessionCreated", this, SLOT(onSessionCreated(QString))); bus.connect(SERVICE, OBJ_PATH, IFACE, "SessionKilled", this, SLOT(onSessionKilled(QString))); bus.connect(SERVICE, OBJ_PATH, IFACE, "StateChanged", this, SLOT(onStateChanged(QString,QString))); bus.connect(SERVICE, OBJ_PATH, IFACE, "ChatUpdated", this, SLOT(onChatUpdated(QString,qlonglong,QString))); refreshSessions(); } void OllieDBusClient::setActiveSessionId(const QString &id) { if (m_activeSessionId == id) return; m_activeSessionId = id; m_chatText.clear(); m_chatOffset = 0; emit activeSessionIdChanged(); emit chatTextChanged(); fetchChat(); fetchConfig(); } void OllieDBusClient::refreshSessions() { QDBusReply reply = m_iface->call("ListSessions"); if (!reply.isValid()) return; m_sessions.clear(); for (const QString &entry : reply.value()) { QStringList parts = entry.split('\t'); if (parts.size() >= 4) { QVariantMap session; session["id"] = parts[0]; session["state"] = parts[1]; session["model"] = parts[2]; session["agent"] = parts[3]; m_sessions.append(session); } } emit sessionsChanged(); // Update active state for (const QVariant &v : m_sessions) { QVariantMap s = v.toMap(); if (s["id"].toString() == m_activeSessionId) { QString newState = s["state"].toString(); if (newState != m_activeState) { m_activeState = newState; emit activeStateChanged(); } return; } } // Active session no longer exists if (!m_activeSessionId.isEmpty() && !m_sessions.isEmpty()) { setActiveSessionId(m_sessions.first().toMap()["id"].toString()); } else if (m_activeSessionId.isEmpty() && !m_sessions.isEmpty()) { setActiveSessionId(m_sessions.first().toMap()["id"].toString()); } else if (m_sessions.isEmpty() && !m_activeSessionId.isEmpty()) { m_activeSessionId.clear(); m_activeState = "idle"; m_chatText.clear(); m_chatOffset = 0; m_activeCwd.clear(); emit activeSessionIdChanged(); emit activeStateChanged(); emit chatTextChanged(); emit activeCwdChanged(); } } void OllieDBusClient::fetchChat() { if (m_activeSessionId.isEmpty()) return; QDBusMessage msg = QDBusMessage::createMethodCall(SERVICE, OBJ_PATH, IFACE, "GetChat"); msg << m_activeSessionId << m_chatOffset; QDBusMessage reply = QDBusConnection::sessionBus().call(msg); if (reply.type() != QDBusMessage::ReplyMessage) return; QList args = reply.arguments(); if (args.size() < 2) return; QString text = args[0].toString(); qlonglong newOffset = args[1].toLongLong(); if (!text.isEmpty()) { if (m_chatOffset == 0) { m_chatText = text; } else { m_chatText += text; } m_chatOffset = newOffset; emit chatTextChanged(); } } void OllieDBusClient::fetchConfig() { if (m_activeSessionId.isEmpty()) { if (!m_activeCwd.isEmpty()) { m_activeCwd.clear(); emit activeCwdChanged(); } return; } QDBusMessage msg = QDBusMessage::createMethodCall(SERVICE, OBJ_PATH, IFACE, "GetConfig"); msg << m_activeSessionId; QDBusMessage reply = QDBusConnection::sessionBus().call(msg); if (reply.type() != QDBusMessage::ReplyMessage) return; QList args = reply.arguments(); if (args.isEmpty()) return; QString config = args[0].toString(); QString newCwd; const QStringList lines = config.split('\n', Qt::SkipEmptyParts); for (const QString &line : lines) { if (line.startsWith(QLatin1String("cwd="))) { newCwd = line.mid(4); break; } } if (newCwd != m_activeCwd) { m_activeCwd = newCwd; emit activeCwdChanged(); } } QString OllieDBusClient::createSession(const QString &cwd) { QString dir = cwd.isEmpty() ? QDir::homePath() : cwd; QDBusReply reply = m_iface->call("CreateSession", dir, QString(), QString(), "default"); if (reply.isValid()) { QString id = reply.value(); refreshSessions(); setActiveSessionId(id); return id; } return QString(); } bool OllieDBusClient::killSession(const QString &sessionId) { QDBusReply reply = m_iface->call("KillSession", sessionId); if (reply.isValid() && reply.value()) { if (sessionId == m_activeSessionId) { m_activeSessionId.clear(); m_chatText.clear(); m_chatOffset = 0; m_activeState = "idle"; m_activeCwd.clear(); emit activeSessionIdChanged(); emit chatTextChanged(); emit activeStateChanged(); emit activeCwdChanged(); } refreshSessions(); return true; } return false; } bool OllieDBusClient::renameSession(const QString &sessionId, const QString &newName) { QDBusReply reply = m_iface->call("RenameSession", sessionId, newName); if (reply.isValid() && reply.value()) { if (sessionId == m_activeSessionId) { m_activeSessionId = newName; emit activeSessionIdChanged(); } refreshSessions(); return true; } return false; } bool OllieDBusClient::setConfig(const QString &sessionId, const QString &key, const QString &value) { QDBusReply reply = m_iface->call("SetConfig", sessionId, key, value); if (reply.isValid() && reply.value()) { if (sessionId == m_activeSessionId && key == "cwd") { m_activeCwd = value; emit activeCwdChanged(); } return true; } return false; } bool OllieDBusClient::submit(const QString &sessionId, const QString &prompt) { QDBusReply reply = m_iface->call("Submit", sessionId, prompt); return reply.isValid() && reply.value(); } bool OllieDBusClient::interrupt(const QString &sessionId) { QDBusReply reply = m_iface->call("Interrupt", sessionId); return reply.isValid() && reply.value(); } // --- D-Bus signal handlers --- void OllieDBusClient::onSessionCreated(const QString &session_id) { Q_UNUSED(session_id); refreshSessions(); } void OllieDBusClient::onSessionKilled(const QString &session_id) { Q_UNUSED(session_id); refreshSessions(); } void OllieDBusClient::onStateChanged(const QString &session_id, const QString &new_state) { if (session_id == m_activeSessionId) { m_activeState = new_state; emit activeStateChanged(); } refreshSessions(); } void OllieDBusClient::onChatUpdated(const QString &session_id, qlonglong offset, const QString &new_text) { if (session_id != m_activeSessionId) return; // Append directly from the signal — no need to poll if (offset == m_chatOffset) { m_chatText += new_text; m_chatOffset += new_text.size(); emit chatTextChanged(); } else { // Offset mismatch — do a full fetch fetchChat(); } }