#include "ollie_runner.h" #include #include #include #include #include #include #include #include #include K_PLUGIN_CLASS_WITH_JSON(OllieRunner, "metadata.json") static QString ollie9pBin() { return QStandardPaths::findExecutable("ollie-9p"); } static QString ollieIconPath() { QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "icons/hicolor/scalable/apps/ollie.svg"); if (path.isEmpty()) return QStringLiteral("ollie"); return path; } #if QT_VERSION_MAJOR >= 6 OllieRunner::OllieRunner(QObject *parent, const KPluginMetaData &data) : AbstractRunner(parent, data) #else OllieRunner::OllieRunner(QObject *parent, const KPluginMetaData &data, const QVariantList &args) : AbstractRunner(parent, data, args) #endif { } QString OllieRunner::serverAddr() { QString ns = qEnvironmentVariable("NAMESPACE"); if (ns.isEmpty()) { QString user = qEnvironmentVariable("USER"); QString display = qEnvironmentVariable("DISPLAY"); ns = "/tmp/ns." + user + "." + display; } return "unix!" + ns + "/ollie"; } QByteArray OllieRunner::run9p(const QStringList &args) { QProcess proc; QStringList fullArgs = {"-a", serverAddr()}; fullArgs.append(args); proc.start(ollie9pBin(), fullArgs); if (!proc.waitForFinished(5000)) return {}; return proc.readAllStandardOutput(); } void OllieRunner::write9p(const QString &path, const QByteArray &data) { QProcess proc; proc.start(ollie9pBin(), {"-a", serverAddr(), "write", path}); proc.waitForStarted(3000); proc.write(data); proc.closeWriteChannel(); proc.waitForFinished(3000); } QStringList OllieRunner::listSessions() { QByteArray out = run9p({"read", "session/idx"}); QString raw = QString::fromUtf8(out).trimmed(); if (raw.isEmpty()) return {}; return raw.split('\n'); } void OllieRunner::killSession(const QString &id) { write9p("session/" + id + "/ctl", "kill"); } void OllieRunner::match(RunnerNS::RunnerContext &context) { const QString query = context.query().trimmed(); if (!query.startsWith(QLatin1String("ollie ")) && query != QLatin1String("ollie")) return; QString args = query.mid(6).trimmed(); if (args.isEmpty() || args == QLatin1String("list")) { QStringList sessions = listSessions(); if (sessions.isEmpty()) { RunnerNS::QueryMatch match(this); match.setId("ollie-no-sessions"); match.setText("No active sessions"); match.setIconName("dialog-information"); match.setRelevance(0.5); context.addMatch(match); } else { for (const QString &entry : sessions) { QStringList parts = entry.split('\t'); // New format: session-id\tsession-name\tpaused\tconnected\tremote\tcwd if (parts.size() < 4) continue; QString sessionId = parts[0]; QString sessionName = parts[1]; bool paused = (parts[2] == "true"); bool connected = (parts[3] == "true"); QString cwd = parts.value(5); RunnerNS::QueryMatch match(this); match.setId("ollie-session-" + sessionId); QString state = connected ? "●" : "○"; if (paused) state = "◐"; match.setText(state + " " + sessionName); match.setSubtext(cwd); match.setIconName(ollieIconPath()); match.setRelevance(0.7); match.setData(sessionId); context.addMatch(match); } } return; } if (args.startsWith(QLatin1String("kill "))) { QString target = args.mid(5).trimmed(); RunnerNS::QueryMatch match(this); match.setId("ollie-kill-" + target); match.setText("Kill session: " + target); match.setSubtext("Terminate ollie session"); match.setIconName("process-stop"); match.setRelevance(0.8); match.setData("kill:" + target); context.addMatch(match); return; } // Default: one-shot prompt via generate file RunnerNS::QueryMatch match(this); match.setId("ollie-prompt"); match.setText("Ask Ollie: " + args); match.setSubtext("One-shot generation"); match.setIconName(ollieIconPath()); match.setRelevance(0.9); match.setData("prompt:" + args); context.addMatch(match); } void OllieRunner::run(const RunnerNS::RunnerContext &context, const RunnerNS::QueryMatch &match) { Q_UNUSED(context); QString data = match.data().toString(); if (data.startsWith("kill:")) { killSession(data.mid(5)); return; } if (data.startsWith("prompt:")) { QString prompt = data.mid(7); // Use the generate rdwr file for one-shot QProcess proc; proc.start(ollie9pBin(), {"-a", serverAddr(), "rdwr", "generate"}); proc.waitForStarted(3000); proc.write(prompt.toUtf8()); proc.closeWriteChannel(); proc.waitForFinished(30000); QString result = QString::fromUtf8(proc.readAllStandardOutput()).trimmed(); if (result.isEmpty()) result = "(no response)"; // Show popup auto *dialog = new QDialog(nullptr); dialog->setWindowTitle("Ollie — " + prompt.left(40)); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->resize(520, 380); auto *layout = new QVBoxLayout(dialog); auto *textEdit = new QTextEdit(dialog); textEdit->setReadOnly(true); textEdit->setPlainText(result); QFont monoFont("monospace"); monoFont.setPointSize(QApplication::font().pointSize()); textEdit->setFont(monoFont); layout->addWidget(textEdit); auto *buttonLayout = new QHBoxLayout(); buttonLayout->addStretch(); auto *copyBtn = new QPushButton("Copy", dialog); QObject::connect(copyBtn, &QPushButton::clicked, [result]() { QApplication::clipboard()->setText(result); }); buttonLayout->addWidget(copyBtn); auto *closeBtn = new QPushButton("Close", dialog); QObject::connect(closeBtn, &QPushButton::clicked, dialog, &QDialog::close); buttonLayout->addWidget(closeBtn); layout->addLayout(buttonLayout); dialog->show(); dialog->raise(); dialog->activateWindow(); return; } // Session selected — launch GUI QProcess::startDetached("ollie-gui", {}); } #include "ollie_runner.moc"