Something went wrong. Try again.
The agentic engineering control plane for the posthuman future
Something went wrong. Try again.
7.8 kB · 218 lines
C++
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219#include "DevDriver.h"
#include "controllers/PanePool.h"#include "AppModel.h"
#include <QDateTime>#include <QFile>#include <QHostAddress>#include <QImage>#include <QJsonDocument>#include <QJsonObject>#include <QJsonParseError>#include <QJsonValue>#include <QQmlContext>#include <QQmlExpression>#include <QQuickWindow>#include <QTcpServer>#include <QTcpSocket>#include <QtGlobal>
// Localhost eval server; see DevDriver.h for the protocol.
namespace {
constexpr quint16 kDefaultPort = 45773;
// Writes one newline-delimited JSON response line.void writeResponse(QTcpSocket* socket, const QJsonValue& id, bool ok, const QJsonValue& value, const QString& error) { if (socket == nullptr) { return; } QJsonObject out; out.insert(QStringLiteral("id"), id); out.insert(QStringLiteral("ok"), ok); if (ok) { out.insert(QStringLiteral("value"), value); } else { out.insert(QStringLiteral("error"), error); } socket->write(QJsonDocument(out).toJson(QJsonDocument::Compact)); socket->write("\n");}
} // namespace
DevDriver::DevDriver(QQmlApplicationEngine* engine, QObject* parent) : QObject(parent), m_engine(engine) { // Resolve the live scene objects and expose them to evaluated code. const QList<QObject*> roots = engine->rootObjects(); auto* rootWindow = qobject_cast<QQuickWindow*>(roots.isEmpty() ? nullptr : roots.first()); QObject* shellObject = nullptr; QObject* devHelpersObject = nullptr; if (rootWindow != nullptr) { shellObject = rootWindow->findChild<QObject*>("xeno-shell"); devHelpersObject = rootWindow->findChild<QObject*>("xeno-dev-helpers"); } engine->rootContext()->setContextProperty(QStringLiteral("ui"), rootWindow); engine->rootContext()->setContextProperty( QStringLiteral("shell"), shellObject); // Helper surface for driving panes / reading screen text from eval scripts. engine->rootContext()->setContextProperty(QStringLiteral("dev"), devHelpersObject); // The model + pool singletons: DevHelpers and eval scripts address the // app state directly (model.workspaces, pool.item(paneId), ...). engine->rootContext()->setContextProperty(QStringLiteral("model"), AppModel::instance()); engine->rootContext()->setContextProperty(QStringLiteral("pool"), PanePool::instance());
// Inert unless explicitly enabled: shipped builds must expose nothing. if (!qEnvironmentVariableIsSet("XENO_DEV_SERVER")) { return; } bool portOk = false; const int parsedPort = qEnvironmentVariableIntValue("XENO_DEV_PORT", &portOk); const quint16 port = (portOk && parsedPort > 0 && parsedPort <= 65535) ? static_cast<quint16>(parsedPort) : kDefaultPort;
connect(&m_server, &QTcpServer::newConnection, this, &DevDriver::acceptConnections); if (!m_server.listen(QHostAddress::LocalHost, port)) { qWarning("dev-driver: listen failed on 127.0.0.1:%u: %s", static_cast<unsigned>(port), m_server.errorString().toUtf8().constData()); return; } qInfo("dev-driver: listening on 127.0.0.1:%d", static_cast<int>(m_server.serverPort()));}
DevDriver::~DevDriver() = default;
void DevDriver::acceptConnections() { while (QTcpSocket* socket = m_server.nextPendingConnection()) { socket->setParent(this); connect(socket, &QTcpSocket::disconnected, socket, &QTcpSocket::deleteLater); connect(socket, &QTcpSocket::disconnected, this, [this, socket] { m_readBuffers.remove(socket); }); connect(socket, &QTcpSocket::readyRead, this, [this, socket] { readRequest(socket); }); }}
void DevDriver::readRequest(QTcpSocket* socket) { QByteArray& buffer = m_readBuffers[socket]; buffer.append(socket->readAll()); while (true) { const qsizetype newline = buffer.indexOf('\n'); if (newline < 0) { break; } const QByteArray line = buffer.left(newline); buffer.remove(0, newline + 1); dispatch(socket, line); }}
void DevDriver::dispatch(QTcpSocket* socket, const QByteArray& line) { if (line.trimmed().isEmpty()) { return; } QJsonParseError parseError{}; const QJsonDocument doc = QJsonDocument::fromJson(line, &parseError); if (!doc.isObject()) { writeResponse(socket, QJsonValue(), false, QJsonValue(), QStringLiteral("bad JSON: %1").arg(parseError.errorString())); return; } const QJsonObject request = doc.object(); const QJsonValue id = request.value(QStringLiteral("id"));
if (request.contains(QLatin1String("ping"))) { writeResponse(socket, id, true, QStringLiteral("pong"), {}); return; }
if (request.contains(QLatin1String("screenshot"))) { auto* window = qobject_cast<QQuickWindow*>( m_engine->rootObjects().isEmpty() ? nullptr : m_engine->rootObjects().first()); if (window == nullptr) { writeResponse(socket, id, false, QJsonValue(), QStringLiteral("no root window to grab")); return; } const QImage image = window->grabWindow(); if (image.isNull()) { writeResponse(socket, id, false, QJsonValue(), QStringLiteral("grabWindow returned an empty image")); return; } const QString path = QStringLiteral("/tmp/xenomorphic-%1.png") .arg(QDateTime::currentMSecsSinceEpoch()); if (!image.save(path, "PNG")) { writeResponse(socket, id, false, QJsonValue(), QStringLiteral("failed to save %1").arg(path)); return; } writeResponse(socket, id, true, path, {}); return; }
const QString filePath = request.value(QStringLiteral("file")).toString(); QString code; if (!filePath.isEmpty()) { QFile file(filePath); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { writeResponse(socket, id, false, QJsonValue(), QStringLiteral("cannot open %1").arg(filePath)); return; } code = QString::fromUtf8(file.readAll()); } else { code = request.value(QStringLiteral("expr")).toString(); } if (code.isEmpty()) { writeResponse(socket, id, false, QJsonValue(), QStringLiteral("need non-empty \"expr\" or \"file\"")); return; }
// Evaluate inside the root object's own context so expressions see the // document's imports (SplitView etc.) and ids; the engine root context // has neither. auto* rootObject = m_engine->rootObjects().isEmpty() ? nullptr : m_engine->rootObjects().first(); QQmlExpression expression( rootObject != nullptr ? qmlContext(rootObject) : m_engine->rootContext(), rootObject, code); const QVariant result = expression.evaluate(); if (expression.hasError()) { const QQmlError err = expression.error(); writeResponse(socket, id, false, QJsonValue(), QStringLiteral("%1 (at %2:%3)") .arg(err.description()) .arg(err.line()) .arg(err.column())); return; } // QQmlExpression::evaluate() returns a QVariant that usually wraps a // QJSValue (the engine's value box). QJsonValue::fromVariant cannot // descend into a QJSValue, so object/array results would degrade to // JSON null. Unwrap with toVariant() so expressions can return real // structured JSON (nested objects and arrays) to the client. QVariant unwrapped = result; if (result.metaType() == QMetaType::fromType<QJSValue>()) { unwrapped = result.value<QJSValue>().toVariant(); } writeResponse(socket, id, true, QJsonValue::fromVariant(unwrapped), {});}