/* cst-agent-app: the Qt front end. * * cst-agent-app --workspace DIR [--provider p] [--model m] [--api-key k] * cst-agent-app --selftest embedding checks, no window, no network * * The engine runs in this process (see app/README.md); nothing listens on a * port and no second process is started. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core/AgentCore.h" #include "core/ToolProvider.h" #include "cst_version.h" namespace { int runSelftest() { QTextStream out(stdout); int bad = 0; QTemporaryDir tmp; if (!tmp.isValid()) { out << "selftest: cannot create a scratch dir\n"; return 1; } AgentCore core; QString err; if (!core.open(tmp.path(), QStringLiteral("deepseek"), QStringLiteral("deepseek-flash"), QStringLiteral("test-key"), &err)) { out << "selftest: engine did not start: " << err << "\n"; return 1; } out << "selftest: engine started in-process\n"; if (core.agent() == nullptr) { out << "selftest: no agent handle\n"; return 1; } /* the front end's own tools go through the engine's registry */ const int n = app::registerPlatformTools(&core); out << "selftest: platform tools registered: " << n << "\n"; const cst_tool_def_t *t = cst_tool_find("notification_send"); if (!t) { out << "selftest: notification_send is not in the registry\n"; bad++; } else { if (t->owner == nullptr || strncmp(t->owner, "app:", 4) != 0) { out << "selftest: notification_send has no app owner\n"; bad++; } char why[160] = ""; const bool ok = cst_tool_available(t, why, sizeof(why)) == 0; out << "selftest: notification_send available=" << (ok ? "yes" : "no") << (ok ? "" : QStringLiteral(" (%1)").arg(QString::fromUtf8(why))) << "\n"; } /* the mode comes from the engine, not from the UI */ core.setMode(QStringLiteral("general")); if (core.mode() != QLatin1String("general")) { out << "selftest: mode did not stick\n"; bad++; } app::unregisterPlatformTools(&core); if (cst_tool_find("notification_send") != nullptr) { out << "selftest: platform tools were not dropped\n"; bad++; } core.close(); out << (bad ? "selftest: FAILED\n" : "selftest: OK\n"); out.flush(); return bad ? 1 : 0; } } // namespace int main(int argc, char **argv) { /* --selftest stays headless: no window, no QML, no display needed. */ bool selftest = false; for (int i = 1; i < argc; i++) if (strcmp(argv[i], "--selftest") == 0) selftest = true; if (selftest) { QCoreApplication app(argc, argv); return runSelftest(); } QGuiApplication app(argc, argv); QGuiApplication::setApplicationName(QStringLiteral("cst-agent-app")); QGuiApplication::setApplicationVersion(QStringLiteral(CST_VERSION)); QGuiApplication::setOrganizationName(QStringLiteral("cst-agent")); QCommandLineParser parser; parser.setApplicationDescription( QStringLiteral("CST-agent desktop front end (engine in-process)")); parser.addHelpOption(); parser.addVersionOption(); QCommandLineOption wsOpt({QStringLiteral("w"), QStringLiteral("workspace")}, QStringLiteral("workspace directory"), QStringLiteral("dir")); QCommandLineOption providerOpt(QStringLiteral("provider"), QStringLiteral("provider name"), QStringLiteral("name")); QCommandLineOption modelOpt(QStringLiteral("model"), QStringLiteral("model name"), QStringLiteral("name")); QCommandLineOption planOpt(QStringLiteral("plan"), QStringLiteral("start in plan mode")); parser.addOption(wsOpt); parser.addOption(providerOpt); parser.addOption(modelOpt); parser.addOption(planOpt); parser.process(app); QString ws = parser.value(wsOpt); if (ws.isEmpty()) ws = QDir::currentPath(); ws = QFileInfo(ws).absoluteFilePath(); AgentCore core; QString err; if (!core.open(ws, parser.value(providerOpt), parser.value(modelOpt), QString(), &err)) { QTextStream(stderr) << "cannot start the engine: " << err << "\n"; return 1; } app::registerPlatformTools(&core); /* the app's own tools */ if (parser.isSet(planOpt)) core.setPlanMode(true); QQmlApplicationEngine engine; engine.rootContext()->setContextProperty(QStringLiteral("agent"), &core); const QUrl url(QStringLiteral("qrc:/qt/qml/CstAgent/qml/Main.qml")); QObject::connect( &engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); engine.load(url); if (engine.rootObjects().isEmpty()) return 1; return app.exec(); }