diff --git a/gui/ollie9pclient.cpp b/gui/ollie9pclient.cpp index 7b5d454..29ffc19 100644 --- a/gui/ollie9pclient.cpp +++ b/gui/ollie9pclient.cpp @@ -391,57 +391,48 @@ void Ollie9pClient::refreshSessions() QString Ollie9pClient::readLog() { if (m_activeSessionId.isEmpty()) return {}; - QByteArray out = run9p({"read", agentPath() + "/log"}); - return QString::fromUtf8(out); + if (m_9p && m_9p->isConnected()) { + return QString::fromUtf8(m_9p->read(agentPath() + "/log")); + } + return QString::fromUtf8(run9p({"read", agentPath() + "/log"})); } QString Ollie9pClient::readLogForSession(const QString &sessionId, const QString &agentId) { if (sessionId.isEmpty() || agentId.isEmpty()) return {}; // Use immutable IDs — the 9P namespace resolves them via aliases. - QByteArray out = run9p({"read", "session/" + sessionId + "/agent/" + agentId + "/log"}); - return QString::fromUtf8(out); + QString path = "session/" + sessionId + "/agent/" + agentId + "/log"; + if (m_9p && m_9p->isConnected()) { + return QString::fromUtf8(m_9p->read(path)); + } + return QString::fromUtf8(run9p({"read", path})); } bool Ollie9pClient::submit(const QString &prompt) { if (m_activeSessionId.isEmpty() || m_agentId.isEmpty() || prompt.trimmed().isEmpty()) return false; - QProcess proc; - proc.start(ninepBin(), {"-a", serverAddr(), "write", agentPath() + "/prompt"}); - proc.waitForStarted(3000); - proc.write(prompt.toUtf8()); - proc.closeWriteChannel(); - proc.waitForFinished(5000); - return proc.exitCode() == 0; + if (!m_9p || !m_9p->isConnected()) return false; + return m_9p->write(agentPath() + "/prompt", prompt.toUtf8()); } bool Ollie9pClient::interrupt() { if (m_activeSessionId.isEmpty() || m_agentId.isEmpty()) return false; - QProcess proc; - proc.start(ninepBin(), {"-a", serverAddr(), "write", agentPath() + "/ctl"}); - proc.waitForStarted(3000); - proc.write("stop"); - proc.closeWriteChannel(); - proc.waitForFinished(3000); - return proc.exitCode() == 0; + if (!m_9p || !m_9p->isConnected()) return false; + return m_9p->write(agentPath() + "/ctl", "stop"); } bool Ollie9pClient::kill() { if (m_activeSessionId.isEmpty() || m_agentId.isEmpty()) return false; - QProcess proc; - proc.start(ninepBin(), {"-a", serverAddr(), "write", agentPath() + "/ctl"}); - proc.waitForStarted(3000); - proc.write("kill"); - proc.closeWriteChannel(); - proc.waitForFinished(3000); - return proc.exitCode() == 0; + if (!m_9p || !m_9p->isConnected()) return false; + return m_9p->write(agentPath() + "/ctl", "kill"); } bool Ollie9pClient::killSession(const QString &sessionId) { if (sessionId.isEmpty()) return false; + if (!m_9p || !m_9p->isConnected()) return false; // If killing the active session, shut down its streams and deselect. if (sessionId == m_activeSessionId) { @@ -454,26 +445,16 @@ bool Ollie9pClient::killSession(const QString &sessionId) // Use immutable ID — the 9P namespace resolves it via alias. QString path = "session/" + sessionId + "/ctl"; - QProcess proc; - proc.start(ninepBin(), {"-a", serverAddr(), "write", path}); - proc.waitForStarted(3000); - proc.write("kill"); - proc.closeWriteChannel(); - proc.waitForFinished(3000); - return proc.exitCode() == 0; + return m_9p->write(path, "kill"); } bool Ollie9pClient::pauseSession(const QString &sessionId) { if (sessionId.isEmpty()) return false; + if (!m_9p || !m_9p->isConnected()) return false; + QString path = "session/" + sessionId + "/ctl"; - QProcess proc; - proc.start(ninepBin(), {"-a", serverAddr(), "write", path}); - proc.waitForStarted(3000); - proc.write("pause"); - proc.closeWriteChannel(); - proc.waitForFinished(3000); - if (proc.exitCode() == 0) { + if (m_9p->write(path, "pause")) { refreshSessions(); return true; } @@ -483,14 +464,10 @@ bool Ollie9pClient::pauseSession(const QString &sessionId) bool Ollie9pClient::resumeSession(const QString &sessionId) { if (sessionId.isEmpty()) return false; + if (!m_9p || !m_9p->isConnected()) return false; + QString path = "session/" + sessionId + "/ctl"; - QProcess proc; - proc.start(ninepBin(), {"-a", serverAddr(), "write", path}); - proc.waitForStarted(3000); - proc.write("resume"); - proc.closeWriteChannel(); - proc.waitForFinished(3000); - if (proc.exitCode() == 0) { + if (m_9p->write(path, "resume")) { refreshSessions(); return true; } @@ -510,18 +487,25 @@ bool Ollie9pClient::isSessionPaused(const QString &sessionId) const QString Ollie9pClient::getConfig() { if (m_activeSessionId.isEmpty()) return {}; - QByteArray out = run9p({"read", agentPath() + "/cfg"}); - return QString::fromUtf8(out); + if (m_9p && m_9p->isConnected()) { + return QString::fromUtf8(m_9p->read(agentPath() + "/cfg")); + } + return QString::fromUtf8(run9p({"read", agentPath() + "/cfg"})); } QStringList Ollie9pClient::getAgents(const QString &sessionId) { if (sessionId.isEmpty()) return {}; // Use immutable ID — the 9P namespace resolves it via alias. - QByteArray out = run9p({"ls", "session/" + sessionId + "/agent"}); - QString raw = QString::fromUtf8(out).trimmed(); - if (raw.isEmpty()) return {}; - QStringList all = raw.split('\n', Qt::SkipEmptyParts); + QString path = "session/" + sessionId + "/agent"; + QStringList all; + if (m_9p && m_9p->isConnected()) { + all = m_9p->ls(path); + } else { + QString raw = QString::fromUtf8(run9p({"ls", path})).trimmed(); + if (!raw.isEmpty()) + all = raw.split('\n', Qt::SkipEmptyParts); + } QStringList agents; for (const QString &a : all) { QString name = a.trimmed(); @@ -611,30 +595,23 @@ QStringList Ollie9pClient::getAvailableModels(const QString &backend) const bool Ollie9pClient::createQuickSession() { - QProcess proc; - proc.start(ninepBin(), {"-a", serverAddr(), "rdwr", "session/new"}); - proc.waitForStarted(3000); - proc.write("\n"); - proc.closeWriteChannel(); - proc.waitForFinished(5000); - return proc.exitCode() == 0; + if (!m_9p || !m_9p->isConnected()) return false; + QByteArray result = m_9p->rdwr(QStringLiteral("session/new"), "\n"); + return !result.isEmpty() || m_9p->lastError().isEmpty(); } QString Ollie9pClient::createSession(const QString &name, const QString &remote) { + if (!m_9p || !m_9p->isConnected()) return QStringLiteral("not connected"); + QStringList args; if (!name.isEmpty()) args << "name=" + name; if (!remote.isEmpty()) args << "remote=" + remote; - QProcess proc; - proc.start(ninepBin(), {"-a", serverAddr(), "rdwr", "session/new"}); - proc.waitForStarted(3000); - proc.write((args.join(" ") + "\n").toUtf8()); - proc.closeWriteChannel(); - proc.waitForFinished(5000); - if (proc.exitCode() != 0) { - QString err = QString::fromUtf8(proc.readAllStandardError()).trimmed(); - return err.isEmpty() ? QStringLiteral("failed to create session") : err; + QByteArray result = m_9p->rdwr(QStringLiteral("session/new"), (args.join(" ") + "\n").toUtf8()); + QString err = m_9p->lastError(); + if (!err.isEmpty()) { + return err; } return QString(); // success } @@ -642,6 +619,7 @@ QString Ollie9pClient::createSession(const QString &name, const QString &remote) QString Ollie9pClient::createAgent(const QString &sessionId, const QString &cwd, const QString &backend, const QString &model, const QString &agent, const QString &remote, const QString &agentAlias) { if (sessionId.isEmpty() || cwd.isEmpty()) return QStringLiteral("session ID and directory are required"); + if (!m_9p || !m_9p->isConnected()) return QStringLiteral("not connected"); QStringList agentArgs; agentArgs << "cwd=" + cwd; @@ -651,20 +629,11 @@ QString Ollie9pClient::createAgent(const QString &sessionId, const QString &cwd, if (!remote.isEmpty()) agentArgs << "remote=" + remote; if (!agentAlias.isEmpty()) agentArgs << "name=" + agentAlias; - QProcess proc; // Use immutable ID — the 9P namespace resolves it via alias. - proc.start(ninepBin(), {"-a", serverAddr(), "rdwr", "session/" + sessionId + "/agent/new"}); - proc.waitForStarted(3000); - proc.write((agentArgs.join(" ") + "\n").toUtf8()); - proc.closeWriteChannel(); - proc.waitForFinished(5000); - - if (proc.exitCode() != 0) { - QString err = QString::fromUtf8(proc.readAllStandardError()).trimmed(); - if (err.isEmpty()) - err = QString::fromUtf8(proc.readAllStandardOutput()).trimmed(); - if (err.isEmpty()) - err = QStringLiteral("agent creation failed"); + QString path = "session/" + sessionId + "/agent/new"; + QByteArray result = m_9p->rdwr(path, (agentArgs.join(" ") + "\n").toUtf8()); + QString err = m_9p->lastError(); + if (!err.isEmpty()) { qDebug() << "createAgent failed:" << err; return err; } @@ -674,87 +643,47 @@ QString Ollie9pClient::createAgent(const QString &sessionId, const QString &cwd, bool Ollie9pClient::killAgent(const QString &sessionId, const QString &agentId) { if (sessionId.isEmpty() || agentId.isEmpty()) return false; + if (!m_9p || !m_9p->isConnected()) return false; // Use immutable IDs — the 9P namespace resolves them via aliases. QString path = "session/" + sessionId + "/agent/" + agentId + "/ctl"; - QProcess proc; - proc.start(ninepBin(), {"-a", serverAddr(), "write", path}); - proc.waitForStarted(3000); - proc.write("kill"); - proc.closeWriteChannel(); - proc.waitForFinished(3000); - return proc.exitCode() == 0; + return m_9p->write(path, "kill"); } bool Ollie9pClient::renameSession(const QString &sessionId, const QString &newName) { if (sessionId.isEmpty() || newName.isEmpty()) return false; - - QString bin = ollie9pBin(); - if (bin.isEmpty()) { - qDebug() << "renameSession: ollie-9p not found, cannot rename"; - return false; - } + if (!m_9p || !m_9p->isConnected()) return false; // Session names are mutable display names. Writing the name file triggers // the rename. Use the immutable ID to address the session. - QProcess proc; - proc.setProgram(bin); - proc.setArguments({"-a", serverAddr(), "write", "session/" + sessionId + "/name"}); - proc.start(); - if (!proc.waitForStarted(3000)) { - qDebug() << "renameSession failed to start:" << proc.errorString(); - return false; + QString path = "session/" + sessionId + "/name"; + if (m_9p->write(path, (newName + "\n").toUtf8())) { + // With immutable-ID paths, renaming no longer invalidates streams or + // the active selection. Just refresh the session list to pick up the + // new display name. + refreshSessions(); + return true; } - proc.write((newName + "\n").toUtf8()); - proc.closeWriteChannel(); - proc.waitForFinished(5000); - - if (proc.exitCode() != 0) { - qDebug() << "renameSession failed:" << QString::fromUtf8(proc.readAllStandardError()).trimmed(); - return false; - } - - // With immutable-ID paths, renaming no longer invalidates streams or - // the active selection. Just refresh the session list to pick up the - // new display name. - refreshSessions(); - return true; + qDebug() << "renameSession failed:" << m_9p->lastError(); + return false; } bool Ollie9pClient::renameAgent(const QString &sessionId, const QString &agentId, const QString &newName) { if (sessionId.isEmpty() || agentId.isEmpty() || newName.isEmpty()) return false; - - QString bin = ollie9pBin(); - if (bin.isEmpty()) { - qDebug() << "renameAgent: ollie-9p not found, cannot rename"; - return false; - } + if (!m_9p || !m_9p->isConnected()) return false; // Agent names are mutable display names. Write the name file directly // using immutable IDs to address the path. - QProcess proc; - proc.setProgram(bin); - proc.setArguments({"-a", serverAddr(), "write", - "session/" + sessionId + "/agent/" + agentId + "/name"}); - proc.start(); - if (!proc.waitForStarted(3000)) { - qDebug() << "renameAgent failed to start:" << proc.errorString(); - return false; + QString path = "session/" + sessionId + "/agent/" + agentId + "/name"; + if (m_9p->write(path, (newName + "\n").toUtf8())) { + // With immutable-ID paths, renaming no longer invalidates streams. + refreshSessions(); + return true; } - proc.write((newName + "\n").toUtf8()); - proc.closeWriteChannel(); - proc.waitForFinished(5000); - - if (proc.exitCode() != 0) { - qDebug() << "renameAgent failed:" << QString::fromUtf8(proc.readAllStandardError()).trimmed(); - return false; - } - - // With immutable-ID paths, renaming no longer invalidates streams. - refreshSessions(); - return true; + qDebug() << "renameAgent failed:" << m_9p->lastError(); + return false; } // --- Root data loading (lazy) ---