212 lines
6.6 KiB
C++
212 lines
6.6 KiB
C++
#include "ollie_runner.h"
|
|
|
|
#include <KPluginFactory>
|
|
#include <QDialog>
|
|
#include <QVBoxLayout>
|
|
#include <QHBoxLayout>
|
|
#include <QTextEdit>
|
|
#include <QPushButton>
|
|
#include <QClipboard>
|
|
#include <QApplication>
|
|
#include <QStandardPaths>
|
|
|
|
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"
|